Merge branch 'for-next/mtd'
This commit is contained in:
commit
feefc3ef3b
|
@ -30,6 +30,7 @@
|
|||
#include <mach/mci.h>
|
||||
#include <mach/fb.h>
|
||||
#include <mach/ocotp.h>
|
||||
#include <mach/devices.h>
|
||||
#include <spi/spi.h>
|
||||
|
||||
#include <asm/armlinux.h>
|
||||
|
@ -270,8 +271,7 @@ static int mx28_evk_devices_init(void)
|
|||
add_generic_device("imx28-fec", 0, NULL, IMX_FEC0_BASE, 0x4000,
|
||||
IORESOURCE_MEM, &fec_info);
|
||||
|
||||
add_generic_device("mxs_nand", 0, NULL, MXS_GPMI_BASE, 0x2000,
|
||||
IORESOURCE_MEM, NULL);
|
||||
imx28_add_nand();
|
||||
|
||||
spi_register_board_info(mx28evk_spi_board_info,
|
||||
ARRAY_SIZE(mx28evk_spi_board_info));
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <io.h>
|
||||
#include <generated/mach-types.h>
|
||||
#include <mach/imx-regs.h>
|
||||
#include <mach/devices.h>
|
||||
#include <asm/mmu.h>
|
||||
|
||||
/* setup the CPU card internal signals */
|
||||
|
@ -92,8 +93,7 @@ static int tx28_devices_init(void)
|
|||
|
||||
base_board_init();
|
||||
|
||||
add_generic_device("mxs_nand", 0, NULL, MXS_GPMI_BASE, 0x2000,
|
||||
IORESOURCE_MEM, NULL);
|
||||
imx28_add_nand();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -43,8 +43,6 @@ static int nhk8815_nand_init(void)
|
|||
}
|
||||
|
||||
static struct nomadik_nand_platform_data nhk8815_nand_data = {
|
||||
.options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING \
|
||||
| NAND_NO_READRDY | NAND_NO_AUTOINCR,
|
||||
.init = nhk8815_nand_init,
|
||||
};
|
||||
|
||||
|
|
|
@ -303,6 +303,7 @@ static int imx6_ccm_probe(struct device_d *dev)
|
|||
clkdev_add_physbase(clks[ahb], MX6_SATA_BASE_ADDR, NULL);
|
||||
clkdev_add_physbase(clks[usbphy1], MX6_USBPHY1_BASE_ADDR, NULL);
|
||||
clkdev_add_physbase(clks[usbphy2], MX6_USBPHY2_BASE_ADDR, NULL);
|
||||
clkdev_add_physbase(clks[enfc_podf], MX6_GPMI_BASE_ADDR, NULL);
|
||||
|
||||
writel(0xffffffff, ccm_base + CCGR0);
|
||||
writel(0xffffffff, ccm_base + CCGR1);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef __MACH_IMX6_REGS_H
|
||||
#define __MACH_IMX6_REGS_H
|
||||
|
||||
#define MX6_GPMI_BASE_ADDR 0x00112000
|
||||
|
||||
#define MX6_AIPS1_ARB_BASE_ADDR 0x02000000
|
||||
#define MX6_AIPS2_ARB_BASE_ADDR 0x02100000
|
||||
|
||||
|
|
|
@ -24,11 +24,13 @@ choice
|
|||
|
||||
config ARCH_IMX23
|
||||
bool "i.MX23"
|
||||
select STMP_DEVICE
|
||||
select CPU_ARM926T
|
||||
|
||||
config ARCH_IMX28
|
||||
bool "i.MX28"
|
||||
select CPU_ARM926T
|
||||
select STMP_DEVICE
|
||||
select ARCH_HAS_FEC_IMX
|
||||
|
||||
endchoice
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
obj-y += imx.o iomux-imx.o power.o common.o
|
||||
obj-y += imx.o iomux-imx.o power.o
|
||||
obj-$(CONFIG_ARCH_IMX23) += clocksource-imx23.o usb-imx23.o soc-imx23.o
|
||||
obj-$(CONFIG_ARCH_IMX28) += clocksource-imx28.o usb-imx28.o soc-imx28.o
|
||||
obj-$(CONFIG_MXS_OCOTP) += ocotp.o
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <complete.h>
|
||||
#include <init.h>
|
||||
#include <io.h>
|
||||
#include <stmp-device.h>
|
||||
|
||||
#include <mach/generic.h>
|
||||
#include <mach/imx-regs.h>
|
||||
|
@ -39,7 +40,7 @@ static int imx_reset_usb_bootstrap(void)
|
|||
* To prevent this (and boot from the configured bootsource instead)
|
||||
* clear this bit here.
|
||||
*/
|
||||
writel(0x2, IMX_WDT_BASE + HW_RTC_PERSISTENT1 + BIT_CLR);
|
||||
writel(0x2, IMX_WDT_BASE + HW_RTC_PERSISTENT1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef __MACH_MXS_DEVICES_H
|
||||
#define __MACH_MXS_DEVICES_H
|
||||
|
||||
#include <common.h>
|
||||
#include <sizes.h>
|
||||
#include <xfuncs.h>
|
||||
#include <driver.h>
|
||||
#include <mach/imx-regs.h>
|
||||
|
||||
static inline struct device_d *mxs_add_nand(unsigned long gpmi_base, unsigned long bch_base)
|
||||
{
|
||||
struct resource res[] = {
|
||||
{
|
||||
.start = gpmi_base,
|
||||
.end = gpmi_base + SZ_8K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}, {
|
||||
.start = bch_base,
|
||||
.end = bch_base + SZ_8K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
struct device_d *dev = xzalloc(sizeof(*dev));
|
||||
|
||||
dev->resource = xzalloc(sizeof(struct resource) * ARRAY_SIZE(res));
|
||||
memcpy(dev->resource, res, sizeof(struct resource) * ARRAY_SIZE(res));
|
||||
dev->num_resources = ARRAY_SIZE(res);
|
||||
strcpy(dev->name, "mxs_nand");
|
||||
dev->id = DEVICE_ID_DYNAMIC;
|
||||
|
||||
platform_device_register(dev);
|
||||
|
||||
return dev;
|
||||
};
|
||||
|
||||
static inline struct device_d *imx23_add_nand(void)
|
||||
{
|
||||
return mxs_add_nand(MXS_GPMI_BASE, MXS_BCH_BASE);
|
||||
}
|
||||
|
||||
static inline struct device_d *imx28_add_nand(void)
|
||||
{
|
||||
return mxs_add_nand(MXS_GPMI_BASE, MXS_BCH_BASE);
|
||||
}
|
||||
|
||||
#endif /* __MACH_MXS_DEVICES_H */
|
|
@ -16,11 +16,6 @@
|
|||
#ifndef _IMX_REGS_H
|
||||
# define _IMX_REGS_H
|
||||
|
||||
/* Note: Some registers do not support this bit change feature! */
|
||||
#define BIT_SET 0x04
|
||||
#define BIT_CLR 0x08
|
||||
#define BIT_TGL 0x0C
|
||||
|
||||
#if defined CONFIG_ARCH_IMX23
|
||||
# include <mach/imx23-regs.h>
|
||||
#endif
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef __MACH_MXS_H
|
||||
#define __MACH_MXS_H
|
||||
|
||||
int mxs_reset_block(void __iomem *reg, int just_enable);
|
||||
|
||||
#endif /* __MACH_MXS_H */
|
|
@ -18,6 +18,7 @@
|
|||
#include <gpio.h>
|
||||
#include <errno.h>
|
||||
#include <io.h>
|
||||
#include <stmp-device.h>
|
||||
#include <mach/imx-regs.h>
|
||||
|
||||
#define HW_PINCTRL_CTRL 0x000
|
||||
|
@ -112,22 +113,24 @@ void imx_gpio_mode(uint32_t m)
|
|||
reg_offset = calc_strength_reg(gpio_pin);
|
||||
if (GET_VOLTAGE(m) == 1)
|
||||
writel(0x1 << (((gpio_pin % 8) << 2) + 2),
|
||||
IMX_IOMUXC_BASE + reg_offset + BIT_SET);
|
||||
IMX_IOMUXC_BASE + reg_offset + STMP_OFFSET_REG_SET);
|
||||
else
|
||||
writel(0x1 << (((gpio_pin % 8) << 2) + 2),
|
||||
IMX_IOMUXC_BASE + reg_offset + BIT_CLR);
|
||||
IMX_IOMUXC_BASE + reg_offset + STMP_OFFSET_REG_CLR);
|
||||
}
|
||||
|
||||
if (PE_PRESENT(m)) {
|
||||
reg_offset = calc_pullup_reg(gpio_pin);
|
||||
writel(0x1 << (gpio_pin % 32), IMX_IOMUXC_BASE + reg_offset +
|
||||
(GET_PULLUP(m) == 1 ? BIT_SET : BIT_CLR));
|
||||
(GET_PULLUP(m) == 1 ?
|
||||
STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR));
|
||||
}
|
||||
|
||||
if (BK_PRESENT(m)) {
|
||||
reg_offset = calc_pullup_reg(gpio_pin);
|
||||
writel(0x1 << (gpio_pin % 32), IMX_IOMUXC_BASE + reg_offset +
|
||||
(GET_BITKEEPER(m) == 1 ? BIT_CLR : BIT_SET));
|
||||
(GET_BITKEEPER(m) == 1 ?
|
||||
STMP_OFFSET_REG_CLR : STMP_OFFSET_REG_SET));
|
||||
}
|
||||
|
||||
if (GET_FUNC(m) == IS_GPIO) {
|
||||
|
@ -135,16 +138,17 @@ void imx_gpio_mode(uint32_t m)
|
|||
/* first set the output value */
|
||||
reg_offset = calc_output_reg(gpio_pin);
|
||||
writel(0x1 << (gpio_pin % 32), IMX_IOMUXC_BASE +
|
||||
reg_offset + (GET_GPIOVAL(m) == 1 ? BIT_SET : BIT_CLR));
|
||||
reg_offset + (GET_GPIOVAL(m) == 1 ?
|
||||
STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR));
|
||||
/* then the direction */
|
||||
reg_offset = calc_output_enable_reg(gpio_pin);
|
||||
writel(0x1 << (gpio_pin % 32),
|
||||
IMX_IOMUXC_BASE + reg_offset + BIT_SET);
|
||||
IMX_IOMUXC_BASE + reg_offset + STMP_OFFSET_REG_SET);
|
||||
} else {
|
||||
/* then the direction */
|
||||
reg_offset = calc_output_enable_reg(gpio_pin);
|
||||
writel(0x1 << (gpio_pin % 32),
|
||||
IMX_IOMUXC_BASE + reg_offset + BIT_CLR);
|
||||
IMX_IOMUXC_BASE + reg_offset + STMP_OFFSET_REG_CLR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +161,7 @@ int gpio_direction_input(unsigned gpio)
|
|||
return -EINVAL;
|
||||
|
||||
reg_offset = calc_output_enable_reg(gpio);
|
||||
writel(0x1 << (gpio % 32), IMX_IOMUXC_BASE + reg_offset + BIT_CLR);
|
||||
writel(0x1 << (gpio % 32), IMX_IOMUXC_BASE + reg_offset + STMP_OFFSET_REG_CLR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -172,10 +176,10 @@ int gpio_direction_output(unsigned gpio, int val)
|
|||
/* first set the output value... */
|
||||
reg_offset = calc_output_reg(gpio);
|
||||
writel(0x1 << (gpio % 32), IMX_IOMUXC_BASE +
|
||||
reg_offset + (val != 0 ? BIT_SET : BIT_CLR));
|
||||
reg_offset + (val != 0 ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR));
|
||||
/* ...then the direction */
|
||||
reg_offset = calc_output_enable_reg(gpio);
|
||||
writel(0x1 << (gpio % 32), IMX_IOMUXC_BASE + reg_offset + BIT_SET);
|
||||
writel(0x1 << (gpio % 32), IMX_IOMUXC_BASE + reg_offset + STMP_OFFSET_REG_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -186,7 +190,8 @@ void gpio_set_value(unsigned gpio, int val)
|
|||
|
||||
reg_offset = calc_output_reg(gpio);
|
||||
writel(0x1 << (gpio % 32), IMX_IOMUXC_BASE +
|
||||
reg_offset + (val != 0 ? BIT_SET : BIT_CLR));
|
||||
reg_offset + (val != 0 ?
|
||||
STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR));
|
||||
}
|
||||
|
||||
int gpio_get_value(unsigned gpio)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <io.h>
|
||||
#include <stmp-device.h>
|
||||
#include <clock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
|
@ -75,13 +76,13 @@ static ssize_t mxs_ocotp_cdev_read(struct cdev *cdev, void *buf, size_t count,
|
|||
*/
|
||||
|
||||
/* try to clear ERROR bit */
|
||||
writel(OCOTP_CTRL_ERROR, base + OCOTP_CTRL + BIT_CLR);
|
||||
writel(OCOTP_CTRL_ERROR, base + OCOTP_CTRL + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (mxs_ocotp_wait_busy(priv))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* open OCOTP banks for read */
|
||||
writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + BIT_SET);
|
||||
writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + STMP_OFFSET_REG_SET);
|
||||
|
||||
/* approximately wait 32 hclk cycles */
|
||||
udelay(1);
|
||||
|
@ -96,7 +97,7 @@ static ssize_t mxs_ocotp_cdev_read(struct cdev *cdev, void *buf, size_t count,
|
|||
(((i + offset) & 0xfc) << 2) + ((i + offset) & 3));
|
||||
|
||||
/* close banks for power saving */
|
||||
writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + BIT_CLR);
|
||||
writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + STMP_OFFSET_REG_CLR);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@ -139,7 +140,7 @@ static ssize_t mxs_ocotp_cdev_write(struct cdev *cdev, const void *buf, size_t c
|
|||
clk_set_rate(priv->clk, 24000000);
|
||||
imx_set_vddio(2800000);
|
||||
|
||||
writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + BIT_CLR);
|
||||
writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (mxs_ocotp_wait_busy(priv)) {
|
||||
ret = -ETIMEDOUT;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
#include <common.h>
|
||||
#include <io.h>
|
||||
#include <stmp-device.h>
|
||||
#include <errno.h>
|
||||
#include <mach/imx-regs.h>
|
||||
|
||||
|
@ -72,11 +73,11 @@ void imx_power_prepare_usbphy(void)
|
|||
* Set these bits so that we can force the OTG bits high
|
||||
* so the ARC core operates properly
|
||||
*/
|
||||
writel(POWER_CTRL_CLKGATE, POWER_CTRL + BIT_CLR);
|
||||
writel(POWER_CTRL_CLKGATE, POWER_CTRL + STMP_OFFSET_REG_CLR);
|
||||
|
||||
writel(POWER_DEBUG_VBUSVALIDPIOLOCK |
|
||||
POWER_DEBUG_AVALIDPIOLOCK |
|
||||
POWER_DEBUG_BVALIDPIOLOCK, POWER_DEBUG + BIT_SET);
|
||||
POWER_DEBUG_BVALIDPIOLOCK, POWER_DEBUG + STMP_OFFSET_REG_SET);
|
||||
|
||||
reg = readl(POWER_STS);
|
||||
reg |= POWER_STS_BVALID | POWER_STS_AVALID | POWER_STS_VBUSVALID;
|
||||
|
|
|
@ -38,7 +38,7 @@ EXPORT_SYMBOL(reset_cpu);
|
|||
|
||||
static int imx23_devices_init(void)
|
||||
{
|
||||
|
||||
add_generic_device("imx23-dma-apbh", 0, NULL, MXS_APBH_BASE, 0x2000, IORESOURCE_MEM, NULL);
|
||||
add_generic_device("imx23-clkctrl", 0, NULL, IMX_CCM_BASE, 0x100, IORESOURCE_MEM, NULL);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -56,7 +56,7 @@ postcore_initcall(imx28_init);
|
|||
|
||||
static int imx28_devices_init(void)
|
||||
{
|
||||
|
||||
add_generic_device("imx28-dma-apbh", 0, NULL, MXS_APBH_BASE, 0x2000, IORESOURCE_MEM, NULL);
|
||||
add_generic_device("imx28-clkctrl", 0, NULL, IMX_CCM_BASE, 0x100, IORESOURCE_MEM, NULL);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -401,13 +401,13 @@ config CMD_FLASH
|
|||
|
||||
config CMD_UBI
|
||||
tristate
|
||||
default y if UBI
|
||||
depends on UBI
|
||||
default y if MTD_UBI
|
||||
depends on MTD_UBI
|
||||
prompt "ubimkvol, ubirmvol, ubiattach"
|
||||
|
||||
config CMD_UBIFORMAT
|
||||
tristate
|
||||
depends on UBI
|
||||
depends on MTD_UBI
|
||||
select LIBMTD
|
||||
select LIBSCAN
|
||||
select LIBUBIGEN
|
||||
|
|
|
@ -71,12 +71,17 @@ static int do_ubiattach(int argc, char *argv[])
|
|||
}
|
||||
|
||||
ret = ioctl(fd, MEMGETINFO, &user);
|
||||
if (!ret)
|
||||
ret = ubi_attach_mtd_dev(user.mtd, UBI_DEV_NUM_AUTO, 0);
|
||||
if (ret) {
|
||||
printf("MEMGETINFO failed: %s\n", strerror(-ret));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
ret = ubi_attach_mtd_dev(user.mtd, UBI_DEV_NUM_AUTO, 0, 20);
|
||||
if (ret < 0)
|
||||
printf("failed to attach: %s\n", strerror(-ret));
|
||||
|
||||
else
|
||||
ret = 0;
|
||||
err:
|
||||
close(fd);
|
||||
|
||||
return ret ? 1 : 0;
|
||||
|
@ -92,6 +97,32 @@ BAREBOX_CMD_START(ubiattach)
|
|||
BAREBOX_CMD_HELP(cmd_ubiattach_help)
|
||||
BAREBOX_CMD_END
|
||||
|
||||
static int do_ubidetach(int argc, char *argv[])
|
||||
{
|
||||
int ubi_num, ret;
|
||||
|
||||
if (argc != 2)
|
||||
return COMMAND_ERROR_USAGE;
|
||||
|
||||
ubi_num = simple_strtoul(argv[1], NULL, 0);
|
||||
ret = ubi_detach_mtd_dev(ubi_num, 1);
|
||||
|
||||
if (ret)
|
||||
printf("failed to detach: %s\n", strerror(-ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const __maybe_unused char cmd_ubidetach_help[] =
|
||||
"Usage: ubidetach <ubinum>\n"
|
||||
"Detach <ubinum> from ubi\n";
|
||||
|
||||
BAREBOX_CMD_START(ubidetach)
|
||||
.cmd = do_ubidetach,
|
||||
.usage = "detach an ubi dev",
|
||||
BAREBOX_CMD_HELP(cmd_ubidetach_help)
|
||||
BAREBOX_CMD_END
|
||||
|
||||
static int do_ubirmvol(int argc, char *argv[])
|
||||
{
|
||||
struct ubi_mkvol_req req;
|
||||
|
|
|
@ -2,7 +2,8 @@ menu "DMA support"
|
|||
|
||||
config MXS_APBH_DMA
|
||||
tristate "MXS APBH DMA ENGINE"
|
||||
depends on ARCH_IMX23 || ARCH_IMX28
|
||||
depends on ARCH_IMX23 || ARCH_IMX28 || ARCH_IMX6
|
||||
select STMP_DEVICE
|
||||
help
|
||||
Experimental!
|
||||
endmenu
|
||||
|
|
|
@ -15,17 +15,17 @@
|
|||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <dma/apbh-dma.h>
|
||||
#include <stmp-device.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <common.h>
|
||||
#include <driver.h>
|
||||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
#include <init.h>
|
||||
#include <io.h>
|
||||
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/io.h>
|
||||
#include <mach/clock.h>
|
||||
#include <mach/imx-regs.h>
|
||||
#include <mach/dma.h>
|
||||
#include <mach/mxs.h>
|
||||
|
||||
#define HW_APBHX_CTRL0 0x000
|
||||
#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29)
|
||||
|
@ -37,18 +37,29 @@
|
|||
#define HW_APBHX_CTRL2 0x020
|
||||
#define HW_APBHX_CHANNEL_CTRL 0x030
|
||||
#define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL 16
|
||||
#define HW_APBH_VERSION (cpu_is_mx23() ? 0x3f0 : 0x800)
|
||||
#define HW_APBX_VERSION 0x800
|
||||
#define BP_APBHX_VERSION_MAJOR 24
|
||||
#define HW_APBHX_CHn_NXTCMDAR(n) \
|
||||
((apbh_is_old ? 0x050 : 0x110) + (n) * 0x70)
|
||||
#define HW_APBHX_CHn_SEMA(n) \
|
||||
((apbh_is_old ? 0x080 : 0x140) + (n) * 0x70)
|
||||
#define HW_APBHX_CHn_NXTCMDAR_MX23(n) (0x050 + (n) * 0x70)
|
||||
#define HW_APBHX_CHn_NXTCMDAR_MX28(n) (0x110 + (n) * 0x70)
|
||||
#define HW_APBHX_CHn_SEMA_MX23(n) (0x080 + (n) * 0x70)
|
||||
#define HW_APBHX_CHn_SEMA_MX28(n) (0x140 + (n) * 0x70)
|
||||
#define BM_APBHX_CHn_SEMA_PHORE (0xff << 16)
|
||||
#define BP_APBHX_CHn_SEMA_PHORE 16
|
||||
|
||||
static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS];
|
||||
static bool apbh_is_old;
|
||||
|
||||
enum mxs_dma_id {
|
||||
IMX23_DMA,
|
||||
IMX28_DMA,
|
||||
};
|
||||
|
||||
struct apbh_dma {
|
||||
void __iomem *regs;
|
||||
enum mxs_dma_id id;
|
||||
};
|
||||
|
||||
#define apbh_dma_is_imx23(aphb) ((apbh)->id == IMX23_DMA)
|
||||
|
||||
static struct apbh_dma *apbh_dma;
|
||||
|
||||
/*
|
||||
* Test is the DMA channel is valid channel
|
||||
|
@ -91,7 +102,7 @@ static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc)
|
|||
*/
|
||||
static int mxs_dma_read_semaphore(int channel)
|
||||
{
|
||||
void __iomem *apbh_regs = (void *)MXS_APBH_BASE;
|
||||
struct apbh_dma *apbh = apbh_dma;
|
||||
uint32_t tmp;
|
||||
int ret;
|
||||
|
||||
|
@ -99,7 +110,10 @@ static int mxs_dma_read_semaphore(int channel)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp = readl(apbh_regs + HW_APBHX_CHn_SEMA(channel));
|
||||
if (apbh_dma_is_imx23(apbh))
|
||||
tmp = readl(apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel));
|
||||
else
|
||||
tmp = readl(apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel));
|
||||
|
||||
tmp &= BM_APBHX_CHn_SEMA_PHORE;
|
||||
tmp >>= BP_APBHX_CHn_SEMA_PHORE;
|
||||
|
@ -118,7 +132,7 @@ static int mxs_dma_read_semaphore(int channel)
|
|||
*/
|
||||
static int mxs_dma_enable(int channel)
|
||||
{
|
||||
void __iomem *apbh_regs = (void *)MXS_APBH_BASE;
|
||||
struct apbh_dma *apbh = apbh_dma;
|
||||
unsigned int sem;
|
||||
struct mxs_dma_chan *pchan;
|
||||
struct mxs_dma_desc *pdesc;
|
||||
|
@ -150,22 +164,40 @@ static int mxs_dma_enable(int channel)
|
|||
if (sem == 1) {
|
||||
pdesc = list_entry(pdesc->node.next,
|
||||
struct mxs_dma_desc, node);
|
||||
writel(mxs_dma_cmd_address(pdesc),
|
||||
apbh_regs + HW_APBHX_CHn_NXTCMDAR(channel));
|
||||
if (apbh_dma_is_imx23(apbh))
|
||||
writel(mxs_dma_cmd_address(pdesc),
|
||||
apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(channel));
|
||||
else
|
||||
writel(mxs_dma_cmd_address(pdesc),
|
||||
apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(channel));
|
||||
}
|
||||
writel(pchan->pending_num,
|
||||
apbh_regs + HW_APBHX_CHn_SEMA(channel));
|
||||
|
||||
if (apbh_dma_is_imx23(apbh))
|
||||
writel(pchan->pending_num,
|
||||
apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel));
|
||||
else
|
||||
writel(pchan->pending_num,
|
||||
apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel));
|
||||
|
||||
pchan->active_num += pchan->pending_num;
|
||||
pchan->pending_num = 0;
|
||||
} else {
|
||||
pchan->active_num += pchan->pending_num;
|
||||
pchan->pending_num = 0;
|
||||
writel(mxs_dma_cmd_address(pdesc),
|
||||
apbh_regs + HW_APBHX_CHn_NXTCMDAR(channel));
|
||||
writel(pchan->active_num,
|
||||
apbh_regs + HW_APBHX_CHn_SEMA(channel));
|
||||
channel_bit = channel + (apbh_is_old ? BP_APBH_CTRL0_CLKGATE_CHANNEL : 0);
|
||||
writel(1 << channel_bit, apbh_regs + HW_APBHX_CTRL0 + BIT_CLR);
|
||||
if (apbh_dma_is_imx23(apbh)) {
|
||||
writel(mxs_dma_cmd_address(pdesc),
|
||||
apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(channel));
|
||||
writel(pchan->active_num,
|
||||
apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel));
|
||||
channel_bit = channel + BP_APBH_CTRL0_CLKGATE_CHANNEL;
|
||||
} else {
|
||||
writel(mxs_dma_cmd_address(pdesc),
|
||||
apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(channel));
|
||||
writel(pchan->active_num,
|
||||
apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel));
|
||||
channel_bit = channel;
|
||||
}
|
||||
writel(1 << channel_bit, apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
}
|
||||
|
||||
pchan->flags |= MXS_DMA_FLAGS_BUSY;
|
||||
|
@ -189,7 +221,7 @@ static int mxs_dma_enable(int channel)
|
|||
static int mxs_dma_disable(int channel)
|
||||
{
|
||||
struct mxs_dma_chan *pchan;
|
||||
void __iomem *apbh_regs = (void *)MXS_APBH_BASE;
|
||||
struct apbh_dma *apbh = apbh_dma;
|
||||
int channel_bit, ret;
|
||||
|
||||
ret = mxs_dma_validate_chan(channel);
|
||||
|
@ -201,8 +233,12 @@ static int mxs_dma_disable(int channel)
|
|||
if (!(pchan->flags & MXS_DMA_FLAGS_BUSY))
|
||||
return -EINVAL;
|
||||
|
||||
channel_bit = channel + (apbh_is_old ? BP_APBH_CTRL0_CLKGATE_CHANNEL : 0);
|
||||
writel(1 << channel_bit, apbh_regs + HW_APBHX_CTRL0 + BIT_SET);
|
||||
if (apbh_dma_is_imx23(apbh))
|
||||
channel_bit = channel + BP_APBH_CTRL0_CLKGATE_CHANNEL;
|
||||
else
|
||||
channel_bit = channel + 0;
|
||||
|
||||
writel(1 << channel_bit, apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
pchan->flags &= ~MXS_DMA_FLAGS_BUSY;
|
||||
pchan->active_num = 0;
|
||||
|
@ -217,19 +253,19 @@ static int mxs_dma_disable(int channel)
|
|||
*/
|
||||
static int mxs_dma_reset(int channel)
|
||||
{
|
||||
void __iomem *apbh_regs = (void *)MXS_APBH_BASE;
|
||||
struct apbh_dma *apbh = apbh_dma;
|
||||
int ret;
|
||||
|
||||
ret = mxs_dma_validate_chan(channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (apbh_is_old)
|
||||
if (apbh_dma_is_imx23(apbh))
|
||||
writel(1 << (channel + BP_APBH_CTRL0_RESET_CHANNEL),
|
||||
apbh_regs + HW_APBHX_CTRL0 + BIT_SET);
|
||||
apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
else
|
||||
writel(1 << (channel + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL),
|
||||
apbh_regs + HW_APBHX_CHANNEL_CTRL + BIT_SET);
|
||||
apbh->regs + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -241,7 +277,7 @@ static int mxs_dma_reset(int channel)
|
|||
*/
|
||||
static int mxs_dma_enable_irq(int channel, int enable)
|
||||
{
|
||||
void __iomem *apbh_regs = (void *)MXS_APBH_BASE;
|
||||
struct apbh_dma *apbh = apbh_dma;
|
||||
int ret;
|
||||
|
||||
ret = mxs_dma_validate_chan(channel);
|
||||
|
@ -250,10 +286,10 @@ static int mxs_dma_enable_irq(int channel, int enable)
|
|||
|
||||
if (enable)
|
||||
writel(1 << (channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN),
|
||||
apbh_regs + HW_APBHX_CTRL1 + BIT_SET);
|
||||
apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
else
|
||||
writel(1 << (channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN),
|
||||
apbh_regs + HW_APBHX_CTRL1 + BIT_CLR);
|
||||
apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -266,15 +302,15 @@ static int mxs_dma_enable_irq(int channel, int enable)
|
|||
*/
|
||||
static int mxs_dma_ack_irq(int channel)
|
||||
{
|
||||
void __iomem *apbh_regs = (void *)MXS_APBH_BASE;
|
||||
struct apbh_dma *apbh = apbh_dma;
|
||||
int ret;
|
||||
|
||||
ret = mxs_dma_validate_chan(channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(1 << channel, apbh_regs + HW_APBHX_CTRL1 + BIT_CLR);
|
||||
writel(1 << channel, apbh_regs + HW_APBHX_CTRL2 + BIT_CLR);
|
||||
writel(1 << channel, apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
writel(1 << channel, apbh->regs + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -496,7 +532,7 @@ static int mxs_dma_finish(int channel, struct list_head *head)
|
|||
*/
|
||||
static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
|
||||
{
|
||||
void __iomem *apbh_regs = (void *)MXS_APBH_BASE;
|
||||
struct apbh_dma *apbh = apbh_dma;
|
||||
int ret;
|
||||
|
||||
ret = mxs_dma_validate_chan(chan);
|
||||
|
@ -504,7 +540,7 @@ static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
|
|||
return ret;
|
||||
|
||||
while (--timeout) {
|
||||
if (readl(apbh_regs + HW_APBHX_CTRL1) & (1 << chan))
|
||||
if (readl(apbh->regs + HW_APBHX_CTRL1) & (1 << chan))
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
|
@ -548,31 +584,33 @@ int mxs_dma_go(int chan)
|
|||
/*
|
||||
* Initialize the DMA hardware
|
||||
*/
|
||||
int mxs_dma_init(void)
|
||||
static int apbh_dma_probe(struct device_d *dev)
|
||||
{
|
||||
void __iomem *apbh_regs = (void *)MXS_APBH_BASE;
|
||||
struct apbh_dma *apbh;
|
||||
struct mxs_dma_chan *pchan;
|
||||
enum mxs_dma_id id;
|
||||
int ret, channel;
|
||||
u32 val, reg;
|
||||
|
||||
ret = mxs_reset_block(apbh_regs, 0);
|
||||
ret = dev_get_drvdata(dev, (unsigned long *)&id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* HACK: Get CPUID and determine APBH version */
|
||||
val = readl(0x8001c310) >> 16;
|
||||
if (val == 0x2800)
|
||||
reg = MXS_APBH_BASE + 0x0800;
|
||||
else
|
||||
reg = MXS_APBH_BASE + 0x03f0;
|
||||
apbh_dma = apbh = xzalloc(sizeof(*apbh));
|
||||
apbh->regs = dev_request_mem_region(dev, 0);
|
||||
if (!apbh->regs)
|
||||
return -EBUSY;
|
||||
|
||||
apbh_is_old = (readl((void *)reg) >> 24) < 3;
|
||||
apbh->id = id;
|
||||
|
||||
ret = stmp_reset_block(apbh->regs, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(BM_APBH_CTRL0_APB_BURST8_EN,
|
||||
apbh_regs + HW_APBHX_CTRL0 + BIT_SET);
|
||||
apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
writel(BM_APBH_CTRL0_APB_BURST_EN,
|
||||
apbh_regs + HW_APBHX_CTRL0 + BIT_SET);
|
||||
apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
for (channel = 0; channel < MXS_MAX_DMA_CHANNELS; channel++) {
|
||||
pchan = mxs_dma_channels + channel;
|
||||
|
@ -598,3 +636,33 @@ err:
|
|||
mxs_dma_release(channel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_device_id apbh_ids[] = {
|
||||
{
|
||||
.name = "imx23-dma-apbh",
|
||||
.driver_data = (unsigned long)IMX23_DMA,
|
||||
}, {
|
||||
.name = "imx28-dma-apbh",
|
||||
.driver_data = (unsigned long)IMX28_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static __maybe_unused struct of_device_id apbh_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx23-dma-apbh",
|
||||
.data = (unsigned long)IMX23_DMA,
|
||||
}, {
|
||||
.compatible = "fsl,imx28-dma-apbh",
|
||||
.data = (unsigned long)IMX28_DMA,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
|
||||
static struct driver_d apbh_dma_driver = {
|
||||
.name = "dma-apbh",
|
||||
.id_table = apbh_ids,
|
||||
.of_compatible = DRV_OF_COMPAT(apbh_dt_ids),
|
||||
.probe = apbh_dma_probe,
|
||||
};
|
||||
coredevice_platform_driver(apbh_dma_driver);
|
||||
|
|
|
@ -36,10 +36,10 @@
|
|||
#include <errno.h>
|
||||
#include <clock.h>
|
||||
#include <io.h>
|
||||
#include <stmp-device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <asm/bitops.h>
|
||||
#include <mach/mxs.h>
|
||||
#include <mach/imx-regs.h>
|
||||
#include <mach/mci.h>
|
||||
#include <mach/clock.h>
|
||||
|
@ -457,7 +457,7 @@ static int mxs_mci_initialize(struct mci_host *host, struct device_d *mci_dev)
|
|||
writel(SSP_CTRL0_CLKGATE, mxs_mci->regs + HW_SSP_CTRL0 + 8);
|
||||
|
||||
/* reset the unit */
|
||||
mxs_reset_block(mxs_mci->regs + HW_SSP_CTRL0, 0);
|
||||
stmp_reset_block(mxs_mci->regs + HW_SSP_CTRL0, 0);
|
||||
|
||||
/* restore the last settings */
|
||||
mxs_mci_setup_timeout(mxs_mci, 0xffff);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
obj-$(CONFIG_NAND) += nand/
|
||||
obj-$(CONFIG_DRIVER_CFI) += nor/
|
||||
obj-$(CONFIG_UBI) += ubi/
|
||||
obj-$(CONFIG_MTD_UBI) += ubi/
|
||||
obj-y += devices/
|
||||
obj-$(CONFIG_PARTITION_NEED_MTD) += partition.o
|
||||
obj-$(CONFIG_MTD) += core.o
|
||||
|
|
|
@ -327,6 +327,27 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|||
return mtd->erase(mtd, instr);
|
||||
}
|
||||
|
||||
int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
|
||||
{
|
||||
int ret_code;
|
||||
|
||||
ops->retlen = ops->oobretlen = 0;
|
||||
if (!mtd->read_oob)
|
||||
return -EOPNOTSUPP;
|
||||
/*
|
||||
* In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
|
||||
* similar to mtd->_read(), returning a non-negative integer
|
||||
* representing max bitflips. In other cases, mtd->_read_oob() may
|
||||
* return -EUCLEAN. In all cases, perform similar logic to mtd_read().
|
||||
*/
|
||||
ret_code = mtd->read_oob(mtd, from, ops);
|
||||
if (unlikely(ret_code < 0))
|
||||
return ret_code;
|
||||
if (mtd->ecc_strength == 0)
|
||||
return 0; /* device lacks ecc */
|
||||
return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
|
||||
}
|
||||
|
||||
static struct file_operations mtd_ops = {
|
||||
.read = mtd_op_read,
|
||||
#ifdef CONFIG_MTD_WRITE
|
||||
|
|
|
@ -468,7 +468,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
else
|
||||
ooblen = 0;
|
||||
|
||||
if (oobbuf && ops->mode == MTD_OOB_PLACE)
|
||||
if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB)
|
||||
oobbuf += ops->ooboffs;
|
||||
|
||||
doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n",
|
||||
|
@ -537,7 +537,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) &&
|
||||
(eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) &&
|
||||
(eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) &&
|
||||
(ops->mode != MTD_OOB_RAW) &&
|
||||
(ops->mode != MTD_OPS_RAW) &&
|
||||
(nbdata == DOC_LAYOUT_PAGE_SIZE)) {
|
||||
ret = doc_ecc_bch_fix_data(docg3, buf, hwecc);
|
||||
if (ret < 0) {
|
||||
|
@ -577,7 +577,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
memset(&ops, 0, sizeof(ops));
|
||||
ops.datbuf = buf;
|
||||
ops.len = len;
|
||||
ops.mode = MTD_OOB_AUTO;
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
|
||||
ret = doc_read_oob(mtd, from, &ops);
|
||||
*retlen = ops.retlen;
|
||||
|
@ -631,11 +631,11 @@ static int doc_guess_autoecc(struct mtd_oob_ops *ops)
|
|||
int autoecc;
|
||||
|
||||
switch (ops->mode) {
|
||||
case MTD_OOB_PLACE:
|
||||
case MTD_OOB_AUTO:
|
||||
case MTD_OPS_PLACE_OOB:
|
||||
case MTD_OPS_AUTO_OOB:
|
||||
autoecc = 1;
|
||||
break;
|
||||
case MTD_OOB_RAW:
|
||||
case MTD_OPS_RAW:
|
||||
autoecc = 0;
|
||||
break;
|
||||
default:
|
||||
|
@ -663,7 +663,7 @@ static int doc_backup_oob(struct docg3 *docg3, loff_t to,
|
|||
|
||||
docg3->oob_write_ofs = to;
|
||||
docg3->oob_autoecc = autoecc;
|
||||
if (ops->mode == MTD_OOB_AUTO) {
|
||||
if (ops->mode == MTD_OPS_AUTO_OOB) {
|
||||
doc_fill_autooob(docg3->oob_write_buf, ops->oobbuf);
|
||||
ops->oobretlen = 8;
|
||||
} else {
|
||||
|
@ -960,17 +960,17 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
|||
else
|
||||
ooblen = 0;
|
||||
|
||||
if (oobbuf && ops->mode == MTD_OOB_PLACE)
|
||||
if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB)
|
||||
oobbuf += ops->ooboffs;
|
||||
|
||||
doc_dbg("doc_write_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n",
|
||||
ofs, ops->mode, buf, len, oobbuf, ooblen);
|
||||
switch (ops->mode) {
|
||||
case MTD_OOB_PLACE:
|
||||
case MTD_OOB_RAW:
|
||||
case MTD_OPS_PLACE_OOB:
|
||||
case MTD_OPS_RAW:
|
||||
oobdelta = mtd->oobsize;
|
||||
break;
|
||||
case MTD_OOB_AUTO:
|
||||
case MTD_OPS_AUTO_OOB:
|
||||
oobdelta = mtd->ecclayout->oobavail;
|
||||
break;
|
||||
default:
|
||||
|
@ -1005,7 +1005,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
|||
memset(oob, 0, sizeof(oob));
|
||||
if (ofs == docg3->oob_write_ofs)
|
||||
memcpy(oob, docg3->oob_write_buf, DOC_LAYOUT_OOB_SIZE);
|
||||
else if (ooblen > 0 && ops->mode == MTD_OOB_AUTO)
|
||||
else if (ooblen > 0 && ops->mode == MTD_OPS_AUTO_OOB)
|
||||
doc_fill_autooob(oob, oobbuf);
|
||||
else if (ooblen > 0)
|
||||
memcpy(oob, oobbuf, DOC_LAYOUT_OOB_SIZE);
|
||||
|
@ -1036,7 +1036,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
doc_dbg("doc_write(to=%lld, len=%zu)\n", to, len);
|
||||
ops.datbuf = (char *)buf;
|
||||
ops.len = len;
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
ops.oobbuf = NULL;
|
||||
ops.ooblen = 0;
|
||||
ops.ooboffs = 0;
|
||||
|
|
|
@ -37,7 +37,7 @@ static struct mtd_info *to_mtd(struct cdev *cdev)
|
|||
return mtdoob->mtd;
|
||||
}
|
||||
|
||||
static ssize_t mtd_read_oob(struct cdev *cdev, void *buf, size_t count,
|
||||
static ssize_t mtd_op_read_oob(struct cdev *cdev, void *buf, size_t count,
|
||||
loff_t _offset, ulong flags)
|
||||
{
|
||||
struct mtd_info *mtd = to_mtd(cdev);
|
||||
|
@ -48,7 +48,7 @@ static ssize_t mtd_read_oob(struct cdev *cdev, void *buf, size_t count,
|
|||
if (count < mtd->oobsize)
|
||||
return -EINVAL;
|
||||
|
||||
ops.mode = MTD_OOB_RAW;
|
||||
ops.mode = MTD_OPS_RAW;
|
||||
ops.ooboffs = 0;
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ops.oobbuf = buf;
|
||||
|
@ -56,7 +56,7 @@ static ssize_t mtd_read_oob(struct cdev *cdev, void *buf, size_t count,
|
|||
ops.len = mtd->oobsize;
|
||||
|
||||
offset /= mtd->oobsize;
|
||||
ret = mtd->read_oob(mtd, offset * mtd->writesize, &ops);
|
||||
ret = mtd_read_oob(mtd, offset * mtd->writesize, &ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -64,7 +64,7 @@ static ssize_t mtd_read_oob(struct cdev *cdev, void *buf, size_t count,
|
|||
}
|
||||
|
||||
static struct file_operations mtd_ops_oob = {
|
||||
.read = mtd_read_oob,
|
||||
.read = mtd_op_read_oob,
|
||||
.ioctl = mtd_ioctl,
|
||||
.lseek = dev_lseek_default,
|
||||
};
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
* - no actual mtd->write if done
|
||||
* A second write of 512 bytes triggers:
|
||||
* - copy of the 16 first bytes into writebuf
|
||||
* - a mtd->write_oob() from writebuf
|
||||
* - a mtd_write_oob() from writebuf
|
||||
* - empty writebuf
|
||||
* - copy the remaining 496 bytes into writebuf
|
||||
* => write_fill = 496, write_ofs = offset + 528
|
||||
|
@ -97,13 +97,13 @@ static ssize_t mtdraw_read_unaligned(struct mtd_info *mtd, void *dst,
|
|||
tmp = malloc(mtd->writesize + mtd->oobsize);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
ops.mode = MTD_OOB_RAW;
|
||||
ops.mode = MTD_OPS_RAW;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = tmp;
|
||||
ops.len = mtd->writesize;
|
||||
ops.oobbuf = tmp + mtd->writesize;
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ret = mtd->read_oob(mtd, offset, &ops);
|
||||
ret = mtd_read_oob(mtd, offset, &ops);
|
||||
if (ret)
|
||||
goto err;
|
||||
if (partial)
|
||||
|
@ -152,13 +152,13 @@ static ssize_t mtdraw_blkwrite(struct mtd_info *mtd, const void *buf,
|
|||
struct mtd_oob_ops ops;
|
||||
int ret;
|
||||
|
||||
ops.mode = MTD_OOB_RAW;
|
||||
ops.mode = MTD_OPS_RAW;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = (void *)buf;
|
||||
ops.len = mtd->writesize;
|
||||
ops.oobbuf = (void *)buf + mtd->writesize;
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ret = mtd->write_oob(mtd, offset, &ops);
|
||||
ret = mtd_write_oob(mtd, offset, &ops);
|
||||
if (!ret)
|
||||
ret = ops.retlen + ops.oobretlen;
|
||||
return ret;
|
||||
|
|
|
@ -13,11 +13,20 @@ config NAND_ECC_SOFT
|
|||
default y
|
||||
prompt "Support software ecc"
|
||||
|
||||
config NAND_ECC_BCH
|
||||
select BCH
|
||||
bool
|
||||
prompt "Support software BCH ecc"
|
||||
|
||||
config NAND_ECC_HW
|
||||
bool
|
||||
default y
|
||||
prompt "Support hardware ecc"
|
||||
|
||||
config NAND_ECC_HW_OOB_FIRST
|
||||
bool
|
||||
prompt "Support hardware ecc (oob first)"
|
||||
|
||||
config NAND_ECC_HW_SYNDROME
|
||||
bool
|
||||
default y
|
||||
|
@ -64,13 +73,14 @@ config NAND_IMX
|
|||
|
||||
config NAND_IMX_BBM
|
||||
bool
|
||||
prompt "i.MX NAND flash bbt creation command"
|
||||
depends on NAND_BBT
|
||||
depends on NAND_IMX
|
||||
prompt "i.MX NAND flash bbt creation command"
|
||||
|
||||
config NAND_MXS
|
||||
bool
|
||||
select NAND_BBT
|
||||
prompt "i.MX23/28 NAND driver"
|
||||
prompt "i.MX23/28/6 NAND driver"
|
||||
depends on MXS_APBH_DMA
|
||||
|
||||
config NAND_OMAP_GPMC
|
||||
|
@ -100,15 +110,6 @@ config NAND_S3C24XX
|
|||
help
|
||||
Add support for processor's NAND device controller.
|
||||
|
||||
config MTD_NAND_VERIFY_WRITE
|
||||
bool "Verify NAND page writes"
|
||||
help
|
||||
This adds an extra check when data is written to the flash. The
|
||||
NAND flash device internally checks only bits transitioning
|
||||
from 1 to 0. There is a rare possibility that even though the
|
||||
device thinks the write was successful, a bit could have been
|
||||
flipped accidentally due to device wear or something else.
|
||||
|
||||
config MTD_NAND_ECC_SMC
|
||||
bool "NAND ECC Smart Media byte order"
|
||||
default n
|
||||
|
@ -116,14 +117,6 @@ config MTD_NAND_ECC_SMC
|
|||
Software ECC according to the Smart Media Specification.
|
||||
The original Linux implementation had byte 0 and 1 swapped.
|
||||
|
||||
config MTD_NAND_MUSEUM_IDS
|
||||
bool "Enable chip ids for obsolete ancient NAND devices"
|
||||
default n
|
||||
help
|
||||
Enable this option only when your board has first generation
|
||||
NAND chips (page size 256 byte, erase size 4-8KiB). The IDs
|
||||
of these chips were reused by later, larger chips.
|
||||
|
||||
config MTD_NAND_IDS
|
||||
tristate
|
||||
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
|
||||
# Generic NAND options
|
||||
obj-$(CONFIG_NAND) += nand_ecc.o
|
||||
obj-$(CONFIG_MTD_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_NAND_ECC_BCH) += nand_bch.o
|
||||
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
|
||||
obj-$(CONFIG_NAND) += nand_base.o nand-bb.o
|
||||
obj-$(CONFIG_NAND_BBT) += nand_bbt.o
|
||||
|
|
|
@ -624,7 +624,7 @@ normal_check:
|
|||
}
|
||||
|
||||
static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf)
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct atmel_nand_host *host = chip->priv;
|
||||
int eccsize = chip->ecc.size;
|
||||
|
@ -659,8 +659,9 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_nand_pmecc_write_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf)
|
||||
static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf,
|
||||
int oob_required)
|
||||
{
|
||||
struct atmel_nand_host *host = chip->priv;
|
||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||
|
@ -681,7 +682,7 @@ static void atmel_nand_pmecc_write_page(struct mtd_info *mtd,
|
|||
!(pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY));
|
||||
if (ret) {
|
||||
dev_err(host->dev, "PMECC: Timeout to get ECC value.\n");
|
||||
return;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
for (i = 0; i < host->pmecc_sector_number; i++) {
|
||||
|
@ -694,6 +695,8 @@ static void atmel_nand_pmecc_write_page(struct mtd_info *mtd,
|
|||
}
|
||||
}
|
||||
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_pmecc_core_init(struct mtd_info *mtd)
|
||||
|
@ -881,7 +884,7 @@ static int atmel_nand_calculate(struct mtd_info *mtd,
|
|||
* buf: buffer to store read data
|
||||
*/
|
||||
static int atmel_nand_read_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf)
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -1201,7 +1204,7 @@ static int __init atmel_nand_probe(struct device_d *dev)
|
|||
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(mtd, 1)) {
|
||||
if (nand_scan_ident(mtd, 1, NULL)) {
|
||||
res = -ENXIO;
|
||||
goto err_scan_ident;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ 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);
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, 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);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* This file provides ECC correction for more than 1 bit per block of data,
|
||||
* using binary BCH codes. It relies on the generic BCH library lib/bch.c.
|
||||
*
|
||||
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
|
||||
*
|
||||
* This file 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 or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This file 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 file; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/bch.h>
|
||||
|
||||
/**
|
||||
* struct nand_bch_control - private NAND BCH control structure
|
||||
* @bch: BCH control structure
|
||||
* @ecclayout: private ecc layout for this BCH configuration
|
||||
* @errloc: error location array
|
||||
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
|
||||
*/
|
||||
struct nand_bch_control {
|
||||
struct bch_control *bch;
|
||||
struct nand_ecclayout ecclayout;
|
||||
unsigned int *errloc;
|
||||
unsigned char *eccmask;
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
|
||||
* @mtd: MTD block structure
|
||||
* @buf: input buffer with raw data
|
||||
* @code: output buffer with ECC
|
||||
*/
|
||||
int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
|
||||
unsigned char *code)
|
||||
{
|
||||
const struct nand_chip *chip = mtd->priv;
|
||||
struct nand_bch_control *nbc = chip->ecc.priv;
|
||||
unsigned int i;
|
||||
|
||||
memset(code, 0, chip->ecc.bytes);
|
||||
encode_bch(nbc->bch, buf, chip->ecc.size, code);
|
||||
|
||||
/* apply mask so that an erased page is a valid codeword */
|
||||
for (i = 0; i < chip->ecc.bytes; i++)
|
||||
code[i] ^= nbc->eccmask[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_calculate_ecc);
|
||||
|
||||
/**
|
||||
* nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
|
||||
* @mtd: MTD block structure
|
||||
* @buf: raw data read from the chip
|
||||
* @read_ecc: ECC from the chip
|
||||
* @calc_ecc: the ECC calculated from raw data
|
||||
*
|
||||
* Detect and correct bit errors for a data byte block
|
||||
*/
|
||||
int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
{
|
||||
const struct nand_chip *chip = mtd->priv;
|
||||
struct nand_bch_control *nbc = chip->ecc.priv;
|
||||
unsigned int *errloc = nbc->errloc;
|
||||
int i, count;
|
||||
|
||||
count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
|
||||
NULL, errloc);
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
if (errloc[i] < (chip->ecc.size*8))
|
||||
/* error is located in data, correct it */
|
||||
buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
|
||||
/* else error in ecc, no action needed */
|
||||
|
||||
pr_debug("%s: corrected bitflip %u\n", __func__,
|
||||
errloc[i]);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
printk(KERN_ERR "ecc unrecoverable error\n");
|
||||
count = -1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_correct_data);
|
||||
|
||||
/**
|
||||
* nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
|
||||
* @mtd: MTD block structure
|
||||
* @eccsize: ecc block size in bytes
|
||||
* @eccbytes: ecc length in bytes
|
||||
* @ecclayout: output default layout
|
||||
*
|
||||
* Returns:
|
||||
* a pointer to a new NAND BCH control structure, or NULL upon failure
|
||||
*
|
||||
* Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
|
||||
* are used to compute BCH parameters m (Galois field order) and t (error
|
||||
* correction capability). @eccbytes should be equal to the number of bytes
|
||||
* required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
|
||||
*
|
||||
* Example: to configure 4 bit correction per 512 bytes, you should pass
|
||||
* @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
|
||||
* @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
|
||||
*/
|
||||
struct nand_bch_control *
|
||||
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
|
||||
struct nand_ecclayout **ecclayout)
|
||||
{
|
||||
unsigned int m, t, eccsteps, i;
|
||||
struct nand_ecclayout *layout;
|
||||
struct nand_bch_control *nbc = NULL;
|
||||
unsigned char *erased_page;
|
||||
|
||||
if (!eccsize || !eccbytes) {
|
||||
printk(KERN_WARNING "ecc parameters not supplied\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
m = fls(1+8*eccsize);
|
||||
t = (eccbytes*8)/m;
|
||||
|
||||
nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
|
||||
if (!nbc)
|
||||
goto fail;
|
||||
|
||||
nbc->bch = init_bch(m, t, 0);
|
||||
if (!nbc->bch)
|
||||
goto fail;
|
||||
|
||||
/* verify that eccbytes has the expected value */
|
||||
if (nbc->bch->ecc_bytes != eccbytes) {
|
||||
printk(KERN_WARNING "invalid eccbytes %u, should be %u\n",
|
||||
eccbytes, nbc->bch->ecc_bytes);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
eccsteps = mtd->writesize/eccsize;
|
||||
|
||||
/* if no ecc placement scheme was provided, build one */
|
||||
if (!*ecclayout) {
|
||||
|
||||
/* handle large page devices only */
|
||||
if (mtd->oobsize < 64) {
|
||||
printk(KERN_WARNING "must provide an oob scheme for "
|
||||
"oobsize %d\n", mtd->oobsize);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
layout = &nbc->ecclayout;
|
||||
layout->eccbytes = eccsteps*eccbytes;
|
||||
|
||||
/* reserve 2 bytes for bad block marker */
|
||||
if (layout->eccbytes+2 > mtd->oobsize) {
|
||||
printk(KERN_WARNING "no suitable oob scheme available "
|
||||
"for oobsize %d eccbytes %u\n", mtd->oobsize,
|
||||
eccbytes);
|
||||
goto fail;
|
||||
}
|
||||
/* put ecc bytes at oob tail */
|
||||
for (i = 0; i < layout->eccbytes; i++)
|
||||
layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i;
|
||||
|
||||
layout->oobfree[0].offset = 2;
|
||||
layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
|
||||
|
||||
*ecclayout = layout;
|
||||
}
|
||||
|
||||
/* sanity checks */
|
||||
if (8*(eccsize+eccbytes) >= (1 << m)) {
|
||||
printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
|
||||
goto fail;
|
||||
}
|
||||
if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) {
|
||||
printk(KERN_WARNING "invalid ecc layout\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL);
|
||||
nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL);
|
||||
if (!nbc->eccmask || !nbc->errloc)
|
||||
goto fail;
|
||||
/*
|
||||
* compute and store the inverted ecc of an erased ecc block
|
||||
*/
|
||||
erased_page = kmalloc(eccsize, GFP_KERNEL);
|
||||
if (!erased_page)
|
||||
goto fail;
|
||||
|
||||
memset(erased_page, 0xff, eccsize);
|
||||
memset(nbc->eccmask, 0, eccbytes);
|
||||
encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
|
||||
kfree(erased_page);
|
||||
|
||||
for (i = 0; i < eccbytes; i++)
|
||||
nbc->eccmask[i] ^= 0xff;
|
||||
|
||||
return nbc;
|
||||
fail:
|
||||
nand_bch_free(nbc);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_init);
|
||||
|
||||
/**
|
||||
* nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
|
||||
* @nbc: NAND BCH control structure
|
||||
*/
|
||||
void nand_bch_free(struct nand_bch_control *nbc)
|
||||
{
|
||||
if (nbc) {
|
||||
free_bch(nbc->bch);
|
||||
kfree(nbc->errloc);
|
||||
kfree(nbc->eccmask);
|
||||
kfree(nbc);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_free);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
|
||||
MODULE_DESCRIPTION("NAND software BCH ECC support");
|
|
@ -1,103 +0,0 @@
|
|||
#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 <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_MTD_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_MTD_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
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
#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 <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_MTD_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_MTD_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_MTD_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
|
||||
}
|
|
@ -3,184 +3,178 @@
|
|||
*
|
||||
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
|
||||
*
|
||||
* $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $
|
||||
*
|
||||
* 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>
|
||||
#include <sizes.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
#ifdef CONFIG_NAND_INFO
|
||||
#define __NANDSTR(str) str
|
||||
#define __STR(str) str
|
||||
#else
|
||||
#define __NANDSTR(str) ""
|
||||
#define __STR(str) ""
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Chip ID list
|
||||
*
|
||||
* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
|
||||
* options
|
||||
*
|
||||
* Pagesize; 0, 256, 512
|
||||
* 0 get this information from the extended chip ID
|
||||
+ 256 256 Byte page size
|
||||
* 512 512 Byte page size
|
||||
*/
|
||||
struct nand_flash_dev nand_flash_ids[] = {
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
|
||||
{__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
|
||||
|
||||
{__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},
|
||||
|
||||
{__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},
|
||||
|
||||
{__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},
|
||||
|
||||
{__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},
|
||||
|
||||
{__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
|
||||
* erasesize is determined from the extended id bytes
|
||||
*/
|
||||
#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR)
|
||||
#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
|
||||
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
/*512 Megabit */
|
||||
{__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 */
|
||||
{__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 */
|
||||
{__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 */
|
||||
{__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 */
|
||||
{__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 */
|
||||
{__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},
|
||||
|
||||
/* 32 Gigabit */
|
||||
{__NANDSTR("NAND 4GiB 1,8V 8-bit"), 0xA7, 0, 4096, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 4GiB 3,3V 8-bit"), 0xD7, 0, 4096, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 4GiB 1,8V 16-bit"), 0xB7, 0, 4096, 0, LP_OPTIONS16},
|
||||
{__NANDSTR("NAND 4GiB 3,3V 16-bit"), 0xC7, 0, 4096, 0, LP_OPTIONS16},
|
||||
|
||||
/* 64 Gigabit */
|
||||
{__NANDSTR("NAND 8GiB 1,8V 8-bit"), 0xAE, 0, 8192, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 8GiB 3,3V 8-bit"), 0xDE, 0, 8192, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 8GiB 1,8V 16-bit"), 0xBE, 0, 8192, 0, LP_OPTIONS16},
|
||||
{__NANDSTR("NAND 8GiB 3,3V 16-bit"), 0xCE, 0, 8192, 0, LP_OPTIONS16},
|
||||
|
||||
/* 128 Gigabit */
|
||||
{__NANDSTR("NAND 16GiB 1,8V 8-bit"), 0x1A, 0, 16384, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 16GiB 3,3V 8-bit"), 0x3A, 0, 16384, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 16GiB 1,8V 16-bit"), 0x2A, 0, 16384, 0, LP_OPTIONS16},
|
||||
{__NANDSTR("NAND 16GiB 3,3V 16-bit"), 0x4A, 0, 16384, 0, LP_OPTIONS16},
|
||||
|
||||
/* 256 Gigabit */
|
||||
{__NANDSTR("NAND 32GiB 1,8V 8-bit"), 0x1C, 0, 32768, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 32GiB 3,3V 8-bit"), 0x3C, 0, 32768, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 32GiB 1,8V 16-bit"), 0x2C, 0, 32768, 0, LP_OPTIONS16},
|
||||
{__NANDSTR("NAND 32GiB 3,3V 16-bit"), 0x4C, 0, 32768, 0, LP_OPTIONS16},
|
||||
|
||||
/* 512 Gigabit */
|
||||
{__NANDSTR("NAND 64GiB 1,8V 8-bit"), 0x1E, 0, 65536, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 64GiB 3,3V 8-bit"), 0x3E, 0, 65536, 0, LP_OPTIONS},
|
||||
{__NANDSTR("NAND 64GiB 1,8V 16-bit"), 0x2E, 0, 65536, 0, LP_OPTIONS16},
|
||||
{__NANDSTR("NAND 64GiB 3,3V 16-bit"), 0x4E, 0, 65536, 0, LP_OPTIONS16},
|
||||
|
||||
/*
|
||||
* Renesas AND 1 Gigabit. Those chips do not support extended id and
|
||||
* have a strange page/block layout ! The chosen minimum erasesize is
|
||||
* 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page
|
||||
* planes 1 block = 2 pages, but due to plane arrangement the blocks
|
||||
* 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would
|
||||
* increase the eraseblock size so we chose a combined one which can be
|
||||
* erased in one go There are more speed improvements for reads and
|
||||
* writes possible, but not implemented now
|
||||
*/
|
||||
{__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
|
||||
},
|
||||
|
||||
{NULL,}
|
||||
};
|
||||
#define SP_OPTIONS NAND_NEED_READRDY
|
||||
#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
/*
|
||||
* Manufacturer ID list
|
||||
*/
|
||||
* The chip ID list:
|
||||
* name, device ID, page size, chip size in MiB, eraseblock size, options
|
||||
*
|
||||
* If page size and eraseblock size are 0, the sizes are taken from the
|
||||
* extended chip ID.
|
||||
*/
|
||||
struct nand_flash_dev nand_flash_ids[] = {
|
||||
/*
|
||||
* Some incompatible NAND chips share device ID's and so must be
|
||||
* listed by full ID. We list them first so that we can easily identify
|
||||
* the most specific match.
|
||||
*/
|
||||
{__STR("TC58NVG2S0F 4G 3.3V 8-bit"),
|
||||
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
||||
SZ_4K, SZ_512, SZ_256K, 0, 8, 224},
|
||||
{__STR("TC58NVG3S0F 8G 3.3V 8-bit"),
|
||||
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
|
||||
SZ_4K, SZ_1K, SZ_256K, 0, 8, 232},
|
||||
{__STR("TC58NVG5D2 32G 3.3V 8-bit"),
|
||||
{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
|
||||
SZ_8K, SZ_4K, SZ_1M, 0, 8, 640},
|
||||
{__STR("TC58NVG6D2 64G 3.3V 8-bit"),
|
||||
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
|
||||
SZ_8K, SZ_8K, SZ_2M, 0, 8, 640},
|
||||
|
||||
LEGACY_ID_NAND(__STR("NAND 4MiB 5V 8-bit"), 0x6B, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 4MiB 3,3V 8-bit"), 0xE3, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 4MiB 3,3V 8-bit"), 0xE5, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 8MiB 3,3V 8-bit"), 0xD6, 8, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 8MiB 3,3V 8-bit"), 0xE6, 8, SZ_8K, SP_OPTIONS),
|
||||
|
||||
LEGACY_ID_NAND(__STR("NAND 16MiB 1,8V 8-bit"), 0x33, 16, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 16MiB 3,3V 8-bit"), 0x73, 16, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 16MiB 1,8V 16-bit"), 0x43, 16, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND(__STR("NAND 16MiB 3,3V 16-bit"), 0x53, 16, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND(__STR("NAND 32MiB 1,8V 8-bit"), 0x35, 32, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 32MiB 3,3V 8-bit"), 0x75, 32, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 32MiB 1,8V 16-bit"), 0x45, 32, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND(__STR("NAND 32MiB 3,3V 16-bit"), 0x55, 32, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND(__STR("NAND 64MiB 1,8V 8-bit"), 0x36, 64, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 64MiB 3,3V 8-bit"), 0x76, 64, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 64MiB 1,8V 16-bit"), 0x46, 64, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND(__STR("NAND 64MiB 3,3V 16-bit"), 0x56, 64, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND(__STR("NAND 128MiB 1,8V 8-bit"), 0x78, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 128MiB 1,8V 8-bit"), 0x39, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 128MiB 3,3V 8-bit"), 0x79, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND(__STR("NAND 128MiB 1,8V 16-bit"), 0x72, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND(__STR("NAND 128MiB 1,8V 16-bit"), 0x49, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND(__STR("NAND 128MiB 3,3V 16-bit"), 0x74, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND(__STR("NAND 128MiB 3,3V 16-bit"), 0x59, 128, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND(__STR("NAND 256MiB 3,3V 8-bit"), 0x71, 256, SZ_16K, SP_OPTIONS),
|
||||
|
||||
/*
|
||||
* These are the new chips with large page size. Their page size and
|
||||
* eraseblock size are determined from the extended ID bytes.
|
||||
*/
|
||||
|
||||
/* 512 Megabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 64MiB 1,8V 8-bit"), 0xA2, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64MiB 1,8V 8-bit"), 0xA0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 8-bit"), 0xF2, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 8-bit"), 0xD0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 8-bit"), 0xF0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64MiB 1,8V 16-bit"), 0xB2, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64MiB 1,8V 16-bit"), 0xB0, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 16-bit"), 0xC2, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 16-bit"), 0xC0, 64, LP_OPTIONS16),
|
||||
|
||||
/* 1 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 128MiB 1,8V 8-bit"), 0xA1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 128MiB 3,3V 8-bit"), 0xF1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 128MiB 3,3V 8-bit"), 0xD1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 128MiB 1,8V 16-bit"), 0xB1, 128, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 128MiB 3,3V 16-bit"), 0xC1, 128, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 128MiB 1,8V 16-bit"), 0xAD, 128, LP_OPTIONS16),
|
||||
|
||||
/* 2 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 256MiB 1,8V 8-bit"), 0xAA, 256, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 256MiB 3,3V 8-bit"), 0xDA, 256, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 256MiB 1,8V 16-bit"), 0xBA, 256, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 256MiB 3,3V 16-bit"), 0xCA, 256, LP_OPTIONS16),
|
||||
|
||||
/* 4 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 512MiB 1,8V 8-bit"), 0xAC, 512, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 512MiB 3,3V 8-bit"), 0xDC, 512, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 512MiB 1,8V 16-bit"), 0xBC, 512, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 512MiB 3,3V 16-bit"), 0xCC, 512, LP_OPTIONS16),
|
||||
|
||||
/* 8 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 1GiB 1,8V 8-bit"), 0xA3, 1024, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 1GiB 3,3V 8-bit"), 0xD3, 1024, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 1GiB 1,8V 16-bit"), 0xB3, 1024, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 1GiB 3,3V 16-bit"), 0xC3, 1024, LP_OPTIONS16),
|
||||
|
||||
/* 16 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 2GiB 1,8V 8-bit"), 0xA5, 2048, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 2GiB 3,3V 8-bit"), 0xD5, 2048, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 2GiB 1,8V 16-bit"), 0xB5, 2048, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 2GiB 3,3V 16-bit"), 0xC5, 2048, LP_OPTIONS16),
|
||||
|
||||
/* 32 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 4GiB 1,8V 8-bit"), 0xA7, 4096, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 4GiB 3,3V 8-bit"), 0xD7, 4096, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 4GiB 1,8V 16-bit"), 0xB7, 4096, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 4GiB 3,3V 16-bit"), 0xC7, 4096, LP_OPTIONS16),
|
||||
|
||||
/* 64 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 8GiB 1,8V 8-bit"), 0xAE, 8192, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 8GiB 3,3V 8-bit"), 0xDE, 8192, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 8GiB 1,8V 16-bit"), 0xBE, 8192, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 8GiB 3,3V 16-bit"), 0xCE, 8192, LP_OPTIONS16),
|
||||
|
||||
/* 128 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 16GiB 1,8V 8-bit"), 0x1A, 16384, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 16GiB 3,3V 8-bit"), 0x3A, 16384, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 16GiB 1,8V 16-bit"), 0x2A, 16384, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 16GiB 3,3V 16-bit"), 0x4A, 16384, LP_OPTIONS16),
|
||||
|
||||
/* 256 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 32GiB 1,8V 8-bit"), 0x1C, 32768, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 32GiB 3,3V 8-bit"), 0x3C, 32768, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 32GiB 1,8V 16-bit"), 0x2C, 32768, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 32GiB 3,3V 16-bit"), 0x4C, 32768, LP_OPTIONS16),
|
||||
|
||||
/* 512 Gigabit */
|
||||
EXTENDED_ID_NAND(__STR("NAND 64GiB 1,8V 8-bit"), 0x1E, 65536, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64GiB 3,3V 8-bit"), 0x3E, 65536, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64GiB 1,8V 16-bit"), 0x2E, 65536, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND(__STR("NAND 64GiB 3,3V 16-bit"), 0x4E, 65536, LP_OPTIONS16),
|
||||
|
||||
{NULL}
|
||||
};
|
||||
|
||||
/* Manufacturer IDs */
|
||||
struct nand_manufacturers nand_manuf_ids[] = {
|
||||
{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/Spansion")},
|
||||
{NAND_MFR_MACRONIX, __NANDSTR("Macronix")},
|
||||
{NAND_MFR_EON, __NANDSTR("Eon")},
|
||||
{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/Spansion"},
|
||||
{NAND_MFR_MACRONIX, "Macronix"},
|
||||
{NAND_MFR_EON, "Eon"},
|
||||
{0x0, "Unknown"}
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <mach/generic.h>
|
||||
#include <mach/imx-nand.h>
|
||||
#include <io.h>
|
||||
#include <of_mtd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00)
|
||||
|
@ -100,6 +101,10 @@ struct imx_nand_host {
|
|||
int spare_len;
|
||||
int eccsize;
|
||||
|
||||
int hw_ecc;
|
||||
int data_width;
|
||||
int flash_bbt;
|
||||
|
||||
void (*preset)(struct mtd_info *);
|
||||
void (*send_cmd)(struct imx_nand_host *, uint16_t);
|
||||
void (*send_addr)(struct imx_nand_host *, uint16_t);
|
||||
|
@ -681,23 +686,6 @@ static void copy_spare(struct mtd_info *mtd, int bfrom)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is used by the upper layer to verify the data in NAND Flash
|
||||
* with the data in the \b buf.
|
||||
*
|
||||
* @param mtd MTD structure for the NAND Flash
|
||||
* @param buf data to be verified
|
||||
* @param len length of the data to be verified
|
||||
*
|
||||
* @return -EFAULT if error else 0
|
||||
*
|
||||
*/
|
||||
static int
|
||||
imx_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
|
||||
{
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is used by upper layer for select and deselect of the NAND
|
||||
* chip
|
||||
|
@ -1087,6 +1075,31 @@ static struct nand_bbt_descr bbt_mirror_descr = {
|
|||
.pattern = mirror_pattern,
|
||||
};
|
||||
|
||||
static int __init mxcnd_probe_dt(struct imx_nand_host *host)
|
||||
{
|
||||
struct device_node *np = host->dev->device_node;
|
||||
int buswidth;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_OFDEVICE))
|
||||
return 1;
|
||||
|
||||
if (!np)
|
||||
return 1;
|
||||
|
||||
if (of_get_nand_ecc_mode(np) == NAND_ECC_HW)
|
||||
host->hw_ecc = 1;
|
||||
|
||||
host->flash_bbt = of_get_nand_on_flash_bbt(np);
|
||||
|
||||
buswidth = of_get_nand_bus_width(np);
|
||||
if (buswidth < 0)
|
||||
return buswidth;
|
||||
|
||||
host->data_width = buswidth / 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called during the driver binding process.
|
||||
*
|
||||
|
@ -1101,7 +1114,6 @@ static int __init imxnd_probe(struct device_d *dev)
|
|||
{
|
||||
struct nand_chip *this;
|
||||
struct mtd_info *mtd;
|
||||
struct imx_nand_platform_data *pdata = dev->platform_data;
|
||||
struct imx_nand_host *host;
|
||||
struct nand_ecclayout *oob_smallpage, *oob_largepage, *oob_4kpage;
|
||||
int err = 0;
|
||||
|
@ -1112,6 +1124,21 @@ static int __init imxnd_probe(struct device_d *dev)
|
|||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
host->dev = dev;
|
||||
|
||||
err = mxcnd_probe_dt(host);
|
||||
if (err < 0)
|
||||
goto escan;
|
||||
|
||||
if (err > 0) {
|
||||
struct imx_nand_platform_data *pdata;
|
||||
|
||||
pdata = dev->platform_data;
|
||||
host->flash_bbt = pdata->flash_bbt;
|
||||
host->data_width = pdata->width;
|
||||
host->hw_ecc = pdata->hw_ecc;
|
||||
}
|
||||
|
||||
host->data_buf = (uint8_t *)(host + 1);
|
||||
|
||||
if (nfc_is_v1() || nfc_is_v21()) {
|
||||
|
@ -1173,7 +1200,6 @@ static int __init imxnd_probe(struct device_d *dev)
|
|||
goto escan;
|
||||
}
|
||||
|
||||
host->dev = dev;
|
||||
/* structures must be linked */
|
||||
this = &host->nand;
|
||||
mtd = &host->mtd;
|
||||
|
@ -1192,9 +1218,8 @@ static int __init imxnd_probe(struct device_d *dev)
|
|||
this->read_word = imx_nand_read_word;
|
||||
this->write_buf = imx_nand_write_buf;
|
||||
this->read_buf = imx_nand_read_buf;
|
||||
this->verify_buf = imx_nand_verify_buf;
|
||||
|
||||
if (pdata->hw_ecc) {
|
||||
if (host->hw_ecc) {
|
||||
this->ecc.calculate = imx_nand_calculate_ecc;
|
||||
this->ecc.hwctl = imx_nand_enable_hwecc;
|
||||
if (nfc_is_v1())
|
||||
|
@ -1211,13 +1236,13 @@ static int __init imxnd_probe(struct device_d *dev)
|
|||
this->ecc.layout = oob_smallpage;
|
||||
|
||||
/* NAND bus width determines access functions used by upper layer */
|
||||
if (pdata->width == 2) {
|
||||
if (host->data_width == 2) {
|
||||
this->options |= NAND_BUSWIDTH_16;
|
||||
this->ecc.layout = &nandv1_hw_eccoob_smallpage;
|
||||
imx_nand_set_layout(0, 16);
|
||||
}
|
||||
|
||||
if (pdata->flash_bbt) {
|
||||
if (host->flash_bbt) {
|
||||
this->bbt_td = &bbt_main_descr;
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
/* update flash based bbt */
|
||||
|
@ -1225,7 +1250,7 @@ static int __init imxnd_probe(struct device_d *dev)
|
|||
}
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(mtd, 1)) {
|
||||
if (nand_scan_ident(mtd, 1, NULL)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
|
@ -1233,10 +1258,10 @@ static int __init imxnd_probe(struct device_d *dev)
|
|||
/* Call preset again, with correct writesize this time */
|
||||
host->preset(mtd);
|
||||
|
||||
imx_nand_set_layout(mtd->writesize, pdata->width == 2 ? 16 : 8);
|
||||
imx_nand_set_layout(mtd->writesize, host->data_width == 2 ? 16 : 8);
|
||||
|
||||
if (mtd->writesize >= 2048) {
|
||||
if (!pdata->flash_bbt)
|
||||
if (!host->flash_bbt)
|
||||
dev_warn(dev, "2k or 4k flash detected without flash_bbt. "
|
||||
"You will loose factory bad block markers!\n");
|
||||
|
||||
|
@ -1255,13 +1280,16 @@ static int __init imxnd_probe(struct device_d *dev)
|
|||
writew(NFC_V2_SPAS_SPARESIZE(16), host->regs + NFC_V2_SPAS);
|
||||
}
|
||||
|
||||
if (this->ecc.mode == NAND_ECC_HW)
|
||||
this->ecc.strength = host->eccsize;
|
||||
|
||||
/* second phase scan */
|
||||
if (nand_scan_tail(mtd)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
|
||||
if (pdata->flash_bbt && this->bbt_td->pages[0] == -1 && this->bbt_md->pages[0] == -1) {
|
||||
if (host->flash_bbt && this->bbt_td->pages[0] == -1 && this->bbt_md->pages[0] == -1) {
|
||||
dev_warn(dev, "no BBT found. create one using the imx_nand_bbm command\n");
|
||||
} else {
|
||||
bbt_main_descr.options |= NAND_BBT_WRITE | NAND_BBT_CREATE;
|
||||
|
@ -1281,9 +1309,26 @@ escan:
|
|||
|
||||
}
|
||||
|
||||
static __maybe_unused struct of_device_id imx_nand_compatible[] = {
|
||||
{
|
||||
.compatible = "fsl,imx21-nand",
|
||||
}, {
|
||||
.compatible = "fsl,imx25-nand",
|
||||
}, {
|
||||
.compatible = "fsl,imx27-nand",
|
||||
}, {
|
||||
.compatible = "fsl,imx51-nand",
|
||||
}, {
|
||||
.compatible = "fsl,imx53-nand",
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
|
||||
static struct driver_d imx_nand_driver = {
|
||||
.name = "imx_nand",
|
||||
.probe = imxnd_probe,
|
||||
.of_compatible = DRV_OF_COMPAT(imx_nand_compatible),
|
||||
};
|
||||
device_platform_driver(imx_nand_driver);
|
||||
|
||||
|
|
|
@ -23,17 +23,16 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <of_mtd.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
#include <driver.h>
|
||||
#include <init.h>
|
||||
#include <io.h>
|
||||
#include <dma/apbh-dma.h>
|
||||
#include <stmp-device.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/io.h>
|
||||
#include <mach/clock.h>
|
||||
#include <mach/imx-regs.h>
|
||||
#include <mach/dma.h>
|
||||
#include <mach/mxs.h>
|
||||
|
||||
#define MX28_BLOCK_SFTRST (1 << 31)
|
||||
#define MX28_BLOCK_CLKGATE (1 << 30)
|
||||
|
@ -123,12 +122,14 @@
|
|||
#define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16
|
||||
#define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12)
|
||||
#define BCH_FLASHLAYOUT0_ECC0_OFFSET 12
|
||||
#define IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET 11
|
||||
|
||||
#define BCH_FLASH0LAYOUT1 0x00000090
|
||||
#define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16)
|
||||
#define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16
|
||||
#define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12)
|
||||
#define BCH_FLASHLAYOUT1_ECCN_OFFSET 12
|
||||
#define IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET 11
|
||||
|
||||
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
|
||||
|
||||
|
@ -139,11 +140,19 @@
|
|||
|
||||
#define MXS_NAND_BCH_TIMEOUT 10000
|
||||
|
||||
enum gpmi_type {
|
||||
GPMI_MXS,
|
||||
GPMI_IMX6,
|
||||
};
|
||||
|
||||
struct mxs_nand_info {
|
||||
struct nand_chip nand_chip;
|
||||
void __iomem *io_base;
|
||||
void __iomem *bch_base;
|
||||
struct clk *clk;
|
||||
struct mtd_info mtd;
|
||||
enum gpmi_type type;
|
||||
int dma_channel_base;
|
||||
u32 version;
|
||||
|
||||
int cur_chip;
|
||||
|
@ -172,6 +181,11 @@ struct mxs_nand_info {
|
|||
|
||||
struct nand_ecclayout fake_ecc_layout;
|
||||
|
||||
static inline int mxs_nand_is_imx6(struct mxs_nand_info *info)
|
||||
{
|
||||
return info->type == GPMI_IMX6;
|
||||
}
|
||||
|
||||
static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
|
||||
{
|
||||
struct mxs_dma_desc *desc;
|
||||
|
@ -299,9 +313,9 @@ static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
|
|||
/*
|
||||
* Wait for BCH complete IRQ and clear the IRQ
|
||||
*/
|
||||
static int mxs_nand_wait_for_bch_complete(void)
|
||||
static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info)
|
||||
{
|
||||
void __iomem *bch_regs = (void __iomem *)MXS_BCH_BASE;
|
||||
void __iomem *bch_regs = nand_info->bch_base;
|
||||
int timeout = MXS_NAND_BCH_TIMEOUT;
|
||||
int ret;
|
||||
|
||||
|
@ -313,7 +327,7 @@ static int mxs_nand_wait_for_bch_complete(void)
|
|||
|
||||
ret = (timeout == 0) ? -ETIMEDOUT : 0;
|
||||
|
||||
writel(BCH_CTRL_COMPLETE_IRQ, bch_regs + BCH_CTRL + BIT_CLR);
|
||||
writel(BCH_CTRL_COMPLETE_IRQ, bch_regs + BCH_CTRL + STMP_OFFSET_REG_CLR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -333,7 +347,7 @@ static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
|
|||
struct nand_chip *nand = mtd->priv;
|
||||
struct mxs_nand_info *nand_info = nand->priv;
|
||||
struct mxs_dma_desc *d;
|
||||
uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
|
||||
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
|
@ -408,7 +422,7 @@ static int mxs_nand_device_ready(struct mtd_info *mtd)
|
|||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct mxs_nand_info *nand_info = chip->priv;
|
||||
void __iomem *gpmi_regs = (void *)MXS_GPMI_BASE;
|
||||
void __iomem *gpmi_regs = nand_info->io_base;
|
||||
uint32_t tmp;
|
||||
|
||||
if (nand_info->version > GPMI_VERSION_TYPE_MX23) {
|
||||
|
@ -486,7 +500,7 @@ static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length)
|
|||
struct nand_chip *nand = mtd->priv;
|
||||
struct mxs_nand_info *nand_info = nand->priv;
|
||||
struct mxs_dma_desc *d;
|
||||
uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
|
||||
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
|
||||
int ret;
|
||||
|
||||
if (length > NAND_MAX_PAGESIZE) {
|
||||
|
@ -564,7 +578,7 @@ static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
|
|||
struct nand_chip *nand = mtd->priv;
|
||||
struct mxs_nand_info *nand_info = nand->priv;
|
||||
struct mxs_dma_desc *d;
|
||||
uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
|
||||
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
|
||||
int ret;
|
||||
|
||||
if (length > NAND_MAX_PAGESIZE) {
|
||||
|
@ -620,11 +634,11 @@ static uint8_t mxs_nand_read_byte(struct mtd_info *mtd)
|
|||
* Read a page from NAND.
|
||||
*/
|
||||
static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
|
||||
uint8_t *buf)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct mxs_nand_info *nand_info = nand->priv;
|
||||
struct mxs_dma_desc *d;
|
||||
uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
|
||||
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
|
||||
uint32_t corrected = 0, failed = 0;
|
||||
uint8_t *status;
|
||||
int i, ret;
|
||||
|
@ -708,7 +722,7 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
|
|||
goto rtn;
|
||||
}
|
||||
|
||||
ret = mxs_nand_wait_for_bch_complete();
|
||||
ret = mxs_nand_wait_for_bch_complete(nand_info);
|
||||
if (ret) {
|
||||
printf("MXS NAND: BCH read timeout\n");
|
||||
goto rtn;
|
||||
|
@ -762,13 +776,14 @@ rtn:
|
|||
/*
|
||||
* Write a page to NAND.
|
||||
*/
|
||||
static void mxs_nand_ecc_write_page(struct mtd_info *mtd,
|
||||
struct nand_chip *nand, const uint8_t *buf)
|
||||
static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
|
||||
struct nand_chip *nand, const uint8_t *buf,
|
||||
int oob_required)
|
||||
{
|
||||
struct mxs_nand_info *nand_info = nand->priv;
|
||||
struct mxs_dma_desc *d;
|
||||
uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
|
||||
int ret;
|
||||
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
|
||||
int ret = 0;
|
||||
|
||||
memcpy(nand_info->data_buf, buf, mtd->writesize);
|
||||
memcpy(nand_info->oob_buf, nand->oob_poi, mtd->oobsize);
|
||||
|
@ -808,7 +823,7 @@ static void mxs_nand_ecc_write_page(struct mtd_info *mtd,
|
|||
goto rtn;
|
||||
}
|
||||
|
||||
ret = mxs_nand_wait_for_bch_complete();
|
||||
ret = mxs_nand_wait_for_bch_complete(nand_info);
|
||||
if (ret) {
|
||||
printf("MXS NAND: BCH write timeout\n");
|
||||
goto rtn;
|
||||
|
@ -816,6 +831,8 @@ static void mxs_nand_ecc_write_page(struct mtd_info *mtd,
|
|||
|
||||
rtn:
|
||||
mxs_nand_return_dma_descs(nand_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -831,7 +848,7 @@ static int mxs_nand_hook_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
struct mxs_nand_info *nand_info = chip->priv;
|
||||
int ret;
|
||||
|
||||
if (ops->mode == MTD_OOB_RAW)
|
||||
if (ops->mode == MTD_OPS_RAW)
|
||||
nand_info->raw_oob_mode = 1;
|
||||
else
|
||||
nand_info->raw_oob_mode = 0;
|
||||
|
@ -856,7 +873,7 @@ static int mxs_nand_hook_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
struct mxs_nand_info *nand_info = chip->priv;
|
||||
int ret;
|
||||
|
||||
if (ops->mode == MTD_OOB_RAW)
|
||||
if (ops->mode == MTD_OPS_RAW)
|
||||
nand_info->raw_oob_mode = 1;
|
||||
else
|
||||
nand_info->raw_oob_mode = 0;
|
||||
|
@ -934,7 +951,7 @@ static int mxs_nand_hook_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|||
* what to do.
|
||||
*/
|
||||
static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
|
||||
int page, int cmd)
|
||||
int page)
|
||||
{
|
||||
struct mxs_nand_info *nand_info = nand->priv;
|
||||
int column;
|
||||
|
@ -1040,37 +1057,52 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)
|
|||
{
|
||||
struct nand_chip *nand = mtd->priv;
|
||||
struct mxs_nand_info *nand_info = nand->priv;
|
||||
void __iomem *bch_regs = (void __iomem *)MXS_BCH_BASE;
|
||||
uint32_t tmp;
|
||||
void __iomem *bch_regs = nand_info->bch_base;
|
||||
uint32_t layout0, layout1;
|
||||
int ret;
|
||||
|
||||
/* Reset BCH. Don't use SFTRST on MX23 due to Errata #2847 */
|
||||
ret = mxs_reset_block(bch_regs + BCH_CTRL,
|
||||
ret = stmp_reset_block(bch_regs + BCH_CTRL,
|
||||
nand_info->version == GPMI_VERSION_TYPE_MX23);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Configure layout 0 */
|
||||
tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1)
|
||||
<< BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
|
||||
tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
|
||||
tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
|
||||
<< BCH_FLASHLAYOUT0_ECC0_OFFSET;
|
||||
tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
|
||||
writel(tmp, bch_regs + BCH_FLASH0LAYOUT0);
|
||||
if (mxs_nand_is_imx6(nand_info)) {
|
||||
layout0 = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1)
|
||||
<< BCH_FLASHLAYOUT0_NBLOCKS_OFFSET |
|
||||
MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET |
|
||||
(mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
|
||||
<< IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET |
|
||||
MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2;
|
||||
|
||||
tmp = (mtd->writesize + mtd->oobsize)
|
||||
<< BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
|
||||
tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
|
||||
<< BCH_FLASHLAYOUT1_ECCN_OFFSET;
|
||||
tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
|
||||
writel(tmp, bch_regs + BCH_FLASH0LAYOUT1);
|
||||
layout1 = (mtd->writesize + mtd->oobsize)
|
||||
<< BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET |
|
||||
(mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
|
||||
<< IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET |
|
||||
MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2;
|
||||
} else {
|
||||
layout0 = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1)
|
||||
<< BCH_FLASHLAYOUT0_NBLOCKS_OFFSET |
|
||||
MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET |
|
||||
(mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
|
||||
<< BCH_FLASHLAYOUT0_ECC0_OFFSET |
|
||||
MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
|
||||
|
||||
layout1 = (mtd->writesize + mtd->oobsize)
|
||||
<< BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET |
|
||||
(mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
|
||||
<< BCH_FLASHLAYOUT1_ECCN_OFFSET |
|
||||
MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
writel(layout0, bch_regs + BCH_FLASH0LAYOUT0);
|
||||
writel(layout1, bch_regs + BCH_FLASH0LAYOUT1);
|
||||
|
||||
/* Set *all* chip selects to use layout 0 */
|
||||
writel(0, bch_regs + BCH_LAYOUTSELECT);
|
||||
|
||||
/* Enable BCH complete interrupt */
|
||||
writel(BCH_CTRL_COMPLETE_IRQ_EN, bch_regs + BCH_CTRL + BIT_SET);
|
||||
writel(BCH_CTRL_COMPLETE_IRQ_EN, bch_regs + BCH_CTRL + STMP_OFFSET_REG_SET);
|
||||
|
||||
/* Hook some operations at the MTD level. */
|
||||
if (mtd->read_oob != mxs_nand_hook_read_oob) {
|
||||
|
@ -1130,8 +1162,8 @@ int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info)
|
|||
*/
|
||||
int mxs_nand_hw_init(struct mxs_nand_info *info)
|
||||
{
|
||||
void __iomem *gpmi_regs = (void *)MXS_GPMI_BASE;
|
||||
void __iomem *bch_regs = (void __iomem *)MXS_BCH_BASE;
|
||||
void __iomem *gpmi_regs = info->io_base;
|
||||
void __iomem *bch_regs = info->bch_base;
|
||||
int i = 0, ret;
|
||||
u32 val;
|
||||
|
||||
|
@ -1147,11 +1179,8 @@ int mxs_nand_hw_init(struct mxs_nand_info *info)
|
|||
goto err2;
|
||||
}
|
||||
|
||||
/* Init the DMA controller. */
|
||||
mxs_dma_init();
|
||||
|
||||
/* Reset the GPMI block. */
|
||||
ret = mxs_reset_block(gpmi_regs + GPMI_CTRL0, 0);
|
||||
ret = stmp_reset_block(gpmi_regs + GPMI_CTRL0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1159,7 +1188,7 @@ int mxs_nand_hw_init(struct mxs_nand_info *info)
|
|||
info->version = val >> GPMI_VERSION_MINOR_OFFSET;
|
||||
|
||||
/* Reset BCH. Don't use SFTRST on MX23 due to Errata #2847 */
|
||||
ret = mxs_reset_block(bch_regs + BCH_CTRL,
|
||||
ret = stmp_reset_block(bch_regs + BCH_CTRL,
|
||||
info->version == GPMI_VERSION_TYPE_MX23);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1185,27 +1214,52 @@ err1:
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void mxs_nand_probe_dt(struct device_d *dev, struct mxs_nand_info *nand_info)
|
||||
{
|
||||
struct nand_chip *nand = &nand_info->nand_chip;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_OFTREE))
|
||||
return;
|
||||
|
||||
if (of_get_nand_on_flash_bbt(dev->device_node))
|
||||
nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
|
||||
}
|
||||
|
||||
static int mxs_nand_probe(struct device_d *dev)
|
||||
{
|
||||
struct mxs_nand_info *nand_info;
|
||||
struct nand_chip *nand;
|
||||
struct mtd_info *mtd;
|
||||
enum gpmi_type type;
|
||||
int err;
|
||||
|
||||
err = dev_get_drvdata(dev, (unsigned long *)&type);
|
||||
if (err)
|
||||
type = GPMI_MXS;
|
||||
|
||||
nand_info = kzalloc(sizeof(struct mxs_nand_info), GFP_KERNEL);
|
||||
if (!nand_info) {
|
||||
printf("MXS NAND: Failed to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* XXX: Remove u-boot specific access pointers and use io_base instead? */
|
||||
mxs_nand_probe_dt(dev, nand_info);
|
||||
|
||||
nand_info->type = type;
|
||||
nand_info->io_base = dev_request_mem_region(dev, 0);
|
||||
nand_info->bch_base = dev_request_mem_region(dev, 1);
|
||||
|
||||
nand_info->clk = clk_get(dev, NULL);
|
||||
if (IS_ERR(nand_info->clk))
|
||||
return PTR_ERR(nand_info->clk);
|
||||
|
||||
clk_enable(nand_info->clk);
|
||||
if (mxs_nand_is_imx6(nand_info)) {
|
||||
clk_set_rate(nand_info->clk, 96000000);
|
||||
clk_enable(nand_info->clk);
|
||||
nand_info->dma_channel_base = 0;
|
||||
} else {
|
||||
nand_info->dma_channel_base = MXS_DMA_CHANNEL_AHB_APBH_GPMI0;
|
||||
}
|
||||
|
||||
err = mxs_nand_alloc_buffers(nand_info);
|
||||
if (err)
|
||||
|
@ -1246,9 +1300,10 @@ static int mxs_nand_probe(struct device_d *dev)
|
|||
nand->ecc.mode = NAND_ECC_HW;
|
||||
nand->ecc.bytes = 9;
|
||||
nand->ecc.size = 512;
|
||||
nand->ecc.strength = 8;
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
err = nand_scan_ident(mtd, 1);
|
||||
err = nand_scan_ident(mtd, 1, NULL);
|
||||
if (err)
|
||||
goto err2;
|
||||
|
||||
|
@ -1268,9 +1323,25 @@ err1:
|
|||
return err;
|
||||
}
|
||||
|
||||
static __maybe_unused struct of_device_id gpmi_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx23-gpmi-nand",
|
||||
.data = GPMI_MXS,
|
||||
}, {
|
||||
.compatible = "fsl,imx28-gpmi-nand",
|
||||
.data = GPMI_MXS,
|
||||
}, {
|
||||
.compatible = "fsl,imx6q-gpmi-nand",
|
||||
.data = GPMI_IMX6,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
|
||||
static struct driver_d mxs_nand_driver = {
|
||||
.name = "mxs_nand",
|
||||
.probe = mxs_nand_probe,
|
||||
.of_compatible = DRV_OF_COMPAT(gpmi_dt_ids),
|
||||
};
|
||||
device_platform_driver(mxs_nand_driver);
|
||||
|
||||
|
|
|
@ -85,6 +85,9 @@
|
|||
#define GPMC_ECC_SIZE_CONFIG_ECCSIZE0(x) ((x) << 12)
|
||||
#define GPMC_ECC_SIZE_CONFIG_ECCSIZE1(x) ((x) << 22)
|
||||
|
||||
#define BCH8_MAX_ERROR 8 /* upto 8 bit correctable */
|
||||
#define BCH4_MAX_ERROR 4 /* upto 4 bit correctable */
|
||||
|
||||
int omap_gpmc_decode_bch(int select_4_8, unsigned char *ecc, unsigned int *err_loc);
|
||||
|
||||
static char *ecc_mode_strings[] = {
|
||||
|
@ -694,7 +697,7 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
|
|||
* generate dummy eccs for the unprotected oob area.
|
||||
*/
|
||||
static int omap_gpmc_read_page_bch_rom_mode(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf)
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct gpmc_nand_info *oinfo = chip->priv;
|
||||
int dev_width = chip->options & NAND_BUSWIDTH_16 ? GPMC_ECC_CONFIG_ECC16B : 0;
|
||||
|
@ -785,6 +788,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
|
|||
case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
|
||||
oinfo->nand.ecc.bytes = 3;
|
||||
oinfo->nand.ecc.size = 512;
|
||||
oinfo->nand.ecc.strength = 1;
|
||||
oinfo->ecc_parity_pairs = 12;
|
||||
if (oinfo->nand.options & NAND_BUSWIDTH_16) {
|
||||
offset = 2;
|
||||
|
@ -802,6 +806,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
|
|||
case OMAP_ECC_BCH4_CODE_HW:
|
||||
oinfo->nand.ecc.bytes = 4 * 7;
|
||||
oinfo->nand.ecc.size = 4 * 512;
|
||||
oinfo->nand.ecc.strength = BCH4_MAX_ERROR;
|
||||
omap_oobinfo.oobfree->offset = offset;
|
||||
omap_oobinfo.oobfree->length = minfo->oobsize -
|
||||
offset - omap_oobinfo.eccbytes;
|
||||
|
@ -812,6 +817,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
|
|||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
oinfo->nand.ecc.bytes = 4 * 13;
|
||||
oinfo->nand.ecc.size = 4 * 512;
|
||||
oinfo->nand.ecc.strength = BCH8_MAX_ERROR;
|
||||
omap_oobinfo.oobfree->offset = offset;
|
||||
omap_oobinfo.oobfree->length = minfo->oobsize -
|
||||
offset - omap_oobinfo.eccbytes;
|
||||
|
@ -822,6 +828,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
|
|||
case OMAP_ECC_BCH8_CODE_HW_ROMCODE:
|
||||
oinfo->nand.ecc.bytes = 4 * 13;
|
||||
oinfo->nand.ecc.size = 4 * 512;
|
||||
oinfo->nand.ecc.strength = BCH8_MAX_ERROR;
|
||||
nand->ecc.read_page = omap_gpmc_read_page_bch_rom_mode;
|
||||
omap_oobinfo.oobfree->length = 0;
|
||||
j = 0;
|
||||
|
@ -837,6 +844,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
|
|||
case OMAP_ECC_SOFT:
|
||||
nand->ecc.layout = NULL;
|
||||
nand->ecc.mode = NAND_ECC_SOFT;
|
||||
oinfo->nand.ecc.strength = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -878,7 +886,7 @@ static int omap_gpmc_eccmode_set(struct device_d *dev, struct param_d *param, co
|
|||
return omap_gpmc_eccmode(oinfo, i);
|
||||
}
|
||||
|
||||
static int gpmc_set_buswidth(struct mtd_info *mtd, struct nand_chip *chip, int buswidth)
|
||||
static int gpmc_set_buswidth(struct nand_chip *chip, int buswidth)
|
||||
{
|
||||
struct gpmc_nand_info *oinfo = chip->priv;
|
||||
|
||||
|
@ -999,8 +1007,6 @@ static int gpmc_nand_probe(struct device_d *pdev)
|
|||
nand->options |= NAND_OWN_BUFFERS;
|
||||
nand->buffers = xzalloc(sizeof(*nand->buffers));
|
||||
|
||||
nand->set_buswidth = gpmc_set_buswidth;
|
||||
|
||||
/* State my controller */
|
||||
nand->controller = &oinfo->controller;
|
||||
|
||||
|
@ -1023,11 +1029,13 @@ static int gpmc_nand_probe(struct device_d *pdev)
|
|||
mdelay(1);
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(minfo, 1)) {
|
||||
if (nand_scan_ident(minfo, 1, NULL)) {
|
||||
err = -ENXIO;
|
||||
goto out_release_mem;
|
||||
}
|
||||
|
||||
gpmc_set_buswidth(nand, nand->options & NAND_BUSWIDTH_16);
|
||||
|
||||
if (nand->options & NAND_BUSWIDTH_16) {
|
||||
lsp = &ecc_sp_x16;
|
||||
llp = &ecc_lp_x16;
|
||||
|
|
|
@ -456,6 +456,8 @@ static int s3c24x0_nand_probe(struct device_d *dev)
|
|||
*/
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.bytes = 3; /* always 24 bit ECC per turn */
|
||||
chip->ecc.strength = 1;
|
||||
|
||||
#ifdef CONFIG_CPU_S3C2440
|
||||
if (readl(host->base) & 0x8) {
|
||||
/* large page (2048 bytes per page) */
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
#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 <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_MTD_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_MTD_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;
|
||||
}
|
|
@ -1,747 +0,0 @@
|
|||
#define pr_fmt(fmt) "nand: " fmt
|
||||
|
||||
#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 <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 (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
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 = 0, subpage;
|
||||
|
||||
ops->retlen = 0;
|
||||
if (!writelen)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
} else {
|
||||
/* We still need to erase leftover OOB data */
|
||||
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
||||
}
|
||||
|
||||
if (oob || !mtd_all_ff(wbuf, mtd->writesize)) {
|
||||
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 (!mtd->allow_erasebad &&
|
||||
nand_block_checkbad(mtd, ((loff_t) page) <<
|
||||
chip->page_shift, 0, allowbbt)) {
|
||||
pr_warn("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;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_NAND_BBT))
|
||||
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);
|
||||
}
|
|
@ -209,6 +209,7 @@ static int nomadik_nand_probe(struct device_d *dev)
|
|||
nand->ecc.hwctl = nomadik_ecc_control;
|
||||
nand->ecc.size = 512;
|
||||
nand->ecc.bytes = 3;
|
||||
nand->ecc.strength = 1;
|
||||
|
||||
nand->options = pdata->options;
|
||||
|
||||
|
|
|
@ -1,7 +1,81 @@
|
|||
config UBI
|
||||
bool "UBI support"
|
||||
select PARTITION_NEED_MTD
|
||||
menuconfig MTD_UBI
|
||||
tristate "Enable UBI - Unsorted block images"
|
||||
select CRC32
|
||||
select PARTITION_NEED_MTD
|
||||
help
|
||||
This enables support for UBI (unsorted block images)
|
||||
UBI is a software layer above MTD layer which admits of LVM-like
|
||||
logical volumes on top of MTD devices, hides some complexities of
|
||||
flash chips like wear and bad blocks and provides some other useful
|
||||
capabilities. Please, consult the MTD web site for more details
|
||||
(www.linux-mtd.infradead.org).
|
||||
|
||||
if MTD_UBI
|
||||
|
||||
config MTD_UBI_WL_THRESHOLD
|
||||
int "UBI wear-leveling threshold"
|
||||
default 4096
|
||||
range 2 65536
|
||||
help
|
||||
This parameter defines the maximum difference between the highest
|
||||
erase counter value and the lowest erase counter value of eraseblocks
|
||||
of UBI devices. When this threshold is exceeded, UBI starts performing
|
||||
wear leveling by means of moving data from eraseblock with low erase
|
||||
counter to eraseblocks with high erase counter.
|
||||
|
||||
The default value should be OK for SLC NAND flashes, NOR flashes and
|
||||
other flashes which have eraseblock life-cycle 100000 or more.
|
||||
However, in case of MLC NAND flashes which typically have eraseblock
|
||||
life-cycle less than 10000, the threshold should be lessened (e.g.,
|
||||
to 128 or 256, although it does not have to be power of 2).
|
||||
|
||||
config MTD_UBI_BEB_LIMIT
|
||||
int "Maximum expected bad eraseblock count per 1024 eraseblocks"
|
||||
default 20
|
||||
range 0 768
|
||||
help
|
||||
This option specifies the maximum bad physical eraseblocks UBI
|
||||
expects on the MTD device (per 1024 eraseblocks). If the underlying
|
||||
flash does not admit of bad eraseblocks (e.g. NOR flash), this value
|
||||
is ignored.
|
||||
|
||||
NAND datasheets often specify the minimum and maximum NVM (Number of
|
||||
Valid Blocks) for the flashes' endurance lifetime. The maximum
|
||||
expected bad eraseblocks per 1024 eraseblocks then can be calculated
|
||||
as "1024 * (1 - MinNVB / MaxNVB)", which gives 20 for most NANDs
|
||||
(MaxNVB is basically the total count of eraseblocks on the chip).
|
||||
|
||||
To put it differently, if this value is 20, UBI will try to reserve
|
||||
about 1.9% of physical eraseblocks for bad blocks handling. And that
|
||||
will be 1.9% of eraseblocks on the entire NAND chip, not just the MTD
|
||||
partition UBI attaches. This means that if you have, say, a NAND
|
||||
flash chip admits maximum 40 bad eraseblocks, and it is split on two
|
||||
MTD partitions of the same size, UBI will reserve 40 eraseblocks when
|
||||
attaching a partition.
|
||||
|
||||
This option can be overridden by the "mtd=" UBI module parameter or
|
||||
by the "attach" ioctl.
|
||||
|
||||
Leave the default value if unsure.
|
||||
|
||||
config MTD_UBI_FASTMAP
|
||||
bool "UBI Fastmap (Experimental feature)"
|
||||
default n
|
||||
help
|
||||
Important: this feature is experimental so far and the on-flash
|
||||
format for fastmap may change in the next kernel versions
|
||||
|
||||
Fastmap is a mechanism which allows attaching an UBI device
|
||||
in nearly constant time. Instead of scanning the whole MTD device it
|
||||
only has to locate a checkpoint (called fastmap) on the device.
|
||||
The on-flash fastmap contains all information needed to attach
|
||||
the device. Using fastmap makes only sense on large devices where
|
||||
attaching by scanning takes long. UBI will not automatically install
|
||||
a fastmap on old images, but you can set the UBI module parameter
|
||||
fm_autoconvert to 1 if you want so. Please note that fastmap-enabled
|
||||
images are still usable with UBI implementations without
|
||||
fastmap support. On typical flash devices the whole fastmap fits
|
||||
into one PEB. UBI will reserve PEBs to hold two fastmaps.
|
||||
|
||||
If in doubt, say "N".
|
||||
|
||||
endif # MTD_UBI
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
obj-y += build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o scan.o misc.o debug.o cdev.o
|
||||
|
||||
obj-$(CONFIG_MTD_UBI) += ubi.o
|
||||
|
||||
ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o attach.o
|
||||
ubi-y += misc.o debug.o
|
||||
ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -23,7 +23,7 @@ static ssize_t ubi_volume_cdev_read(struct cdev *cdev, void *buf, size_t size,
|
|||
loff_t offp = offset;
|
||||
int usable_leb_size = vol->usable_leb_size;
|
||||
|
||||
printf("%s: %zd @ 0x%08llx\n", __func__, size, offset);
|
||||
debug("%s: %zd @ 0x%08llx\n", __func__, size, offset);
|
||||
|
||||
len = size > usable_leb_size ? usable_leb_size : size;
|
||||
|
||||
|
@ -103,13 +103,16 @@ static int ubi_volume_cdev_close(struct cdev *cdev)
|
|||
(priv->written % vol->usable_leb_size);
|
||||
|
||||
if (remaining) {
|
||||
void *buf = xzalloc(remaining);
|
||||
void *buf = kmalloc(remaining, GFP_KERNEL);
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(buf, 0xff, remaining);
|
||||
|
||||
err = ubi_more_update_data(ubi, vol, buf, remaining);
|
||||
|
||||
free(buf);
|
||||
kfree(buf);
|
||||
|
||||
if (err < 0) {
|
||||
printf("Couldnt or partially wrote data \n");
|
||||
|
@ -134,7 +137,7 @@ static int ubi_volume_cdev_close(struct cdev *cdev)
|
|||
}
|
||||
|
||||
vol->checked = 1;
|
||||
ubi_gluebi_updated(vol);
|
||||
ubi_volume_notify(ubi, vol, UBI_VOLUME_UPDATED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -165,7 +168,7 @@ int ubi_volume_cdev_add(struct ubi_device *ubi, struct ubi_volume *vol)
|
|||
struct ubi_volume_cdev_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = xzalloc(sizeof(*priv));
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
|
||||
priv->vol = vol;
|
||||
priv->ubi = ubi;
|
||||
|
@ -177,7 +180,7 @@ int ubi_volume_cdev_add(struct ubi_device *ubi, struct ubi_volume *vol)
|
|||
printf("registering %s as /dev/%s\n", vol->name, cdev->name);
|
||||
ret = devfs_create(cdev);
|
||||
if (ret) {
|
||||
free(priv);
|
||||
kfree(priv);
|
||||
free(cdev->name);
|
||||
}
|
||||
|
||||
|
@ -190,8 +193,8 @@ void ubi_volume_cdev_remove(struct ubi_volume *vol)
|
|||
struct ubi_volume_cdev_priv *priv = cdev->priv;
|
||||
|
||||
devfs_remove(cdev);
|
||||
free(cdev->name);
|
||||
free(priv);
|
||||
kfree(cdev->name);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf)
|
||||
|
@ -206,7 +209,8 @@ static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf)
|
|||
UBI_EXCLUSIVE);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
ubi_remove_volume(desc);
|
||||
ubi_remove_volume(desc, 0);
|
||||
ubi_close_volume(desc);
|
||||
break;
|
||||
case UBI_IOCMKVOL:
|
||||
if (!req->bytes)
|
||||
|
@ -217,7 +221,6 @@ static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct file_operations ubi_fops = {
|
||||
.ioctl = ubi_cdev_ioctl,
|
||||
};
|
||||
|
@ -235,7 +238,7 @@ int ubi_cdev_add(struct ubi_device *ubi)
|
|||
printf("registering /dev/%s\n", cdev->name);
|
||||
ret = devfs_create(cdev);
|
||||
if (ret)
|
||||
free(cdev->name);
|
||||
kfree(cdev->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -247,5 +250,5 @@ void ubi_cdev_remove(struct ubi_device *ubi)
|
|||
printf("removing %s\n", cdev->name);
|
||||
|
||||
devfs_remove(cdev);
|
||||
free(cdev->name);
|
||||
kfree(cdev->name);
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* There are multiple 16-bit CRC polynomials in common use, but this is
|
||||
* *the* standard CRC-32 polynomial, first popularized by Ethernet.
|
||||
* x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
|
||||
*/
|
||||
#define CRCPOLY_LE 0xedb88320
|
||||
#define CRCPOLY_BE 0x04c11db7
|
||||
|
||||
/* How many bits at a time to use. Requires a table of 4<<CRC_xx_BITS bytes. */
|
||||
/* For less performance-sensitive, use 4 */
|
||||
#ifndef CRC_LE_BITS
|
||||
# define CRC_LE_BITS 8
|
||||
#endif
|
||||
#ifndef CRC_BE_BITS
|
||||
# define CRC_BE_BITS 8
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Little-endian CRC computation. Used with serial bit streams sent
|
||||
* lsbit-first. Be sure to use cpu_to_le32() to append the computed CRC.
|
||||
*/
|
||||
#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1
|
||||
# error CRC_LE_BITS must be a power of 2 between 1 and 8
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Big-endian CRC computation. Used with serial bit streams sent
|
||||
* msbit-first. Be sure to use cpu_to_be32() to append the computed CRC.
|
||||
*/
|
||||
#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1
|
||||
# error CRC_BE_BITS must be a power of 2 between 1 and 8
|
||||
#endif
|
|
@ -11,179 +11,204 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Here we keep all the UBI debugging stuff which should normally be disabled
|
||||
* and compiled-out, but it is extremely helpful when hunting bugs or doing big
|
||||
* changes.
|
||||
*/
|
||||
#include "ubi-barebox.h"
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_MSG
|
||||
|
||||
#include "ubi.h"
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_ec_hdr - dump an erase counter header.
|
||||
* ubi_dump_flash - dump a region of flash.
|
||||
* @ubi: UBI device description object
|
||||
* @pnum: the physical eraseblock number to dump
|
||||
* @offset: the starting offset within the physical eraseblock to dump
|
||||
* @len: the length of the region to dump
|
||||
*/
|
||||
void ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len)
|
||||
{
|
||||
int err;
|
||||
size_t read;
|
||||
void *buf;
|
||||
loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
|
||||
|
||||
buf = vmalloc(len);
|
||||
if (!buf)
|
||||
return;
|
||||
err = mtd_read(ubi->mtd, addr, len, &read, buf);
|
||||
if (err && err != -EUCLEAN) {
|
||||
ubi_err("error %d while reading %d bytes from PEB %d:%d, read %zd bytes",
|
||||
err, len, pnum, offset, read);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ubi_msg("dumping %d bytes of data from PEB %d, offset %d",
|
||||
len, pnum, offset);
|
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1);
|
||||
out:
|
||||
vfree(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dump_ec_hdr - dump an erase counter header.
|
||||
* @ec_hdr: the erase counter header to dump
|
||||
*/
|
||||
void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
|
||||
void ubi_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
|
||||
{
|
||||
dbg_msg("erase counter header dump:");
|
||||
dbg_msg("magic %#08x", be32_to_cpu(ec_hdr->magic));
|
||||
dbg_msg("version %d", (int)ec_hdr->version);
|
||||
dbg_msg("ec %llu", (long long)be64_to_cpu(ec_hdr->ec));
|
||||
dbg_msg("vid_hdr_offset %d", be32_to_cpu(ec_hdr->vid_hdr_offset));
|
||||
dbg_msg("data_offset %d", be32_to_cpu(ec_hdr->data_offset));
|
||||
dbg_msg("hdr_crc %#08x", be32_to_cpu(ec_hdr->hdr_crc));
|
||||
dbg_msg("erase counter header hexdump:");
|
||||
pr_err("Erase counter header dump:\n");
|
||||
pr_err("\tmagic %#08x\n", be32_to_cpu(ec_hdr->magic));
|
||||
pr_err("\tversion %d\n", (int)ec_hdr->version);
|
||||
pr_err("\tec %llu\n", (long long)be64_to_cpu(ec_hdr->ec));
|
||||
pr_err("\tvid_hdr_offset %d\n", be32_to_cpu(ec_hdr->vid_hdr_offset));
|
||||
pr_err("\tdata_offset %d\n", be32_to_cpu(ec_hdr->data_offset));
|
||||
pr_err("\timage_seq %d\n", be32_to_cpu(ec_hdr->image_seq));
|
||||
pr_err("\thdr_crc %#08x\n", be32_to_cpu(ec_hdr->hdr_crc));
|
||||
pr_err("erase counter header hexdump:\n");
|
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
|
||||
ec_hdr, UBI_EC_HDR_SIZE, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_vid_hdr - dump a volume identifier header.
|
||||
* ubi_dump_vid_hdr - dump a volume identifier header.
|
||||
* @vid_hdr: the volume identifier header to dump
|
||||
*/
|
||||
void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
|
||||
void ubi_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
|
||||
{
|
||||
dbg_msg("volume identifier header dump:");
|
||||
dbg_msg("magic %08x", be32_to_cpu(vid_hdr->magic));
|
||||
dbg_msg("version %d", (int)vid_hdr->version);
|
||||
dbg_msg("vol_type %d", (int)vid_hdr->vol_type);
|
||||
dbg_msg("copy_flag %d", (int)vid_hdr->copy_flag);
|
||||
dbg_msg("compat %d", (int)vid_hdr->compat);
|
||||
dbg_msg("vol_id %d", be32_to_cpu(vid_hdr->vol_id));
|
||||
dbg_msg("lnum %d", be32_to_cpu(vid_hdr->lnum));
|
||||
dbg_msg("leb_ver %u", be32_to_cpu(vid_hdr->leb_ver));
|
||||
dbg_msg("data_size %d", be32_to_cpu(vid_hdr->data_size));
|
||||
dbg_msg("used_ebs %d", be32_to_cpu(vid_hdr->used_ebs));
|
||||
dbg_msg("data_pad %d", be32_to_cpu(vid_hdr->data_pad));
|
||||
dbg_msg("sqnum %llu",
|
||||
pr_err("Volume identifier header dump:\n");
|
||||
pr_err("\tmagic %08x\n", be32_to_cpu(vid_hdr->magic));
|
||||
pr_err("\tversion %d\n", (int)vid_hdr->version);
|
||||
pr_err("\tvol_type %d\n", (int)vid_hdr->vol_type);
|
||||
pr_err("\tcopy_flag %d\n", (int)vid_hdr->copy_flag);
|
||||
pr_err("\tcompat %d\n", (int)vid_hdr->compat);
|
||||
pr_err("\tvol_id %d\n", be32_to_cpu(vid_hdr->vol_id));
|
||||
pr_err("\tlnum %d\n", be32_to_cpu(vid_hdr->lnum));
|
||||
pr_err("\tdata_size %d\n", be32_to_cpu(vid_hdr->data_size));
|
||||
pr_err("\tused_ebs %d\n", be32_to_cpu(vid_hdr->used_ebs));
|
||||
pr_err("\tdata_pad %d\n", be32_to_cpu(vid_hdr->data_pad));
|
||||
pr_err("\tsqnum %llu\n",
|
||||
(unsigned long long)be64_to_cpu(vid_hdr->sqnum));
|
||||
dbg_msg("hdr_crc %08x", be32_to_cpu(vid_hdr->hdr_crc));
|
||||
dbg_msg("volume identifier header hexdump:");
|
||||
pr_err("\thdr_crc %08x\n", be32_to_cpu(vid_hdr->hdr_crc));
|
||||
pr_err("Volume identifier header hexdump:\n");
|
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
|
||||
vid_hdr, UBI_VID_HDR_SIZE, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_vol_info- dump volume information.
|
||||
* ubi_dump_vol_info - dump volume information.
|
||||
* @vol: UBI volume description object
|
||||
*/
|
||||
void ubi_dbg_dump_vol_info(const struct ubi_volume *vol)
|
||||
void ubi_dump_vol_info(const struct ubi_volume *vol)
|
||||
{
|
||||
dbg_msg("volume information dump:");
|
||||
dbg_msg("vol_id %d", vol->vol_id);
|
||||
dbg_msg("reserved_pebs %d", vol->reserved_pebs);
|
||||
dbg_msg("alignment %d", vol->alignment);
|
||||
dbg_msg("data_pad %d", vol->data_pad);
|
||||
dbg_msg("vol_type %d", vol->vol_type);
|
||||
dbg_msg("name_len %d", vol->name_len);
|
||||
dbg_msg("usable_leb_size %d", vol->usable_leb_size);
|
||||
dbg_msg("used_ebs %d", vol->used_ebs);
|
||||
dbg_msg("used_bytes %lld", vol->used_bytes);
|
||||
dbg_msg("last_eb_bytes %d", vol->last_eb_bytes);
|
||||
dbg_msg("corrupted %d", vol->corrupted);
|
||||
dbg_msg("upd_marker %d", vol->upd_marker);
|
||||
pr_err("Volume information dump:\n");
|
||||
pr_err("\tvol_id %d\n", vol->vol_id);
|
||||
pr_err("\treserved_pebs %d\n", vol->reserved_pebs);
|
||||
pr_err("\talignment %d\n", vol->alignment);
|
||||
pr_err("\tdata_pad %d\n", vol->data_pad);
|
||||
pr_err("\tvol_type %d\n", vol->vol_type);
|
||||
pr_err("\tname_len %d\n", vol->name_len);
|
||||
pr_err("\tusable_leb_size %d\n", vol->usable_leb_size);
|
||||
pr_err("\tused_ebs %d\n", vol->used_ebs);
|
||||
pr_err("\tused_bytes %lld\n", vol->used_bytes);
|
||||
pr_err("\tlast_eb_bytes %d\n", vol->last_eb_bytes);
|
||||
pr_err("\tcorrupted %d\n", vol->corrupted);
|
||||
pr_err("\tupd_marker %d\n", vol->upd_marker);
|
||||
|
||||
if (vol->name_len <= UBI_VOL_NAME_MAX &&
|
||||
strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
|
||||
dbg_msg("name %s", vol->name);
|
||||
pr_err("\tname %s\n", vol->name);
|
||||
} else {
|
||||
dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c",
|
||||
vol->name[0], vol->name[1], vol->name[2],
|
||||
vol->name[3], vol->name[4]);
|
||||
pr_err("\t1st 5 characters of name: %c%c%c%c%c\n",
|
||||
vol->name[0], vol->name[1], vol->name[2],
|
||||
vol->name[3], vol->name[4]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
|
||||
* ubi_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
|
||||
* @r: the object to dump
|
||||
* @idx: volume table index
|
||||
*/
|
||||
void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
|
||||
void ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
|
||||
{
|
||||
int name_len = be16_to_cpu(r->name_len);
|
||||
|
||||
dbg_msg("volume table record %d dump:", idx);
|
||||
dbg_msg("reserved_pebs %d", be32_to_cpu(r->reserved_pebs));
|
||||
dbg_msg("alignment %d", be32_to_cpu(r->alignment));
|
||||
dbg_msg("data_pad %d", be32_to_cpu(r->data_pad));
|
||||
dbg_msg("vol_type %d", (int)r->vol_type);
|
||||
dbg_msg("upd_marker %d", (int)r->upd_marker);
|
||||
dbg_msg("name_len %d", name_len);
|
||||
pr_err("Volume table record %d dump:\n", idx);
|
||||
pr_err("\treserved_pebs %d\n", be32_to_cpu(r->reserved_pebs));
|
||||
pr_err("\talignment %d\n", be32_to_cpu(r->alignment));
|
||||
pr_err("\tdata_pad %d\n", be32_to_cpu(r->data_pad));
|
||||
pr_err("\tvol_type %d\n", (int)r->vol_type);
|
||||
pr_err("\tupd_marker %d\n", (int)r->upd_marker);
|
||||
pr_err("\tname_len %d\n", name_len);
|
||||
|
||||
if (r->name[0] == '\0') {
|
||||
dbg_msg("name NULL");
|
||||
pr_err("\tname NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (name_len <= UBI_VOL_NAME_MAX &&
|
||||
strnlen(&r->name[0], name_len + 1) == name_len) {
|
||||
dbg_msg("name %s", &r->name[0]);
|
||||
pr_err("\tname %s\n", &r->name[0]);
|
||||
} else {
|
||||
dbg_msg("1st 5 characters of the name: %c%c%c%c%c",
|
||||
pr_err("\t1st 5 characters of name: %c%c%c%c%c\n",
|
||||
r->name[0], r->name[1], r->name[2], r->name[3],
|
||||
r->name[4]);
|
||||
}
|
||||
dbg_msg("crc %#08x", be32_to_cpu(r->crc));
|
||||
pr_err("\tcrc %#08x\n", be32_to_cpu(r->crc));
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object.
|
||||
* @sv: the object to dump
|
||||
* ubi_dump_av - dump a &struct ubi_ainf_volume object.
|
||||
* @av: the object to dump
|
||||
*/
|
||||
void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv)
|
||||
void ubi_dump_av(const struct ubi_ainf_volume *av)
|
||||
{
|
||||
dbg_msg("volume scanning information dump:");
|
||||
dbg_msg("vol_id %d", sv->vol_id);
|
||||
dbg_msg("highest_lnum %d", sv->highest_lnum);
|
||||
dbg_msg("leb_count %d", sv->leb_count);
|
||||
dbg_msg("compat %d", sv->compat);
|
||||
dbg_msg("vol_type %d", sv->vol_type);
|
||||
dbg_msg("used_ebs %d", sv->used_ebs);
|
||||
dbg_msg("last_data_size %d", sv->last_data_size);
|
||||
dbg_msg("data_pad %d", sv->data_pad);
|
||||
pr_err("Volume attaching information dump:\n");
|
||||
pr_err("\tvol_id %d\n", av->vol_id);
|
||||
pr_err("\thighest_lnum %d\n", av->highest_lnum);
|
||||
pr_err("\tleb_count %d\n", av->leb_count);
|
||||
pr_err("\tcompat %d\n", av->compat);
|
||||
pr_err("\tvol_type %d\n", av->vol_type);
|
||||
pr_err("\tused_ebs %d\n", av->used_ebs);
|
||||
pr_err("\tlast_data_size %d\n", av->last_data_size);
|
||||
pr_err("\tdata_pad %d\n", av->data_pad);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object.
|
||||
* @seb: the object to dump
|
||||
* ubi_dump_aeb - dump a &struct ubi_ainf_peb object.
|
||||
* @aeb: the object to dump
|
||||
* @type: object type: 0 - not corrupted, 1 - corrupted
|
||||
*/
|
||||
void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type)
|
||||
void ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type)
|
||||
{
|
||||
dbg_msg("eraseblock scanning information dump:");
|
||||
dbg_msg("ec %d", seb->ec);
|
||||
dbg_msg("pnum %d", seb->pnum);
|
||||
pr_err("eraseblock attaching information dump:\n");
|
||||
pr_err("\tec %d\n", aeb->ec);
|
||||
pr_err("\tpnum %d\n", aeb->pnum);
|
||||
if (type == 0) {
|
||||
dbg_msg("lnum %d", seb->lnum);
|
||||
dbg_msg("scrub %d", seb->scrub);
|
||||
dbg_msg("sqnum %llu", seb->sqnum);
|
||||
dbg_msg("leb_ver %u", seb->leb_ver);
|
||||
pr_err("\tlnum %d\n", aeb->lnum);
|
||||
pr_err("\tscrub %d\n", aeb->scrub);
|
||||
pr_err("\tsqnum %llu\n", aeb->sqnum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
|
||||
* ubi_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
|
||||
* @req: the object to dump
|
||||
*/
|
||||
void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req)
|
||||
void ubi_dump_mkvol_req(const struct ubi_mkvol_req *req)
|
||||
{
|
||||
char nm[17];
|
||||
|
||||
dbg_msg("volume creation request dump:");
|
||||
dbg_msg("vol_id %d", req->vol_id);
|
||||
dbg_msg("alignment %d", req->alignment);
|
||||
dbg_msg("bytes %lld", (long long)req->bytes);
|
||||
dbg_msg("vol_type %d", req->vol_type);
|
||||
dbg_msg("name_len %d", req->name_len);
|
||||
pr_err("Volume creation request dump:\n");
|
||||
pr_err("\tvol_id %d\n", req->vol_id);
|
||||
pr_err("\talignment %d\n", req->alignment);
|
||||
pr_err("\tbytes %lld\n", (long long)req->bytes);
|
||||
pr_err("\tvol_type %d\n", req->vol_type);
|
||||
pr_err("\tname_len %d\n", req->name_len);
|
||||
|
||||
memcpy(nm, req->name, 16);
|
||||
nm[16] = 0;
|
||||
dbg_msg("the 1st 16 characters of the name: %s", nm);
|
||||
pr_err("\t1st 16 characters of name: %s\n", nm);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
@ -18,132 +21,109 @@
|
|||
#ifndef __UBI_DEBUG_H__
|
||||
#define __UBI_DEBUG_H__
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG
|
||||
#ifdef UBI_LINUX
|
||||
#include <linux/random.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ubi_assert(expr) BUG_ON(!(expr))
|
||||
#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ubi_assert(expr) ({})
|
||||
#define dbg_err(fmt, ...) ({})
|
||||
#endif
|
||||
void ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len);
|
||||
void ubi_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr);
|
||||
void ubi_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr);
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
|
||||
#define DBG_DISABLE_BGT 1
|
||||
#else
|
||||
#define DBG_DISABLE_BGT 0
|
||||
#endif
|
||||
#define ubi_assert(expr) do { \
|
||||
if (unlikely(!(expr))) { \
|
||||
pr_crit("UBI assert failed in %s at %u\n", \
|
||||
__func__, __LINE__); \
|
||||
dump_stack(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_MSG
|
||||
/* Generic debugging message */
|
||||
#define dbg_msg(fmt, ...) \
|
||||
printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", \
|
||||
__FUNCTION__, ##__VA_ARGS__)
|
||||
#define ubi_dbg_print_hex_dump(l, ps, pt, r, g, b, len, a) \
|
||||
print_hex_dump(l, ps, pt, r, g, b, len, a)
|
||||
|
||||
#define ubi_dbg_dump_stack() dump_stack()
|
||||
#define ubi_dbg_msg(type, fmt, ...) \
|
||||
pr_debug("UBI DBG " type ": " fmt "\n", \
|
||||
##__VA_ARGS__)
|
||||
|
||||
struct ubi_ec_hdr;
|
||||
struct ubi_vid_hdr;
|
||||
struct ubi_volume;
|
||||
struct ubi_vtbl_record;
|
||||
struct ubi_scan_volume;
|
||||
struct ubi_scan_leb;
|
||||
struct ubi_mkvol_req;
|
||||
|
||||
void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr);
|
||||
void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr);
|
||||
void ubi_dbg_dump_vol_info(const struct ubi_volume *vol);
|
||||
void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx);
|
||||
void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv);
|
||||
void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
|
||||
void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
|
||||
|
||||
#else
|
||||
|
||||
#define dbg_msg(fmt, ...) ({})
|
||||
#define ubi_dbg_dump_stack() ({})
|
||||
#define ubi_dbg_dump_ec_hdr(ec_hdr) ({})
|
||||
#define ubi_dbg_dump_vid_hdr(vid_hdr) ({})
|
||||
#define ubi_dbg_dump_vol_info(vol) ({})
|
||||
#define ubi_dbg_dump_vtbl_record(r, idx) ({})
|
||||
#define ubi_dbg_dump_sv(sv) ({})
|
||||
#define ubi_dbg_dump_seb(seb, type) ({})
|
||||
#define ubi_dbg_dump_mkvol_req(req) ({})
|
||||
|
||||
#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA
|
||||
/* Messages from the eraseblock association unit */
|
||||
#define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_eba(fmt, ...) ({})
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL
|
||||
/* Messages from the wear-leveling unit */
|
||||
#define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_wl(fmt, ...) ({})
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO
|
||||
/* Messages from the input/output unit */
|
||||
#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_io(fmt, ...) ({})
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD
|
||||
/* General debugging messages */
|
||||
#define dbg_gen(fmt, ...) ubi_dbg_msg("gen", fmt, ##__VA_ARGS__)
|
||||
/* Messages from the eraseblock association sub-system */
|
||||
#define dbg_eba(fmt, ...) ubi_dbg_msg("eba", fmt, ##__VA_ARGS__)
|
||||
/* Messages from the wear-leveling sub-system */
|
||||
#define dbg_wl(fmt, ...) ubi_dbg_msg("wl", fmt, ##__VA_ARGS__)
|
||||
/* Messages from the input/output sub-system */
|
||||
#define dbg_io(fmt, ...) ubi_dbg_msg("io", fmt, ##__VA_ARGS__)
|
||||
/* Initialization and build messages */
|
||||
#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_bld(fmt, ...) ({})
|
||||
#endif
|
||||
#define dbg_bld(fmt, ...) ubi_dbg_msg("bld", fmt, ##__VA_ARGS__)
|
||||
|
||||
void ubi_dump_vol_info(const struct ubi_volume *vol);
|
||||
void ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx);
|
||||
void ubi_dump_av(const struct ubi_ainf_volume *av);
|
||||
void ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type);
|
||||
void ubi_dump_mkvol_req(const struct ubi_mkvol_req *req);
|
||||
int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
|
||||
int len);
|
||||
int ubi_debugfs_init(void);
|
||||
void ubi_debugfs_exit(void);
|
||||
int ubi_debugfs_init_dev(struct ubi_device *ubi);
|
||||
|
||||
/**
|
||||
* ubi_dbg_is_bgt_disabled - if the background thread is disabled.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns non-zero if the UBI background thread is disabled for testing
|
||||
* purposes.
|
||||
*/
|
||||
static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi)
|
||||
{
|
||||
return ubi->dbg.disable_bgt;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS
|
||||
/**
|
||||
* ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns non-zero if a bit-flip should be emulated, otherwise returns zero.
|
||||
*/
|
||||
static inline int ubi_dbg_is_bitflip(void)
|
||||
static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi)
|
||||
{
|
||||
return !(random32() % 200);
|
||||
if (ubi->dbg.emulate_bitflips)
|
||||
return !(random32() % 200);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ubi_dbg_is_bitflip() 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES
|
||||
/**
|
||||
* ubi_dbg_is_write_failure - if it is time to emulate a write failure.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns non-zero if a write failure should be emulated, otherwise returns
|
||||
* zero.
|
||||
*/
|
||||
static inline int ubi_dbg_is_write_failure(void)
|
||||
static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi)
|
||||
{
|
||||
return !(random32() % 500);
|
||||
if (ubi->dbg.emulate_io_failures)
|
||||
return !(random32() % 500);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ubi_dbg_is_write_failure() 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES
|
||||
/**
|
||||
* ubi_dbg_is_erase_failure - if its time to emulate an erase failure.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns non-zero if an erase failure should be emulated, otherwise returns
|
||||
* zero.
|
||||
*/
|
||||
static inline int ubi_dbg_is_erase_failure(void)
|
||||
static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi)
|
||||
{
|
||||
if (ubi->dbg.emulate_io_failures)
|
||||
return !(random32() % 400);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ubi_dbg_is_erase_failure() 0
|
||||
#endif
|
||||
|
||||
static inline int ubi_dbg_chk_io(const struct ubi_device *ubi)
|
||||
{
|
||||
return ubi->dbg.chk_io;
|
||||
}
|
||||
|
||||
static inline int ubi_dbg_chk_gen(const struct ubi_device *ubi)
|
||||
{
|
||||
return ubi->dbg.chk_gen;
|
||||
}
|
||||
#endif /* !__UBI_DEBUG_H__ */
|
||||
|
|
|
@ -11,25 +11,28 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
||||
/*
|
||||
* The UBI Eraseblock Association (EBA) unit.
|
||||
* The UBI Eraseblock Association (EBA) sub-system.
|
||||
*
|
||||
* This unit is responsible for I/O to/from logical eraseblock.
|
||||
* This sub-system is responsible for I/O to/from logical eraseblock.
|
||||
*
|
||||
* Although in this implementation the EBA table is fully kept and managed in
|
||||
* RAM, which assumes poor scalability, it might be (partially) maintained on
|
||||
* flash in future implementations.
|
||||
*
|
||||
* The EBA unit implements per-logical eraseblock locking. Before accessing a
|
||||
* logical eraseblock it is locked for reading or writing. The per-logical
|
||||
* eraseblock locking is implemented by means of the lock tree. The lock tree
|
||||
* is an RB-tree which refers all the currently locked logical eraseblocks. The
|
||||
* lock tree elements are &struct ubi_ltree_entry objects. They are indexed by
|
||||
* (@vol_id, @lnum) pairs.
|
||||
* The EBA sub-system implements per-logical eraseblock locking. Before
|
||||
* accessing a logical eraseblock it is locked for reading or writing. The
|
||||
* per-logical eraseblock locking is implemented by means of the lock tree. The
|
||||
* lock tree is an RB-tree which refers all the currently locked logical
|
||||
* eraseblocks. The lock tree elements are &struct ubi_ltree_entry objects.
|
||||
* They are indexed by (@vol_id, @lnum) pairs.
|
||||
*
|
||||
* EBA also maintains the global sequence counter which is incremented each
|
||||
* time a logical eraseblock is mapped to a physical eraseblock and it is
|
||||
|
@ -38,13 +41,7 @@
|
|||
* 64 bits is enough to never overflow.
|
||||
*/
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#include <linux/slab.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/err.h>
|
||||
#endif
|
||||
|
||||
#include "ubi-barebox.h"
|
||||
#include "ubi.h"
|
||||
|
||||
/* Number of physical eraseblocks reserved for atomic LEB change operation */
|
||||
|
@ -58,13 +55,11 @@
|
|||
* global sequence counter value. It also increases the global sequence
|
||||
* counter.
|
||||
*/
|
||||
static unsigned long long next_sqnum(struct ubi_device *ubi)
|
||||
unsigned long long ubi_next_sqnum(struct ubi_device *ubi)
|
||||
{
|
||||
unsigned long long sqnum;
|
||||
|
||||
spin_lock(&ubi->ltree_lock);
|
||||
sqnum = ubi->global_sqnum++;
|
||||
spin_unlock(&ubi->ltree_lock);
|
||||
|
||||
return sqnum;
|
||||
}
|
||||
|
@ -143,11 +138,9 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
le->users = 0;
|
||||
init_rwsem(&le->mutex);
|
||||
le->vol_id = vol_id;
|
||||
le->lnum = lnum;
|
||||
|
||||
spin_lock(&ubi->ltree_lock);
|
||||
le1 = ltree_lookup(ubi, vol_id, lnum);
|
||||
|
||||
if (le1) {
|
||||
|
@ -188,11 +181,8 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi,
|
|||
rb_insert_color(&le->rb, &ubi->ltree);
|
||||
}
|
||||
le->users += 1;
|
||||
spin_unlock(&ubi->ltree_lock);
|
||||
|
||||
if (le_free)
|
||||
kfree(le_free);
|
||||
|
||||
kfree(le_free);
|
||||
return le;
|
||||
}
|
||||
|
||||
|
@ -212,7 +202,6 @@ static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum)
|
|||
le = ltree_add_entry(ubi, vol_id, lnum);
|
||||
if (IS_ERR(le))
|
||||
return PTR_ERR(le);
|
||||
down_read(&le->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -224,22 +213,15 @@ static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum)
|
|||
*/
|
||||
static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum)
|
||||
{
|
||||
int _free = 0;
|
||||
struct ubi_ltree_entry *le;
|
||||
|
||||
spin_lock(&ubi->ltree_lock);
|
||||
le = ltree_lookup(ubi, vol_id, lnum);
|
||||
le->users -= 1;
|
||||
ubi_assert(le->users >= 0);
|
||||
if (le->users == 0) {
|
||||
rb_erase(&le->rb, &ubi->ltree);
|
||||
_free = 1;
|
||||
}
|
||||
spin_unlock(&ubi->ltree_lock);
|
||||
|
||||
up_read(&le->mutex);
|
||||
if (_free)
|
||||
kfree(le);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -258,7 +240,6 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
|
|||
le = ltree_add_entry(ubi, vol_id, lnum);
|
||||
if (IS_ERR(le))
|
||||
return PTR_ERR(le);
|
||||
down_write(&le->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -275,27 +256,19 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
|
|||
*/
|
||||
static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
|
||||
{
|
||||
int _free;
|
||||
struct ubi_ltree_entry *le;
|
||||
|
||||
le = ltree_add_entry(ubi, vol_id, lnum);
|
||||
if (IS_ERR(le))
|
||||
return PTR_ERR(le);
|
||||
if (down_write_trylock(&le->mutex))
|
||||
return 0;
|
||||
|
||||
/* Contention, cancel */
|
||||
spin_lock(&ubi->ltree_lock);
|
||||
le->users -= 1;
|
||||
ubi_assert(le->users >= 0);
|
||||
if (le->users == 0) {
|
||||
rb_erase(&le->rb, &ubi->ltree);
|
||||
_free = 1;
|
||||
} else
|
||||
_free = 0;
|
||||
spin_unlock(&ubi->ltree_lock);
|
||||
if (_free)
|
||||
kfree(le);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -308,23 +281,15 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
|
|||
*/
|
||||
static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum)
|
||||
{
|
||||
int _free;
|
||||
struct ubi_ltree_entry *le;
|
||||
|
||||
spin_lock(&ubi->ltree_lock);
|
||||
le = ltree_lookup(ubi, vol_id, lnum);
|
||||
le->users -= 1;
|
||||
ubi_assert(le->users >= 0);
|
||||
if (le->users == 0) {
|
||||
rb_erase(&le->rb, &ubi->ltree);
|
||||
_free = 1;
|
||||
} else
|
||||
_free = 0;
|
||||
spin_unlock(&ubi->ltree_lock);
|
||||
|
||||
up_write(&le->mutex);
|
||||
if (_free)
|
||||
kfree(le);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -357,7 +322,7 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum);
|
||||
|
||||
vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED;
|
||||
err = ubi_wl_put_peb(ubi, pnum, 0);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 0);
|
||||
|
||||
out_unlock:
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
|
@ -434,9 +399,10 @@ retry:
|
|||
* may try to recover data. FIXME: but this is
|
||||
* not implemented.
|
||||
*/
|
||||
if (err == UBI_IO_BAD_VID_HDR) {
|
||||
ubi_warn("bad VID header at PEB %d, LEB"
|
||||
"%d:%d", pnum, vol_id, lnum);
|
||||
if (err == UBI_IO_BAD_HDR_EBADMSG ||
|
||||
err == UBI_IO_BAD_HDR) {
|
||||
ubi_warn("corrupted VID header at PEB %d, LEB %d:%d",
|
||||
pnum, vol_id, lnum);
|
||||
err = -EBADMSG;
|
||||
} else
|
||||
ubi_ro_mode(ubi);
|
||||
|
@ -457,7 +423,7 @@ retry:
|
|||
if (err == UBI_IO_BITFLIPS) {
|
||||
scrub = 1;
|
||||
err = 0;
|
||||
} else if (err == -EBADMSG) {
|
||||
} else if (mtd_is_eccerr(err)) {
|
||||
if (vol->vol_type == UBI_DYNAMIC_VOLUME)
|
||||
goto out_unlock;
|
||||
scrub = 1;
|
||||
|
@ -517,16 +483,12 @@ static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
|
|||
struct ubi_vid_hdr *vid_hdr;
|
||||
|
||||
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
|
||||
if (!vid_hdr) {
|
||||
if (!vid_hdr)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_lock(&ubi->buf_mutex);
|
||||
|
||||
retry:
|
||||
new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN);
|
||||
new_pnum = ubi_wl_get_peb(ubi);
|
||||
if (new_pnum < 0) {
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return new_pnum;
|
||||
}
|
||||
|
@ -540,39 +502,38 @@ retry:
|
|||
goto out_put;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
|
||||
if (err)
|
||||
goto write_error;
|
||||
|
||||
data_size = offset + len;
|
||||
memset(ubi->peb_buf1 + offset, 0xFF, len);
|
||||
memset(ubi->peb_buf + offset, 0xFF, len);
|
||||
|
||||
/* Read everything before the area where the write failure happened */
|
||||
if (offset > 0) {
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf1, pnum, 0, offset);
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset);
|
||||
if (err && err != UBI_IO_BITFLIPS)
|
||||
goto out_put;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
memcpy(ubi->peb_buf1 + offset, buf, len);
|
||||
memcpy(ubi->peb_buf + offset, buf, len);
|
||||
|
||||
err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size);
|
||||
err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size);
|
||||
if (err)
|
||||
goto write_error;
|
||||
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
|
||||
vol->eba_tbl[lnum] = new_pnum;
|
||||
ubi_wl_put_peb(ubi, pnum, 1);
|
||||
ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
||||
|
||||
ubi_msg("data was successfully recovered");
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
out_put:
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
ubi_wl_put_peb(ubi, new_pnum, 1);
|
||||
ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return err;
|
||||
|
||||
|
@ -582,9 +543,8 @@ write_error:
|
|||
* get another one.
|
||||
*/
|
||||
ubi_warn("failed to write to PEB %d", new_pnum);
|
||||
ubi_wl_put_peb(ubi, new_pnum, 1);
|
||||
ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1);
|
||||
if (++tries > UBI_IO_RETRIES) {
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return err;
|
||||
}
|
||||
|
@ -600,7 +560,6 @@ write_error:
|
|||
* @buf: the data to write
|
||||
* @offset: offset within the logical eraseblock where to write
|
||||
* @len: how many bytes to write
|
||||
* @dtype: data type
|
||||
*
|
||||
* This function writes data to logical eraseblock @lnum of a dynamic volume
|
||||
* @vol. Returns zero in case of success and a negative error code in case
|
||||
|
@ -608,7 +567,7 @@ write_error:
|
|||
* written to the flash media, but may be some garbage.
|
||||
*/
|
||||
int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
const void *buf, int offset, int len, int dtype)
|
||||
const void *buf, int offset, int len)
|
||||
{
|
||||
int err, pnum, tries = 0, vol_id = vol->vol_id;
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
|
@ -649,14 +608,14 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
|||
}
|
||||
|
||||
vid_hdr->vol_type = UBI_VID_DYNAMIC;
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
vid_hdr->vol_id = cpu_to_be32(vol_id);
|
||||
vid_hdr->lnum = cpu_to_be32(lnum);
|
||||
vid_hdr->compat = ubi_get_compat(ubi, vol_id);
|
||||
vid_hdr->data_pad = cpu_to_be32(vol->data_pad);
|
||||
|
||||
retry:
|
||||
pnum = ubi_wl_get_peb(ubi, dtype);
|
||||
pnum = ubi_wl_get_peb(ubi);
|
||||
if (pnum < 0) {
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
|
@ -676,9 +635,8 @@ retry:
|
|||
if (len) {
|
||||
err = ubi_io_write_data(ubi, buf, pnum, offset, len);
|
||||
if (err) {
|
||||
ubi_warn("failed to write %d bytes at offset %d of "
|
||||
"LEB %d:%d, PEB %d", len, offset, vol_id,
|
||||
lnum, pnum);
|
||||
ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, PEB %d",
|
||||
len, offset, vol_id, lnum, pnum);
|
||||
goto write_error;
|
||||
}
|
||||
}
|
||||
|
@ -702,7 +660,7 @@ write_error:
|
|||
* eraseblock, so just put it and request a new one. We assume that if
|
||||
* this physical eraseblock went bad, the erase code will handle that.
|
||||
*/
|
||||
err = ubi_wl_put_peb(ubi, pnum, 1);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
||||
if (err || ++tries > UBI_IO_RETRIES) {
|
||||
ubi_ro_mode(ubi);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
|
@ -710,7 +668,7 @@ write_error:
|
|||
return err;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
ubi_msg("try another PEB");
|
||||
goto retry;
|
||||
}
|
||||
|
@ -722,7 +680,6 @@ write_error:
|
|||
* @lnum: logical eraseblock number
|
||||
* @buf: data to write
|
||||
* @len: how many bytes to write
|
||||
* @dtype: data type
|
||||
* @used_ebs: how many logical eraseblocks will this volume contain
|
||||
*
|
||||
* This function writes data to logical eraseblock @lnum of static volume
|
||||
|
@ -734,13 +691,12 @@ write_error:
|
|||
* to the real data size, although the @buf buffer has to contain the
|
||||
* alignment. In all other cases, @len has to be aligned.
|
||||
*
|
||||
* It is prohibited to write more then once to logical eraseblocks of static
|
||||
* It is prohibited to write more than once to logical eraseblocks of static
|
||||
* volumes. This function returns zero in case of success and a negative error
|
||||
* code in case of failure.
|
||||
*/
|
||||
int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
int lnum, const void *buf, int len, int dtype,
|
||||
int used_ebs)
|
||||
int lnum, const void *buf, int len, int used_ebs)
|
||||
{
|
||||
int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id;
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
|
@ -765,7 +721,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
return err;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
vid_hdr->vol_id = cpu_to_be32(vol_id);
|
||||
vid_hdr->lnum = cpu_to_be32(lnum);
|
||||
vid_hdr->compat = ubi_get_compat(ubi, vol_id);
|
||||
|
@ -778,7 +734,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
vid_hdr->data_crc = cpu_to_be32(crc);
|
||||
|
||||
retry:
|
||||
pnum = ubi_wl_get_peb(ubi, dtype);
|
||||
pnum = ubi_wl_get_peb(ubi);
|
||||
if (pnum < 0) {
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
|
@ -822,7 +778,7 @@ write_error:
|
|||
return err;
|
||||
}
|
||||
|
||||
err = ubi_wl_put_peb(ubi, pnum, 1);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
||||
if (err || ++tries > UBI_IO_RETRIES) {
|
||||
ubi_ro_mode(ubi);
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
|
@ -830,7 +786,7 @@ write_error:
|
|||
return err;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
ubi_msg("try another PEB");
|
||||
goto retry;
|
||||
}
|
||||
|
@ -842,7 +798,6 @@ write_error:
|
|||
* @lnum: logical eraseblock number
|
||||
* @buf: data to write
|
||||
* @len: how many bytes to write
|
||||
* @dtype: data type
|
||||
*
|
||||
* This function changes the contents of a logical eraseblock atomically. @buf
|
||||
* has to contain new logical eraseblock data, and @len - the length of the
|
||||
|
@ -854,7 +809,7 @@ write_error:
|
|||
* LEB change may be done at a time. This is ensured by @ubi->alc_mutex.
|
||||
*/
|
||||
int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
int lnum, const void *buf, int len, int dtype)
|
||||
int lnum, const void *buf, int len)
|
||||
{
|
||||
int err, pnum, tries = 0, vol_id = vol->vol_id;
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
|
@ -871,19 +826,18 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
err = ubi_eba_unmap_leb(ubi, vol, lnum);
|
||||
if (err)
|
||||
return err;
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype);
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0);
|
||||
}
|
||||
|
||||
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
|
||||
if (!vid_hdr)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&ubi->alc_mutex);
|
||||
err = leb_write_lock(ubi, vol_id, lnum);
|
||||
if (err)
|
||||
goto out_mutex;
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
vid_hdr->vol_id = cpu_to_be32(vol_id);
|
||||
vid_hdr->lnum = cpu_to_be32(lnum);
|
||||
vid_hdr->compat = ubi_get_compat(ubi, vol_id);
|
||||
|
@ -896,7 +850,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
vid_hdr->data_crc = cpu_to_be32(crc);
|
||||
|
||||
retry:
|
||||
pnum = ubi_wl_get_peb(ubi, dtype);
|
||||
pnum = ubi_wl_get_peb(ubi);
|
||||
if (pnum < 0) {
|
||||
err = pnum;
|
||||
goto out_leb_unlock;
|
||||
|
@ -920,7 +874,7 @@ retry:
|
|||
}
|
||||
|
||||
if (vol->eba_tbl[lnum] >= 0) {
|
||||
err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, vol->eba_tbl[lnum], 0);
|
||||
if (err)
|
||||
goto out_leb_unlock;
|
||||
}
|
||||
|
@ -930,7 +884,6 @@ retry:
|
|||
out_leb_unlock:
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
out_mutex:
|
||||
mutex_unlock(&ubi->alc_mutex);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return err;
|
||||
|
||||
|
@ -945,17 +898,44 @@ write_error:
|
|||
goto out_leb_unlock;
|
||||
}
|
||||
|
||||
err = ubi_wl_put_peb(ubi, pnum, 1);
|
||||
err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1);
|
||||
if (err || ++tries > UBI_IO_RETRIES) {
|
||||
ubi_ro_mode(ubi);
|
||||
goto out_leb_unlock;
|
||||
}
|
||||
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
ubi_msg("try another PEB");
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_error_sane - check whether a read error is sane.
|
||||
* @err: code of the error happened during reading
|
||||
*
|
||||
* This is a helper function for 'ubi_eba_copy_leb()' which is called when we
|
||||
* cannot read data from the target PEB (an error @err happened). If the error
|
||||
* code is sane, then we treat this error as non-fatal. Otherwise the error is
|
||||
* fatal and UBI will be switched to R/O mode later.
|
||||
*
|
||||
* The idea is that we try not to switch to R/O mode if the read error is
|
||||
* something which suggests there was a real read problem. E.g., %-EIO. Or a
|
||||
* memory allocation failed (-%ENOMEM). Otherwise, it is safer to switch to R/O
|
||||
* mode, simply because we do not know what happened at the MTD level, and we
|
||||
* cannot handle this. E.g., the underlying driver may have become crazy, and
|
||||
* it is safer to switch to R/O mode to preserve the data.
|
||||
*
|
||||
* And bear in mind, this is about reading from the target PEB, i.e. the PEB
|
||||
* which we have just written.
|
||||
*/
|
||||
static int is_error_sane(int err)
|
||||
{
|
||||
if (err == -EIO || err == -ENOMEM || err == UBI_IO_BAD_HDR ||
|
||||
err == UBI_IO_BAD_HDR_EBADMSG || err == -ETIMEDOUT)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_eba_copy_leb - copy logical eraseblock.
|
||||
* @ubi: UBI device description object
|
||||
|
@ -966,10 +946,9 @@ write_error:
|
|||
* This function copies logical eraseblock from physical eraseblock @from to
|
||||
* physical eraseblock @to. The @vid_hdr buffer may be changed by this
|
||||
* function. Returns:
|
||||
* o %0 in case of success;
|
||||
* o %1 if the operation was canceled and should be tried later (e.g.,
|
||||
* because a bit-flip was detected at the target PEB);
|
||||
* o %2 if the volume is being deleted and this LEB should not be moved.
|
||||
* o %0 in case of success;
|
||||
* o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, %MOVE_TARGET_BITFLIPS, etc;
|
||||
* o a negative error code in case of failure.
|
||||
*/
|
||||
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
struct ubi_vid_hdr *vid_hdr)
|
||||
|
@ -981,7 +960,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
|||
vol_id = be32_to_cpu(vid_hdr->vol_id);
|
||||
lnum = be32_to_cpu(vid_hdr->lnum);
|
||||
|
||||
dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to);
|
||||
dbg_wl("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to);
|
||||
|
||||
if (vid_hdr->vol_type == UBI_VID_STATIC) {
|
||||
data_size = be32_to_cpu(vid_hdr->data_size);
|
||||
|
@ -991,21 +970,18 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
|||
ubi->leb_size - be32_to_cpu(vid_hdr->data_pad);
|
||||
|
||||
idx = vol_id2idx(ubi, vol_id);
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
/*
|
||||
* Note, we may race with volume deletion, which means that the volume
|
||||
* this logical eraseblock belongs to might be being deleted. Since the
|
||||
* volume deletion unmaps all the volume's logical eraseblocks, it will
|
||||
* volume deletion un-maps all the volume's logical eraseblocks, it will
|
||||
* be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish.
|
||||
*/
|
||||
vol = ubi->volumes[idx];
|
||||
if (!vol) {
|
||||
/* No need to do further work, cancel */
|
||||
dbg_eba("volume %d is being removed, cancel", vol_id);
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
return 2;
|
||||
dbg_wl("volume %d is being removed, cancel", vol_id);
|
||||
return MOVE_CANCEL_RACE;
|
||||
}
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
/*
|
||||
* We do not want anybody to write to this logical eraseblock while we
|
||||
|
@ -1017,12 +993,15 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
|||
* (@from). This task locks the LEB and goes sleep in the
|
||||
* 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are
|
||||
* holding @ubi->move_mutex and go sleep on the LEB lock. So, if the
|
||||
* LEB is already locked, we just do not move it and return %1.
|
||||
* LEB is already locked, we just do not move it and return
|
||||
* %MOVE_RETRY. Note, we do not return %MOVE_CANCEL_RACE here because
|
||||
* we do not know the reasons of the contention - it may be just a
|
||||
* normal I/O on this LEB, so we want to re-try.
|
||||
*/
|
||||
err = leb_write_trylock(ubi, vol_id, lnum);
|
||||
if (err) {
|
||||
dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum);
|
||||
return err;
|
||||
dbg_wl("contention on LEB %d:%d, cancel", vol_id, lnum);
|
||||
return MOVE_RETRY;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1031,30 +1010,29 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
|||
* cancel it.
|
||||
*/
|
||||
if (vol->eba_tbl[lnum] != from) {
|
||||
dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to "
|
||||
"PEB %d, cancel", vol_id, lnum, from,
|
||||
vol->eba_tbl[lnum]);
|
||||
err = 1;
|
||||
dbg_wl("LEB %d:%d is no longer mapped to PEB %d, mapped to PEB %d, cancel",
|
||||
vol_id, lnum, from, vol->eba_tbl[lnum]);
|
||||
err = MOVE_CANCEL_RACE;
|
||||
goto out_unlock_leb;
|
||||
}
|
||||
|
||||
/*
|
||||
* OK, now the LEB is locked and we can safely start moving iy. Since
|
||||
* this function utilizes thie @ubi->peb1_buf buffer which is shared
|
||||
* with some other functions, so lock the buffer by taking the
|
||||
* OK, now the LEB is locked and we can safely start moving it. Since
|
||||
* this function utilizes the @ubi->peb_buf buffer which is shared
|
||||
* with some other functions - we lock the buffer by taking the
|
||||
* @ubi->buf_mutex.
|
||||
*/
|
||||
mutex_lock(&ubi->buf_mutex);
|
||||
dbg_eba("read %d bytes of data", aldata_size);
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size);
|
||||
dbg_wl("read %d bytes of data", aldata_size);
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf, from, 0, aldata_size);
|
||||
if (err && err != UBI_IO_BITFLIPS) {
|
||||
ubi_warn("error %d while reading data from PEB %d",
|
||||
err, from);
|
||||
err = MOVE_SOURCE_RD_ERR;
|
||||
goto out_unlock_buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we have got to calculate how much data we have to to copy. In
|
||||
* Now we have got to calculate how much data we have to copy. In
|
||||
* case of a static volume it is fairly easy - the VID header contains
|
||||
* the data size. In case of a dynamic volume it is more difficult - we
|
||||
* have to read the contents, cut 0xFF bytes from the end and copy only
|
||||
|
@ -1065,14 +1043,12 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
|||
*/
|
||||
if (vid_hdr->vol_type == UBI_VID_DYNAMIC)
|
||||
aldata_size = data_size =
|
||||
ubi_calc_data_len(ubi, ubi->peb_buf1, data_size);
|
||||
ubi_calc_data_len(ubi, ubi->peb_buf, data_size);
|
||||
|
||||
cond_resched();
|
||||
crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size);
|
||||
cond_resched();
|
||||
crc = crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size);
|
||||
|
||||
/*
|
||||
* It may turn out to me that the whole @from physical eraseblock
|
||||
* It may turn out to be that the whole @from physical eraseblock
|
||||
* contains only 0xFF bytes. Then we have to only write the VID header
|
||||
* and do not write any data. This also means we should not set
|
||||
* @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc.
|
||||
|
@ -1082,51 +1058,57 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
|||
vid_hdr->data_size = cpu_to_be32(data_size);
|
||||
vid_hdr->data_crc = cpu_to_be32(crc);
|
||||
}
|
||||
vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
|
||||
vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
||||
|
||||
err = ubi_io_write_vid_hdr(ubi, to, vid_hdr);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (err == -EIO)
|
||||
err = MOVE_TARGET_WR_ERR;
|
||||
goto out_unlock_buf;
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
/* Read the VID header back and check if it was written correctly */
|
||||
err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1);
|
||||
if (err) {
|
||||
if (err != UBI_IO_BITFLIPS)
|
||||
ubi_warn("cannot read VID header back from PEB %d", to);
|
||||
else
|
||||
err = 1;
|
||||
if (err != UBI_IO_BITFLIPS) {
|
||||
ubi_warn("error %d while reading VID header back from PEB %d",
|
||||
err, to);
|
||||
if (is_error_sane(err))
|
||||
err = MOVE_TARGET_RD_ERR;
|
||||
} else
|
||||
err = MOVE_TARGET_BITFLIPS;
|
||||
goto out_unlock_buf;
|
||||
}
|
||||
|
||||
if (data_size > 0) {
|
||||
err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size);
|
||||
if (err)
|
||||
err = ubi_io_write_data(ubi, ubi->peb_buf, to, 0, aldata_size);
|
||||
if (err) {
|
||||
if (err == -EIO)
|
||||
err = MOVE_TARGET_WR_ERR;
|
||||
goto out_unlock_buf;
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
/*
|
||||
* We've written the data and are going to read it back to make
|
||||
* sure it was written correctly.
|
||||
*/
|
||||
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size);
|
||||
memset(ubi->peb_buf, 0xFF, aldata_size);
|
||||
err = ubi_io_read_data(ubi, ubi->peb_buf, to, 0, aldata_size);
|
||||
if (err) {
|
||||
if (err != UBI_IO_BITFLIPS)
|
||||
ubi_warn("cannot read data back from PEB %d",
|
||||
to);
|
||||
else
|
||||
err = 1;
|
||||
if (err != UBI_IO_BITFLIPS) {
|
||||
ubi_warn("error %d while reading data back from PEB %d",
|
||||
err, to);
|
||||
if (is_error_sane(err))
|
||||
err = MOVE_TARGET_RD_ERR;
|
||||
} else
|
||||
err = MOVE_TARGET_BITFLIPS;
|
||||
goto out_unlock_buf;
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
|
||||
if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) {
|
||||
ubi_warn("read data back from PEB %d - it is different",
|
||||
if (crc != crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size)) {
|
||||
ubi_warn("read data back from PEB %d and it is different",
|
||||
to);
|
||||
err = -EINVAL;
|
||||
goto out_unlock_buf;
|
||||
}
|
||||
}
|
||||
|
@ -1135,43 +1117,175 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
|||
vol->eba_tbl[lnum] = to;
|
||||
|
||||
out_unlock_buf:
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
out_unlock_leb:
|
||||
leb_write_unlock(ubi, vol_id, lnum);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_eba_init_scan - initialize the EBA unit using scanning information.
|
||||
* print_rsvd_warning - warn about not having enough reserved PEBs.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
*
|
||||
* This function returns zero in case of success and a negative error code in
|
||||
* case of failure.
|
||||
* This is a helper function for 'ubi_eba_init()' which is called when UBI
|
||||
* cannot reserve enough PEBs for bad block handling. This function makes a
|
||||
* decision whether we have to print a warning or not. The algorithm is as
|
||||
* follows:
|
||||
* o if this is a new UBI image, then just print the warning
|
||||
* o if this is an UBI image which has already been used for some time, print
|
||||
* a warning only if we can reserve less than 10% of the expected amount of
|
||||
* the reserved PEB.
|
||||
*
|
||||
* The idea is that when UBI is used, PEBs become bad, and the reserved pool
|
||||
* of PEBs becomes smaller, which is normal and we do not want to scare users
|
||||
* with a warning every time they attach the MTD device. This was an issue
|
||||
* reported by real users.
|
||||
*/
|
||||
int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
static void print_rsvd_warning(struct ubi_device *ubi,
|
||||
struct ubi_attach_info *ai)
|
||||
{
|
||||
int i, j, err, num_volumes;
|
||||
struct ubi_scan_volume *sv;
|
||||
/*
|
||||
* The 1 << 18 (256KiB) number is picked randomly, just a reasonably
|
||||
* large number to distinguish between newly flashed and used images.
|
||||
*/
|
||||
if (ai->max_sqnum > (1 << 18)) {
|
||||
int min = ubi->beb_rsvd_level / 10;
|
||||
|
||||
if (!min)
|
||||
min = 1;
|
||||
if (ubi->beb_rsvd_pebs > min)
|
||||
return;
|
||||
}
|
||||
|
||||
ubi_warn("cannot reserve enough PEBs for bad PEB handling, reserved %d, need %d",
|
||||
ubi->beb_rsvd_pebs, ubi->beb_rsvd_level);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_warn("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* self_check_eba - run a self check on the EBA table constructed by fastmap.
|
||||
* @ubi: UBI device description object
|
||||
* @ai_fastmap: UBI attach info object created by fastmap
|
||||
* @ai_scan: UBI attach info object created by scanning
|
||||
*
|
||||
* Returns < 0 in case of an internal error, 0 otherwise.
|
||||
* If a bad EBA table entry was found it will be printed out and
|
||||
* ubi_assert() triggers.
|
||||
*/
|
||||
int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap,
|
||||
struct ubi_attach_info *ai_scan)
|
||||
{
|
||||
int i, j, num_volumes, ret = 0;
|
||||
int **scan_eba, **fm_eba;
|
||||
struct ubi_ainf_volume *av;
|
||||
struct ubi_volume *vol;
|
||||
struct ubi_scan_leb *seb;
|
||||
struct ubi_ainf_peb *aeb;
|
||||
struct rb_node *rb;
|
||||
|
||||
dbg_eba("initialize EBA unit");
|
||||
|
||||
spin_lock_init(&ubi->ltree_lock);
|
||||
mutex_init(&ubi->alc_mutex);
|
||||
ubi->ltree = RB_ROOT;
|
||||
|
||||
ubi->global_sqnum = si->max_sqnum + 1;
|
||||
num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
|
||||
|
||||
scan_eba = kmalloc(sizeof(*scan_eba) * num_volumes, GFP_KERNEL);
|
||||
if (!scan_eba)
|
||||
return -ENOMEM;
|
||||
|
||||
fm_eba = kmalloc(sizeof(*fm_eba) * num_volumes, GFP_KERNEL);
|
||||
if (!fm_eba) {
|
||||
kfree(scan_eba);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_volumes; i++) {
|
||||
vol = ubi->volumes[i];
|
||||
if (!vol)
|
||||
continue;
|
||||
|
||||
cond_resched();
|
||||
scan_eba[i] = kmalloc(vol->reserved_pebs * sizeof(**scan_eba),
|
||||
GFP_KERNEL);
|
||||
if (!scan_eba[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
fm_eba[i] = kmalloc(vol->reserved_pebs * sizeof(**fm_eba),
|
||||
GFP_KERNEL);
|
||||
if (!fm_eba[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
for (j = 0; j < vol->reserved_pebs; j++)
|
||||
scan_eba[i][j] = fm_eba[i][j] = UBI_LEB_UNMAPPED;
|
||||
|
||||
av = ubi_find_av(ai_scan, idx2vol_id(ubi, i));
|
||||
if (!av)
|
||||
continue;
|
||||
|
||||
ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb)
|
||||
scan_eba[i][aeb->lnum] = aeb->pnum;
|
||||
|
||||
av = ubi_find_av(ai_fastmap, idx2vol_id(ubi, i));
|
||||
if (!av)
|
||||
continue;
|
||||
|
||||
ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb)
|
||||
fm_eba[i][aeb->lnum] = aeb->pnum;
|
||||
|
||||
for (j = 0; j < vol->reserved_pebs; j++) {
|
||||
if (scan_eba[i][j] != fm_eba[i][j]) {
|
||||
if (scan_eba[i][j] == UBI_LEB_UNMAPPED ||
|
||||
fm_eba[i][j] == UBI_LEB_UNMAPPED)
|
||||
continue;
|
||||
|
||||
ubi_err("LEB:%i:%i is PEB:%i instead of %i!",
|
||||
vol->vol_id, i, fm_eba[i][j],
|
||||
scan_eba[i][j]);
|
||||
ubi_assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out_free:
|
||||
for (i = 0; i < num_volumes; i++) {
|
||||
if (!ubi->volumes[i])
|
||||
continue;
|
||||
|
||||
kfree(scan_eba[i]);
|
||||
kfree(fm_eba[i]);
|
||||
}
|
||||
|
||||
kfree(scan_eba);
|
||||
kfree(fm_eba);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_eba_init - initialize the EBA sub-system using attaching information.
|
||||
* @ubi: UBI device description object
|
||||
* @ai: attaching information
|
||||
*
|
||||
* This function returns zero in case of success and a negative error code in
|
||||
* case of failure.
|
||||
*/
|
||||
int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
||||
{
|
||||
int i, j, err, num_volumes;
|
||||
struct ubi_ainf_volume *av;
|
||||
struct ubi_volume *vol;
|
||||
struct ubi_ainf_peb *aeb;
|
||||
struct rb_node *rb;
|
||||
|
||||
dbg_eba("initialize EBA sub-system");
|
||||
|
||||
ubi->ltree = RB_ROOT;
|
||||
|
||||
ubi->global_sqnum = ai->max_sqnum + 1;
|
||||
num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
|
||||
|
||||
for (i = 0; i < num_volumes; i++) {
|
||||
vol = ubi->volumes[i];
|
||||
if (!vol)
|
||||
continue;
|
||||
|
||||
vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int),
|
||||
GFP_KERNEL);
|
||||
|
@ -1183,24 +1297,27 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
|
|||
for (j = 0; j < vol->reserved_pebs; j++)
|
||||
vol->eba_tbl[j] = UBI_LEB_UNMAPPED;
|
||||
|
||||
sv = ubi_scan_find_sv(si, idx2vol_id(ubi, i));
|
||||
if (!sv)
|
||||
av = ubi_find_av(ai, idx2vol_id(ubi, i));
|
||||
if (!av)
|
||||
continue;
|
||||
|
||||
ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
|
||||
if (seb->lnum >= vol->reserved_pebs)
|
||||
ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) {
|
||||
if (aeb->lnum >= vol->reserved_pebs)
|
||||
/*
|
||||
* This may happen in case of an unclean reboot
|
||||
* during re-size.
|
||||
*/
|
||||
ubi_scan_move_to_list(sv, seb, &si->erase);
|
||||
vol->eba_tbl[seb->lnum] = seb->pnum;
|
||||
ubi_move_aeb_to_list(av, aeb, &ai->erase);
|
||||
vol->eba_tbl[aeb->lnum] = aeb->pnum;
|
||||
}
|
||||
}
|
||||
|
||||
if (ubi->avail_pebs < EBA_RESERVED_PEBS) {
|
||||
ubi_err("no enough physical eraseblocks (%d, need %d)",
|
||||
ubi->avail_pebs, EBA_RESERVED_PEBS);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_err("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
err = -ENOSPC;
|
||||
goto out_free;
|
||||
}
|
||||
|
@ -1213,9 +1330,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
|
|||
if (ubi->avail_pebs < ubi->beb_rsvd_level) {
|
||||
/* No enough free physical eraseblocks */
|
||||
ubi->beb_rsvd_pebs = ubi->avail_pebs;
|
||||
ubi_warn("cannot reserve enough PEBs for bad PEB "
|
||||
"handling, reserved %d, need %d",
|
||||
ubi->beb_rsvd_pebs, ubi->beb_rsvd_level);
|
||||
print_rsvd_warning(ubi, ai);
|
||||
} else
|
||||
ubi->beb_rsvd_pebs = ubi->beb_rsvd_level;
|
||||
|
||||
|
@ -1223,7 +1338,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
|
|||
ubi->rsvd_pebs += ubi->beb_rsvd_pebs;
|
||||
}
|
||||
|
||||
dbg_eba("EBA unit is initialized");
|
||||
dbg_eba("EBA sub-system is initialized");
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
|
@ -1231,23 +1346,7 @@ out_free:
|
|||
if (!ubi->volumes[i])
|
||||
continue;
|
||||
kfree(ubi->volumes[i]->eba_tbl);
|
||||
ubi->volumes[i]->eba_tbl = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_eba_close - close EBA unit.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
void ubi_eba_close(const struct ubi_device *ubi)
|
||||
{
|
||||
int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
|
||||
|
||||
dbg_eba("close EBA unit");
|
||||
|
||||
for (i = 0; i < num_volumes; i++) {
|
||||
if (!ubi->volumes[i])
|
||||
continue;
|
||||
kfree(ubi->volumes[i]->eba_tbl);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -11,77 +11,19 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
||||
/* This file mostly implements UBI kernel API functions */
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <asm/div64.h>
|
||||
#endif
|
||||
|
||||
#include "ubi-barebox.h"
|
||||
#include <asm-generic/div64.h>
|
||||
#include "ubi.h"
|
||||
|
||||
/**
|
||||
* ubi_get_device_info - get information about UBI device.
|
||||
* @ubi_num: UBI device number
|
||||
* @di: the information is stored here
|
||||
*
|
||||
* This function returns %0 in case of success, %-EINVAL if the UBI device
|
||||
* number is invalid, and %-ENODEV if there is no such UBI device.
|
||||
*/
|
||||
int ubi_get_device_info(int ubi_num, struct ubi_device_info *di)
|
||||
{
|
||||
struct ubi_device *ubi;
|
||||
|
||||
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
|
||||
return -EINVAL;
|
||||
|
||||
ubi = ubi_get_device(ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
|
||||
di->ubi_num = ubi->ubi_num;
|
||||
di->leb_size = ubi->leb_size;
|
||||
di->min_io_size = ubi->min_io_size;
|
||||
di->ro_mode = ubi->ro_mode;
|
||||
di->cdev = &ubi->cdev;
|
||||
|
||||
ubi_put_device(ubi);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_get_device_info);
|
||||
|
||||
/**
|
||||
* ubi_get_volume_info - get information about UBI volume.
|
||||
* @desc: volume descriptor
|
||||
* @vi: the information is stored here
|
||||
*/
|
||||
void ubi_get_volume_info(struct ubi_volume_desc *desc,
|
||||
struct ubi_volume_info *vi)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
|
||||
vi->vol_id = vol->vol_id;
|
||||
vi->ubi_num = ubi->ubi_num;
|
||||
vi->size = vol->reserved_pebs;
|
||||
vi->used_bytes = vol->used_bytes;
|
||||
vi->vol_type = vol->vol_type;
|
||||
vi->corrupted = vol->corrupted;
|
||||
vi->upd_marker = vol->upd_marker;
|
||||
vi->alignment = vol->alignment;
|
||||
vi->usable_leb_size = vol->usable_leb_size;
|
||||
vi->name_len = vol->name_len;
|
||||
vi->name = vol->name;
|
||||
vi->cdev = &vol->cdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_get_volume_info);
|
||||
|
||||
/**
|
||||
* ubi_open_volume - open UBI volume.
|
||||
* @ubi_num: UBI device number
|
||||
|
@ -107,7 +49,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
|
|||
struct ubi_device *ubi;
|
||||
struct ubi_volume *vol;
|
||||
|
||||
dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode);
|
||||
dbg_gen("open device %d, volume %d, mode %d", ubi_num, vol_id, mode);
|
||||
|
||||
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -135,10 +77,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
|
|||
}
|
||||
|
||||
err = -ENODEV;
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
goto out_free;
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
vol = ubi->volumes[vol_id];
|
||||
if (!vol)
|
||||
goto out_unlock;
|
||||
|
@ -163,19 +102,15 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
|
|||
vol->exclusive = 1;
|
||||
break;
|
||||
}
|
||||
get_device(&vol->dev);
|
||||
vol->ref_count += 1;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
desc->vol = vol;
|
||||
desc->mode = mode;
|
||||
|
||||
mutex_lock(&ubi->ckvol_mutex);
|
||||
if (!vol->checked) {
|
||||
/* This is the first open - check the volume */
|
||||
err = ubi_check_volume(ubi, vol_id);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&ubi->ckvol_mutex);
|
||||
ubi_close_volume(desc);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
@ -186,17 +121,15 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
|
|||
}
|
||||
vol->checked = 1;
|
||||
}
|
||||
mutex_unlock(&ubi->ckvol_mutex);
|
||||
|
||||
return desc;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
module_put(THIS_MODULE);
|
||||
out_free:
|
||||
kfree(desc);
|
||||
out_put_ubi:
|
||||
ubi_put_device(ubi);
|
||||
ubi_err("cannot open device %d, volume %d, error %d",
|
||||
ubi_num, vol_id, err);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_open_volume);
|
||||
|
@ -216,7 +149,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
|
|||
struct ubi_device *ubi;
|
||||
struct ubi_volume_desc *ret;
|
||||
|
||||
dbg_msg("open volume %s, mode %d", name, mode);
|
||||
dbg_gen("open device %d, volume %s, mode %d", ubi_num, name, mode);
|
||||
|
||||
if (!name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -232,7 +165,6 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
|
|||
if (!ubi)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
/* Walk all volumes of this UBI device */
|
||||
for (i = 0; i < ubi->vtbl_slots; i++) {
|
||||
struct ubi_volume *vol = ubi->volumes[i];
|
||||
|
@ -242,7 +174,6 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
|
|||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
if (vol_id >= 0)
|
||||
ret = ubi_open_volume(ubi_num, vol_id, mode);
|
||||
|
@ -267,9 +198,9 @@ void ubi_close_volume(struct ubi_volume_desc *desc)
|
|||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
|
||||
dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode);
|
||||
dbg_gen("close device %d, volume %d, mode %d",
|
||||
ubi->ubi_num, vol->vol_id, desc->mode);
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
switch (desc->mode) {
|
||||
case UBI_READONLY:
|
||||
vol->readers -= 1;
|
||||
|
@ -281,12 +212,9 @@ void ubi_close_volume(struct ubi_volume_desc *desc)
|
|||
vol->exclusive = 0;
|
||||
}
|
||||
vol->ref_count -= 1;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
kfree(desc);
|
||||
put_device(&vol->dev);
|
||||
ubi_put_device(ubi);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_close_volume);
|
||||
|
||||
|
@ -324,7 +252,7 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
|
|||
struct ubi_device *ubi = vol->ubi;
|
||||
int err, vol_id = vol->vol_id;
|
||||
|
||||
dbg_msg("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset);
|
||||
dbg_gen("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset);
|
||||
|
||||
if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 ||
|
||||
lnum >= vol->used_ebs || offset < 0 || len < 0 ||
|
||||
|
@ -346,7 +274,7 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
|
|||
return 0;
|
||||
|
||||
err = ubi_eba_read_leb(ubi, vol, lnum, buf, offset, len, check);
|
||||
if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) {
|
||||
if (err && mtd_is_eccerr(err) && vol->vol_type == UBI_STATIC_VOLUME) {
|
||||
ubi_warn("mark volume %d as corrupted", vol_id);
|
||||
vol->corrupted = 1;
|
||||
}
|
||||
|
@ -362,11 +290,9 @@ EXPORT_SYMBOL_GPL(ubi_leb_read);
|
|||
* @buf: data to write
|
||||
* @offset: offset within the logical eraseblock where to write
|
||||
* @len: how many bytes to write
|
||||
* @dtype: expected data type
|
||||
*
|
||||
* This function writes @len bytes of data from @buf to offset @offset of
|
||||
* logical eraseblock @lnum. The @dtype argument describes expected lifetime of
|
||||
* the data.
|
||||
* logical eraseblock @lnum.
|
||||
*
|
||||
* This function takes care of physical eraseblock write failures. If write to
|
||||
* the physical eraseblock write operation fails, the logical eraseblock is
|
||||
|
@ -383,13 +309,13 @@ EXPORT_SYMBOL_GPL(ubi_leb_read);
|
|||
* returns immediately with %-EBADF code.
|
||||
*/
|
||||
int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
int offset, int len, int dtype)
|
||||
int offset, int len)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
int vol_id = vol->vol_id;
|
||||
|
||||
dbg_msg("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset);
|
||||
dbg_gen("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset);
|
||||
|
||||
if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
|
||||
return -EINVAL;
|
||||
|
@ -402,17 +328,13 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
|||
offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
|
||||
dtype != UBI_UNKNOWN)
|
||||
return -EINVAL;
|
||||
|
||||
if (vol->upd_marker)
|
||||
return -EBADF;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len, dtype);
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_leb_write);
|
||||
|
||||
|
@ -422,24 +344,23 @@ EXPORT_SYMBOL_GPL(ubi_leb_write);
|
|||
* @lnum: logical eraseblock number to change
|
||||
* @buf: data to write
|
||||
* @len: how many bytes to write
|
||||
* @dtype: expected data type
|
||||
*
|
||||
* This function changes the contents of a logical eraseblock atomically. @buf
|
||||
* has to contain new logical eraseblock data, and @len - the length of the
|
||||
* data, which has to be aligned. The length may be shorter then the logical
|
||||
* data, which has to be aligned. The length may be shorter than the logical
|
||||
* eraseblock size, ant the logical eraseblock may be appended to more times
|
||||
* later on. This function guarantees that in case of an unclean reboot the old
|
||||
* contents is preserved. Returns zero in case of success and a negative error
|
||||
* code in case of failure.
|
||||
*/
|
||||
int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
int len, int dtype)
|
||||
int len)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
int vol_id = vol->vol_id;
|
||||
|
||||
dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum);
|
||||
dbg_gen("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum);
|
||||
|
||||
if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
|
||||
return -EINVAL;
|
||||
|
@ -451,17 +372,13 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
|||
len > vol->usable_leb_size || len & (ubi->min_io_size - 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
|
||||
dtype != UBI_UNKNOWN)
|
||||
return -EINVAL;
|
||||
|
||||
if (vol->upd_marker)
|
||||
return -EBADF;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len, dtype);
|
||||
return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_leb_change);
|
||||
|
||||
|
@ -483,7 +400,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
|
|||
struct ubi_device *ubi = vol->ubi;
|
||||
int err;
|
||||
|
||||
dbg_msg("erase LEB %d:%d", vol->vol_id, lnum);
|
||||
dbg_gen("erase LEB %d:%d", vol->vol_id, lnum);
|
||||
|
||||
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
|
||||
return -EROFS;
|
||||
|
@ -498,7 +415,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
return ubi_wl_flush(ubi);
|
||||
return ubi_wl_flush(ubi, vol->vol_id, lnum);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_leb_erase);
|
||||
|
||||
|
@ -509,7 +426,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase);
|
|||
*
|
||||
* This function un-maps logical eraseblock @lnum and schedules the
|
||||
* corresponding physical eraseblock for erasure, so that it will eventually be
|
||||
* physically erased in background. This operation is much faster then the
|
||||
* physically erased in background. This operation is much faster than the
|
||||
* erase operation.
|
||||
*
|
||||
* Unlike erase, the un-map operation does not guarantee that the logical
|
||||
|
@ -528,7 +445,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase);
|
|||
*
|
||||
* The main and obvious use-case of this function is when the contents of a
|
||||
* logical eraseblock has to be re-written. Then it is much more efficient to
|
||||
* first un-map it, then write new data, rather then first erase it, then write
|
||||
* first un-map it, then write new data, rather than first erase it, then write
|
||||
* new data. Note, once new data has been written to the logical eraseblock,
|
||||
* UBI guarantees that the old contents has gone forever. In other words, if an
|
||||
* unclean reboot happens after the logical eraseblock has been un-mapped and
|
||||
|
@ -543,7 +460,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
|
|||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
|
||||
dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum);
|
||||
dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum);
|
||||
|
||||
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
|
||||
return -EROFS;
|
||||
|
@ -559,13 +476,12 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
|
|||
EXPORT_SYMBOL_GPL(ubi_leb_unmap);
|
||||
|
||||
/**
|
||||
* ubi_leb_map - map logical erasblock to a physical eraseblock.
|
||||
* ubi_leb_map - map logical eraseblock to a physical eraseblock.
|
||||
* @desc: volume descriptor
|
||||
* @lnum: logical eraseblock number
|
||||
* @dtype: expected data type
|
||||
*
|
||||
* This function maps an un-mapped logical eraseblock @lnum to a physical
|
||||
* eraseblock. This means, that after a successfull invocation of this
|
||||
* eraseblock. This means, that after a successful invocation of this
|
||||
* function the logical eraseblock @lnum will be empty (contain only %0xFF
|
||||
* bytes) and be mapped to a physical eraseblock, even if an unclean reboot
|
||||
* happens.
|
||||
|
@ -575,12 +491,12 @@ EXPORT_SYMBOL_GPL(ubi_leb_unmap);
|
|||
* eraseblock is already mapped, and other negative error codes in case of
|
||||
* other failures.
|
||||
*/
|
||||
int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
|
||||
int ubi_leb_map(struct ubi_volume_desc *desc, int lnum)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
|
||||
dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum);
|
||||
dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum);
|
||||
|
||||
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
|
||||
return -EROFS;
|
||||
|
@ -588,17 +504,13 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
|
|||
if (lnum < 0 || lnum >= vol->reserved_pebs)
|
||||
return -EINVAL;
|
||||
|
||||
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
|
||||
dtype != UBI_UNKNOWN)
|
||||
return -EINVAL;
|
||||
|
||||
if (vol->upd_marker)
|
||||
return -EBADF;
|
||||
|
||||
if (vol->eba_tbl[lnum] >= 0)
|
||||
return -EBADMSG;
|
||||
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype);
|
||||
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_leb_map);
|
||||
|
||||
|
@ -622,7 +534,7 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
|
|||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
|
||||
dbg_msg("test LEB %d:%d", vol->vol_id, lnum);
|
||||
dbg_gen("test LEB %d:%d", vol->vol_id, lnum);
|
||||
|
||||
if (lnum < 0 || lnum >= vol->reserved_pebs)
|
||||
return -EINVAL;
|
||||
|
@ -633,3 +545,30 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
|
|||
return vol->eba_tbl[lnum] >= 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_is_mapped);
|
||||
|
||||
/**
|
||||
* ubi_flush - flush UBI work queue.
|
||||
* @ubi_num: UBI device to flush work queue
|
||||
* @vol_id: volume id to flush for
|
||||
* @lnum: logical eraseblock number to flush for
|
||||
*
|
||||
* This function executes all pending works for a particular volume id / logical
|
||||
* eraseblock number pair. If either value is set to %UBI_ALL, then it acts as
|
||||
* a wildcard for all of the corresponding volume numbers or logical
|
||||
* eraseblock numbers. It returns zero in case of success and a negative error
|
||||
* code in case of failure.
|
||||
*/
|
||||
int ubi_flush(int ubi_num, int vol_id, int lnum)
|
||||
{
|
||||
struct ubi_device *ubi;
|
||||
int err = 0;
|
||||
|
||||
ubi = ubi_get_device(ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
|
||||
err = ubi_wl_flush(ubi, vol_id, lnum);
|
||||
ubi_put_device(ubi);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ubi_flush);
|
||||
|
|
|
@ -11,13 +11,15 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
||||
/* Here we keep miscellaneous functions which are used all over the UBI code */
|
||||
|
||||
#include "ubi-barebox.h"
|
||||
#include "ubi.h"
|
||||
|
||||
/**
|
||||
|
@ -79,7 +81,7 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id)
|
|||
|
||||
err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1);
|
||||
if (err) {
|
||||
if (err == -EBADMSG)
|
||||
if (mtd_is_eccerr(err))
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -90,14 +92,62 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id)
|
|||
}
|
||||
|
||||
/**
|
||||
* ubi_calculate_rsvd_pool - calculate how many PEBs must be reserved for bad
|
||||
* ubi_update_reserved - update bad eraseblock handling accounting data.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* This function calculates the gap between current number of PEBs reserved for
|
||||
* bad eraseblock handling and the required level of PEBs that must be
|
||||
* reserved, and if necessary, reserves more PEBs to fill that gap, according
|
||||
* to availability. Should be called with ubi->volumes_lock held.
|
||||
*/
|
||||
void ubi_update_reserved(struct ubi_device *ubi)
|
||||
{
|
||||
int need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
|
||||
|
||||
if (need <= 0 || ubi->avail_pebs == 0)
|
||||
return;
|
||||
|
||||
need = min_t(int, need, ubi->avail_pebs);
|
||||
ubi->avail_pebs -= need;
|
||||
ubi->rsvd_pebs += need;
|
||||
ubi->beb_rsvd_pebs += need;
|
||||
ubi_msg("reserved more %d PEBs for bad PEB handling", need);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_calculate_reserved - calculate how many PEBs must be reserved for bad
|
||||
* eraseblock handling.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
void ubi_calculate_reserved(struct ubi_device *ubi)
|
||||
{
|
||||
ubi->beb_rsvd_level = ubi->good_peb_count/100;
|
||||
ubi->beb_rsvd_level *= CONFIG_MTD_UBI_BEB_RESERVE;
|
||||
if (ubi->beb_rsvd_level < MIN_RESEVED_PEBS)
|
||||
ubi->beb_rsvd_level = MIN_RESEVED_PEBS;
|
||||
/*
|
||||
* Calculate the actual number of PEBs currently needed to be reserved
|
||||
* for future bad eraseblock handling.
|
||||
*/
|
||||
ubi->beb_rsvd_level = ubi->bad_peb_limit - ubi->bad_peb_count;
|
||||
if (ubi->beb_rsvd_level < 0) {
|
||||
ubi->beb_rsvd_level = 0;
|
||||
ubi_warn("number of bad PEBs (%d) is above the expected limit (%d), not reserving any PEBs for bad PEB handling, will use available PEBs (if any)",
|
||||
ubi->bad_peb_count, ubi->bad_peb_limit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_check_pattern - check if buffer contains only a certain byte pattern.
|
||||
* @buf: buffer to check
|
||||
* @patt: the pattern to check
|
||||
* @size: buffer size in bytes
|
||||
*
|
||||
* This function returns %1 in there are only @patt bytes in @buf, and %0 if
|
||||
* something else was also found.
|
||||
*/
|
||||
int ubi_check_pattern(const void *buf, uint8_t patt, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
if (((const uint8_t *)buf)[i] != patt)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) International Business Machines Corp., 2006
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
||||
#ifndef __UBI_SCAN_H__
|
||||
#define __UBI_SCAN_H__
|
||||
|
||||
/* The erase counter value for this physical eraseblock is unknown */
|
||||
#define UBI_SCAN_UNKNOWN_EC (-1)
|
||||
|
||||
/**
|
||||
* struct ubi_scan_leb - scanning information about a physical eraseblock.
|
||||
* @ec: erase counter (%UBI_SCAN_UNKNOWN_EC if it is unknown)
|
||||
* @pnum: physical eraseblock number
|
||||
* @lnum: logical eraseblock number
|
||||
* @scrub: if this physical eraseblock needs scrubbing
|
||||
* @sqnum: sequence number
|
||||
* @u: unions RB-tree or @list links
|
||||
* @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects
|
||||
* @u.list: link in one of the eraseblock lists
|
||||
* @leb_ver: logical eraseblock version (obsolete)
|
||||
*
|
||||
* One object of this type is allocated for each physical eraseblock during
|
||||
* scanning.
|
||||
*/
|
||||
struct ubi_scan_leb {
|
||||
int ec;
|
||||
int pnum;
|
||||
int lnum;
|
||||
int scrub;
|
||||
unsigned long long sqnum;
|
||||
union {
|
||||
struct rb_node rb;
|
||||
struct list_head list;
|
||||
} u;
|
||||
uint32_t leb_ver;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_scan_volume - scanning information about a volume.
|
||||
* @vol_id: volume ID
|
||||
* @highest_lnum: highest logical eraseblock number in this volume
|
||||
* @leb_count: number of logical eraseblocks in this volume
|
||||
* @vol_type: volume type
|
||||
* @used_ebs: number of used logical eraseblocks in this volume (only for
|
||||
* static volumes)
|
||||
* @last_data_size: amount of data in the last logical eraseblock of this
|
||||
* volume (always equivalent to the usable logical eraseblock size in case of
|
||||
* dynamic volumes)
|
||||
* @data_pad: how many bytes at the end of logical eraseblocks of this volume
|
||||
* are not used (due to volume alignment)
|
||||
* @compat: compatibility flags of this volume
|
||||
* @rb: link in the volume RB-tree
|
||||
* @root: root of the RB-tree containing all the eraseblock belonging to this
|
||||
* volume (&struct ubi_scan_leb objects)
|
||||
*
|
||||
* One object of this type is allocated for each volume during scanning.
|
||||
*/
|
||||
struct ubi_scan_volume {
|
||||
int vol_id;
|
||||
int highest_lnum;
|
||||
int leb_count;
|
||||
int vol_type;
|
||||
int used_ebs;
|
||||
int last_data_size;
|
||||
int data_pad;
|
||||
int compat;
|
||||
struct rb_node rb;
|
||||
struct rb_root root;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_scan_info - UBI scanning information.
|
||||
* @volumes: root of the volume RB-tree
|
||||
* @corr: list of corrupted physical eraseblocks
|
||||
* @free: list of free physical eraseblocks
|
||||
* @erase: list of physical eraseblocks which have to be erased
|
||||
* @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
|
||||
* @bad_peb_count: count of bad physical eraseblocks
|
||||
* those belonging to "preserve"-compatible internal volumes)
|
||||
* @vols_found: number of volumes found during scanning
|
||||
* @highest_vol_id: highest volume ID
|
||||
* @alien_peb_count: count of physical eraseblocks in the @alien list
|
||||
* @is_empty: flag indicating whether the MTD device is empty or not
|
||||
* @min_ec: lowest erase counter value
|
||||
* @max_ec: highest erase counter value
|
||||
* @max_sqnum: highest sequence number value
|
||||
* @mean_ec: mean erase counter value
|
||||
* @ec_sum: a temporary variable used when calculating @mean_ec
|
||||
* @ec_count: a temporary variable used when calculating @mean_ec
|
||||
*
|
||||
* This data structure contains the result of scanning and may be used by other
|
||||
* UBI units to build final UBI data structures, further error-recovery and so
|
||||
* on.
|
||||
*/
|
||||
struct ubi_scan_info {
|
||||
struct rb_root volumes;
|
||||
struct list_head corr;
|
||||
struct list_head free;
|
||||
struct list_head erase;
|
||||
struct list_head alien;
|
||||
int bad_peb_count;
|
||||
int vols_found;
|
||||
int highest_vol_id;
|
||||
int alien_peb_count;
|
||||
int is_empty;
|
||||
int min_ec;
|
||||
int max_ec;
|
||||
unsigned long long max_sqnum;
|
||||
int mean_ec;
|
||||
uint64_t ec_sum;
|
||||
int ec_count;
|
||||
};
|
||||
|
||||
struct ubi_device;
|
||||
struct ubi_vid_hdr;
|
||||
|
||||
/*
|
||||
* ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a
|
||||
* list.
|
||||
*
|
||||
* @sv: volume scanning information
|
||||
* @seb: scanning eraseblock infprmation
|
||||
* @list: the list to move to
|
||||
*/
|
||||
static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv,
|
||||
struct ubi_scan_leb *seb,
|
||||
struct list_head *list)
|
||||
{
|
||||
rb_erase(&seb->u.rb, &sv->root);
|
||||
list_add_tail(&seb->u.list, list);
|
||||
}
|
||||
|
||||
int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
|
||||
int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
|
||||
int bitflips);
|
||||
struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
|
||||
int vol_id);
|
||||
struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv,
|
||||
int lnum);
|
||||
void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv);
|
||||
struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi,
|
||||
struct ubi_scan_info *si);
|
||||
int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
int pnum, int ec);
|
||||
struct ubi_scan_info *ubi_scan(struct ubi_device *ubi);
|
||||
void ubi_scan_destroy_si(struct ubi_scan_info *si);
|
||||
|
||||
#endif /* !__UBI_SCAN_H__ */
|
|
@ -25,74 +25,20 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/ubi.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#define crc32(seed, data, length) crc32_no_comp(seed, (unsigned char const *)data, length)
|
||||
|
||||
#define DPRINTK(format, args...) \
|
||||
do { \
|
||||
printf("%s[%d]: " format "\n", __func__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
/* configurable */
|
||||
#define CONFIG_MTD_UBI_WL_THRESHOLD 4096
|
||||
#define CONFIG_MTD_UBI_BEB_RESERVE 1
|
||||
#define UBI_IO_DEBUG 0
|
||||
|
||||
/* debug options (Linux: drivers/mtd/ubi/Kconfig.debug) */
|
||||
#undef CONFIG_MTD_UBI_DEBUG
|
||||
#undef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||
#undef CONFIG_MTD_UBI_DEBUG_MSG
|
||||
#undef CONFIG_MTD_UBI_DEBUG_MSG_EBA
|
||||
#undef CONFIG_MTD_UBI_DEBUG_MSG_WL
|
||||
#undef CONFIG_MTD_UBI_DEBUG_MSG_IO
|
||||
#undef CONFIG_MTD_UBI_DEBUG_MSG_BLD
|
||||
#define CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
|
||||
|
||||
/* build.c */
|
||||
#define get_device(...)
|
||||
#define put_device(...)
|
||||
#define ubi_sysfs_init(...) 0
|
||||
#define ubi_sysfs_close(...) do { } while (0)
|
||||
|
||||
/* FIXME */
|
||||
#define MKDEV(...) 0
|
||||
#define MAJOR(dev) 0
|
||||
#define MINOR(dev) 0
|
||||
|
||||
#define alloc_chrdev_region(...) 0
|
||||
#define unregister_chrdev_region(...)
|
||||
|
||||
#define class_create(...) __builtin_return_address(0)
|
||||
#define class_create_file(...) 0
|
||||
#define class_remove_file(...)
|
||||
#define class_destroy(...)
|
||||
#define misc_register(...) 0
|
||||
#define misc_deregister(...)
|
||||
|
||||
/* vmt.c */
|
||||
#define device_register(...) 0
|
||||
#define volume_sysfs_init(...) 0
|
||||
#define volume_sysfs_close(...) do { } while (0)
|
||||
|
||||
/* kapi.c */
|
||||
|
||||
/* eba.c */
|
||||
|
||||
/* io.c */
|
||||
#define init_waitqueue_head(...) do { } while (0)
|
||||
#define wait_event_interruptible(...) 0
|
||||
#define wake_up_interruptible(...) do { } while (0)
|
||||
#define print_hex_dump(...) do { } while (0)
|
||||
#define dump_stack(...) do { } while (0)
|
||||
|
||||
/* wl.c */
|
||||
#define task_pid_nr(x) 0
|
||||
#define set_freezable(...) do { } while (0)
|
||||
#define try_to_freeze(...) 0
|
||||
#define set_current_state(...) do { } while (0)
|
||||
#define kthread_should_stop(...) 0
|
||||
#define schedule() do { } while (0)
|
||||
#define DUMP_PREFIX_OFFSET 0
|
||||
static inline void print_hex_dump(const char *level, const char *prefix_str,
|
||||
int prefix_type, int rowsize, int groupsize,
|
||||
const void *buf, size_t len, bool ascii)
|
||||
{
|
||||
memory_display(buf, 0, len, 4, 0);
|
||||
}
|
||||
|
||||
/* upd.c */
|
||||
static inline unsigned long copy_from_user(void *dest, const void *src,
|
||||
|
@ -103,84 +49,23 @@ static inline unsigned long copy_from_user(void *dest, const void *src,
|
|||
}
|
||||
|
||||
/* common */
|
||||
typedef int spinlock_t;
|
||||
typedef int wait_queue_head_t;
|
||||
#define spin_lock_init(...)
|
||||
#define spin_lock(...)
|
||||
#define spin_unlock(...)
|
||||
|
||||
#define mutex_init(...)
|
||||
#define mutex_lock(...)
|
||||
#define mutex_unlock(...)
|
||||
|
||||
#define init_rwsem(...) do { } while (0)
|
||||
#define down_read(...) do { } while (0)
|
||||
#define down_write(...) do { } while (0)
|
||||
#define down_write_trylock(...) 1
|
||||
#define up_read(...) do { } while (0)
|
||||
#define up_write(...) do { } while (0)
|
||||
|
||||
struct kmem_cache { int i; };
|
||||
#define kmem_cache_create(...) 1
|
||||
#define kmem_cache_alloc(obj, gfp) malloc(sizeof(struct ubi_wl_entry))
|
||||
#define kmem_cache_free(obj, size) free(size)
|
||||
#define kmem_cache_destroy(...)
|
||||
|
||||
#define cond_resched() do { } while (0)
|
||||
#define yield() do { } while (0)
|
||||
|
||||
#define GFP_KERNEL 0
|
||||
#define GFP_NOFS 1
|
||||
|
||||
#define __init
|
||||
#define __exit
|
||||
|
||||
#define kthread_create(...) __builtin_return_address(0)
|
||||
#define kthread_stop(...) do { } while (0)
|
||||
#define wake_up_process(...) do { } while (0)
|
||||
|
||||
#define BUS_ID_SIZE 20
|
||||
|
||||
struct rw_semaphore { int i; };
|
||||
struct device {
|
||||
struct device *parent;
|
||||
struct class *class;
|
||||
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
|
||||
dev_t devt; /* dev_t, creates the sysfs "dev" */
|
||||
void (*release)(struct device *dev);
|
||||
};
|
||||
struct mutex { int i; };
|
||||
struct kernel_param { int i; };
|
||||
|
||||
struct cdev_ {
|
||||
int owner;
|
||||
dev_t dev;
|
||||
};
|
||||
#define cdev_init(...) do { } while (0)
|
||||
#define cdev_add(...) 0
|
||||
#define cdev_del(...) do { } while (0)
|
||||
|
||||
#define MAX_ERRNO 4095
|
||||
|
||||
/* module */
|
||||
#define THIS_MODULE 0
|
||||
#define try_module_get(...) 1
|
||||
#define module_put(...) do { } while (0)
|
||||
#define module_init(...)
|
||||
#define module_exit(...)
|
||||
#define module_param_call(...)
|
||||
#define MODULE_PARM_DESC(...)
|
||||
#define MODULE_VERSION(...)
|
||||
|
||||
#ifndef __UBIFS_H__
|
||||
#include "ubi.h"
|
||||
#endif
|
||||
|
||||
/* functions */
|
||||
extern int ubi_mtd_param_parse(const char *val, struct kernel_param *kp);
|
||||
extern int ubi_init(void);
|
||||
extern void ubi_exit(void);
|
||||
|
||||
extern struct ubi_device *ubi_devices[];
|
||||
|
||||
int ubi_cdev_add(struct ubi_device *ubi);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,515 @@
|
|||
/*
|
||||
* Copyright (c) International Business Machines Corp., 2006
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Thomas Gleixner
|
||||
* Frank Haverkamp
|
||||
* Oliver Lohmann
|
||||
* Andreas Arnez
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file defines the layout of UBI headers and all the other UBI on-flash
|
||||
* data structures.
|
||||
*/
|
||||
|
||||
#ifndef __UBI_MEDIA_H__
|
||||
#define __UBI_MEDIA_H__
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/* The version of UBI images supported by this implementation */
|
||||
#define UBI_VERSION 1
|
||||
|
||||
/* The highest erase counter value supported by this implementation */
|
||||
#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF
|
||||
|
||||
/* The initial CRC32 value used when calculating CRC checksums */
|
||||
#define UBI_CRC32_INIT 0xFFFFFFFFU
|
||||
|
||||
/* Erase counter header magic number (ASCII "UBI#") */
|
||||
#define UBI_EC_HDR_MAGIC 0x55424923
|
||||
/* Volume identifier header magic number (ASCII "UBI!") */
|
||||
#define UBI_VID_HDR_MAGIC 0x55424921
|
||||
|
||||
/*
|
||||
* Volume type constants used in the volume identifier header.
|
||||
*
|
||||
* @UBI_VID_DYNAMIC: dynamic volume
|
||||
* @UBI_VID_STATIC: static volume
|
||||
*/
|
||||
enum {
|
||||
UBI_VID_DYNAMIC = 1,
|
||||
UBI_VID_STATIC = 2
|
||||
};
|
||||
|
||||
/*
|
||||
* Volume flags used in the volume table record.
|
||||
*
|
||||
* @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume
|
||||
*
|
||||
* %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume
|
||||
* table. UBI automatically re-sizes the volume which has this flag and makes
|
||||
* the volume to be of largest possible size. This means that if after the
|
||||
* initialization UBI finds out that there are available physical eraseblocks
|
||||
* present on the device, it automatically appends all of them to the volume
|
||||
* (the physical eraseblocks reserved for bad eraseblocks handling and other
|
||||
* reserved physical eraseblocks are not taken). So, if there is a volume with
|
||||
* the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical
|
||||
* eraseblocks will be zero after UBI is loaded, because all of them will be
|
||||
* reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared
|
||||
* after the volume had been initialized.
|
||||
*
|
||||
* The auto-resize feature is useful for device production purposes. For
|
||||
* example, different NAND flash chips may have different amount of initial bad
|
||||
* eraseblocks, depending of particular chip instance. Manufacturers of NAND
|
||||
* chips usually guarantee that the amount of initial bad eraseblocks does not
|
||||
* exceed certain percent, e.g. 2%. When one creates an UBI image which will be
|
||||
* flashed to the end devices in production, he does not know the exact amount
|
||||
* of good physical eraseblocks the NAND chip on the device will have, but this
|
||||
* number is required to calculate the volume sized and put them to the volume
|
||||
* table of the UBI image. In this case, one of the volumes (e.g., the one
|
||||
* which will store the root file system) is marked as "auto-resizable", and
|
||||
* UBI will adjust its size on the first boot if needed.
|
||||
*
|
||||
* Note, first UBI reserves some amount of physical eraseblocks for bad
|
||||
* eraseblock handling, and then re-sizes the volume, not vice-versa. This
|
||||
* means that the pool of reserved physical eraseblocks will always be present.
|
||||
*/
|
||||
enum {
|
||||
UBI_VTBL_AUTORESIZE_FLG = 0x01,
|
||||
};
|
||||
|
||||
/*
|
||||
* Compatibility constants used by internal volumes.
|
||||
*
|
||||
* @UBI_COMPAT_DELETE: delete this internal volume before anything is written
|
||||
* to the flash
|
||||
* @UBI_COMPAT_RO: attach this device in read-only mode
|
||||
* @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its
|
||||
* physical eraseblocks, don't allow the wear-leveling
|
||||
* sub-system to move them
|
||||
* @UBI_COMPAT_REJECT: reject this UBI image
|
||||
*/
|
||||
enum {
|
||||
UBI_COMPAT_DELETE = 1,
|
||||
UBI_COMPAT_RO = 2,
|
||||
UBI_COMPAT_PRESERVE = 4,
|
||||
UBI_COMPAT_REJECT = 5
|
||||
};
|
||||
|
||||
/* Sizes of UBI headers */
|
||||
#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr)
|
||||
#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr)
|
||||
|
||||
/* Sizes of UBI headers without the ending CRC */
|
||||
#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32))
|
||||
#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32))
|
||||
|
||||
/**
|
||||
* struct ubi_ec_hdr - UBI erase counter header.
|
||||
* @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC)
|
||||
* @version: version of UBI implementation which is supposed to accept this
|
||||
* UBI image
|
||||
* @padding1: reserved for future, zeroes
|
||||
* @ec: the erase counter
|
||||
* @vid_hdr_offset: where the VID header starts
|
||||
* @data_offset: where the user data start
|
||||
* @image_seq: image sequence number
|
||||
* @padding2: reserved for future, zeroes
|
||||
* @hdr_crc: erase counter header CRC checksum
|
||||
*
|
||||
* The erase counter header takes 64 bytes and has a plenty of unused space for
|
||||
* future usage. The unused fields are zeroed. The @version field is used to
|
||||
* indicate the version of UBI implementation which is supposed to be able to
|
||||
* work with this UBI image. If @version is greater than the current UBI
|
||||
* version, the image is rejected. This may be useful in future if something
|
||||
* is changed radically. This field is duplicated in the volume identifier
|
||||
* header.
|
||||
*
|
||||
* The @vid_hdr_offset and @data_offset fields contain the offset of the the
|
||||
* volume identifier header and user data, relative to the beginning of the
|
||||
* physical eraseblock. These values have to be the same for all physical
|
||||
* eraseblocks.
|
||||
*
|
||||
* The @image_seq field is used to validate a UBI image that has been prepared
|
||||
* for a UBI device. The @image_seq value can be any value, but it must be the
|
||||
* same on all eraseblocks. UBI will ensure that all new erase counter headers
|
||||
* also contain this value, and will check the value when attaching the flash.
|
||||
* One way to make use of @image_seq is to increase its value by one every time
|
||||
* an image is flashed over an existing image, then, if the flashing does not
|
||||
* complete, UBI will detect the error when attaching the media.
|
||||
*/
|
||||
struct ubi_ec_hdr {
|
||||
__be32 magic;
|
||||
__u8 version;
|
||||
__u8 padding1[3];
|
||||
__be64 ec; /* Warning: the current limit is 31-bit anyway! */
|
||||
__be32 vid_hdr_offset;
|
||||
__be32 data_offset;
|
||||
__be32 image_seq;
|
||||
__u8 padding2[32];
|
||||
__be32 hdr_crc;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_vid_hdr - on-flash UBI volume identifier header.
|
||||
* @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC)
|
||||
* @version: UBI implementation version which is supposed to accept this UBI
|
||||
* image (%UBI_VERSION)
|
||||
* @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
|
||||
* @copy_flag: if this logical eraseblock was copied from another physical
|
||||
* eraseblock (for wear-leveling reasons)
|
||||
* @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE,
|
||||
* %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
|
||||
* @vol_id: ID of this volume
|
||||
* @lnum: logical eraseblock number
|
||||
* @padding1: reserved for future, zeroes
|
||||
* @data_size: how many bytes of data this logical eraseblock contains
|
||||
* @used_ebs: total number of used logical eraseblocks in this volume
|
||||
* @data_pad: how many bytes at the end of this physical eraseblock are not
|
||||
* used
|
||||
* @data_crc: CRC checksum of the data stored in this logical eraseblock
|
||||
* @padding2: reserved for future, zeroes
|
||||
* @sqnum: sequence number
|
||||
* @padding3: reserved for future, zeroes
|
||||
* @hdr_crc: volume identifier header CRC checksum
|
||||
*
|
||||
* The @sqnum is the value of the global sequence counter at the time when this
|
||||
* VID header was created. The global sequence counter is incremented each time
|
||||
* UBI writes a new VID header to the flash, i.e. when it maps a logical
|
||||
* eraseblock to a new physical eraseblock. The global sequence counter is an
|
||||
* unsigned 64-bit integer and we assume it never overflows. The @sqnum
|
||||
* (sequence number) is used to distinguish between older and newer versions of
|
||||
* logical eraseblocks.
|
||||
*
|
||||
* There are 2 situations when there may be more than one physical eraseblock
|
||||
* corresponding to the same logical eraseblock, i.e., having the same @vol_id
|
||||
* and @lnum values in the volume identifier header. Suppose we have a logical
|
||||
* eraseblock L and it is mapped to the physical eraseblock P.
|
||||
*
|
||||
* 1. Because UBI may erase physical eraseblocks asynchronously, the following
|
||||
* situation is possible: L is asynchronously erased, so P is scheduled for
|
||||
* erasure, then L is written to,i.e. mapped to another physical eraseblock P1,
|
||||
* so P1 is written to, then an unclean reboot happens. Result - there are 2
|
||||
* physical eraseblocks P and P1 corresponding to the same logical eraseblock
|
||||
* L. But P1 has greater sequence number, so UBI picks P1 when it attaches the
|
||||
* flash.
|
||||
*
|
||||
* 2. From time to time UBI moves logical eraseblocks to other physical
|
||||
* eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P
|
||||
* to P1, and an unclean reboot happens before P is physically erased, there
|
||||
* are two physical eraseblocks P and P1 corresponding to L and UBI has to
|
||||
* select one of them when the flash is attached. The @sqnum field says which
|
||||
* PEB is the original (obviously P will have lower @sqnum) and the copy. But
|
||||
* it is not enough to select the physical eraseblock with the higher sequence
|
||||
* number, because the unclean reboot could have happen in the middle of the
|
||||
* copying process, so the data in P is corrupted. It is also not enough to
|
||||
* just select the physical eraseblock with lower sequence number, because the
|
||||
* data there may be old (consider a case if more data was added to P1 after
|
||||
* the copying). Moreover, the unclean reboot may happen when the erasure of P
|
||||
* was just started, so it result in unstable P, which is "mostly" OK, but
|
||||
* still has unstable bits.
|
||||
*
|
||||
* UBI uses the @copy_flag field to indicate that this logical eraseblock is a
|
||||
* copy. UBI also calculates data CRC when the data is moved and stores it at
|
||||
* the @data_crc field of the copy (P1). So when UBI needs to pick one physical
|
||||
* eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is
|
||||
* examined. If it is cleared, the situation* is simple and the newer one is
|
||||
* picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC
|
||||
* checksum is correct, this physical eraseblock is selected (P1). Otherwise
|
||||
* the older one (P) is selected.
|
||||
*
|
||||
* There are 2 sorts of volumes in UBI: user volumes and internal volumes.
|
||||
* Internal volumes are not seen from outside and are used for various internal
|
||||
* UBI purposes. In this implementation there is only one internal volume - the
|
||||
* layout volume. Internal volumes are the main mechanism of UBI extensions.
|
||||
* For example, in future one may introduce a journal internal volume. Internal
|
||||
* volumes have their own reserved range of IDs.
|
||||
*
|
||||
* The @compat field is only used for internal volumes and contains the "degree
|
||||
* of their compatibility". It is always zero for user volumes. This field
|
||||
* provides a mechanism to introduce UBI extensions and to be still compatible
|
||||
* with older UBI binaries. For example, if someone introduced a journal in
|
||||
* future, he would probably use %UBI_COMPAT_DELETE compatibility for the
|
||||
* journal volume. And in this case, older UBI binaries, which know nothing
|
||||
* about the journal volume, would just delete this volume and work perfectly
|
||||
* fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image
|
||||
* - it just ignores the Ext3fs journal.
|
||||
*
|
||||
* The @data_crc field contains the CRC checksum of the contents of the logical
|
||||
* eraseblock if this is a static volume. In case of dynamic volumes, it does
|
||||
* not contain the CRC checksum as a rule. The only exception is when the
|
||||
* data of the physical eraseblock was moved by the wear-leveling sub-system,
|
||||
* then the wear-leveling sub-system calculates the data CRC and stores it in
|
||||
* the @data_crc field. And of course, the @copy_flag is %in this case.
|
||||
*
|
||||
* The @data_size field is used only for static volumes because UBI has to know
|
||||
* how many bytes of data are stored in this eraseblock. For dynamic volumes,
|
||||
* this field usually contains zero. The only exception is when the data of the
|
||||
* physical eraseblock was moved to another physical eraseblock for
|
||||
* wear-leveling reasons. In this case, UBI calculates CRC checksum of the
|
||||
* contents and uses both @data_crc and @data_size fields. In this case, the
|
||||
* @data_size field contains data size.
|
||||
*
|
||||
* The @used_ebs field is used only for static volumes and indicates how many
|
||||
* eraseblocks the data of the volume takes. For dynamic volumes this field is
|
||||
* not used and always contains zero.
|
||||
*
|
||||
* The @data_pad is calculated when volumes are created using the alignment
|
||||
* parameter. So, effectively, the @data_pad field reduces the size of logical
|
||||
* eraseblocks of this volume. This is very handy when one uses block-oriented
|
||||
* software (say, cramfs) on top of the UBI volume.
|
||||
*/
|
||||
struct ubi_vid_hdr {
|
||||
__be32 magic;
|
||||
__u8 version;
|
||||
__u8 vol_type;
|
||||
__u8 copy_flag;
|
||||
__u8 compat;
|
||||
__be32 vol_id;
|
||||
__be32 lnum;
|
||||
__u8 padding1[4];
|
||||
__be32 data_size;
|
||||
__be32 used_ebs;
|
||||
__be32 data_pad;
|
||||
__be32 data_crc;
|
||||
__u8 padding2[4];
|
||||
__be64 sqnum;
|
||||
__u8 padding3[12];
|
||||
__be32 hdr_crc;
|
||||
} __packed;
|
||||
|
||||
/* Internal UBI volumes count */
|
||||
#define UBI_INT_VOL_COUNT 1
|
||||
|
||||
/*
|
||||
* Starting ID of internal volumes: 0x7fffefff.
|
||||
* There is reserved room for 4096 internal volumes.
|
||||
*/
|
||||
#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096)
|
||||
|
||||
/* The layout volume contains the volume table */
|
||||
|
||||
#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START
|
||||
#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC
|
||||
#define UBI_LAYOUT_VOLUME_ALIGN 1
|
||||
#define UBI_LAYOUT_VOLUME_EBS 2
|
||||
#define UBI_LAYOUT_VOLUME_NAME "layout volume"
|
||||
#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT
|
||||
|
||||
/* The maximum number of volumes per one UBI device */
|
||||
#define UBI_MAX_VOLUMES 128
|
||||
|
||||
/* The maximum volume name length */
|
||||
#define UBI_VOL_NAME_MAX 127
|
||||
|
||||
/* Size of the volume table record */
|
||||
#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record)
|
||||
|
||||
/* Size of the volume table record without the ending CRC */
|
||||
#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32))
|
||||
|
||||
/**
|
||||
* struct ubi_vtbl_record - a record in the volume table.
|
||||
* @reserved_pebs: how many physical eraseblocks are reserved for this volume
|
||||
* @alignment: volume alignment
|
||||
* @data_pad: how many bytes are unused at the end of the each physical
|
||||
* eraseblock to satisfy the requested alignment
|
||||
* @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
|
||||
* @upd_marker: if volume update was started but not finished
|
||||
* @name_len: volume name length
|
||||
* @name: the volume name
|
||||
* @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG)
|
||||
* @padding: reserved, zeroes
|
||||
* @crc: a CRC32 checksum of the record
|
||||
*
|
||||
* The volume table records are stored in the volume table, which is stored in
|
||||
* the layout volume. The layout volume consists of 2 logical eraseblock, each
|
||||
* of which contains a copy of the volume table (i.e., the volume table is
|
||||
* duplicated). The volume table is an array of &struct ubi_vtbl_record
|
||||
* objects indexed by the volume ID.
|
||||
*
|
||||
* If the size of the logical eraseblock is large enough to fit
|
||||
* %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES
|
||||
* records. Otherwise, it contains as many records as it can fit (i.e., size of
|
||||
* logical eraseblock divided by sizeof(struct ubi_vtbl_record)).
|
||||
*
|
||||
* The @upd_marker flag is used to implement volume update. It is set to %1
|
||||
* before update and set to %0 after the update. So if the update operation was
|
||||
* interrupted, UBI knows that the volume is corrupted.
|
||||
*
|
||||
* The @alignment field is specified when the volume is created and cannot be
|
||||
* later changed. It may be useful, for example, when a block-oriented file
|
||||
* system works on top of UBI. The @data_pad field is calculated using the
|
||||
* logical eraseblock size and @alignment. The alignment must be multiple to the
|
||||
* minimal flash I/O unit. If @alignment is 1, all the available space of
|
||||
* the physical eraseblocks is used.
|
||||
*
|
||||
* Empty records contain all zeroes and the CRC checksum of those zeroes.
|
||||
*/
|
||||
struct ubi_vtbl_record {
|
||||
__be32 reserved_pebs;
|
||||
__be32 alignment;
|
||||
__be32 data_pad;
|
||||
__u8 vol_type;
|
||||
__u8 upd_marker;
|
||||
__be16 name_len;
|
||||
__u8 name[UBI_VOL_NAME_MAX+1];
|
||||
__u8 flags;
|
||||
__u8 padding[23];
|
||||
__be32 crc;
|
||||
} __packed;
|
||||
|
||||
/* UBI fastmap on-flash data structures */
|
||||
|
||||
#define UBI_FM_SB_VOLUME_ID (UBI_LAYOUT_VOLUME_ID + 1)
|
||||
#define UBI_FM_DATA_VOLUME_ID (UBI_LAYOUT_VOLUME_ID + 2)
|
||||
|
||||
/* fastmap on-flash data structure format version */
|
||||
#define UBI_FM_FMT_VERSION 1
|
||||
|
||||
#define UBI_FM_SB_MAGIC 0x7B11D69F
|
||||
#define UBI_FM_HDR_MAGIC 0xD4B82EF7
|
||||
#define UBI_FM_VHDR_MAGIC 0xFA370ED1
|
||||
#define UBI_FM_POOL_MAGIC 0x67AF4D08
|
||||
#define UBI_FM_EBA_MAGIC 0xf0c040a8
|
||||
|
||||
/* A fastmap supber block can be located between PEB 0 and
|
||||
* UBI_FM_MAX_START */
|
||||
#define UBI_FM_MAX_START 64
|
||||
|
||||
/* A fastmap can use up to UBI_FM_MAX_BLOCKS PEBs */
|
||||
#define UBI_FM_MAX_BLOCKS 32
|
||||
|
||||
/* 5% of the total number of PEBs have to be scanned while attaching
|
||||
* from a fastmap.
|
||||
* But the size of this pool is limited to be between UBI_FM_MIN_POOL_SIZE and
|
||||
* UBI_FM_MAX_POOL_SIZE */
|
||||
#define UBI_FM_MIN_POOL_SIZE 8
|
||||
#define UBI_FM_MAX_POOL_SIZE 256
|
||||
|
||||
#define UBI_FM_WL_POOL_SIZE 25
|
||||
|
||||
/**
|
||||
* struct ubi_fm_sb - UBI fastmap super block
|
||||
* @magic: fastmap super block magic number (%UBI_FM_SB_MAGIC)
|
||||
* @version: format version of this fastmap
|
||||
* @data_crc: CRC over the fastmap data
|
||||
* @used_blocks: number of PEBs used by this fastmap
|
||||
* @block_loc: an array containing the location of all PEBs of the fastmap
|
||||
* @block_ec: the erase counter of each used PEB
|
||||
* @sqnum: highest sequence number value at the time while taking the fastmap
|
||||
*
|
||||
*/
|
||||
struct ubi_fm_sb {
|
||||
__be32 magic;
|
||||
__u8 version;
|
||||
__u8 padding1[3];
|
||||
__be32 data_crc;
|
||||
__be32 used_blocks;
|
||||
__be32 block_loc[UBI_FM_MAX_BLOCKS];
|
||||
__be32 block_ec[UBI_FM_MAX_BLOCKS];
|
||||
__be64 sqnum;
|
||||
__u8 padding2[32];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_fm_hdr - header of the fastmap data set
|
||||
* @magic: fastmap header magic number (%UBI_FM_HDR_MAGIC)
|
||||
* @free_peb_count: number of free PEBs known by this fastmap
|
||||
* @used_peb_count: number of used PEBs known by this fastmap
|
||||
* @scrub_peb_count: number of to be scrubbed PEBs known by this fastmap
|
||||
* @bad_peb_count: number of bad PEBs known by this fastmap
|
||||
* @erase_peb_count: number of bad PEBs which have to be erased
|
||||
* @vol_count: number of UBI volumes known by this fastmap
|
||||
*/
|
||||
struct ubi_fm_hdr {
|
||||
__be32 magic;
|
||||
__be32 free_peb_count;
|
||||
__be32 used_peb_count;
|
||||
__be32 scrub_peb_count;
|
||||
__be32 bad_peb_count;
|
||||
__be32 erase_peb_count;
|
||||
__be32 vol_count;
|
||||
__u8 padding[4];
|
||||
} __packed;
|
||||
|
||||
/* struct ubi_fm_hdr is followed by two struct ubi_fm_scan_pool */
|
||||
|
||||
/**
|
||||
* struct ubi_fm_scan_pool - Fastmap pool PEBs to be scanned while attaching
|
||||
* @magic: pool magic numer (%UBI_FM_POOL_MAGIC)
|
||||
* @size: current pool size
|
||||
* @max_size: maximal pool size
|
||||
* @pebs: an array containing the location of all PEBs in this pool
|
||||
*/
|
||||
struct ubi_fm_scan_pool {
|
||||
__be32 magic;
|
||||
__be16 size;
|
||||
__be16 max_size;
|
||||
__be32 pebs[UBI_FM_MAX_POOL_SIZE];
|
||||
__be32 padding[4];
|
||||
} __packed;
|
||||
|
||||
/* ubi_fm_scan_pool is followed by nfree+nused struct ubi_fm_ec records */
|
||||
|
||||
/**
|
||||
* struct ubi_fm_ec - stores the erase counter of a PEB
|
||||
* @pnum: PEB number
|
||||
* @ec: ec of this PEB
|
||||
*/
|
||||
struct ubi_fm_ec {
|
||||
__be32 pnum;
|
||||
__be32 ec;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_fm_volhdr - Fastmap volume header
|
||||
* it identifies the start of an eba table
|
||||
* @magic: Fastmap volume header magic number (%UBI_FM_VHDR_MAGIC)
|
||||
* @vol_id: volume id of the fastmapped volume
|
||||
* @vol_type: type of the fastmapped volume
|
||||
* @data_pad: data_pad value of the fastmapped volume
|
||||
* @used_ebs: number of used LEBs within this volume
|
||||
* @last_eb_bytes: number of bytes used in the last LEB
|
||||
*/
|
||||
struct ubi_fm_volhdr {
|
||||
__be32 magic;
|
||||
__be32 vol_id;
|
||||
__u8 vol_type;
|
||||
__u8 padding1[3];
|
||||
__be32 data_pad;
|
||||
__be32 used_ebs;
|
||||
__be32 last_eb_bytes;
|
||||
__u8 padding2[8];
|
||||
} __packed;
|
||||
|
||||
/* struct ubi_fm_volhdr is followed by one struct ubi_fm_eba records */
|
||||
|
||||
/**
|
||||
* struct ubi_fm_eba - denotes an association beween a PEB and LEB
|
||||
* @magic: EBA table magic number
|
||||
* @reserved_pebs: number of table entries
|
||||
* @pnum: PEB number of LEB (LEB is the index)
|
||||
*/
|
||||
struct ubi_fm_eba {
|
||||
__be32 magic;
|
||||
__be32 reserved_pebs;
|
||||
__be32 pnum[0];
|
||||
} __packed;
|
||||
#endif /* !__UBI_MEDIA_H__ */
|
|
@ -12,6 +12,9 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
@ -19,36 +22,16 @@
|
|||
#ifndef __UBI_UBI_H__
|
||||
#define __UBI_UBI_H__
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#include <linux/init.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/ubi.h>
|
||||
#endif
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/ubi.h>
|
||||
|
||||
#include <mtd/ubi-media.h>
|
||||
|
||||
#include "scan.h"
|
||||
#include "debug.h"
|
||||
#include "ubi-barebox.h"
|
||||
|
||||
/* Maximum number of supported UBI devices */
|
||||
#define UBI_MAX_DEVICES 32
|
||||
|
@ -57,21 +40,21 @@
|
|||
#define UBI_NAME_STR "ubi"
|
||||
|
||||
/* Normal UBI messages */
|
||||
#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__)
|
||||
#define ubi_msg(fmt, ...) pr_info("UBI: " fmt "\n", ##__VA_ARGS__)
|
||||
/* UBI warning messages */
|
||||
#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \
|
||||
__func__, ##__VA_ARGS__)
|
||||
#define ubi_warn(fmt, ...) pr_warn("UBI warning: %s: " fmt "\n", \
|
||||
__func__, ##__VA_ARGS__)
|
||||
/* UBI error messages */
|
||||
#define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \
|
||||
#define ubi_err(fmt, ...) pr_err("UBI error: %s: " fmt "\n", \
|
||||
__func__, ##__VA_ARGS__)
|
||||
|
||||
/* Lowest number PEBs reserved for bad PEB handling */
|
||||
#define MIN_RESEVED_PEBS 2
|
||||
|
||||
/* Background thread name pattern */
|
||||
#define UBI_BGT_NAME_PATTERN "ubi_bgt%dd"
|
||||
|
||||
/* This marker in the EBA table means that the LEB is um-mapped */
|
||||
/*
|
||||
* This marker in the EBA table means that the LEB is um-mapped.
|
||||
* NOTE! It has to have the same value as %UBI_ALL.
|
||||
*/
|
||||
#define UBI_LEB_UNMAPPED -1
|
||||
|
||||
/*
|
||||
|
@ -81,37 +64,98 @@
|
|||
#define UBI_IO_RETRIES 3
|
||||
|
||||
/*
|
||||
* Error codes returned by the I/O unit.
|
||||
* Length of the protection queue. The length is effectively equivalent to the
|
||||
* number of (global) erase cycles PEBs are protected from the wear-leveling
|
||||
* worker.
|
||||
*/
|
||||
#define UBI_PROT_QUEUE_LEN 10
|
||||
|
||||
/* The volume ID/LEB number/erase counter is unknown */
|
||||
#define UBI_UNKNOWN -1
|
||||
|
||||
/*
|
||||
* The UBI debugfs directory name pattern and maximum name length (3 for "ubi"
|
||||
* + 2 for the number plus 1 for the trailing zero byte.
|
||||
*/
|
||||
#define UBI_DFS_DIR_NAME "ubi%d"
|
||||
#define UBI_DFS_DIR_LEN (3 + 2 + 1)
|
||||
|
||||
/*
|
||||
* Error codes returned by the I/O sub-system.
|
||||
*
|
||||
* UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only
|
||||
* 0xFF bytes
|
||||
* UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a
|
||||
* valid erase counter header, and the rest are %0xFF bytes
|
||||
* UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC)
|
||||
* UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or
|
||||
* CRC)
|
||||
* UBI_IO_FF: the read region of flash contains only 0xFFs
|
||||
* UBI_IO_FF_BITFLIPS: the same as %UBI_IO_FF, but also also there was a data
|
||||
* integrity error reported by the MTD driver
|
||||
* (uncorrectable ECC error in case of NAND)
|
||||
* UBI_IO_BAD_HDR: the EC or VID header is corrupted (bad magic or CRC)
|
||||
* UBI_IO_BAD_HDR_EBADMSG: the same as %UBI_IO_BAD_HDR, but also there was a
|
||||
* data integrity error reported by the MTD driver
|
||||
* (uncorrectable ECC error in case of NAND)
|
||||
* UBI_IO_BITFLIPS: bit-flips were detected and corrected
|
||||
*
|
||||
* Note, it is probably better to have bit-flip and ebadmsg as flags which can
|
||||
* be or'ed with other error code. But this is a big change because there are
|
||||
* may callers, so it does not worth the risk of introducing a bug
|
||||
*/
|
||||
enum {
|
||||
UBI_IO_PEB_EMPTY = 1,
|
||||
UBI_IO_PEB_FREE,
|
||||
UBI_IO_BAD_EC_HDR,
|
||||
UBI_IO_BAD_VID_HDR,
|
||||
UBI_IO_BITFLIPS
|
||||
UBI_IO_FF = 1,
|
||||
UBI_IO_FF_BITFLIPS,
|
||||
UBI_IO_BAD_HDR,
|
||||
UBI_IO_BAD_HDR_EBADMSG,
|
||||
UBI_IO_BITFLIPS,
|
||||
};
|
||||
|
||||
/*
|
||||
* Return codes of the 'ubi_eba_copy_leb()' function.
|
||||
*
|
||||
* MOVE_CANCEL_RACE: canceled because the volume is being deleted, the source
|
||||
* PEB was put meanwhile, or there is I/O on the source PEB
|
||||
* MOVE_SOURCE_RD_ERR: canceled because there was a read error from the source
|
||||
* PEB
|
||||
* MOVE_TARGET_RD_ERR: canceled because there was a read error from the target
|
||||
* PEB
|
||||
* MOVE_TARGET_WR_ERR: canceled because there was a write error to the target
|
||||
* PEB
|
||||
* MOVE_TARGET_BITFLIPS: canceled because a bit-flip was detected in the
|
||||
* target PEB
|
||||
* MOVE_RETRY: retry scrubbing the PEB
|
||||
*/
|
||||
enum {
|
||||
MOVE_CANCEL_RACE = 1,
|
||||
MOVE_SOURCE_RD_ERR,
|
||||
MOVE_TARGET_RD_ERR,
|
||||
MOVE_TARGET_WR_ERR,
|
||||
MOVE_TARGET_BITFLIPS,
|
||||
MOVE_RETRY,
|
||||
};
|
||||
|
||||
/*
|
||||
* Return codes of the fastmap sub-system
|
||||
*
|
||||
* UBI_NO_FASTMAP: No fastmap super block was found
|
||||
* UBI_BAD_FASTMAP: A fastmap was found but it's unusable
|
||||
*/
|
||||
enum {
|
||||
UBI_NO_FASTMAP = 1,
|
||||
UBI_BAD_FASTMAP,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_wl_entry - wear-leveling entry.
|
||||
* @rb: link in the corresponding RB-tree
|
||||
* @u.rb: link in the corresponding (free/used) RB-tree
|
||||
* @u.list: link in the protection queue
|
||||
* @ec: erase counter
|
||||
* @pnum: physical eraseblock number
|
||||
*
|
||||
* This data structure is used in the WL unit. Each physical eraseblock has a
|
||||
* corresponding &struct wl_entry object which may be kept in different
|
||||
* RB-trees. See WL unit for details.
|
||||
* This data structure is used in the WL sub-system. Each physical eraseblock
|
||||
* has a corresponding &struct wl_entry object which may be kept in different
|
||||
* RB-trees. See WL sub-system for details.
|
||||
*/
|
||||
struct ubi_wl_entry {
|
||||
struct rb_node rb;
|
||||
union {
|
||||
struct rb_node rb;
|
||||
struct list_head list;
|
||||
} u;
|
||||
int ec;
|
||||
int pnum;
|
||||
};
|
||||
|
@ -125,21 +169,76 @@ struct ubi_wl_entry {
|
|||
* @mutex: read/write mutex to implement read/write access serialization to
|
||||
* the (@vol_id, @lnum) logical eraseblock
|
||||
*
|
||||
* This data structure is used in the EBA unit to implement per-LEB locking.
|
||||
* When a logical eraseblock is being locked - corresponding
|
||||
* This data structure is used in the EBA sub-system to implement per-LEB
|
||||
* locking. When a logical eraseblock is being locked - corresponding
|
||||
* &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree).
|
||||
* See EBA unit for details.
|
||||
* See EBA sub-system for details.
|
||||
*/
|
||||
struct ubi_ltree_entry {
|
||||
struct rb_node rb;
|
||||
int vol_id;
|
||||
int lnum;
|
||||
int users;
|
||||
struct rw_semaphore mutex;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_rename_entry - volume re-name description data structure.
|
||||
* @new_name_len: new volume name length
|
||||
* @new_name: new volume name
|
||||
* @remove: if not zero, this volume should be removed, not re-named
|
||||
* @desc: descriptor of the volume
|
||||
* @list: links re-name entries into a list
|
||||
*
|
||||
* This data structure is utilized in the multiple volume re-name code. Namely,
|
||||
* UBI first creates a list of &struct ubi_rename_entry objects from the
|
||||
* &struct ubi_rnvol_req request object, and then utilizes this list to do all
|
||||
* the job.
|
||||
*/
|
||||
struct ubi_rename_entry {
|
||||
int new_name_len;
|
||||
char new_name[UBI_VOL_NAME_MAX + 1];
|
||||
int remove;
|
||||
struct ubi_volume_desc *desc;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct ubi_volume_desc;
|
||||
|
||||
/**
|
||||
* struct ubi_fastmap_layout - in-memory fastmap data structure.
|
||||
* @e: PEBs used by the current fastmap
|
||||
* @to_be_tortured: if non-zero tortured this PEB
|
||||
* @used_blocks: number of used PEBs
|
||||
* @max_pool_size: maximal size of the user pool
|
||||
* @max_wl_pool_size: maximal size of the pool used by the WL sub-system
|
||||
*/
|
||||
struct ubi_fastmap_layout {
|
||||
struct ubi_wl_entry *e[UBI_FM_MAX_BLOCKS];
|
||||
int to_be_tortured[UBI_FM_MAX_BLOCKS];
|
||||
int used_blocks;
|
||||
int max_pool_size;
|
||||
int max_wl_pool_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_fm_pool - in-memory fastmap pool
|
||||
* @pebs: PEBs in this pool
|
||||
* @used: number of used PEBs
|
||||
* @size: total number of PEBs in this pool
|
||||
* @max_size: maximal size of the pool
|
||||
*
|
||||
* A pool gets filled with up to max_size.
|
||||
* If all PEBs within the pool are used a new fastmap will be written
|
||||
* to the flash and the pool gets refilled with empty PEBs.
|
||||
*
|
||||
*/
|
||||
struct ubi_fm_pool {
|
||||
int pebs[UBI_FM_MAX_POOL_SIZE];
|
||||
int used;
|
||||
int size;
|
||||
int max_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_volume - UBI volume description data structure.
|
||||
* @dev: device object to make use of the the Linux device model
|
||||
|
@ -166,8 +265,6 @@ struct ubi_volume_desc;
|
|||
* @upd_ebs: how many eraseblocks are expected to be updated
|
||||
* @ch_lnum: LEB number which is being changing by the atomic LEB change
|
||||
* operation
|
||||
* @ch_dtype: data persistency type which is being changing by the atomic LEB
|
||||
* change operation
|
||||
* @upd_bytes: how many bytes are expected to be received for volume update or
|
||||
* atomic LEB change
|
||||
* @upd_received: how many bytes were already received for volume update or
|
||||
|
@ -181,10 +278,7 @@ struct ubi_volume_desc;
|
|||
* @upd_marker: %1 if the update marker is set for this volume
|
||||
* @updating: %1 if the volume is being updated
|
||||
* @changing_leb: %1 if the atomic LEB change ioctl command is in progress
|
||||
*
|
||||
* @gluebi_desc: gluebi UBI volume descriptor
|
||||
* @gluebi_refcount: reference count of the gluebi MTD device
|
||||
* @gluebi_mtd: MTD device description object of the gluebi MTD device
|
||||
* @direct_writes: %1 if direct writes are enabled for this volume
|
||||
*
|
||||
* The @corrupted field indicates that the volume's contents is corrupted.
|
||||
* Since UBI protects only static volumes, this field is not relevant to
|
||||
|
@ -195,7 +289,7 @@ struct ubi_volume_desc;
|
|||
* the moment or is damaged because of an unclean reboot.
|
||||
*/
|
||||
struct ubi_volume {
|
||||
struct device dev;
|
||||
struct device_d dev;
|
||||
struct cdev cdev;
|
||||
struct ubi_device *ubi;
|
||||
int vol_id;
|
||||
|
@ -213,11 +307,10 @@ struct ubi_volume {
|
|||
int alignment;
|
||||
int data_pad;
|
||||
int name_len;
|
||||
char name[UBI_VOL_NAME_MAX+1];
|
||||
char name[UBI_VOL_NAME_MAX + 1];
|
||||
|
||||
int upd_ebs;
|
||||
int ch_lnum;
|
||||
int ch_dtype;
|
||||
long long upd_bytes;
|
||||
long long upd_received;
|
||||
void *upd_buf;
|
||||
|
@ -228,22 +321,11 @@ struct ubi_volume {
|
|||
unsigned int upd_marker:1;
|
||||
unsigned int updating:1;
|
||||
unsigned int changing_leb:1;
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_GLUEBI
|
||||
/*
|
||||
* Gluebi-related stuff may be compiled out.
|
||||
* TODO: this should not be built into UBI but should be a separate
|
||||
* ubimtd driver which works on top of UBI and emulates MTD devices.
|
||||
*/
|
||||
struct ubi_volume_desc *gluebi_desc;
|
||||
int gluebi_refcount;
|
||||
struct mtd_info gluebi_mtd;
|
||||
#endif
|
||||
unsigned int direct_writes:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_volume_desc - descriptor of the UBI volume returned when it is
|
||||
* opened.
|
||||
* struct ubi_volume_desc - UBI volume descriptor returned when it is opened.
|
||||
* @vol: reference to the corresponding volume description object
|
||||
* @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE)
|
||||
*/
|
||||
|
@ -254,6 +336,37 @@ struct ubi_volume_desc {
|
|||
|
||||
struct ubi_wl_entry;
|
||||
|
||||
/**
|
||||
* struct ubi_debug_info - debugging information for an UBI device.
|
||||
*
|
||||
* @chk_gen: if UBI general extra checks are enabled
|
||||
* @chk_io: if UBI I/O extra checks are enabled
|
||||
* @disable_bgt: disable the background task for testing purposes
|
||||
* @emulate_bitflips: emulate bit-flips for testing purposes
|
||||
* @emulate_io_failures: emulate write/erase failures for testing purposes
|
||||
* @dfs_dir_name: name of debugfs directory containing files of this UBI device
|
||||
* @dfs_dir: direntry object of the UBI device debugfs directory
|
||||
* @dfs_chk_gen: debugfs knob to enable UBI general extra checks
|
||||
* @dfs_chk_io: debugfs knob to enable UBI I/O extra checks
|
||||
* @dfs_disable_bgt: debugfs knob to disable the background task
|
||||
* @dfs_emulate_bitflips: debugfs knob to emulate bit-flips
|
||||
* @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures
|
||||
*/
|
||||
struct ubi_debug_info {
|
||||
unsigned int chk_gen:1;
|
||||
unsigned int chk_io:1;
|
||||
unsigned int disable_bgt:1;
|
||||
unsigned int emulate_bitflips:1;
|
||||
unsigned int emulate_io_failures:1;
|
||||
char dfs_dir_name[UBI_DFS_DIR_LEN + 1];
|
||||
struct dentry *dfs_dir;
|
||||
struct dentry *dfs_chk_gen;
|
||||
struct dentry *dfs_chk_io;
|
||||
struct dentry *dfs_disable_bgt;
|
||||
struct dentry *dfs_emulate_bitflips;
|
||||
struct dentry *dfs_emulate_io_failures;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_device - UBI device description structure
|
||||
* @dev: UBI device object to use the the Linux device model
|
||||
|
@ -267,6 +380,7 @@ struct ubi_wl_entry;
|
|||
* @vol->readers, @vol->writers, @vol->exclusive,
|
||||
* @vol->ref_count, @vol->mapping and @vol->eba_tbl.
|
||||
* @ref_count: count of references on the UBI device
|
||||
* @image_seq: image sequence number recorded on EC headers
|
||||
*
|
||||
* @rsvd_pebs: count of reserved physical eraseblocks
|
||||
* @avail_pebs: count of available physical eraseblocks
|
||||
|
@ -275,12 +389,13 @@ struct ubi_wl_entry;
|
|||
* @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling
|
||||
*
|
||||
* @autoresize_vol_id: ID of the volume which has to be auto-resized at the end
|
||||
* of UBI ititializetion
|
||||
* of UBI initialization
|
||||
* @vtbl_slots: how many slots are available in the volume table
|
||||
* @vtbl_size: size of the volume table in bytes
|
||||
* @vtbl: in-RAM volume table copy
|
||||
* @volumes_mutex: protects on-flash volume table and serializes volume
|
||||
* changes, like creation, deletion, update, resize
|
||||
* @device_mutex: protects on-flash volume table and serializes volume
|
||||
* creation, deletion, update, re-size, re-name and set
|
||||
* property
|
||||
*
|
||||
* @max_ec: current highest erase counter value
|
||||
* @mean_ec: current mean erase counter value
|
||||
|
@ -290,20 +405,33 @@ struct ubi_wl_entry;
|
|||
* @ltree: the lock tree
|
||||
* @alc_mutex: serializes "atomic LEB change" operations
|
||||
*
|
||||
* @fm_disabled: non-zero if fastmap is disabled (default)
|
||||
* @fm: in-memory data structure of the currently used fastmap
|
||||
* @fm_pool: in-memory data structure of the fastmap pool
|
||||
* @fm_wl_pool: in-memory data structure of the fastmap pool used by the WL
|
||||
* sub-system
|
||||
* @fm_mutex: serializes ubi_update_fastmap() and protects @fm_buf
|
||||
* @fm_buf: vmalloc()'d buffer which holds the raw fastmap
|
||||
* @fm_size: fastmap size in bytes
|
||||
* @fm_sem: allows ubi_update_fastmap() to block EBA table changes
|
||||
* @fm_work: fastmap work queue
|
||||
*
|
||||
* @used: RB-tree of used physical eraseblocks
|
||||
* @erroneous: RB-tree of erroneous used physical eraseblocks
|
||||
* @free: RB-tree of free physical eraseblocks
|
||||
* @free_count: Contains the number of elements in @free
|
||||
* @scrub: RB-tree of physical eraseblocks which need scrubbing
|
||||
* @prot: protection trees
|
||||
* @prot.pnum: protection tree indexed by physical eraseblock numbers
|
||||
* @prot.aec: protection tree indexed by absolute erase counter value
|
||||
* @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from,
|
||||
* @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works
|
||||
* fields
|
||||
* @pq: protection queue (contain physical eraseblocks which are temporarily
|
||||
* protected from the wear-leveling worker)
|
||||
* @pq_head: protection queue head
|
||||
* @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from,
|
||||
* @move_to, @move_to_put @erase_pending, @wl_scheduled, @works,
|
||||
* @erroneous, and @erroneous_peb_count fields
|
||||
* @move_mutex: serializes eraseblock moves
|
||||
* @work_sem: synchronizes the WL worker with use tasks
|
||||
* @wl_scheduled: non-zero if the wear-leveling was scheduled
|
||||
* @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any
|
||||
* physical eraseblock
|
||||
* @abs_ec: absolute erase counter
|
||||
* @move_from: physical eraseblock from where the data is being moved
|
||||
* @move_to: physical eraseblock where the data is being moved to
|
||||
* @move_to_put: if the "to" PEB was put
|
||||
|
@ -316,76 +444,86 @@ struct ubi_wl_entry;
|
|||
* @flash_size: underlying MTD device size (in bytes)
|
||||
* @peb_count: count of physical eraseblocks on the MTD device
|
||||
* @peb_size: physical eraseblock size
|
||||
* @bad_peb_limit: top limit of expected bad physical eraseblocks
|
||||
* @bad_peb_count: count of bad physical eraseblocks
|
||||
* @good_peb_count: count of good physical eraseblocks
|
||||
* @corr_peb_count: count of corrupted physical eraseblocks (preserved and not
|
||||
* used by UBI)
|
||||
* @erroneous_peb_count: count of erroneous physical eraseblocks in @erroneous
|
||||
* @max_erroneous: maximum allowed amount of erroneous physical eraseblocks
|
||||
* @min_io_size: minimal input/output unit size of the underlying MTD device
|
||||
* @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers
|
||||
* @ro_mode: if the UBI device is in read-only mode
|
||||
* @leb_size: logical eraseblock size
|
||||
* @leb_start: starting offset of logical eraseblocks within physical
|
||||
* eraseblocks
|
||||
* eraseblocks
|
||||
* @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size
|
||||
* @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size
|
||||
* @vid_hdr_offset: starting offset of the volume identifier header (might be
|
||||
* unaligned)
|
||||
* unaligned)
|
||||
* @vid_hdr_aloffset: starting offset of the VID header aligned to
|
||||
* @hdrs_min_io_size
|
||||
* @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset
|
||||
* @bad_allowed: whether the MTD device admits of bad physical eraseblocks or
|
||||
* not
|
||||
* @nor_flash: non-zero if working on top of NOR flash
|
||||
* @max_write_size: maximum amount of bytes the underlying flash can write at a
|
||||
* time (MTD write buffer size)
|
||||
* @mtd: MTD device descriptor
|
||||
*
|
||||
* @peb_buf1: a buffer of PEB size used for different purposes
|
||||
* @peb_buf2: another buffer of PEB size used for different purposes
|
||||
* @buf_mutex: proptects @peb_buf1 and @peb_buf2
|
||||
* @dbg_peb_buf: buffer of PEB size used for debugging
|
||||
* @dbg_buf_mutex: proptects @dbg_peb_buf
|
||||
* @peb_buf: a buffer of PEB size used for different purposes
|
||||
* @buf_mutex: protects @peb_buf
|
||||
* @ckvol_mutex: serializes static volume checking when opening
|
||||
*
|
||||
* @dbg: debugging information for this UBI device
|
||||
*/
|
||||
struct ubi_device {
|
||||
struct cdev cdev;
|
||||
struct device dev;
|
||||
struct device_d dev;
|
||||
int ubi_num;
|
||||
char ubi_name[sizeof(UBI_NAME_STR)+5];
|
||||
int vol_count;
|
||||
struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
|
||||
spinlock_t volumes_lock;
|
||||
int ref_count;
|
||||
int image_seq;
|
||||
|
||||
int rsvd_pebs;
|
||||
int avail_pebs;
|
||||
int beb_rsvd_pebs;
|
||||
int beb_rsvd_level;
|
||||
int bad_peb_limit;
|
||||
|
||||
int autoresize_vol_id;
|
||||
int vtbl_slots;
|
||||
int vtbl_size;
|
||||
struct ubi_vtbl_record *vtbl;
|
||||
struct mutex volumes_mutex;
|
||||
|
||||
int max_ec;
|
||||
/* TODO: mean_ec is not updated run-time, fix */
|
||||
/* Note, mean_ec is not updated run-time - should be fixed */
|
||||
int mean_ec;
|
||||
|
||||
/* EBA unit's stuff */
|
||||
/* EBA sub-system's stuff */
|
||||
unsigned long long global_sqnum;
|
||||
spinlock_t ltree_lock;
|
||||
struct rb_root ltree;
|
||||
struct mutex alc_mutex;
|
||||
|
||||
/* Wear-leveling unit's stuff */
|
||||
/* Fastmap stuff */
|
||||
int fm_disabled;
|
||||
struct ubi_fastmap_layout *fm;
|
||||
struct ubi_fm_pool fm_pool;
|
||||
struct ubi_fm_pool fm_wl_pool;
|
||||
void *fm_buf;
|
||||
size_t fm_size;
|
||||
|
||||
/* Wear-leveling sub-system's stuff */
|
||||
struct rb_root used;
|
||||
struct rb_root erroneous;
|
||||
struct rb_root free;
|
||||
int free_count;
|
||||
struct rb_root scrub;
|
||||
struct {
|
||||
struct rb_root pnum;
|
||||
struct rb_root aec;
|
||||
} prot;
|
||||
spinlock_t wl_lock;
|
||||
struct mutex move_mutex;
|
||||
struct rw_semaphore work_sem;
|
||||
struct list_head pq[UBI_PROT_QUEUE_LEN];
|
||||
int pq_head;
|
||||
int wl_scheduled;
|
||||
struct ubi_wl_entry **lookuptbl;
|
||||
unsigned long long abs_ec;
|
||||
struct ubi_wl_entry *move_from;
|
||||
struct ubi_wl_entry *move_to;
|
||||
int move_to_put;
|
||||
|
@ -395,12 +533,15 @@ struct ubi_device {
|
|||
int thread_enabled;
|
||||
char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];
|
||||
|
||||
/* I/O unit's stuff */
|
||||
/* I/O sub-system's stuff */
|
||||
long long flash_size;
|
||||
int peb_count;
|
||||
int peb_size;
|
||||
int bad_peb_count;
|
||||
int good_peb_count;
|
||||
int corr_peb_count;
|
||||
int erroneous_peb_count;
|
||||
int max_erroneous;
|
||||
int min_io_size;
|
||||
int hdrs_min_io_size;
|
||||
int ro_mode;
|
||||
|
@ -411,35 +552,192 @@ struct ubi_device {
|
|||
int vid_hdr_offset;
|
||||
int vid_hdr_aloffset;
|
||||
int vid_hdr_shift;
|
||||
int bad_allowed;
|
||||
unsigned int bad_allowed:1;
|
||||
unsigned int nor_flash:1;
|
||||
int max_write_size;
|
||||
struct mtd_info *mtd;
|
||||
|
||||
void *peb_buf1;
|
||||
void *peb_buf2;
|
||||
struct mutex buf_mutex;
|
||||
struct mutex ckvol_mutex;
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG
|
||||
void *dbg_peb_buf;
|
||||
struct mutex dbg_buf_mutex;
|
||||
#endif
|
||||
void *peb_buf;
|
||||
|
||||
struct ubi_debug_info dbg;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_ainf_peb - attach information about a physical eraseblock.
|
||||
* @ec: erase counter (%UBI_UNKNOWN if it is unknown)
|
||||
* @pnum: physical eraseblock number
|
||||
* @vol_id: ID of the volume this LEB belongs to
|
||||
* @lnum: logical eraseblock number
|
||||
* @scrub: if this physical eraseblock needs scrubbing
|
||||
* @copy_flag: this LEB is a copy (@copy_flag is set in VID header of this LEB)
|
||||
* @sqnum: sequence number
|
||||
* @u: unions RB-tree or @list links
|
||||
* @u.rb: link in the per-volume RB-tree of &struct ubi_ainf_peb objects
|
||||
* @u.list: link in one of the eraseblock lists
|
||||
*
|
||||
* One object of this type is allocated for each physical eraseblock when
|
||||
* attaching an MTD device. Note, if this PEB does not belong to any LEB /
|
||||
* volume, the @vol_id and @lnum fields are initialized to %UBI_UNKNOWN.
|
||||
*/
|
||||
struct ubi_ainf_peb {
|
||||
int ec;
|
||||
int pnum;
|
||||
int vol_id;
|
||||
int lnum;
|
||||
unsigned int scrub:1;
|
||||
unsigned int copy_flag:1;
|
||||
unsigned long long sqnum;
|
||||
union {
|
||||
struct rb_node rb;
|
||||
struct list_head list;
|
||||
} u;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_ainf_volume - attaching information about a volume.
|
||||
* @vol_id: volume ID
|
||||
* @highest_lnum: highest logical eraseblock number in this volume
|
||||
* @leb_count: number of logical eraseblocks in this volume
|
||||
* @vol_type: volume type
|
||||
* @used_ebs: number of used logical eraseblocks in this volume (only for
|
||||
* static volumes)
|
||||
* @last_data_size: amount of data in the last logical eraseblock of this
|
||||
* volume (always equivalent to the usable logical eraseblock
|
||||
* size in case of dynamic volumes)
|
||||
* @data_pad: how many bytes at the end of logical eraseblocks of this volume
|
||||
* are not used (due to volume alignment)
|
||||
* @compat: compatibility flags of this volume
|
||||
* @rb: link in the volume RB-tree
|
||||
* @root: root of the RB-tree containing all the eraseblock belonging to this
|
||||
* volume (&struct ubi_ainf_peb objects)
|
||||
*
|
||||
* One object of this type is allocated for each volume when attaching an MTD
|
||||
* device.
|
||||
*/
|
||||
struct ubi_ainf_volume {
|
||||
int vol_id;
|
||||
int highest_lnum;
|
||||
int leb_count;
|
||||
int vol_type;
|
||||
int used_ebs;
|
||||
int last_data_size;
|
||||
int data_pad;
|
||||
int compat;
|
||||
struct rb_node rb;
|
||||
struct rb_root root;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_attach_info - MTD device attaching information.
|
||||
* @volumes: root of the volume RB-tree
|
||||
* @corr: list of corrupted physical eraseblocks
|
||||
* @free: list of free physical eraseblocks
|
||||
* @erase: list of physical eraseblocks which have to be erased
|
||||
* @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
|
||||
* those belonging to "preserve"-compatible internal volumes)
|
||||
* @corr_peb_count: count of PEBs in the @corr list
|
||||
* @empty_peb_count: count of PEBs which are presumably empty (contain only
|
||||
* 0xFF bytes)
|
||||
* @alien_peb_count: count of PEBs in the @alien list
|
||||
* @bad_peb_count: count of bad physical eraseblocks
|
||||
* @maybe_bad_peb_count: count of bad physical eraseblocks which are not marked
|
||||
* as bad yet, but which look like bad
|
||||
* @vols_found: number of volumes found
|
||||
* @highest_vol_id: highest volume ID
|
||||
* @is_empty: flag indicating whether the MTD device is empty or not
|
||||
* @min_ec: lowest erase counter value
|
||||
* @max_ec: highest erase counter value
|
||||
* @max_sqnum: highest sequence number value
|
||||
* @mean_ec: mean erase counter value
|
||||
* @ec_sum: a temporary variable used when calculating @mean_ec
|
||||
* @ec_count: a temporary variable used when calculating @mean_ec
|
||||
* @aeb_slab_cache: slab cache for &struct ubi_ainf_peb objects
|
||||
*
|
||||
* This data structure contains the result of attaching an MTD device and may
|
||||
* be used by other UBI sub-systems to build final UBI data structures, further
|
||||
* error-recovery and so on.
|
||||
*/
|
||||
struct ubi_attach_info {
|
||||
struct rb_root volumes;
|
||||
struct list_head corr;
|
||||
struct list_head free;
|
||||
struct list_head erase;
|
||||
struct list_head alien;
|
||||
int corr_peb_count;
|
||||
int empty_peb_count;
|
||||
int alien_peb_count;
|
||||
int bad_peb_count;
|
||||
int maybe_bad_peb_count;
|
||||
int vols_found;
|
||||
int highest_vol_id;
|
||||
int is_empty;
|
||||
int min_ec;
|
||||
int max_ec;
|
||||
unsigned long long max_sqnum;
|
||||
int mean_ec;
|
||||
uint64_t ec_sum;
|
||||
int ec_count;
|
||||
struct kmem_cache *aeb_slab_cache;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_work - UBI work description data structure.
|
||||
* @list: a link in the list of pending works
|
||||
* @func: worker function
|
||||
* @e: physical eraseblock to erase
|
||||
* @vol_id: the volume ID on which this erasure is being performed
|
||||
* @lnum: the logical eraseblock number
|
||||
* @torture: if the physical eraseblock has to be tortured
|
||||
* @anchor: produce a anchor PEB to by used by fastmap
|
||||
*
|
||||
* The @func pointer points to the worker function. If the @cancel argument is
|
||||
* not zero, the worker has to free the resources and exit immediately. The
|
||||
* worker has to return zero in case of success and a negative error code in
|
||||
* case of failure.
|
||||
*/
|
||||
struct ubi_work {
|
||||
struct list_head list;
|
||||
int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel);
|
||||
/* The below fields are only relevant to erasure works */
|
||||
struct ubi_wl_entry *e;
|
||||
int vol_id;
|
||||
int lnum;
|
||||
int torture;
|
||||
int anchor;
|
||||
};
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
extern struct kmem_cache *ubi_wl_entry_slab;
|
||||
extern struct file_operations ubi_ctrl_cdev_operations;
|
||||
extern struct file_operations ubi_cdev_operations;
|
||||
extern struct file_operations ubi_vol_cdev_operations;
|
||||
extern const struct file_operations ubi_ctrl_cdev_operations;
|
||||
extern const struct file_operations ubi_cdev_operations;
|
||||
extern const struct file_operations ubi_vol_cdev_operations;
|
||||
extern struct class *ubi_class;
|
||||
extern struct mutex ubi_devices_mutex;
|
||||
extern struct blocking_notifier_head ubi_notifiers;
|
||||
|
||||
/* attach.c */
|
||||
int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,
|
||||
int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips);
|
||||
struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai,
|
||||
int vol_id);
|
||||
void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av);
|
||||
struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi,
|
||||
struct ubi_attach_info *ai);
|
||||
int ubi_attach(struct ubi_device *ubi, int force_scan);
|
||||
void ubi_destroy_ai(struct ubi_attach_info *ai);
|
||||
|
||||
/* vtbl.c */
|
||||
int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
|
||||
struct ubi_vtbl_record *vtbl_rec);
|
||||
int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si);
|
||||
int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
|
||||
struct list_head *rename_list);
|
||||
int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_attach_info *ai);
|
||||
|
||||
/* vmt.c */
|
||||
int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req);
|
||||
int ubi_remove_volume(struct ubi_volume_desc *desc);
|
||||
int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl);
|
||||
int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs);
|
||||
int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list);
|
||||
int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol);
|
||||
void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol);
|
||||
|
||||
|
@ -455,20 +753,12 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
const void __user *buf, int count);
|
||||
|
||||
/* misc.c */
|
||||
int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length);
|
||||
int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
|
||||
int length);
|
||||
int ubi_check_volume(struct ubi_device *ubi, int vol_id);
|
||||
void ubi_update_reserved(struct ubi_device *ubi);
|
||||
void ubi_calculate_reserved(struct ubi_device *ubi);
|
||||
|
||||
/* gluebi.c */
|
||||
#ifdef CONFIG_MTD_UBI_GLUEBI
|
||||
int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol);
|
||||
int ubi_destroy_gluebi(struct ubi_volume *vol);
|
||||
void ubi_gluebi_updated(struct ubi_volume *vol);
|
||||
#else
|
||||
#define ubi_create_gluebi(ubi, vol) 0
|
||||
#define ubi_destroy_gluebi(vol) 0
|
||||
#define ubi_gluebi_updated(vol)
|
||||
#endif
|
||||
int ubi_check_pattern(const void *buf, uint8_t patt, int size);
|
||||
|
||||
/* eba.c */
|
||||
int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
|
@ -476,25 +766,33 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
void *buf, int offset, int len, int check);
|
||||
int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
||||
const void *buf, int offset, int len, int dtype);
|
||||
const void *buf, int offset, int len);
|
||||
int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
int lnum, const void *buf, int len, int dtype,
|
||||
int used_ebs);
|
||||
int lnum, const void *buf, int len, int used_ebs);
|
||||
int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
int lnum, const void *buf, int len, int dtype);
|
||||
int lnum, const void *buf, int len);
|
||||
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
||||
struct ubi_vid_hdr *vid_hdr);
|
||||
int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
|
||||
void ubi_eba_close(const struct ubi_device *ubi);
|
||||
int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai);
|
||||
unsigned long long ubi_next_sqnum(struct ubi_device *ubi);
|
||||
int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap,
|
||||
struct ubi_attach_info *ai_scan);
|
||||
|
||||
/* wl.c */
|
||||
int ubi_wl_get_peb(struct ubi_device *ubi, int dtype);
|
||||
int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture);
|
||||
int ubi_wl_flush(struct ubi_device *ubi);
|
||||
int ubi_wl_get_peb(struct ubi_device *ubi);
|
||||
int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
|
||||
int pnum, int torture);
|
||||
int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum);
|
||||
int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum);
|
||||
int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
|
||||
int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai);
|
||||
void ubi_wl_close(struct ubi_device *ubi);
|
||||
int ubi_thread(void *u);
|
||||
struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor);
|
||||
int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *used_e,
|
||||
int lnum, int torture);
|
||||
int ubi_is_erase_work(struct ubi_work *wrk);
|
||||
void ubi_refill_pools(struct ubi_device *ubi);
|
||||
int ubi_ensure_anchor_pebs(struct ubi_device *ubi);
|
||||
|
||||
/* io.c */
|
||||
int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
|
||||
|
@ -514,12 +812,11 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
|
|||
struct ubi_vid_hdr *vid_hdr);
|
||||
|
||||
/* build.c */
|
||||
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset);
|
||||
int ubi_detach_mtd_dev(struct mtd_info *mtd, int anyway);
|
||||
struct ubi_device *ubi_get_device(int ubi_num);
|
||||
void ubi_put_device(struct ubi_device *ubi);
|
||||
struct ubi_device *ubi_get_by_major(int major);
|
||||
int ubi_major2num(int major);
|
||||
void ubi_free_internal_volumes(struct ubi_device *ubi);
|
||||
|
||||
/* cdev.c */
|
||||
int ubi_cdev_add(struct ubi_device *ubi);
|
||||
|
@ -527,9 +824,23 @@ void ubi_cdev_remove(struct ubi_device *ubi);
|
|||
int ubi_volume_cdev_add(struct ubi_device *ubi, struct ubi_volume *vol);
|
||||
void ubi_volume_cdev_remove(struct ubi_volume *vol);
|
||||
|
||||
/* kapi.c */
|
||||
void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di);
|
||||
void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
struct ubi_volume_info *vi);
|
||||
/* scan.c */
|
||||
int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
|
||||
int pnum, const struct ubi_vid_hdr *vid_hdr);
|
||||
|
||||
/* fastmap.c */
|
||||
size_t ubi_calc_fm_size(struct ubi_device *ubi);
|
||||
int ubi_update_fastmap(struct ubi_device *ubi);
|
||||
int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
int fm_anchor);
|
||||
void ubi_free_fastmap(struct ubi_device *ubi);
|
||||
/*
|
||||
* ubi_rb_for_each_entry - walk an RB-tree.
|
||||
* @rb: a pointer to type 'struct rb_node' to to use as a loop counter
|
||||
* @rb: a pointer to type 'struct rb_node' to use as a loop counter
|
||||
* @pos: a pointer to RB-tree entry type to use as a loop counter
|
||||
* @root: RB-tree's root
|
||||
* @member: the name of the 'struct rb_node' within the RB-tree entry
|
||||
|
@ -538,7 +849,23 @@ void ubi_volume_cdev_remove(struct ubi_volume *vol);
|
|||
for (rb = rb_first(root), \
|
||||
pos = (rb ? container_of(rb, typeof(*pos), member) : NULL); \
|
||||
rb; \
|
||||
rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member))
|
||||
rb = rb_next(rb), \
|
||||
pos = (rb ? container_of(rb, typeof(*pos), member) : NULL))
|
||||
|
||||
/*
|
||||
* ubi_move_aeb_to_list - move a PEB from the volume tree to a list.
|
||||
*
|
||||
* @av: volume attaching information
|
||||
* @aeb: attaching eraseblock information
|
||||
* @list: the list to move to
|
||||
*/
|
||||
static inline void ubi_move_aeb_to_list(struct ubi_ainf_volume *av,
|
||||
struct ubi_ainf_peb *aeb,
|
||||
struct list_head *list)
|
||||
{
|
||||
rb_erase(&aeb->u.rb, &av->root);
|
||||
list_add_tail(&aeb->u.list, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_zalloc_vid_hdr - allocate a volume identifier header object.
|
||||
|
@ -550,7 +877,7 @@ void ubi_volume_cdev_remove(struct ubi_volume *vol);
|
|||
* failure.
|
||||
*/
|
||||
static inline struct ubi_vid_hdr *
|
||||
ubi_zalloc_vid_hdr(const struct ubi_device *ubi, unsigned int gfp_flags)
|
||||
ubi_zalloc_vid_hdr(const struct ubi_device *ubi, gfp_t gfp_flags)
|
||||
{
|
||||
void *vid_hdr;
|
||||
|
||||
|
@ -614,6 +941,7 @@ static inline void ubi_ro_mode(struct ubi_device *ubi)
|
|||
if (!ubi->ro_mode) {
|
||||
ubi->ro_mode = 1;
|
||||
ubi_warn("switch to read-only mode");
|
||||
dump_stack();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*
|
||||
|
@ -35,13 +38,8 @@
|
|||
* transaction with a roll-back capability.
|
||||
*/
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#include <linux/err.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/div64.h>
|
||||
#endif
|
||||
|
||||
#include "ubi-barebox.h"
|
||||
#include <linux/math64.h>
|
||||
#include "ubi.h"
|
||||
|
||||
/**
|
||||
|
@ -57,21 +55,18 @@ static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol)
|
|||
int err;
|
||||
struct ubi_vtbl_record vtbl_rec;
|
||||
|
||||
dbg_msg("set update marker for volume %d", vol->vol_id);
|
||||
dbg_gen("set update marker for volume %d", vol->vol_id);
|
||||
|
||||
if (vol->upd_marker) {
|
||||
ubi_assert(ubi->vtbl[vol->vol_id].upd_marker);
|
||||
dbg_msg("already set");
|
||||
dbg_gen("already set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id],
|
||||
sizeof(struct ubi_vtbl_record));
|
||||
vtbl_rec = ubi->vtbl[vol->vol_id];
|
||||
vtbl_rec.upd_marker = 1;
|
||||
|
||||
mutex_lock(&ubi->volumes_mutex);
|
||||
err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
|
||||
mutex_unlock(&ubi->volumes_mutex);
|
||||
vol->upd_marker = 1;
|
||||
return err;
|
||||
}
|
||||
|
@ -90,30 +85,26 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
long long bytes)
|
||||
{
|
||||
int err;
|
||||
uint64_t tmp;
|
||||
struct ubi_vtbl_record vtbl_rec;
|
||||
|
||||
dbg_msg("clear update marker for volume %d", vol->vol_id);
|
||||
dbg_gen("clear update marker for volume %d", vol->vol_id);
|
||||
|
||||
memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id],
|
||||
sizeof(struct ubi_vtbl_record));
|
||||
vtbl_rec = ubi->vtbl[vol->vol_id];
|
||||
ubi_assert(vol->upd_marker && vtbl_rec.upd_marker);
|
||||
vtbl_rec.upd_marker = 0;
|
||||
|
||||
if (vol->vol_type == UBI_STATIC_VOLUME) {
|
||||
vol->corrupted = 0;
|
||||
vol->used_bytes = tmp = bytes;
|
||||
vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size);
|
||||
vol->used_ebs = tmp;
|
||||
vol->used_bytes = bytes;
|
||||
vol->used_ebs = div_u64_rem(bytes, vol->usable_leb_size,
|
||||
&vol->last_eb_bytes);
|
||||
if (vol->last_eb_bytes)
|
||||
vol->used_ebs += 1;
|
||||
else
|
||||
vol->last_eb_bytes = vol->usable_leb_size;
|
||||
}
|
||||
|
||||
mutex_lock(&ubi->volumes_mutex);
|
||||
err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
|
||||
mutex_unlock(&ubi->volumes_mutex);
|
||||
vol->upd_marker = 0;
|
||||
return err;
|
||||
}
|
||||
|
@ -132,9 +123,8 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
long long bytes)
|
||||
{
|
||||
int i, err;
|
||||
uint64_t tmp;
|
||||
|
||||
dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes);
|
||||
dbg_gen("start update of volume %d, %llu bytes", vol->vol_id, bytes);
|
||||
ubi_assert(!vol->updating && !vol->changing_leb);
|
||||
vol->updating = 1;
|
||||
|
||||
|
@ -150,21 +140,23 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
}
|
||||
|
||||
if (bytes == 0) {
|
||||
err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clear_update_marker(ubi, vol, 0);
|
||||
if (err)
|
||||
return err;
|
||||
err = ubi_wl_flush(ubi);
|
||||
if (!err)
|
||||
vol->updating = 0;
|
||||
vol->updating = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
vol->upd_buf = vmalloc(ubi->leb_size);
|
||||
if (!vol->upd_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
tmp = bytes;
|
||||
vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size);
|
||||
vol->upd_ebs += tmp;
|
||||
vol->upd_ebs = div_u64(bytes + vol->usable_leb_size - 1,
|
||||
vol->usable_leb_size);
|
||||
vol->upd_bytes = bytes;
|
||||
vol->upd_received = 0;
|
||||
return 0;
|
||||
|
@ -178,7 +170,7 @@ int ubi_finish_update(struct ubi_device *ubi, struct ubi_volume *vol)
|
|||
err = clear_update_marker(ubi, vol, vol->upd_bytes);
|
||||
if (err)
|
||||
return err;
|
||||
err = ubi_wl_flush(ubi);
|
||||
err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
|
||||
if (err == 0) {
|
||||
vol->updating = 0;
|
||||
vfree(vol->upd_buf);
|
||||
|
@ -201,17 +193,15 @@ int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
{
|
||||
ubi_assert(!vol->updating && !vol->changing_leb);
|
||||
|
||||
dbg_msg("start changing LEB %d:%d, %u bytes",
|
||||
dbg_gen("start changing LEB %d:%d, %u bytes",
|
||||
vol->vol_id, req->lnum, req->bytes);
|
||||
if (req->bytes == 0)
|
||||
return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0,
|
||||
req->dtype);
|
||||
return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0);
|
||||
|
||||
vol->upd_bytes = req->bytes;
|
||||
vol->upd_received = 0;
|
||||
vol->changing_leb = 1;
|
||||
vol->ch_lnum = req->lnum;
|
||||
vol->ch_dtype = req->dtype;
|
||||
|
||||
vol->upd_buf = vmalloc(req->bytes);
|
||||
if (!vol->upd_buf)
|
||||
|
@ -260,11 +250,11 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
|||
memset(buf + len, 0xFF, l - len);
|
||||
len = ubi_calc_data_len(ubi, buf, l);
|
||||
if (len == 0) {
|
||||
dbg_msg("all %d bytes contain 0xFF - skip", len);
|
||||
dbg_gen("all %d bytes contain 0xFF - skip", len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN);
|
||||
err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len);
|
||||
} else {
|
||||
/*
|
||||
* When writing static volume, and this is the last logical
|
||||
|
@ -276,8 +266,7 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
|||
* contain zeros, not random trash.
|
||||
*/
|
||||
memset(buf + len, 0, vol->usable_leb_size - len);
|
||||
err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len,
|
||||
UBI_UNKNOWN, used_ebs);
|
||||
err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len, used_ebs);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -285,6 +274,7 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
|||
|
||||
/**
|
||||
* ubi_more_update_data - write more update data.
|
||||
* @ubi: UBI device description object
|
||||
* @vol: volume description object
|
||||
* @buf: write data (user-space memory buffer)
|
||||
* @count: how much bytes to write
|
||||
|
@ -298,19 +288,15 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
|
|||
int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
||||
const void __user *buf, int count)
|
||||
{
|
||||
uint64_t tmp;
|
||||
int lnum, offs, err = 0, len, to_write = count;
|
||||
|
||||
dbg_msg("write %d of %lld bytes, %lld already passed",
|
||||
dbg_gen("write %d of %lld bytes, %lld already passed",
|
||||
count, vol->upd_bytes, vol->upd_received);
|
||||
|
||||
if (ubi->ro_mode)
|
||||
return -EROFS;
|
||||
|
||||
tmp = vol->upd_received;
|
||||
offs = do_div(tmp, vol->usable_leb_size);
|
||||
lnum = tmp;
|
||||
|
||||
lnum = div_u64_rem(vol->upd_received, vol->usable_leb_size, &offs);
|
||||
if (vol->upd_received + count > vol->upd_bytes)
|
||||
to_write = count = vol->upd_bytes - vol->upd_received;
|
||||
|
||||
|
@ -334,12 +320,13 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
if (offs + len == vol->usable_leb_size) {
|
||||
if (offs + len == vol->usable_leb_size ||
|
||||
vol->upd_received + len == vol->upd_bytes) {
|
||||
int flush_len = offs + len;
|
||||
|
||||
/*
|
||||
* OK, we gathered the whole eraseblock, it's time to flush
|
||||
* the buffer.
|
||||
* OK, we gathered either the whole eraseblock or this
|
||||
* is the last chunk, it's time to flush the buffer.
|
||||
*/
|
||||
ubi_assert(flush_len <= vol->usable_leb_size);
|
||||
err = write_leb(ubi, vol, lnum, vol->upd_buf, flush_len,
|
||||
|
@ -383,12 +370,25 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
}
|
||||
|
||||
ubi_assert(vol->upd_received <= vol->upd_bytes);
|
||||
if (vol->upd_received == vol->upd_bytes) {
|
||||
err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
|
||||
if (err)
|
||||
return err;
|
||||
/* The update is finished, clear the update marker */
|
||||
err = clear_update_marker(ubi, vol, vol->upd_bytes);
|
||||
if (err)
|
||||
return err;
|
||||
vol->updating = 0;
|
||||
err = to_write;
|
||||
vfree(vol->upd_buf);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_more_leb_change_data - accept more data for atomic LEB change.
|
||||
* @ubi: UBI device description object
|
||||
* @vol: volume description object
|
||||
* @buf: write data (user-space memory buffer)
|
||||
* @count: how much bytes to write
|
||||
|
@ -405,7 +405,7 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
{
|
||||
int err;
|
||||
|
||||
dbg_msg("write %d of %lld bytes, %lld already passed",
|
||||
dbg_gen("write %d of %lld bytes, %lld already passed",
|
||||
count, vol->upd_bytes, vol->upd_received);
|
||||
|
||||
if (ubi->ro_mode)
|
||||
|
@ -423,10 +423,11 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
|
|||
if (vol->upd_received == vol->upd_bytes) {
|
||||
int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size);
|
||||
|
||||
memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes);
|
||||
memset(vol->upd_buf + vol->upd_bytes, 0xFF,
|
||||
len - vol->upd_bytes);
|
||||
len = ubi_calc_data_len(ubi, vol->upd_buf, len);
|
||||
err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum,
|
||||
vol->upd_buf, len, UBI_UNKNOWN);
|
||||
vol->upd_buf, len);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
@ -20,178 +23,11 @@
|
|||
* resizing.
|
||||
*/
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#include <linux/err.h>
|
||||
#include <asm/div64.h>
|
||||
#endif
|
||||
|
||||
#include "ubi-barebox.h"
|
||||
#include <linux/math64.h>
|
||||
#include "ubi.h"
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||
static void paranoid_check_volumes(struct ubi_device *ubi);
|
||||
#else
|
||||
#define paranoid_check_volumes(ubi)
|
||||
#endif
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
static ssize_t vol_attribute_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
/* Device attributes corresponding to files in '/<sysfs>/class/ubi/ubiX_Y' */
|
||||
static struct device_attribute attr_vol_reserved_ebs =
|
||||
__ATTR(reserved_ebs, S_IRUGO, vol_attribute_show, NULL);
|
||||
static struct device_attribute attr_vol_type =
|
||||
__ATTR(type, S_IRUGO, vol_attribute_show, NULL);
|
||||
static struct device_attribute attr_vol_name =
|
||||
__ATTR(name, S_IRUGO, vol_attribute_show, NULL);
|
||||
static struct device_attribute attr_vol_corrupted =
|
||||
__ATTR(corrupted, S_IRUGO, vol_attribute_show, NULL);
|
||||
static struct device_attribute attr_vol_alignment =
|
||||
__ATTR(alignment, S_IRUGO, vol_attribute_show, NULL);
|
||||
static struct device_attribute attr_vol_usable_eb_size =
|
||||
__ATTR(usable_eb_size, S_IRUGO, vol_attribute_show, NULL);
|
||||
static struct device_attribute attr_vol_data_bytes =
|
||||
__ATTR(data_bytes, S_IRUGO, vol_attribute_show, NULL);
|
||||
static struct device_attribute attr_vol_upd_marker =
|
||||
__ATTR(upd_marker, S_IRUGO, vol_attribute_show, NULL);
|
||||
|
||||
/*
|
||||
* "Show" method for files in '/<sysfs>/class/ubi/ubiX_Y/'.
|
||||
*
|
||||
* Consider a situation:
|
||||
* A. process 1 opens a sysfs file related to volume Y, say
|
||||
* /<sysfs>/class/ubi/ubiX_Y/reserved_ebs;
|
||||
* B. process 2 removes volume Y;
|
||||
* C. process 1 starts reading the /<sysfs>/class/ubi/ubiX_Y/reserved_ebs file;
|
||||
*
|
||||
* In this situation, this function will return %-ENODEV because it will find
|
||||
* out that the volume was removed from the @ubi->volumes array.
|
||||
*/
|
||||
static ssize_t vol_attribute_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
|
||||
struct ubi_device *ubi;
|
||||
|
||||
ubi = ubi_get_device(vol->ubi->ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
if (!ubi->volumes[vol->vol_id]) {
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
ubi_put_device(ubi);
|
||||
return -ENODEV;
|
||||
}
|
||||
/* Take a reference to prevent volume removal */
|
||||
vol->ref_count += 1;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
if (attr == &attr_vol_reserved_ebs)
|
||||
ret = sprintf(buf, "%d\n", vol->reserved_pebs);
|
||||
else if (attr == &attr_vol_type) {
|
||||
const char *tp;
|
||||
|
||||
if (vol->vol_type == UBI_DYNAMIC_VOLUME)
|
||||
tp = "dynamic";
|
||||
else
|
||||
tp = "static";
|
||||
ret = sprintf(buf, "%s\n", tp);
|
||||
} else if (attr == &attr_vol_name)
|
||||
ret = sprintf(buf, "%s\n", vol->name);
|
||||
else if (attr == &attr_vol_corrupted)
|
||||
ret = sprintf(buf, "%d\n", vol->corrupted);
|
||||
else if (attr == &attr_vol_alignment)
|
||||
ret = sprintf(buf, "%d\n", vol->alignment);
|
||||
else if (attr == &attr_vol_usable_eb_size)
|
||||
ret = sprintf(buf, "%d\n", vol->usable_leb_size);
|
||||
else if (attr == &attr_vol_data_bytes)
|
||||
ret = sprintf(buf, "%lld\n", vol->used_bytes);
|
||||
else if (attr == &attr_vol_upd_marker)
|
||||
ret = sprintf(buf, "%d\n", vol->upd_marker);
|
||||
else
|
||||
/* This must be a bug */
|
||||
ret = -EINVAL;
|
||||
|
||||
/* We've done the operation, drop volume and UBI device references */
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
vol->ref_count -= 1;
|
||||
ubi_assert(vol->ref_count >= 0);
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
ubi_put_device(ubi);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Release method for volume devices */
|
||||
static void vol_release(struct device *dev)
|
||||
{
|
||||
struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
|
||||
|
||||
kfree(vol);
|
||||
}
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
/**
|
||||
* volume_sysfs_init - initialize sysfs for new volume.
|
||||
* @ubi: UBI device description object
|
||||
* @vol: volume description object
|
||||
*
|
||||
* This function returns zero in case of success and a negative error code in
|
||||
* case of failure.
|
||||
*
|
||||
* Note, this function does not free allocated resources in case of failure -
|
||||
* the caller does it. This is because this would cause release() here and the
|
||||
* caller would oops.
|
||||
*/
|
||||
static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = device_create_file(&vol->dev, &attr_vol_reserved_ebs);
|
||||
if (err)
|
||||
return err;
|
||||
err = device_create_file(&vol->dev, &attr_vol_type);
|
||||
if (err)
|
||||
return err;
|
||||
err = device_create_file(&vol->dev, &attr_vol_name);
|
||||
if (err)
|
||||
return err;
|
||||
err = device_create_file(&vol->dev, &attr_vol_corrupted);
|
||||
if (err)
|
||||
return err;
|
||||
err = device_create_file(&vol->dev, &attr_vol_alignment);
|
||||
if (err)
|
||||
return err;
|
||||
err = device_create_file(&vol->dev, &attr_vol_usable_eb_size);
|
||||
if (err)
|
||||
return err;
|
||||
err = device_create_file(&vol->dev, &attr_vol_data_bytes);
|
||||
if (err)
|
||||
return err;
|
||||
err = device_create_file(&vol->dev, &attr_vol_upd_marker);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* volume_sysfs_close - close sysfs for a volume.
|
||||
* @vol: volume description object
|
||||
*/
|
||||
static void volume_sysfs_close(struct ubi_volume *vol)
|
||||
{
|
||||
device_remove_file(&vol->dev, &attr_vol_upd_marker);
|
||||
device_remove_file(&vol->dev, &attr_vol_data_bytes);
|
||||
device_remove_file(&vol->dev, &attr_vol_usable_eb_size);
|
||||
device_remove_file(&vol->dev, &attr_vol_alignment);
|
||||
device_remove_file(&vol->dev, &attr_vol_corrupted);
|
||||
device_remove_file(&vol->dev, &attr_vol_name);
|
||||
device_remove_file(&vol->dev, &attr_vol_type);
|
||||
device_remove_file(&vol->dev, &attr_vol_reserved_ebs);
|
||||
device_unregister(&vol->dev);
|
||||
}
|
||||
#endif
|
||||
static int self_check_volumes(struct ubi_device *ubi);
|
||||
|
||||
/**
|
||||
* ubi_create_volume - create volume.
|
||||
|
@ -202,15 +38,13 @@ static void volume_sysfs_close(struct ubi_volume *vol)
|
|||
* %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume
|
||||
* and saves it in @req->vol_id. Returns zero in case of success and a negative
|
||||
* error code in case of failure. Note, the caller has to have the
|
||||
* @ubi->volumes_mutex locked.
|
||||
* @ubi->device_mutex locked.
|
||||
*/
|
||||
int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
||||
{
|
||||
int i, err, vol_id = req->vol_id, dont_free = 0;
|
||||
int i, err, vol_id = req->vol_id, do_free = 1;
|
||||
struct ubi_volume *vol;
|
||||
struct ubi_vtbl_record vtbl_rec;
|
||||
uint64_t bytes;
|
||||
dev_t dev;
|
||||
|
||||
if (ubi->ro_mode)
|
||||
return -EROFS;
|
||||
|
@ -219,10 +53,9 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
|||
if (!vol)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
if (vol_id == UBI_VOL_NUM_AUTO) {
|
||||
/* Find unused volume ID */
|
||||
dbg_msg("search for vacant volume ID");
|
||||
dbg_gen("search for vacant volume ID");
|
||||
for (i = 0; i < ubi->vtbl_slots; i++)
|
||||
if (!ubi->volumes[i]) {
|
||||
vol_id = i;
|
||||
|
@ -230,21 +63,21 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
|||
}
|
||||
|
||||
if (vol_id == UBI_VOL_NUM_AUTO) {
|
||||
dbg_err("out of volume IDs");
|
||||
ubi_err("out of volume IDs");
|
||||
err = -ENFILE;
|
||||
goto out_unlock;
|
||||
}
|
||||
req->vol_id = vol_id;
|
||||
}
|
||||
|
||||
dbg_msg("volume ID %d, %llu bytes, type %d, name %s",
|
||||
vol_id, (unsigned long long)req->bytes,
|
||||
dbg_gen("create device %d, volume %d, %llu bytes, type %d, name %s",
|
||||
ubi->ubi_num, vol_id, (unsigned long long)req->bytes,
|
||||
(int)req->vol_type, req->name);
|
||||
|
||||
/* Ensure that this volume does not exist */
|
||||
err = -EEXIST;
|
||||
if (ubi->volumes[vol_id]) {
|
||||
dbg_err("volume %d already exists", vol_id);
|
||||
ubi_err("volume %d already exists", vol_id);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
|
@ -253,40 +86,40 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
|||
if (ubi->volumes[i] &&
|
||||
ubi->volumes[i]->name_len == req->name_len &&
|
||||
!strcmp(ubi->volumes[i]->name, req->name)) {
|
||||
dbg_err("volume \"%s\" exists (ID %d)", req->name, i);
|
||||
ubi_err("volume \"%s\" exists (ID %d)", req->name, i);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Calculate how many eraseblocks are requested */
|
||||
vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
|
||||
bytes = req->bytes;
|
||||
if (do_div(bytes, vol->usable_leb_size))
|
||||
vol->reserved_pebs = 1;
|
||||
vol->reserved_pebs += bytes;
|
||||
vol->reserved_pebs += div_u64(req->bytes + vol->usable_leb_size - 1,
|
||||
vol->usable_leb_size);
|
||||
|
||||
/* Reserve physical eraseblocks */
|
||||
if (vol->reserved_pebs > ubi->avail_pebs) {
|
||||
dbg_err("not enough PEBs, only %d available", ubi->avail_pebs);
|
||||
ubi_err("not enough PEBs, only %d available", ubi->avail_pebs);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_err("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
err = -ENOSPC;
|
||||
goto out_unlock;
|
||||
}
|
||||
ubi->avail_pebs -= vol->reserved_pebs;
|
||||
ubi->rsvd_pebs += vol->reserved_pebs;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
vol->vol_id = vol_id;
|
||||
vol->alignment = req->alignment;
|
||||
vol->data_pad = ubi->leb_size % vol->alignment;
|
||||
vol->vol_type = req->vol_type;
|
||||
vol->name_len = req->name_len;
|
||||
memcpy(vol->name, req->name, vol->name_len + 1);
|
||||
memcpy(vol->name, req->name, vol->name_len);
|
||||
vol->ubi = ubi;
|
||||
|
||||
/*
|
||||
* Finish all pending erases because there may be some LEBs belonging
|
||||
* to the same volume ID.
|
||||
*/
|
||||
err = ubi_wl_flush(ubi);
|
||||
err = ubi_wl_flush(ubi, vol_id, UBI_ALL);
|
||||
if (err)
|
||||
goto out_acc;
|
||||
|
||||
|
@ -305,47 +138,22 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
|||
vol->used_bytes =
|
||||
(long long)vol->used_ebs * vol->usable_leb_size;
|
||||
} else {
|
||||
bytes = vol->used_bytes;
|
||||
vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size);
|
||||
vol->used_ebs = bytes;
|
||||
if (vol->last_eb_bytes)
|
||||
vol->used_ebs = div_u64_rem(vol->used_bytes,
|
||||
vol->usable_leb_size,
|
||||
&vol->last_eb_bytes);
|
||||
if (vol->last_eb_bytes != 0)
|
||||
vol->used_ebs += 1;
|
||||
else
|
||||
vol->last_eb_bytes = vol->usable_leb_size;
|
||||
}
|
||||
|
||||
/* Register character device for the volume */
|
||||
cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
|
||||
#ifdef UBI_LINUX
|
||||
vol->cdev.owner = THIS_MODULE;
|
||||
#endif
|
||||
dev = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1);
|
||||
err = ubi_volume_cdev_add(ubi, vol);
|
||||
if (err) {
|
||||
ubi_err("cannot add character device");
|
||||
goto out_mapping;
|
||||
}
|
||||
|
||||
err = ubi_create_gluebi(ubi, vol);
|
||||
if (err)
|
||||
goto out_cdev;
|
||||
|
||||
vol->dev.release = vol_release;
|
||||
vol->dev.parent = &ubi->dev;
|
||||
vol->dev.devt = dev;
|
||||
vol->dev.class = ubi_class;
|
||||
|
||||
sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id);
|
||||
err = device_register(&vol->dev);
|
||||
if (err) {
|
||||
ubi_err("cannot register device");
|
||||
goto out_gluebi;
|
||||
}
|
||||
|
||||
err = volume_sysfs_init(ubi, vol);
|
||||
if (err)
|
||||
goto out_sysfs;
|
||||
|
||||
/* Fill volume table record */
|
||||
memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record));
|
||||
vtbl_rec.reserved_pebs = cpu_to_be32(vol->reserved_pebs);
|
||||
|
@ -356,49 +164,37 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
|
|||
vtbl_rec.vol_type = UBI_VID_DYNAMIC;
|
||||
else
|
||||
vtbl_rec.vol_type = UBI_VID_STATIC;
|
||||
memcpy(vtbl_rec.name, vol->name, vol->name_len + 1);
|
||||
memcpy(vtbl_rec.name, vol->name, vol->name_len);
|
||||
|
||||
err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
|
||||
if (err)
|
||||
goto out_sysfs;
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
ubi->volumes[vol_id] = vol;
|
||||
ubi->vol_count += 1;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
paranoid_check_volumes(ubi);
|
||||
return 0;
|
||||
ubi_volume_notify(ubi, vol, UBI_VOLUME_ADDED);
|
||||
self_check_volumes(ubi);
|
||||
return err;
|
||||
|
||||
out_sysfs:
|
||||
/*
|
||||
* We have registered our device, we should not free the volume*
|
||||
* We have registered our device, we should not free the volume
|
||||
* description object in this function in case of an error - it is
|
||||
* freed by the release function.
|
||||
*
|
||||
* Get device reference to prevent the release function from being
|
||||
* called just after sysfs has been closed.
|
||||
*/
|
||||
dont_free = 1;
|
||||
get_device(&vol->dev);
|
||||
volume_sysfs_close(vol);
|
||||
out_gluebi:
|
||||
if (ubi_destroy_gluebi(vol))
|
||||
dbg_err("cannot destroy gluebi for volume %d:%d",
|
||||
ubi->ubi_num, vol_id);
|
||||
out_cdev:
|
||||
ubi_volume_cdev_remove(vol);
|
||||
do_free = 0;
|
||||
out_mapping:
|
||||
kfree(vol->eba_tbl);
|
||||
if (do_free)
|
||||
kfree(vol->eba_tbl);
|
||||
out_acc:
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
ubi->rsvd_pebs -= vol->reserved_pebs;
|
||||
ubi->avail_pebs += vol->reserved_pebs;
|
||||
out_unlock:
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
if (dont_free)
|
||||
put_device(&vol->dev);
|
||||
else
|
||||
if (do_free)
|
||||
kfree(vol);
|
||||
ubi_err("cannot create volume %d, error %d", vol_id, err);
|
||||
return err;
|
||||
|
@ -407,26 +203,26 @@ out_unlock:
|
|||
/**
|
||||
* ubi_remove_volume - remove volume.
|
||||
* @desc: volume descriptor
|
||||
* @no_vtbl: do not change volume table if not zero
|
||||
*
|
||||
* This function removes volume described by @desc. The volume has to be opened
|
||||
* in "exclusive" mode. Returns zero in case of success and a negative error
|
||||
* code in case of failure. The caller has to have the @ubi->volumes_mutex
|
||||
* code in case of failure. The caller has to have the @ubi->device_mutex
|
||||
* locked.
|
||||
*/
|
||||
int ubi_remove_volume(struct ubi_volume_desc *desc)
|
||||
int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
|
||||
{
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs;
|
||||
|
||||
dbg_msg("remove UBI volume %d", vol_id);
|
||||
dbg_gen("remove device %d, volume %d", ubi->ubi_num, vol_id);
|
||||
ubi_assert(desc->mode == UBI_EXCLUSIVE);
|
||||
ubi_assert(vol == ubi->volumes[vol_id]);
|
||||
|
||||
if (ubi->ro_mode)
|
||||
return -EROFS;
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
if (vol->ref_count > 1) {
|
||||
/*
|
||||
* The volume is busy, probably someone is reading one of its
|
||||
|
@ -436,15 +232,12 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
|
|||
goto out_unlock;
|
||||
}
|
||||
ubi->volumes[vol_id] = NULL;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
err = ubi_destroy_gluebi(vol);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
err = ubi_change_vtbl_record(ubi, vol_id, NULL);
|
||||
if (err)
|
||||
goto out_err;
|
||||
if (!no_vtbl) {
|
||||
err = ubi_change_vtbl_record(ubi, vol_id, NULL);
|
||||
if (err)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
for (i = 0; i < vol->reserved_pebs; i++) {
|
||||
err = ubi_eba_unmap_leb(ubi, vol, i);
|
||||
|
@ -452,35 +245,23 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
|
|||
goto out_err;
|
||||
}
|
||||
|
||||
kfree(vol->eba_tbl);
|
||||
vol->eba_tbl = NULL;
|
||||
ubi_volume_cdev_remove(vol);
|
||||
volume_sysfs_close(vol);
|
||||
devfs_remove(&vol->cdev);
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
ubi->rsvd_pebs -= reserved_pebs;
|
||||
ubi->avail_pebs += reserved_pebs;
|
||||
i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
|
||||
if (i > 0) {
|
||||
i = ubi->avail_pebs >= i ? i : ubi->avail_pebs;
|
||||
ubi->avail_pebs -= i;
|
||||
ubi->rsvd_pebs += i;
|
||||
ubi->beb_rsvd_pebs += i;
|
||||
if (i > 0)
|
||||
ubi_msg("reserve more %d PEBs", i);
|
||||
}
|
||||
ubi_update_reserved(ubi);
|
||||
ubi->vol_count -= 1;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
paranoid_check_volumes(ubi);
|
||||
return 0;
|
||||
ubi_volume_notify(ubi, vol, UBI_VOLUME_REMOVED);
|
||||
if (!no_vtbl)
|
||||
self_check_volumes(ubi);
|
||||
|
||||
return err;
|
||||
|
||||
out_err:
|
||||
ubi_err("cannot remove volume %d, error %d", vol_id, err);
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
ubi->volumes[vol_id] = vol;
|
||||
out_unlock:
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -491,7 +272,7 @@ out_unlock:
|
|||
*
|
||||
* This function re-sizes the volume and returns zero in case of success, and a
|
||||
* negative error code in case of failure. The caller has to have the
|
||||
* @ubi->volumes_mutex locked.
|
||||
* @ubi->device_mutex locked.
|
||||
*/
|
||||
int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
||||
{
|
||||
|
@ -504,12 +285,12 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
|||
if (ubi->ro_mode)
|
||||
return -EROFS;
|
||||
|
||||
dbg_msg("re-size volume %d to from %d to %d PEBs",
|
||||
vol_id, vol->reserved_pebs, reserved_pebs);
|
||||
dbg_gen("re-size device %d, volume %d to from %d to %d PEBs",
|
||||
ubi->ubi_num, vol_id, vol->reserved_pebs, reserved_pebs);
|
||||
|
||||
if (vol->vol_type == UBI_STATIC_VOLUME &&
|
||||
reserved_pebs < vol->used_ebs) {
|
||||
dbg_err("too small size %d, %d LEBs contain data",
|
||||
ubi_err("too small size %d, %d LEBs contain data",
|
||||
reserved_pebs, vol->used_ebs);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -525,22 +306,20 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
|||
for (i = 0; i < reserved_pebs; i++)
|
||||
new_mapping[i] = UBI_LEB_UNMAPPED;
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
if (vol->ref_count > 1) {
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
err = -EBUSY;
|
||||
goto out_free;
|
||||
}
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
|
||||
/* Reserve physical eraseblocks */
|
||||
pebs = reserved_pebs - vol->reserved_pebs;
|
||||
if (pebs > 0) {
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
if (pebs > ubi->avail_pebs) {
|
||||
dbg_err("not enough PEBs: requested %d, available %d",
|
||||
ubi_err("not enough PEBs: requested %d, available %d",
|
||||
pebs, ubi->avail_pebs);
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_err("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
err = -ENOSPC;
|
||||
goto out_free;
|
||||
}
|
||||
|
@ -550,11 +329,10 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
|||
new_mapping[i] = vol->eba_tbl[i];
|
||||
kfree(vol->eba_tbl);
|
||||
vol->eba_tbl = new_mapping;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
}
|
||||
|
||||
/* Change volume table record */
|
||||
memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record));
|
||||
vtbl_rec = ubi->vtbl[vol_id];
|
||||
vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs);
|
||||
err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
|
||||
if (err)
|
||||
|
@ -566,23 +344,13 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
|||
if (err)
|
||||
goto out_acc;
|
||||
}
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
ubi->rsvd_pebs += pebs;
|
||||
ubi->avail_pebs -= pebs;
|
||||
pebs = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
|
||||
if (pebs > 0) {
|
||||
pebs = ubi->avail_pebs >= pebs ? pebs : ubi->avail_pebs;
|
||||
ubi->avail_pebs -= pebs;
|
||||
ubi->rsvd_pebs += pebs;
|
||||
ubi->beb_rsvd_pebs += pebs;
|
||||
if (pebs > 0)
|
||||
ubi_msg("reserve more %d PEBs", pebs);
|
||||
}
|
||||
ubi_update_reserved(ubi);
|
||||
for (i = 0; i < reserved_pebs; i++)
|
||||
new_mapping[i] = vol->eba_tbl[i];
|
||||
kfree(vol->eba_tbl);
|
||||
vol->eba_tbl = new_mapping;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
}
|
||||
|
||||
vol->reserved_pebs = reserved_pebs;
|
||||
|
@ -593,21 +361,57 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
|
|||
(long long)vol->used_ebs * vol->usable_leb_size;
|
||||
}
|
||||
|
||||
paranoid_check_volumes(ubi);
|
||||
return 0;
|
||||
ubi_volume_notify(ubi, vol, UBI_VOLUME_RESIZED);
|
||||
self_check_volumes(ubi);
|
||||
return err;
|
||||
|
||||
out_acc:
|
||||
if (pebs > 0) {
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
ubi->rsvd_pebs -= pebs;
|
||||
ubi->avail_pebs += pebs;
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
}
|
||||
out_free:
|
||||
kfree(new_mapping);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_rename_volumes - re-name UBI volumes.
|
||||
* @ubi: UBI device description object
|
||||
* @rename_list: list of &struct ubi_rename_entry objects
|
||||
*
|
||||
* This function re-names or removes volumes specified in the re-name list.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list)
|
||||
{
|
||||
int err;
|
||||
struct ubi_rename_entry *re;
|
||||
|
||||
err = ubi_vtbl_rename_volumes(ubi, rename_list);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
list_for_each_entry(re, rename_list, list) {
|
||||
if (re->remove) {
|
||||
err = ubi_remove_volume(re->desc, 1);
|
||||
if (err)
|
||||
break;
|
||||
} else {
|
||||
struct ubi_volume *vol = re->desc->vol;
|
||||
|
||||
vol->name_len = re->new_name_len;
|
||||
memcpy(vol->name, re->new_name, re->new_name_len + 1);
|
||||
ubi_volume_notify(ubi, vol, UBI_VOLUME_RENAMED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!err)
|
||||
self_check_volumes(ubi);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_add_volume - add volume.
|
||||
* @ubi: UBI device description object
|
||||
|
@ -619,53 +423,21 @@ out_free:
|
|||
*/
|
||||
int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
|
||||
{
|
||||
int err, vol_id = vol->vol_id;
|
||||
dev_t dev;
|
||||
int err = 0;
|
||||
|
||||
dbg_msg("add volume %d", vol_id);
|
||||
ubi_dbg_dump_vol_info(vol);
|
||||
dbg_gen("add volume");
|
||||
|
||||
/* Register character device for the volume */
|
||||
cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
|
||||
#ifdef UBI_LINUX
|
||||
vol->cdev.owner = THIS_MODULE;
|
||||
#endif
|
||||
dev = MKDEV(MAJOR(ubi->cdev.dev), vol->vol_id + 1);
|
||||
err = ubi_volume_cdev_add(ubi, vol);
|
||||
if (err) {
|
||||
ubi_err("cannot add character device for volume %d, error %d",
|
||||
vol_id, err);
|
||||
ubi_err("cannot add character device for volume, error %d",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ubi_create_gluebi(ubi, vol);
|
||||
if (err)
|
||||
goto out_cdev;
|
||||
self_check_volumes(ubi);
|
||||
return err;
|
||||
|
||||
vol->dev.release = vol_release;
|
||||
vol->dev.parent = &ubi->dev;
|
||||
vol->dev.devt = dev;
|
||||
vol->dev.class = ubi_class;
|
||||
sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id);
|
||||
err = device_register(&vol->dev);
|
||||
if (err)
|
||||
goto out_gluebi;
|
||||
|
||||
err = volume_sysfs_init(ubi, vol);
|
||||
if (err) {
|
||||
ubi_volume_cdev_remove(vol);
|
||||
err = ubi_destroy_gluebi(vol);
|
||||
volume_sysfs_close(vol);
|
||||
return err;
|
||||
}
|
||||
|
||||
paranoid_check_volumes(ubi);
|
||||
return 0;
|
||||
|
||||
out_gluebi:
|
||||
err = ubi_destroy_gluebi(vol);
|
||||
out_cdev:
|
||||
ubi_volume_cdev_remove(vol);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -679,24 +451,20 @@ out_cdev:
|
|||
*/
|
||||
void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol)
|
||||
{
|
||||
int err;
|
||||
|
||||
dbg_msg("free volume %d", vol->vol_id);
|
||||
dbg_gen("free volume %d", vol->vol_id);
|
||||
|
||||
ubi->volumes[vol->vol_id] = NULL;
|
||||
err = ubi_destroy_gluebi(vol);
|
||||
ubi_volume_cdev_remove(vol);
|
||||
volume_sysfs_close(vol);
|
||||
devfs_remove(&vol->cdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||
|
||||
/**
|
||||
* paranoid_check_volume - check volume information.
|
||||
* self_check_volume - check volume information.
|
||||
* @ubi: UBI device description object
|
||||
* @vol_id: volume ID
|
||||
*
|
||||
* Returns zero if volume is all right and a a negative error code if not.
|
||||
*/
|
||||
static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
||||
static int self_check_volume(struct ubi_device *ubi, int vol_id)
|
||||
{
|
||||
int idx = vol_id2idx(ubi, vol_id);
|
||||
int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker;
|
||||
|
@ -704,7 +472,6 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
|||
long long n;
|
||||
const char *name;
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
reserved_pebs = be32_to_cpu(ubi->vtbl[vol_id].reserved_pebs);
|
||||
vol = ubi->volumes[idx];
|
||||
|
||||
|
@ -713,17 +480,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
|||
ubi_err("no volume info, but volume exists");
|
||||
goto fail;
|
||||
}
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vol->exclusive) {
|
||||
/*
|
||||
* The volume may be being created at the moment, do not check
|
||||
* it (e.g., it may be in the middle of ubi_create_volume().
|
||||
*/
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 ||
|
||||
|
@ -755,7 +512,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
|||
}
|
||||
|
||||
if (vol->upd_marker && vol->corrupted) {
|
||||
dbg_err("update marker and corrupted simultaneously");
|
||||
ubi_err("update marker and corrupted simultaneously");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -775,11 +532,6 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (!vol->name) {
|
||||
ubi_err("NULL volume name");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n = strnlen(vol->name, vol->name_len + 1);
|
||||
if (n != vol->name_len) {
|
||||
ubi_err("bad name_len %lld", n);
|
||||
|
@ -833,31 +585,40 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
|
|||
|
||||
if (alignment != vol->alignment || data_pad != vol->data_pad ||
|
||||
upd_marker != vol->upd_marker || vol_type != vol->vol_type ||
|
||||
name_len!= vol->name_len || strncmp(name, vol->name, name_len)) {
|
||||
name_len != vol->name_len || strncmp(name, vol->name, name_len)) {
|
||||
ubi_err("volume info is different");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
return;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ubi_err("paranoid check failed for volume %d", vol_id);
|
||||
ubi_dbg_dump_vol_info(vol);
|
||||
ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
BUG();
|
||||
ubi_err("self-check failed for volume %d", vol_id);
|
||||
if (vol)
|
||||
ubi_dump_vol_info(vol);
|
||||
ubi_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
|
||||
dump_stack();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* paranoid_check_volumes - check information about all volumes.
|
||||
* self_check_volumes - check information about all volumes.
|
||||
* @ubi: UBI device description object
|
||||
*
|
||||
* Returns zero if volumes are all right and a a negative error code if not.
|
||||
*/
|
||||
static void paranoid_check_volumes(struct ubi_device *ubi)
|
||||
static int self_check_volumes(struct ubi_device *ubi)
|
||||
{
|
||||
int i;
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots; i++)
|
||||
paranoid_check_volume(ubi, i);
|
||||
if (!ubi_dbg_chk_gen(ubi))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots; i++) {
|
||||
err = self_check_volume(ubi, i);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
@ -34,16 +37,15 @@
|
|||
* LEB 1. This scheme guarantees recoverability from unclean reboots.
|
||||
*
|
||||
* In this UBI implementation the on-flash volume table does not contain any
|
||||
* information about how many data static volumes contain. This information may
|
||||
* be found from the scanning data.
|
||||
* information about how much data static volumes contain.
|
||||
*
|
||||
* But it would still be beneficial to store this information in the volume
|
||||
* table. For example, suppose we have a static volume X, and all its physical
|
||||
* eraseblocks became bad for some reasons. Suppose we are attaching the
|
||||
* corresponding MTD device, the scanning has found no logical eraseblocks
|
||||
* corresponding MTD device, for some reason we find no logical eraseblocks
|
||||
* corresponding to the volume X. According to the volume table volume X does
|
||||
* exist. So we don't know whether it is just empty or all its physical
|
||||
* eraseblocks went bad. So we cannot alarm the user about this corruption.
|
||||
* eraseblocks went bad. So we cannot alarm the user properly.
|
||||
*
|
||||
* The volume table also stores so-called "update marker", which is used for
|
||||
* volume updates. Before updating the volume, the update marker is set, and
|
||||
|
@ -53,20 +55,10 @@
|
|||
* damaged.
|
||||
*/
|
||||
|
||||
#ifdef UBI_LINUX
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/err.h>
|
||||
#include <asm/div64.h>
|
||||
#endif
|
||||
|
||||
#include "ubi-barebox.h"
|
||||
#include "ubi.h"
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||
static void paranoid_vtbl_check(const struct ubi_device *ubi);
|
||||
#else
|
||||
#define paranoid_vtbl_check(ubi)
|
||||
#endif
|
||||
static void self_vtbl_check(const struct ubi_device *ubi);
|
||||
|
||||
/* Empty volume table record */
|
||||
static struct ubi_vtbl_record empty_vtbl_record;
|
||||
|
@ -106,18 +98,68 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
|
|||
return err;
|
||||
|
||||
err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0,
|
||||
ubi->vtbl_size, UBI_LONGTERM);
|
||||
ubi->vtbl_size);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
paranoid_vtbl_check(ubi);
|
||||
self_vtbl_check(ubi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vtbl_check - check if volume table is not corrupted and contains sensible
|
||||
* data.
|
||||
* ubi_vtbl_rename_volumes - rename UBI volumes in the volume table.
|
||||
* @ubi: UBI device description object
|
||||
* @rename_list: list of &struct ubi_rename_entry objects
|
||||
*
|
||||
* This function re-names multiple volumes specified in @req in the volume
|
||||
* table. Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
|
||||
struct list_head *rename_list)
|
||||
{
|
||||
int i, err;
|
||||
struct ubi_rename_entry *re;
|
||||
struct ubi_volume *layout_vol;
|
||||
|
||||
list_for_each_entry(re, rename_list, list) {
|
||||
uint32_t crc;
|
||||
struct ubi_volume *vol = re->desc->vol;
|
||||
struct ubi_vtbl_record *vtbl_rec = &ubi->vtbl[vol->vol_id];
|
||||
|
||||
if (re->remove) {
|
||||
memcpy(vtbl_rec, &empty_vtbl_record,
|
||||
sizeof(struct ubi_vtbl_record));
|
||||
continue;
|
||||
}
|
||||
|
||||
vtbl_rec->name_len = cpu_to_be16(re->new_name_len);
|
||||
memcpy(vtbl_rec->name, re->new_name, re->new_name_len);
|
||||
memset(vtbl_rec->name + re->new_name_len, 0,
|
||||
UBI_VOL_NAME_MAX + 1 - re->new_name_len);
|
||||
crc = crc32(UBI_CRC32_INIT, vtbl_rec,
|
||||
UBI_VTBL_RECORD_SIZE_CRC);
|
||||
vtbl_rec->crc = cpu_to_be32(crc);
|
||||
}
|
||||
|
||||
layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
|
||||
for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
|
||||
err = ubi_eba_unmap_leb(ubi, layout_vol, i);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0,
|
||||
ubi->vtbl_size);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vtbl_check - check if volume table is not corrupted and sensible.
|
||||
* @ubi: UBI device description object
|
||||
* @vtbl: volume table
|
||||
*
|
||||
|
@ -133,21 +175,19 @@ static int vtbl_check(const struct ubi_device *ubi,
|
|||
const char *name;
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots; i++) {
|
||||
cond_resched();
|
||||
|
||||
reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs);
|
||||
alignment = be32_to_cpu(vtbl[i].alignment);
|
||||
data_pad = be32_to_cpu(vtbl[i].data_pad);
|
||||
upd_marker = vtbl[i].upd_marker;
|
||||
vol_type = vtbl[i].vol_type;
|
||||
name_len = be16_to_cpu(vtbl[i].name_len);
|
||||
name = (const char *) &vtbl[i].name[0];
|
||||
name = &vtbl[i].name[0];
|
||||
|
||||
crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC);
|
||||
if (be32_to_cpu(vtbl[i].crc) != crc) {
|
||||
ubi_err("bad CRC at record %u: %#08x, not %#08x",
|
||||
i, crc, be32_to_cpu(vtbl[i].crc));
|
||||
ubi_dbg_dump_vtbl_record(&vtbl[i], i);
|
||||
ubi_dump_vtbl_record(&vtbl[i], i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -179,7 +219,7 @@ static int vtbl_check(const struct ubi_device *ubi,
|
|||
|
||||
n = ubi->leb_size % alignment;
|
||||
if (data_pad != n) {
|
||||
dbg_err("bad data_pad, has to be %d", n);
|
||||
ubi_err("bad data_pad, has to be %d", n);
|
||||
err = 6;
|
||||
goto bad;
|
||||
}
|
||||
|
@ -195,8 +235,8 @@ static int vtbl_check(const struct ubi_device *ubi,
|
|||
}
|
||||
|
||||
if (reserved_pebs > ubi->good_peb_count) {
|
||||
dbg_err("too large reserved_pebs, good PEBs %d",
|
||||
ubi->good_peb_count);
|
||||
ubi_err("too large reserved_pebs %d, good PEBs %d",
|
||||
reserved_pebs, ubi->good_peb_count);
|
||||
err = 9;
|
||||
goto bad;
|
||||
}
|
||||
|
@ -224,11 +264,11 @@ static int vtbl_check(const struct ubi_device *ubi,
|
|||
int len2 = be16_to_cpu(vtbl[n].name_len);
|
||||
|
||||
if (len1 > 0 && len1 == len2 &&
|
||||
!strncmp((char *)vtbl[i].name, (char *)vtbl[n].name, len1)) {
|
||||
ubi_err("volumes %d and %d have the same name"
|
||||
" \"%s\"", i, n, vtbl[i].name);
|
||||
ubi_dbg_dump_vtbl_record(&vtbl[i], i);
|
||||
ubi_dbg_dump_vtbl_record(&vtbl[n], n);
|
||||
!strncmp(vtbl[i].name, vtbl[n].name, len1)) {
|
||||
ubi_err("volumes %d and %d have the same name \"%s\"",
|
||||
i, n, vtbl[i].name);
|
||||
ubi_dump_vtbl_record(&vtbl[i], i);
|
||||
ubi_dump_vtbl_record(&vtbl[n], n);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
@ -238,76 +278,64 @@ static int vtbl_check(const struct ubi_device *ubi,
|
|||
|
||||
bad:
|
||||
ubi_err("volume table check failed: record %d, error %d", i, err);
|
||||
ubi_dbg_dump_vtbl_record(&vtbl[i], i);
|
||||
ubi_dump_vtbl_record(&vtbl[i], i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* create_vtbl - create a copy of volume table.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: attaching information
|
||||
* @copy: number of the volume table copy
|
||||
* @vtbl: contents of the volume table
|
||||
*
|
||||
* This function returns zero in case of success and a negative error code in
|
||||
* case of failure.
|
||||
*/
|
||||
static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,
|
||||
static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
int copy, void *vtbl)
|
||||
{
|
||||
int err, tries = 0;
|
||||
static struct ubi_vid_hdr *vid_hdr;
|
||||
struct ubi_scan_volume *sv;
|
||||
struct ubi_scan_leb *new_seb, *old_seb = NULL;
|
||||
struct ubi_vid_hdr *vid_hdr;
|
||||
struct ubi_ainf_peb *new_aeb;
|
||||
|
||||
ubi_msg("create volume table (copy #%d)", copy + 1);
|
||||
dbg_gen("create volume table (copy #%d)", copy + 1);
|
||||
|
||||
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
|
||||
if (!vid_hdr)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Check if there is a logical eraseblock which would have to contain
|
||||
* this volume table copy was found during scanning. It has to be wiped
|
||||
* out.
|
||||
*/
|
||||
sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID);
|
||||
if (sv)
|
||||
old_seb = ubi_scan_find_seb(sv, copy);
|
||||
|
||||
retry:
|
||||
new_seb = ubi_scan_get_free_peb(ubi, si);
|
||||
if (IS_ERR(new_seb)) {
|
||||
err = PTR_ERR(new_seb);
|
||||
new_aeb = ubi_early_get_peb(ubi, ai);
|
||||
if (IS_ERR(new_aeb)) {
|
||||
err = PTR_ERR(new_aeb);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
vid_hdr->vol_type = UBI_VID_DYNAMIC;
|
||||
vid_hdr->vol_type = UBI_LAYOUT_VOLUME_TYPE;
|
||||
vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOLUME_ID);
|
||||
vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT;
|
||||
vid_hdr->data_size = vid_hdr->used_ebs =
|
||||
vid_hdr->data_pad = cpu_to_be32(0);
|
||||
vid_hdr->lnum = cpu_to_be32(copy);
|
||||
vid_hdr->sqnum = cpu_to_be64(++si->max_sqnum);
|
||||
vid_hdr->leb_ver = cpu_to_be32(old_seb ? old_seb->leb_ver + 1: 0);
|
||||
vid_hdr->sqnum = cpu_to_be64(++ai->max_sqnum);
|
||||
|
||||
/* The EC header is already there, write the VID header */
|
||||
err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr);
|
||||
err = ubi_io_write_vid_hdr(ubi, new_aeb->pnum, vid_hdr);
|
||||
if (err)
|
||||
goto write_error;
|
||||
|
||||
/* Write the layout volume contents */
|
||||
err = ubi_io_write_data(ubi, vtbl, new_seb->pnum, 0, ubi->vtbl_size);
|
||||
err = ubi_io_write_data(ubi, vtbl, new_aeb->pnum, 0, ubi->vtbl_size);
|
||||
if (err)
|
||||
goto write_error;
|
||||
|
||||
/*
|
||||
* And add it to the scanning information. Don't delete the old
|
||||
* @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'.
|
||||
* And add it to the attaching information. Don't delete the old version
|
||||
* of this LEB as it will be deleted and freed in 'ubi_add_to_av()'.
|
||||
*/
|
||||
err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec,
|
||||
vid_hdr, 0);
|
||||
kfree(new_seb);
|
||||
err = ubi_add_to_av(ubi, ai, new_aeb->pnum, new_aeb->ec, vid_hdr, 0);
|
||||
kfree(new_aeb);
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return err;
|
||||
|
||||
|
@ -317,10 +345,10 @@ write_error:
|
|||
* Probably this physical eraseblock went bad, try to pick
|
||||
* another one.
|
||||
*/
|
||||
list_add_tail(&new_seb->u.list, &si->corr);
|
||||
list_add(&new_aeb->u.list, &ai->erase);
|
||||
goto retry;
|
||||
}
|
||||
kfree(new_seb);
|
||||
kfree(new_aeb);
|
||||
out_free:
|
||||
ubi_free_vid_hdr(ubi, vid_hdr);
|
||||
return err;
|
||||
|
@ -330,20 +358,20 @@ out_free:
|
|||
/**
|
||||
* process_lvol - process the layout volume.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @sv: layout volume scanning information
|
||||
* @ai: attaching information
|
||||
* @av: layout volume attaching information
|
||||
*
|
||||
* This function is responsible for reading the layout volume, ensuring it is
|
||||
* not corrupted, and recovering from corruptions if needed. Returns volume
|
||||
* table in case of success and a negative error code in case of failure.
|
||||
*/
|
||||
static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
|
||||
struct ubi_scan_info *si,
|
||||
struct ubi_scan_volume *sv)
|
||||
struct ubi_attach_info *ai,
|
||||
struct ubi_ainf_volume *av)
|
||||
{
|
||||
int err;
|
||||
struct rb_node *rb;
|
||||
struct ubi_scan_leb *seb;
|
||||
struct ubi_ainf_peb *aeb;
|
||||
struct ubi_vtbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL };
|
||||
int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = {1, 1};
|
||||
|
||||
|
@ -365,38 +393,37 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
|
|||
* 0 contains more recent information.
|
||||
*
|
||||
* So the plan is to first check LEB 0. Then
|
||||
* a. if LEB 0 is OK, it must be containing the most resent data; then
|
||||
* a. if LEB 0 is OK, it must be containing the most recent data; then
|
||||
* we compare it with LEB 1, and if they are different, we copy LEB
|
||||
* 0 to LEB 1;
|
||||
* b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1
|
||||
* to LEB 0.
|
||||
*/
|
||||
|
||||
dbg_msg("check layout volume");
|
||||
dbg_gen("check layout volume");
|
||||
|
||||
/* Read both LEB 0 and LEB 1 into memory */
|
||||
ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
|
||||
leb[seb->lnum] = vmalloc(ubi->vtbl_size);
|
||||
if (!leb[seb->lnum]) {
|
||||
ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) {
|
||||
leb[aeb->lnum] = vzalloc(ubi->vtbl_size);
|
||||
if (!leb[aeb->lnum]) {
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
memset(leb[seb->lnum], 0, ubi->vtbl_size);
|
||||
|
||||
err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0,
|
||||
err = ubi_io_read_data(ubi, leb[aeb->lnum], aeb->pnum, 0,
|
||||
ubi->vtbl_size);
|
||||
if (err == UBI_IO_BITFLIPS || err == -EBADMSG)
|
||||
if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err))
|
||||
/*
|
||||
* Scrub the PEB later. Note, -EBADMSG indicates an
|
||||
* uncorrectable ECC error, but we have our own CRC and
|
||||
* the data will be checked later. If the data is OK,
|
||||
* the PEB will be scrubbed (because we set
|
||||
* seb->scrub). If the data is not OK, the contents of
|
||||
* aeb->scrub). If the data is not OK, the contents of
|
||||
* the PEB will be recovered from the second copy, and
|
||||
* seb->scrub will be cleared in
|
||||
* 'ubi_scan_add_used()'.
|
||||
* aeb->scrub will be cleared in
|
||||
* 'ubi_add_to_av()'.
|
||||
*/
|
||||
seb->scrub = 1;
|
||||
aeb->scrub = 1;
|
||||
else if (err)
|
||||
goto out_free;
|
||||
}
|
||||
|
@ -411,10 +438,11 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
|
|||
if (!leb_corrupted[0]) {
|
||||
/* LEB 0 is OK */
|
||||
if (leb[1])
|
||||
leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size);
|
||||
leb_corrupted[1] = memcmp(leb[0], leb[1],
|
||||
ubi->vtbl_size);
|
||||
if (leb_corrupted[1]) {
|
||||
ubi_warn("volume table copy #2 is corrupted");
|
||||
err = create_vtbl(ubi, si, 1, leb[0]);
|
||||
err = create_vtbl(ubi, ai, 1, leb[0]);
|
||||
if (err)
|
||||
goto out_free;
|
||||
ubi_msg("volume table was restored");
|
||||
|
@ -437,7 +465,7 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
|
|||
}
|
||||
|
||||
ubi_warn("volume table copy #1 is corrupted");
|
||||
err = create_vtbl(ubi, si, 0, leb[1]);
|
||||
err = create_vtbl(ubi, ai, 0, leb[1]);
|
||||
if (err)
|
||||
goto out_free;
|
||||
ubi_msg("volume table was restored");
|
||||
|
@ -455,21 +483,20 @@ out_free:
|
|||
/**
|
||||
* create_empty_lvol - create empty layout volume.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: attaching information
|
||||
*
|
||||
* This function returns volume table contents in case of success and a
|
||||
* negative error code in case of failure.
|
||||
*/
|
||||
static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi,
|
||||
struct ubi_scan_info *si)
|
||||
struct ubi_attach_info *ai)
|
||||
{
|
||||
int i;
|
||||
struct ubi_vtbl_record *vtbl;
|
||||
|
||||
vtbl = vmalloc(ubi->vtbl_size);
|
||||
vtbl = vzalloc(ubi->vtbl_size);
|
||||
if (!vtbl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
memset(vtbl, 0, ubi->vtbl_size);
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots; i++)
|
||||
memcpy(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE);
|
||||
|
@ -477,7 +504,7 @@ static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi,
|
|||
for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
|
||||
int err;
|
||||
|
||||
err = create_vtbl(ubi, si, i, vtbl);
|
||||
err = create_vtbl(ubi, ai, i, vtbl);
|
||||
if (err) {
|
||||
vfree(vtbl);
|
||||
return ERR_PTR(err);
|
||||
|
@ -490,23 +517,22 @@ static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi,
|
|||
/**
|
||||
* init_volumes - initialize volume information for existing volumes.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: scanning information
|
||||
* @vtbl: volume table
|
||||
*
|
||||
* This function allocates volume description objects for existing volumes.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
||||
static int init_volumes(struct ubi_device *ubi,
|
||||
const struct ubi_attach_info *ai,
|
||||
const struct ubi_vtbl_record *vtbl)
|
||||
{
|
||||
int i, reserved_pebs = 0;
|
||||
struct ubi_scan_volume *sv;
|
||||
struct ubi_ainf_volume *av;
|
||||
struct ubi_volume *vol;
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots; i++) {
|
||||
cond_resched();
|
||||
|
||||
if (be32_to_cpu(vtbl[i].reserved_pebs) == 0)
|
||||
continue; /* Empty record */
|
||||
|
||||
|
@ -517,6 +543,7 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
|||
vol->reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs);
|
||||
vol->alignment = be32_to_cpu(vtbl[i].alignment);
|
||||
vol->data_pad = be32_to_cpu(vtbl[i].data_pad);
|
||||
vol->upd_marker = vtbl[i].upd_marker;
|
||||
vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ?
|
||||
UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME;
|
||||
vol->name_len = be16_to_cpu(vtbl[i].name_len);
|
||||
|
@ -528,8 +555,8 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
|||
if (vtbl[i].flags & UBI_VTBL_AUTORESIZE_FLG) {
|
||||
/* Auto re-size flag may be set only for one volume */
|
||||
if (ubi->autoresize_vol_id != -1) {
|
||||
ubi_err("more then one auto-resize volume (%d "
|
||||
"and %d)", ubi->autoresize_vol_id, i);
|
||||
ubi_err("more than one auto-resize volume (%d and %d)",
|
||||
ubi->autoresize_vol_id, i);
|
||||
kfree(vol);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -556,8 +583,8 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
|||
}
|
||||
|
||||
/* Static volumes only */
|
||||
sv = ubi_scan_find_sv(si, i);
|
||||
if (!sv) {
|
||||
av = ubi_find_av(ai, i);
|
||||
if (!av) {
|
||||
/*
|
||||
* No eraseblocks belonging to this volume found. We
|
||||
* don't actually know whether this static volume is
|
||||
|
@ -569,22 +596,22 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (sv->leb_count != sv->used_ebs) {
|
||||
if (av->leb_count != av->used_ebs) {
|
||||
/*
|
||||
* We found a static volume which misses several
|
||||
* eraseblocks. Treat it as corrupted.
|
||||
*/
|
||||
ubi_warn("static volume %d misses %d LEBs - corrupted",
|
||||
sv->vol_id, sv->used_ebs - sv->leb_count);
|
||||
av->vol_id, av->used_ebs - av->leb_count);
|
||||
vol->corrupted = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
vol->used_ebs = sv->used_ebs;
|
||||
vol->used_ebs = av->used_ebs;
|
||||
vol->used_bytes =
|
||||
(long long)(vol->used_ebs - 1) * vol->usable_leb_size;
|
||||
vol->used_bytes += sv->last_data_size;
|
||||
vol->last_eb_bytes = sv->last_data_size;
|
||||
vol->used_bytes += av->last_data_size;
|
||||
vol->last_eb_bytes = av->last_data_size;
|
||||
}
|
||||
|
||||
/* And add the layout volume */
|
||||
|
@ -593,7 +620,7 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
|||
return -ENOMEM;
|
||||
|
||||
vol->reserved_pebs = UBI_LAYOUT_VOLUME_EBS;
|
||||
vol->alignment = 1;
|
||||
vol->alignment = UBI_LAYOUT_VOLUME_ALIGN;
|
||||
vol->vol_type = UBI_DYNAMIC_VOLUME;
|
||||
vol->name_len = sizeof(UBI_LAYOUT_VOLUME_NAME) - 1;
|
||||
memcpy(vol->name, UBI_LAYOUT_VOLUME_NAME, vol->name_len + 1);
|
||||
|
@ -611,9 +638,13 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
|||
ubi->vol_count += 1;
|
||||
vol->ubi = ubi;
|
||||
|
||||
if (reserved_pebs > ubi->avail_pebs)
|
||||
if (reserved_pebs > ubi->avail_pebs) {
|
||||
ubi_err("not enough PEBs, required %d, available %d",
|
||||
reserved_pebs, ubi->avail_pebs);
|
||||
if (ubi->corr_peb_count)
|
||||
ubi_err("%d PEBs are corrupted and not used",
|
||||
ubi->corr_peb_count);
|
||||
}
|
||||
ubi->rsvd_pebs += reserved_pebs;
|
||||
ubi->avail_pebs -= reserved_pebs;
|
||||
|
||||
|
@ -621,105 +652,102 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
|
|||
}
|
||||
|
||||
/**
|
||||
* check_sv - check volume scanning information.
|
||||
* check_av - check volume attaching information.
|
||||
* @vol: UBI volume description object
|
||||
* @sv: volume scanning information
|
||||
* @av: volume attaching information
|
||||
*
|
||||
* This function returns zero if the volume scanning information is consistent
|
||||
* This function returns zero if the volume attaching information is consistent
|
||||
* to the data read from the volume tabla, and %-EINVAL if not.
|
||||
*/
|
||||
static int check_sv(const struct ubi_volume *vol,
|
||||
const struct ubi_scan_volume *sv)
|
||||
static int check_av(const struct ubi_volume *vol,
|
||||
const struct ubi_ainf_volume *av)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (sv->highest_lnum >= vol->reserved_pebs) {
|
||||
if (av->highest_lnum >= vol->reserved_pebs) {
|
||||
err = 1;
|
||||
goto bad;
|
||||
}
|
||||
if (sv->leb_count > vol->reserved_pebs) {
|
||||
if (av->leb_count > vol->reserved_pebs) {
|
||||
err = 2;
|
||||
goto bad;
|
||||
}
|
||||
if (sv->vol_type != vol->vol_type) {
|
||||
if (av->vol_type != vol->vol_type) {
|
||||
err = 3;
|
||||
goto bad;
|
||||
}
|
||||
if (sv->used_ebs > vol->reserved_pebs) {
|
||||
if (av->used_ebs > vol->reserved_pebs) {
|
||||
err = 4;
|
||||
goto bad;
|
||||
}
|
||||
if (sv->data_pad != vol->data_pad) {
|
||||
if (av->data_pad != vol->data_pad) {
|
||||
err = 5;
|
||||
goto bad;
|
||||
}
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
ubi_err("bad scanning information, error %d", err);
|
||||
ubi_dbg_dump_sv(sv);
|
||||
ubi_dbg_dump_vol_info(vol);
|
||||
ubi_err("bad attaching information, error %d", err);
|
||||
ubi_dump_av(av);
|
||||
ubi_dump_vol_info(vol);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_scanning_info - check that scanning information.
|
||||
* check_attaching_info - check that attaching information.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: attaching information
|
||||
*
|
||||
* Even though we protect on-flash data by CRC checksums, we still don't trust
|
||||
* the media. This function ensures that scanning information is consistent to
|
||||
* the information read from the volume table. Returns zero if the scanning
|
||||
* the media. This function ensures that attaching information is consistent to
|
||||
* the information read from the volume table. Returns zero if the attaching
|
||||
* information is OK and %-EINVAL if it is not.
|
||||
*/
|
||||
static int check_scanning_info(const struct ubi_device *ubi,
|
||||
struct ubi_scan_info *si)
|
||||
static int check_attaching_info(const struct ubi_device *ubi,
|
||||
struct ubi_attach_info *ai)
|
||||
{
|
||||
int err, i;
|
||||
struct ubi_scan_volume *sv;
|
||||
struct ubi_ainf_volume *av;
|
||||
struct ubi_volume *vol;
|
||||
|
||||
if (si->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) {
|
||||
ubi_err("scanning found %d volumes, maximum is %d + %d",
|
||||
si->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots);
|
||||
if (ai->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) {
|
||||
ubi_err("found %d volumes while attaching, maximum is %d + %d",
|
||||
ai->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT &&
|
||||
si->highest_vol_id < UBI_INTERNAL_VOL_START) {
|
||||
ubi_err("too large volume ID %d found by scanning",
|
||||
si->highest_vol_id);
|
||||
if (ai->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT &&
|
||||
ai->highest_vol_id < UBI_INTERNAL_VOL_START) {
|
||||
ubi_err("too large volume ID %d found", ai->highest_vol_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
|
||||
cond_resched();
|
||||
|
||||
sv = ubi_scan_find_sv(si, i);
|
||||
av = ubi_find_av(ai, i);
|
||||
vol = ubi->volumes[i];
|
||||
if (!vol) {
|
||||
if (sv)
|
||||
ubi_scan_rm_volume(si, sv);
|
||||
if (av)
|
||||
ubi_remove_av(ai, av);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vol->reserved_pebs == 0) {
|
||||
ubi_assert(i < ubi->vtbl_slots);
|
||||
|
||||
if (!sv)
|
||||
if (!av)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* During scanning we found a volume which does not
|
||||
* During attaching we found a volume which does not
|
||||
* exist according to the information in the volume
|
||||
* table. This must have happened due to an unclean
|
||||
* reboot while the volume was being removed. Discard
|
||||
* these eraseblocks.
|
||||
*/
|
||||
ubi_msg("finish volume %d removal", sv->vol_id);
|
||||
ubi_scan_rm_volume(si, sv);
|
||||
} else if (sv) {
|
||||
err = check_sv(vol, sv);
|
||||
ubi_msg("finish volume %d removal", av->vol_id);
|
||||
ubi_remove_av(ai, av);
|
||||
} else if (av) {
|
||||
err = check_av(vol, av);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
@ -729,19 +757,18 @@ static int check_scanning_info(const struct ubi_device *ubi,
|
|||
}
|
||||
|
||||
/**
|
||||
* ubi_read_volume_table - read volume table.
|
||||
* information.
|
||||
* ubi_read_volume_table - read the volume table.
|
||||
* @ubi: UBI device description object
|
||||
* @si: scanning information
|
||||
* @ai: attaching information
|
||||
*
|
||||
* This function reads volume table, checks it, recover from errors if needed,
|
||||
* or creates it if needed. Returns zero in case of success and a negative
|
||||
* error code in case of failure.
|
||||
*/
|
||||
int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
||||
int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
||||
{
|
||||
int i, err;
|
||||
struct ubi_scan_volume *sv;
|
||||
struct ubi_ainf_volume *av;
|
||||
|
||||
empty_vtbl_record.crc = cpu_to_be32(0xf116c36b);
|
||||
|
||||
|
@ -756,8 +783,8 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
|||
ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE;
|
||||
ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size);
|
||||
|
||||
sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID);
|
||||
if (!sv) {
|
||||
av = ubi_find_av(ai, UBI_LAYOUT_VOLUME_ID);
|
||||
if (!av) {
|
||||
/*
|
||||
* No logical eraseblocks belonging to the layout volume were
|
||||
* found. This could mean that the flash is just empty. In
|
||||
|
@ -766,8 +793,8 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
|||
* But if flash is not empty this must be a corruption or the
|
||||
* MTD device just contains garbage.
|
||||
*/
|
||||
if (si->is_empty) {
|
||||
ubi->vtbl = create_empty_lvol(ubi, si);
|
||||
if (ai->is_empty) {
|
||||
ubi->vtbl = create_empty_lvol(ubi, ai);
|
||||
if (IS_ERR(ubi->vtbl))
|
||||
return PTR_ERR(ubi->vtbl);
|
||||
} else {
|
||||
|
@ -775,33 +802,33 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
|||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
if (sv->leb_count > UBI_LAYOUT_VOLUME_EBS) {
|
||||
if (av->leb_count > UBI_LAYOUT_VOLUME_EBS) {
|
||||
/* This must not happen with proper UBI images */
|
||||
dbg_err("too many LEBs (%d) in layout volume",
|
||||
sv->leb_count);
|
||||
ubi_err("too many LEBs (%d) in layout volume",
|
||||
av->leb_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ubi->vtbl = process_lvol(ubi, si, sv);
|
||||
ubi->vtbl = process_lvol(ubi, ai, av);
|
||||
if (IS_ERR(ubi->vtbl))
|
||||
return PTR_ERR(ubi->vtbl);
|
||||
}
|
||||
|
||||
ubi->avail_pebs = ubi->good_peb_count;
|
||||
ubi->avail_pebs = ubi->good_peb_count - ubi->corr_peb_count;
|
||||
|
||||
/*
|
||||
* The layout volume is OK, initialize the corresponding in-RAM data
|
||||
* structures.
|
||||
*/
|
||||
err = init_volumes(ubi, si, ubi->vtbl);
|
||||
err = init_volumes(ubi, ai, ubi->vtbl);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
/*
|
||||
* Get sure that the scanning information is consistent to the
|
||||
* Make sure that the attaching information is consistent to the
|
||||
* information stored in the volume table.
|
||||
*/
|
||||
err = check_scanning_info(ubi, si);
|
||||
err = check_attaching_info(ubi, ai);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
|
@ -809,26 +836,24 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
|
|||
|
||||
out_free:
|
||||
vfree(ubi->vtbl);
|
||||
for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++)
|
||||
if (ubi->volumes[i]) {
|
||||
kfree(ubi->volumes[i]);
|
||||
ubi->volumes[i] = NULL;
|
||||
}
|
||||
for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
|
||||
kfree(ubi->volumes[i]);
|
||||
ubi->volumes[i] = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
|
||||
|
||||
/**
|
||||
* paranoid_vtbl_check - check volume table.
|
||||
* self_vtbl_check - check volume table.
|
||||
* @ubi: UBI device description object
|
||||
*/
|
||||
static void paranoid_vtbl_check(const struct ubi_device *ubi)
|
||||
static void self_vtbl_check(const struct ubi_device *ubi)
|
||||
{
|
||||
if (!ubi_dbg_chk_gen(ubi))
|
||||
return;
|
||||
|
||||
if (vtbl_check(ubi, ubi->vtbl)) {
|
||||
ubi_err("paranoid check failed");
|
||||
ubi_err("self-check failed");
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
|
||||
|
|
1701
drivers/mtd/ubi/wl.c
1701
drivers/mtd/ubi/wl.c
File diff suppressed because it is too large
Load Diff
|
@ -3,3 +3,4 @@ obj-$(CONFIG_OFTREE_MEM_GENERIC) += mem_generic.o
|
|||
obj-$(CONFIG_GPIOLIB) += of_gpio.o
|
||||
obj-y += partition.o
|
||||
obj-y += of_net.o
|
||||
obj-$(CONFIG_MTD) += of_mtd.o
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
|
||||
*
|
||||
* OF helpers for mtd.
|
||||
*
|
||||
* This file is released under the GPLv2
|
||||
*
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <of_mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
/**
|
||||
* It maps 'enum nand_ecc_modes_t' found in include/linux/mtd/nand.h
|
||||
* into the device tree binding of 'nand-ecc', so that MTD
|
||||
* device driver can get nand ecc from device tree.
|
||||
*/
|
||||
static const char *nand_ecc_modes[] = {
|
||||
[NAND_ECC_NONE] = "none",
|
||||
[NAND_ECC_SOFT] = "soft",
|
||||
[NAND_ECC_HW] = "hw",
|
||||
[NAND_ECC_HW_SYNDROME] = "hw_syndrome",
|
||||
};
|
||||
|
||||
/**
|
||||
* of_get_nand_ecc_mode - Get nand ecc mode for given device_node
|
||||
* @np: Pointer to the given device_node
|
||||
*
|
||||
* The function gets ecc mode string from property 'nand-ecc-mode',
|
||||
* and return its index in nand_ecc_modes table, or errno in error case.
|
||||
*/
|
||||
int of_get_nand_ecc_mode(struct device_node *np)
|
||||
{
|
||||
const char *pm;
|
||||
int err, i;
|
||||
|
||||
err = of_property_read_string(np, "nand-ecc-mode", &pm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
|
||||
if (!strcasecmp(pm, nand_ecc_modes[i]))
|
||||
return i;
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
|
||||
|
||||
/**
|
||||
* of_get_nand_bus_width - Get nand bus witdh for given device_node
|
||||
* @np: Pointer to the given device_node
|
||||
*
|
||||
* return bus width option, or errno in error case.
|
||||
*/
|
||||
int of_get_nand_bus_width(struct device_node *np)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (of_property_read_u32(np, "nand-bus-width", &val))
|
||||
return 8;
|
||||
|
||||
switch(val) {
|
||||
case 8:
|
||||
case 16:
|
||||
return val;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_get_nand_bus_width);
|
||||
|
||||
/**
|
||||
* of_get_nand_on_flash_bbt - Get nand on flash bbt for given device_node
|
||||
* @np: Pointer to the given device_node
|
||||
*
|
||||
* return true if present false other wise
|
||||
*/
|
||||
bool of_get_nand_on_flash_bbt(struct device_node *np)
|
||||
{
|
||||
return of_property_read_bool(np, "nand-on-flash-bbt");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_get_nand_on_flash_bbt);
|
|
@ -171,7 +171,7 @@ static int auart_clocksource_clock_change(struct notifier_block *nb, unsigned lo
|
|||
|
||||
static void auart_serial_init_port(struct auart_priv *priv)
|
||||
{
|
||||
mxs_reset_block(priv->base + HW_UARTAPP_CTRL0, 0);
|
||||
stmp_reset_block(priv->base + HW_UARTAPP_CTRL0, 0);
|
||||
|
||||
/* Disable UART */
|
||||
writel(0x0, priv->base + HW_UARTAPP_CTRL2);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <clock.h>
|
||||
#include <errno.h>
|
||||
#include <io.h>
|
||||
#include <stmp-device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <asm/mmu.h>
|
||||
|
@ -99,11 +100,11 @@ static int mxs_spi_setup(struct spi_device *spi)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
mxs_reset_block(mxs->regs + HW_SSP_CTRL0, 0);
|
||||
stmp_reset_block(mxs->regs + HW_SSP_CTRL0);
|
||||
|
||||
val |= SSP_CTRL0_SSP_ASSERT_OUT(spi->chip_select);
|
||||
val |= SSP_CTRL0_BUS_WIDTH(0);
|
||||
writel(val, mxs->regs + HW_SSP_CTRL0 + BIT_SET);
|
||||
writel(val, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
val = SSP_CTRL1_SSP_MODE(0) | SSP_CTRL1_WORD_LENGTH(7);
|
||||
val |= (mxs->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0;
|
||||
|
@ -120,14 +121,14 @@ static int mxs_spi_setup(struct spi_device *spi)
|
|||
|
||||
static void mxs_spi_start_xfer(struct mxs_spi *mxs)
|
||||
{
|
||||
writel(SSP_CTRL0_LOCK_CS, mxs->regs + HW_SSP_CTRL0 + BIT_SET);
|
||||
writel(SSP_CTRL0_IGNORE_CRC, mxs->regs + HW_SSP_CTRL0 + BIT_CLR);
|
||||
writel(SSP_CTRL0_LOCK_CS, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
writel(SSP_CTRL0_IGNORE_CRC, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
}
|
||||
|
||||
static void mxs_spi_end_xfer(struct mxs_spi *mxs)
|
||||
{
|
||||
writel(SSP_CTRL0_LOCK_CS, mxs->regs + HW_SSP_CTRL0 + BIT_CLR);
|
||||
writel(SSP_CTRL0_IGNORE_CRC, mxs->regs + HW_SSP_CTRL0 + BIT_SET);
|
||||
writel(SSP_CTRL0_LOCK_CS, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(SSP_CTRL0_IGNORE_CRC, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
static void mxs_spi_set_cs(struct spi_device *spi)
|
||||
|
@ -136,8 +137,8 @@ static void mxs_spi_set_cs(struct spi_device *spi)
|
|||
const uint32_t mask = SSP_CTRL0_WAIT_FOR_CMD | SSP_CTRL0_WAIT_FOR_IRQ;
|
||||
uint32_t select = SSP_CTRL0_SSP_ASSERT_OUT(spi->chip_select);
|
||||
|
||||
writel(mask, mxs->regs + HW_SSP_CTRL0 + BIT_CLR);
|
||||
writel(select, mxs->regs + HW_SSP_CTRL0 + BIT_SET);
|
||||
writel(mask, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(select, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
static int mxs_spi_xfer_pio(struct spi_device *spi,
|
||||
|
@ -159,11 +160,11 @@ static int mxs_spi_xfer_pio(struct spi_device *spi,
|
|||
writel(1, mxs->regs + HW_SSP_XFER_COUNT);
|
||||
|
||||
if (write)
|
||||
writel(SSP_CTRL0_READ, mxs->regs + HW_SSP_CTRL0 + BIT_CLR);
|
||||
writel(SSP_CTRL0_READ, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
else
|
||||
writel(SSP_CTRL0_READ, mxs->regs + HW_SSP_CTRL0 + BIT_SET);
|
||||
writel(SSP_CTRL0_READ, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
writel(SSP_CTRL0_RUN, mxs->regs + HW_SSP_CTRL0 + BIT_SET);
|
||||
writel(SSP_CTRL0_RUN, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
if (wait_on_timeout(MXS_SPI_MAX_TIMEOUT,
|
||||
(readl(mxs->regs + HW_SSP_CTRL0) & SSP_CTRL0_RUN) == SSP_CTRL0_RUN)) {
|
||||
|
@ -174,7 +175,7 @@ static int mxs_spi_xfer_pio(struct spi_device *spi,
|
|||
if (write)
|
||||
writel(*data++, mxs->regs + HW_SSP_DATA);
|
||||
|
||||
writel(SSP_CTRL0_DATA_XFER, mxs->regs + HW_SSP_CTRL0 + BIT_SET);
|
||||
writel(SSP_CTRL0_DATA_XFER, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
if (!write) {
|
||||
if (wait_on_timeout(MXS_SPI_MAX_TIMEOUT,
|
||||
|
@ -240,7 +241,7 @@ static int mxs_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
|
|||
}
|
||||
}
|
||||
|
||||
writel(SSP_CTRL1_DMA_ENABLE, mxs->regs + HW_SSP_CTRL1 + BIT_CLR);
|
||||
writel(SSP_CTRL1_DMA_ENABLE, mxs->regs + HW_SSP_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
ret = mxs_spi_xfer_pio(spi, data, t->len, write, flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <errno.h>
|
||||
#include <xfuncs.h>
|
||||
#include <io.h>
|
||||
#include <stmp-device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <mach/imx-regs.h>
|
||||
|
@ -222,7 +223,7 @@ static void stmfb_enable_controller(struct fb_info *fb_info)
|
|||
* Sometimes some data is still present in the FIFO. This leads into
|
||||
* a correct but shifted picture. Clearing the FIFO helps
|
||||
*/
|
||||
writel(CTRL1_FIFO_CLEAR, fbi->base + HW_LCDIF_CTRL1 + BIT_SET);
|
||||
writel(CTRL1_FIFO_CLEAR, fbi->base + HW_LCDIF_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
|
||||
/* if it was disabled, re-enable the mode again */
|
||||
reg = readl(fbi->base + HW_LCDIF_CTRL);
|
||||
|
@ -255,14 +256,14 @@ static void stmfb_enable_controller(struct fb_info *fb_info)
|
|||
}
|
||||
|
||||
/* stop FIFO reset */
|
||||
writel(CTRL1_FIFO_CLEAR, fbi->base + HW_LCDIF_CTRL1 + BIT_CLR);
|
||||
writel(CTRL1_FIFO_CLEAR, fbi->base + HW_LCDIF_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
/* enable LCD using LCD_RESET signal*/
|
||||
if (fbi->pdata->flags & USE_LCD_RESET)
|
||||
writel(CTRL1_RESET, fbi->base + HW_LCDIF_CTRL1 + BIT_SET);
|
||||
writel(CTRL1_RESET, fbi->base + HW_LCDIF_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
|
||||
/* start the engine right now */
|
||||
writel(CTRL_RUN, fbi->base + HW_LCDIF_CTRL + BIT_SET);
|
||||
writel(CTRL_RUN, fbi->base + HW_LCDIF_CTRL + STMP_OFFSET_REG_SET);
|
||||
|
||||
if (fbi->pdata->enable)
|
||||
fbi->pdata->enable(1);
|
||||
|
@ -277,7 +278,7 @@ static void stmfb_disable_controller(struct fb_info *fb_info)
|
|||
|
||||
/* disable LCD using LCD_RESET signal*/
|
||||
if (fbi->pdata->flags & USE_LCD_RESET)
|
||||
writel(CTRL1_RESET, fbi->base + HW_LCDIF_CTRL1 + BIT_CLR);
|
||||
writel(CTRL1_RESET, fbi->base + HW_LCDIF_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (fbi->pdata->enable)
|
||||
fbi->pdata->enable(0);
|
||||
|
|
|
@ -133,6 +133,8 @@
|
|||
#define ERESTARTNOHAND 514 /* restart if no handler.. */
|
||||
#define ENOIOCTLCMD 515 /* No ioctl command */
|
||||
|
||||
#define _LAST_ERRNO 515
|
||||
#define ENOTSUPP 524 /* Operation is not supported */
|
||||
|
||||
#define _LAST_ERRNO 524
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,12 +28,6 @@
|
|||
|
||||
#include <linux/list.h>
|
||||
|
||||
#ifndef CONFIG_ARCH_DMA_PIO_WORDS
|
||||
#define DMA_PIO_WORDS 15
|
||||
#else
|
||||
#define DMA_PIO_WORDS CONFIG_ARCH_DMA_PIO_WORDS
|
||||
#endif
|
||||
|
||||
#define MXS_DMA_ALIGNMENT 32
|
||||
|
||||
/*
|
||||
|
@ -90,7 +84,8 @@ struct mxs_dma_cmd {
|
|||
dma_addr_t address;
|
||||
unsigned long alternate;
|
||||
};
|
||||
unsigned long pio_words[DMA_PIO_WORDS];
|
||||
#define APBH_DMA_PIO_WORDS 15
|
||||
unsigned long pio_words[APBH_DMA_PIO_WORDS];
|
||||
};
|
||||
|
||||
/*
|
|
@ -415,6 +415,8 @@ int platform_driver_register(struct driver_d *drv);
|
|||
} \
|
||||
level##_initcall(drv##_register)
|
||||
|
||||
#define coredevice_platform_driver(drv) \
|
||||
register_driver_macro(coredevice,platform,drv)
|
||||
#define device_platform_driver(drv) \
|
||||
register_driver_macro(device,platform,drv)
|
||||
#define console_platform_driver(drv) \
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define kzalloc(len, mode) xzalloc(len)
|
||||
#define vmalloc(len) malloc(len)
|
||||
#define kfree(ptr) free(ptr)
|
||||
#define vzalloc(len) kzalloc(len, 0)
|
||||
#define vfree(ptr) free(ptr)
|
||||
|
||||
#define KERN_EMERG "" /* system is unusable */
|
||||
|
@ -17,6 +18,11 @@
|
|||
#define KERN_NOTICE "" /* normal but significant condition */
|
||||
#define KERN_INFO "" /* informational */
|
||||
#define KERN_DEBUG "" /* debug-level messages */
|
||||
#define KERN_CONT ""
|
||||
|
||||
#define GFP_KERNEL 0
|
||||
|
||||
typedef int gfp_t;
|
||||
|
||||
#define printk printf
|
||||
|
||||
|
|
|
@ -105,5 +105,17 @@
|
|||
} \
|
||||
)
|
||||
|
||||
/*
|
||||
* Multiplies an integer by a fraction, while avoiding unnecessary
|
||||
* overflow or loss of precision.
|
||||
*/
|
||||
#define mult_frac(x, numer, denom)( \
|
||||
{ \
|
||||
typeof(x) quot = (x) / (denom); \
|
||||
typeof(x) rem = (x) % (denom); \
|
||||
(quot * (numer)) + ((rem * (numer)) / (denom)); \
|
||||
} \
|
||||
)
|
||||
|
||||
#endif /* _LINUX_KERNEL_H */
|
||||
|
||||
|
|
|
@ -4,16 +4,33 @@
|
|||
* NAND family Bad Block Management (BBM) header file
|
||||
* - Bad Block Table (BBT) implementation
|
||||
*
|
||||
* Copyright (c) 2005 Samsung Electronics
|
||||
* Copyright © 2005 Samsung Electronics
|
||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||
*
|
||||
* Copyright (c) 2000-2005
|
||||
* Copyright © 2000-2005
|
||||
* Thomas Gleixner <tglx@linuxtronix.de>
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __LINUX_MTD_BBM_H
|
||||
#define __LINUX_MTD_BBM_H
|
||||
|
||||
/* The maximum number of NAND chips in an array */
|
||||
#define NAND_MAX_CHIPS 8
|
||||
|
||||
/**
|
||||
* struct nand_bbt_descr - bad block table descriptor
|
||||
* @options: options for this descriptor
|
||||
|
@ -38,15 +55,15 @@
|
|||
* of the first block.
|
||||
*/
|
||||
struct nand_bbt_descr {
|
||||
int options;
|
||||
int pages[NAND_MAX_CHIPS];
|
||||
int offs;
|
||||
int veroffs;
|
||||
uint8_t version[NAND_MAX_CHIPS];
|
||||
int len;
|
||||
int maxblocks;
|
||||
int reserved_block_code;
|
||||
uint8_t *pattern;
|
||||
int options;
|
||||
int pages[NAND_MAX_CHIPS];
|
||||
int offs;
|
||||
int veroffs;
|
||||
uint8_t version[NAND_MAX_CHIPS];
|
||||
int len;
|
||||
int maxblocks;
|
||||
int reserved_block_code;
|
||||
uint8_t *pattern;
|
||||
};
|
||||
|
||||
/* Options for the bad block table descriptors */
|
||||
|
@ -58,37 +75,102 @@ struct nand_bbt_descr {
|
|||
#define NAND_BBT_4BIT 0x00000004
|
||||
#define NAND_BBT_8BIT 0x00000008
|
||||
/* The bad block table is in the last good block of the device */
|
||||
#define NAND_BBT_LASTBLOCK 0x00000010
|
||||
#define NAND_BBT_LASTBLOCK 0x00000010
|
||||
/* The bbt is at the given page, else we must scan for the bbt */
|
||||
#define NAND_BBT_ABSPAGE 0x00000020
|
||||
/* The bbt is at the given page, else we must scan for the bbt */
|
||||
#define NAND_BBT_SEARCH 0x00000040
|
||||
/* bbt is stored per chip on multichip devices */
|
||||
#define NAND_BBT_PERCHIP 0x00000080
|
||||
/* bbt has a version counter at offset veroffs */
|
||||
#define NAND_BBT_VERSION 0x00000100
|
||||
/* Create a bbt if none axists */
|
||||
/* Create a bbt if none exists */
|
||||
#define NAND_BBT_CREATE 0x00000200
|
||||
/*
|
||||
* Create an empty BBT with no vendor information. Vendor's information may be
|
||||
* unavailable, for example, if the NAND controller has a different data and OOB
|
||||
* layout or if this information is already purged. Must be used in conjunction
|
||||
* with NAND_BBT_CREATE.
|
||||
*/
|
||||
#define NAND_BBT_CREATE_EMPTY 0x00000400
|
||||
/* Search good / bad pattern through all pages of a block */
|
||||
#define NAND_BBT_SCANALLPAGES 0x00000400
|
||||
#define NAND_BBT_SCANALLPAGES 0x00000800
|
||||
/* Scan block empty during good / bad block scan */
|
||||
#define NAND_BBT_SCANEMPTY 0x00000800
|
||||
#define NAND_BBT_SCANEMPTY 0x00001000
|
||||
/* Write bbt if neccecary */
|
||||
#define NAND_BBT_WRITE 0x00001000
|
||||
#define NAND_BBT_WRITE 0x00002000
|
||||
/* Read and write back block contents when writing bbt */
|
||||
#define NAND_BBT_SAVECONTENT 0x00002000
|
||||
#define NAND_BBT_SAVECONTENT 0x00004000
|
||||
/* Search good / bad pattern on the first and the second page */
|
||||
#define NAND_BBT_SCAN2NDPAGE 0x00004000
|
||||
#define NAND_BBT_SCAN2NDPAGE 0x00008000
|
||||
/* Search good / bad pattern on the last page of the eraseblock */
|
||||
#define NAND_BBT_SCANLASTPAGE 0x00010000
|
||||
|
||||
/*
|
||||
* Use a flash based bad block table. By default, OOB identifier is saved in
|
||||
* OOB area. This option is passed to the default bad block table function.
|
||||
*/
|
||||
#define NAND_BBT_USE_FLASH 0x00020000
|
||||
#define NAND_BBT_USE_FLASH 0x00020000
|
||||
/*
|
||||
* Do not store flash based bad block table marker in the OOB area; store it
|
||||
* in-band.
|
||||
*/
|
||||
#define NAND_BBT_NO_OOB 0x00040000
|
||||
/*
|
||||
* Do not write new bad block markers to OOB; useful, e.g., when ECC covers
|
||||
* entire spare area. Must be used with NAND_BBT_USE_FLASH.
|
||||
*/
|
||||
#define NAND_BBT_NO_OOB_BBM 0x00080000
|
||||
|
||||
/*
|
||||
* Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
|
||||
* was allocated dynamicaly and must be freed in nand_release(). Has no meaning
|
||||
* in nand_chip.bbt_options.
|
||||
*/
|
||||
#define NAND_BBT_DYNAMICSTRUCT 0x80000000
|
||||
|
||||
/* The maximum number of blocks to scan for a bbt */
|
||||
#define NAND_BBT_SCAN_MAXBLOCKS 4
|
||||
|
||||
#endif /* __LINUX_MTD_BBM_H */
|
||||
/*
|
||||
* Constants for oob configuration
|
||||
*/
|
||||
#define NAND_SMALL_BADBLOCK_POS 5
|
||||
#define NAND_LARGE_BADBLOCK_POS 0
|
||||
#define ONENAND_BADBLOCK_POS 0
|
||||
|
||||
/*
|
||||
* Bad block scanning errors
|
||||
*/
|
||||
#define ONENAND_BBT_READ_ERROR 1
|
||||
#define ONENAND_BBT_READ_ECC_ERROR 2
|
||||
#define ONENAND_BBT_READ_FATAL_ERROR 4
|
||||
|
||||
/**
|
||||
* struct bbm_info - [GENERIC] Bad Block Table data structure
|
||||
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
|
||||
* @badblockpos: [INTERN] position of the bad block marker in the oob area
|
||||
* @options: options for this descriptor
|
||||
* @bbt: [INTERN] bad block table pointer
|
||||
* @isbad_bbt: function to determine if a block is bad
|
||||
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for
|
||||
* initial bad block scan
|
||||
* @priv: [OPTIONAL] pointer to private bbm date
|
||||
*/
|
||||
struct bbm_info {
|
||||
int bbt_erase_shift;
|
||||
int badblockpos;
|
||||
int options;
|
||||
|
||||
uint8_t *bbt;
|
||||
|
||||
int (*isbad_bbt)(struct mtd_info *mtd, loff_t ofs, int allowbbt);
|
||||
|
||||
/* TODO Add more NAND specific fileds */
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/* OneNAND BBT interface */
|
||||
extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
|
||||
extern int onenand_default_bbt(struct mtd_info *mtd);
|
||||
|
||||
#endif /* __LINUX_MTD_BBM_H */
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright © 2000 Red Hat UK Limited
|
||||
* Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MTD_FLASHCHIP_H__
|
||||
#define __MTD_FLASHCHIP_H__
|
||||
|
||||
typedef enum {
|
||||
FL_READY,
|
||||
FL_STATUS,
|
||||
FL_CFI_QUERY,
|
||||
FL_JEDEC_QUERY,
|
||||
FL_ERASING,
|
||||
FL_ERASE_SUSPENDING,
|
||||
FL_ERASE_SUSPENDED,
|
||||
FL_WRITING,
|
||||
FL_WRITING_TO_BUFFER,
|
||||
FL_OTP_WRITE,
|
||||
FL_WRITE_SUSPENDING,
|
||||
FL_WRITE_SUSPENDED,
|
||||
FL_PM_SUSPENDED,
|
||||
FL_SYNCING,
|
||||
FL_UNLOADING,
|
||||
FL_LOCKING,
|
||||
FL_UNLOCKING,
|
||||
FL_POINT,
|
||||
FL_XIP_WHILE_ERASING,
|
||||
FL_XIP_WHILE_WRITING,
|
||||
FL_SHUTDOWN,
|
||||
/* These 2 come from nand_state_t, which has been unified here */
|
||||
FL_READING,
|
||||
FL_CACHEDPRG,
|
||||
/* These 4 come from onenand_state_t, which has been unified here */
|
||||
FL_RESETING,
|
||||
FL_OTPING,
|
||||
FL_PREPARING_ERASE,
|
||||
FL_VERIFYING_ERASE,
|
||||
|
||||
FL_UNKNOWN
|
||||
} flstate_t;
|
||||
|
||||
|
||||
|
||||
/* NOTE: confusingly, this can be used to refer to more than one chip at a time,
|
||||
if they're interleaved. This can even refer to individual partitions on
|
||||
the same physical chip when present. */
|
||||
|
||||
struct flchip {
|
||||
unsigned long start; /* Offset within the map */
|
||||
// unsigned long len;
|
||||
/* We omit len for now, because when we group them together
|
||||
we insist that they're all of the same size, and the chip size
|
||||
is held in the next level up. If we get more versatile later,
|
||||
it'll make it a damn sight harder to find which chip we want from
|
||||
a given offset, and we'll want to add the per-chip length field
|
||||
back in.
|
||||
*/
|
||||
int ref_point_counter;
|
||||
flstate_t state;
|
||||
flstate_t oldstate;
|
||||
|
||||
unsigned int write_suspended:1;
|
||||
unsigned int erase_suspended:1;
|
||||
unsigned long in_progress_block_addr;
|
||||
|
||||
int word_write_time;
|
||||
int buffer_write_time;
|
||||
int erase_time;
|
||||
|
||||
int word_write_time_max;
|
||||
int buffer_write_time_max;
|
||||
int erase_time_max;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/* This is used to handle contention on write/erase operations
|
||||
between partitions of the same physical chip. */
|
||||
struct flchip_shared {
|
||||
struct flchip *writing;
|
||||
struct flchip *erasing;
|
||||
};
|
||||
|
||||
|
||||
#endif /* __MTD_FLASHCHIP_H__ */
|
|
@ -20,6 +20,25 @@ struct mtd_oob_buf {
|
|||
unsigned char *ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* MTD operation modes
|
||||
*
|
||||
* @MTD_OPS_PLACE_OOB: OOB data are placed at the given offset (default)
|
||||
* @MTD_OPS_AUTO_OOB: OOB data are automatically placed at the free areas
|
||||
* which are defined by the internal ecclayout
|
||||
* @MTD_OPS_RAW: data are transferred as-is, with no error correction;
|
||||
* this mode implies %MTD_OPS_PLACE_OOB
|
||||
*
|
||||
* These modes can be passed to ioctl(MEMWRITE) and are also used internally.
|
||||
* See notes on "MTD file modes" for discussion on %MTD_OPS_RAW vs.
|
||||
* %MTD_FILE_MODE_RAW.
|
||||
*/
|
||||
enum {
|
||||
MTD_OPS_PLACE_OOB = 0,
|
||||
MTD_OPS_AUTO_OOB = 1,
|
||||
MTD_OPS_RAW = 2,
|
||||
};
|
||||
|
||||
#define MTD_ABSENT 0
|
||||
#define MTD_RAM 1
|
||||
#define MTD_ROM 2
|
||||
|
|
|
@ -52,22 +52,6 @@ struct mtd_erase_region_info {
|
|||
unsigned long *lockmap; /* If keeping bitmap of locks */
|
||||
};
|
||||
|
||||
/*
|
||||
* oob operation modes
|
||||
*
|
||||
* MTD_OOB_PLACE: oob data are placed at the given offset
|
||||
* MTD_OOB_AUTO: oob data are automatically placed at the free areas
|
||||
* which are defined by the ecclayout
|
||||
* MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data
|
||||
* is inserted into the data. Thats a raw image of the
|
||||
* flash contents.
|
||||
*/
|
||||
typedef enum {
|
||||
MTD_OOB_PLACE,
|
||||
MTD_OOB_AUTO,
|
||||
MTD_OOB_RAW,
|
||||
} mtd_oob_mode_t;
|
||||
|
||||
/**
|
||||
* struct mtd_oob_ops - oob operation operands
|
||||
* @mode: operation mode
|
||||
|
@ -88,7 +72,7 @@ typedef enum {
|
|||
* OOB area.
|
||||
*/
|
||||
struct mtd_oob_ops {
|
||||
mtd_oob_mode_t mode;
|
||||
unsigned int mode;
|
||||
size_t len;
|
||||
size_t retlen;
|
||||
size_t ooblen;
|
||||
|
@ -117,9 +101,29 @@ struct mtd_info {
|
|||
*/
|
||||
u_int32_t writesize;
|
||||
|
||||
/*
|
||||
* Size of the write buffer used by the MTD. MTD devices having a write
|
||||
* buffer can write multiple writesize chunks at a time. E.g. while
|
||||
* writing 4 * writesize bytes to a device with 2 * writesize bytes
|
||||
* buffer the MTD driver can (but doesn't have to) do 2 writesize
|
||||
* operations, but not 4. Currently, all NANDs have writebufsize
|
||||
* equivalent to writesize (NAND page size). Some NOR flashes do have
|
||||
* writebufsize greater than writesize.
|
||||
*/
|
||||
uint32_t writebufsize;
|
||||
|
||||
u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
|
||||
u_int32_t oobavail; // Available OOB bytes per block
|
||||
|
||||
/*
|
||||
* read ops return -EUCLEAN if max number of bitflips corrected on any
|
||||
* one region comprising an ecc step equals or exceeds this value.
|
||||
* Settable by driver, else defaults to ecc_strength. User can override
|
||||
* in sysfs. N.B. The meaning of the -EUCLEAN return code has changed;
|
||||
* see Documentation/ABI/testing/sysfs-class-mtd for more detail.
|
||||
*/
|
||||
unsigned int bitflip_threshold;
|
||||
|
||||
// Kernel-only stuff starts here.
|
||||
char *name;
|
||||
int index;
|
||||
|
@ -127,6 +131,9 @@ struct mtd_info {
|
|||
/* ecc layout structure pointer - read only ! */
|
||||
struct nand_ecclayout *ecclayout;
|
||||
|
||||
/* max number of correctible bit errors per ecc step */
|
||||
unsigned int ecc_strength;
|
||||
|
||||
/* Data for variable erase regions. If numeraseregions is zero,
|
||||
* it means that the whole device has erasesize as given above.
|
||||
*/
|
||||
|
@ -217,6 +224,24 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
|||
int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
|
||||
const u_char *buf);
|
||||
|
||||
int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
|
||||
|
||||
static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
ops->retlen = ops->oobretlen = 0;
|
||||
if (!mtd->write_oob)
|
||||
return -EOPNOTSUPP;
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
return mtd->write_oob(mtd, to, ops);
|
||||
}
|
||||
|
||||
static inline int mtd_can_have_bb(const struct mtd_info *mtd)
|
||||
{
|
||||
return !!mtd->block_isbad;
|
||||
}
|
||||
|
||||
static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
|
||||
{
|
||||
do_div(sz, mtd->erasesize);
|
||||
|
@ -289,4 +314,16 @@ int mtd_all_ff(const void *buf, unsigned int len);
|
|||
|
||||
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
|
||||
|
||||
static inline int mtd_is_bitflip(int err) {
|
||||
return err == -EUCLEAN;
|
||||
}
|
||||
|
||||
static inline int mtd_is_eccerr(int err) {
|
||||
return err == -EBADMSG;
|
||||
}
|
||||
|
||||
static inline int mtd_is_bitflip_or_eccerr(int err) {
|
||||
return mtd_is_bitflip(err) || mtd_is_eccerr(err);
|
||||
}
|
||||
|
||||
#endif /* __MTD_MTD_H__ */
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/*
|
||||
* linux/include/linux/mtd/nand.h
|
||||
*
|
||||
* Copyright (c) 2000 David Woodhouse <dwmw2@infradead.org>
|
||||
* Steven J. Hill <sjhill@realitydiluted.com>
|
||||
* Thomas Gleixner <tglx@linutronix.de>
|
||||
*
|
||||
* $Id: nand.h,v 1.74 2005/09/15 13:58:50 vwool Exp $
|
||||
* Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
|
||||
* Steven J. Hill <sjhill@realitydiluted.com>
|
||||
* Thomas Gleixner <tglx@linutronix.de>
|
||||
*
|
||||
* 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
|
||||
|
@ -20,34 +18,43 @@
|
|||
#ifndef __LINUX_MTD_NAND_H
|
||||
#define __LINUX_MTD_NAND_H
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
/* The maximum number of NAND chips in an array */
|
||||
#define NAND_MAX_CHIPS 8
|
||||
|
||||
#include <linux/mtd/flashchip.h>
|
||||
#include <linux/mtd/bbm.h>
|
||||
|
||||
struct mtd_info;
|
||||
struct nand_flash_dev;
|
||||
/* Scan and identify a NAND device */
|
||||
extern int nand_scan (struct mtd_info *mtd, int max_chips);
|
||||
/* Separate phases of nand_scan(), allowing board driver to intervene
|
||||
* and override command or ECC setup according to flash type */
|
||||
extern int nand_scan_ident(struct mtd_info *mtd, int max_chips);
|
||||
extern int nand_scan(struct mtd_info *mtd, int max_chips);
|
||||
/*
|
||||
* Separate phases of nand_scan(), allowing board driver to intervene
|
||||
* and override command or ECC setup according to flash type.
|
||||
*/
|
||||
extern int nand_scan_ident(struct mtd_info *mtd, int max_chips,
|
||||
struct nand_flash_dev *table);
|
||||
extern int nand_scan_tail(struct mtd_info *mtd);
|
||||
|
||||
/* Free resources held by the NAND device */
|
||||
extern void nand_release (struct mtd_info *mtd);
|
||||
extern void nand_release(struct mtd_info *mtd);
|
||||
|
||||
/* Internal helper for board drivers which need to override command function */
|
||||
extern void nand_wait_ready(struct mtd_info *mtd);
|
||||
|
||||
/* This constant declares the max. oobsize / page, which
|
||||
/* locks all blocks present in the device */
|
||||
extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
|
||||
/* unlocks specified locked blocks */
|
||||
extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
|
||||
/* The maximum number of NAND chips in an array */
|
||||
#define NAND_MAX_CHIPS 8
|
||||
|
||||
/*
|
||||
* This constant declares the max. oobsize / page, which
|
||||
* is supported now. If you add a chip with bigger oobsize/page
|
||||
* adjust this accordingly.
|
||||
*/
|
||||
#define NAND_MAX_OOBSIZE 576
|
||||
#define NAND_MAX_OOBSIZE 640
|
||||
#define NAND_MAX_PAGESIZE 8192
|
||||
|
||||
/*
|
||||
|
@ -77,38 +84,24 @@ extern void nand_wait_ready(struct mtd_info *mtd);
|
|||
#define NAND_CMD_READOOB 0x50
|
||||
#define NAND_CMD_ERASE1 0x60
|
||||
#define NAND_CMD_STATUS 0x70
|
||||
#define NAND_CMD_STATUS_MULTI 0x71
|
||||
#define NAND_CMD_SEQIN 0x80
|
||||
#define NAND_CMD_RNDIN 0x85
|
||||
#define NAND_CMD_READID 0x90
|
||||
#define NAND_CMD_ERASE2 0xd0
|
||||
#define NAND_CMD_PARAM 0xec
|
||||
#define NAND_CMD_GET_FEATURES 0xee
|
||||
#define NAND_CMD_SET_FEATURES 0xef
|
||||
#define NAND_CMD_RESET 0xff
|
||||
|
||||
#define NAND_CMD_LOCK 0x2a
|
||||
#define NAND_CMD_UNLOCK1 0x23
|
||||
#define NAND_CMD_UNLOCK2 0x24
|
||||
|
||||
/* Extended commands for large page devices */
|
||||
#define NAND_CMD_READSTART 0x30
|
||||
#define NAND_CMD_RNDOUTSTART 0xE0
|
||||
#define NAND_CMD_CACHEDPROG 0x15
|
||||
|
||||
/* Extended commands for AG-AND device */
|
||||
/*
|
||||
* Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but
|
||||
* there is no way to distinguish that from NAND_CMD_READ0
|
||||
* until the remaining sequence of commands has been completed
|
||||
* so add a high order bit and mask it off in the command.
|
||||
*/
|
||||
#define NAND_CMD_DEPLETE1 0x100
|
||||
#define NAND_CMD_DEPLETE2 0x38
|
||||
#define NAND_CMD_STATUS_MULTI 0x71
|
||||
#define NAND_CMD_STATUS_ERROR 0x72
|
||||
/* multi-bank error status (banks 0-3) */
|
||||
#define NAND_CMD_STATUS_ERROR0 0x73
|
||||
#define NAND_CMD_STATUS_ERROR1 0x74
|
||||
#define NAND_CMD_STATUS_ERROR2 0x75
|
||||
#define NAND_CMD_STATUS_ERROR3 0x76
|
||||
#define NAND_CMD_STATUS_RESET 0x7f
|
||||
#define NAND_CMD_STATUS_CLEAR 0xff
|
||||
|
||||
#define NAND_CMD_NONE -1
|
||||
|
||||
/* Status bits */
|
||||
|
@ -126,6 +119,8 @@ typedef enum {
|
|||
NAND_ECC_SOFT,
|
||||
NAND_ECC_HW,
|
||||
NAND_ECC_HW_SYNDROME,
|
||||
NAND_ECC_HW_OOB_FIRST,
|
||||
NAND_ECC_SOFT_BCH,
|
||||
} nand_ecc_modes_t;
|
||||
|
||||
/*
|
||||
|
@ -135,65 +130,65 @@ typedef enum {
|
|||
#define NAND_ECC_READ 0
|
||||
/* Reset Hardware ECC for write */
|
||||
#define NAND_ECC_WRITE 1
|
||||
/* Enable Hardware ECC before syndrom is read back from flash */
|
||||
/* Enable Hardware ECC before syndrome is read back from flash */
|
||||
#define NAND_ECC_READSYN 2
|
||||
|
||||
/* Bit mask for flags passed to do_nand_read_ecc */
|
||||
#define NAND_GET_DEVICE 0x80
|
||||
|
||||
|
||||
/* Option constants for bizarre disfunctionality and real
|
||||
* features
|
||||
*/
|
||||
/* Chip can not auto increment pages */
|
||||
#define NAND_NO_AUTOINCR 0x00000001
|
||||
/* Buswitdh is 16 bit */
|
||||
/*
|
||||
* Option constants for bizarre disfunctionality and real
|
||||
* features.
|
||||
*/
|
||||
/* Buswidth is 16 bit */
|
||||
#define NAND_BUSWIDTH_16 0x00000002
|
||||
/* Device supports partial programming without padding */
|
||||
#define NAND_NO_PADDING 0x00000004
|
||||
/* Chip has cache program function */
|
||||
#define NAND_CACHEPRG 0x00000008
|
||||
/* Chip has copy back function */
|
||||
#define NAND_COPYBACK 0x00000010
|
||||
/* AND Chip which has 4 banks and a confusing page / block
|
||||
* assignment. See Renesas datasheet for further information */
|
||||
#define NAND_IS_AND 0x00000020
|
||||
/* Chip has a array of 4 pages which can be read without
|
||||
* additional ready /busy waits */
|
||||
#define NAND_4PAGE_ARRAY 0x00000040
|
||||
/* Chip requires that BBT is periodically rewritten to prevent
|
||||
* bits from adjacent blocks from 'leaking' in altering data.
|
||||
* This happens with the Renesas AG-AND chips, possibly others. */
|
||||
#define BBT_AUTO_REFRESH 0x00000080
|
||||
/* Chip does not require ready check on read. True
|
||||
* for all large page devices, as they do not support
|
||||
* autoincrement.*/
|
||||
#define NAND_NO_READRDY 0x00000100
|
||||
/*
|
||||
* Chip requires ready check on read (for auto-incremented sequential read).
|
||||
* True only for small page devices; large page devices do not support
|
||||
* autoincrement.
|
||||
*/
|
||||
#define NAND_NEED_READRDY 0x00000100
|
||||
|
||||
/* Chip does not allow subpage writes */
|
||||
#define NAND_NO_SUBPAGE_WRITE 0x00000200
|
||||
/* Buswitdh shal be autodetected */
|
||||
#define NAND_BUSWIDTH_AUTO 0x00080000
|
||||
|
||||
/* Device is one of 'new' xD cards that expose fake nand command set */
|
||||
#define NAND_BROKEN_XD 0x00000400
|
||||
|
||||
/* Device behaves just like nand, but is readonly */
|
||||
#define NAND_ROM 0x00000800
|
||||
|
||||
/* Device supports subpage reads */
|
||||
#define NAND_SUBPAGE_READ 0x00001000
|
||||
|
||||
/* Options valid for Samsung large page devices */
|
||||
#define NAND_SAMSUNG_LP_OPTIONS \
|
||||
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
|
||||
#define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG
|
||||
|
||||
/* Macros to identify the above */
|
||||
#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
|
||||
#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
|
||||
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
|
||||
#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
|
||||
|
||||
/* Mask to zero out the chip options, which come from the id table */
|
||||
#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
|
||||
#define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ))
|
||||
|
||||
/* Non chip related options */
|
||||
/* This option skips the bbt scan during initialization. */
|
||||
#define NAND_SKIP_BBTSCAN 0x00020000
|
||||
/* This option is defined if the board driver allocates its own buffers
|
||||
(e.g. because it needs them DMA-coherent */
|
||||
#define NAND_OWN_BUFFERS 0x00040000
|
||||
#define NAND_SKIP_BBTSCAN 0x00010000
|
||||
/*
|
||||
* This option is defined if the board driver allocates its own buffers
|
||||
* (e.g. because it needs them DMA-coherent).
|
||||
*/
|
||||
#define NAND_OWN_BUFFERS 0x00020000
|
||||
/* Chip may not exist, so silence any errors in scan */
|
||||
#define NAND_SCAN_SILENT_NODEV 0x00040000
|
||||
/*
|
||||
* Autodetect nand buswidth with readid/onfi.
|
||||
* This suppose the driver will configure the hardware in 8 bits mode
|
||||
* when calling nand_scan_ident, and update its configuration
|
||||
* before calling nand_scan_tail.
|
||||
*/
|
||||
#define NAND_BUSWIDTH_AUTO 0x00080000
|
||||
|
||||
/* Options set by nand scan */
|
||||
/* Nand scan has allocated controller struct */
|
||||
#define NAND_CONTROLLER_ALLOC 0x80000000
|
||||
|
@ -202,23 +197,24 @@ typedef enum {
|
|||
#define NAND_CI_CHIPNR_MSK 0x03
|
||||
#define NAND_CI_CELLTYPE_MSK 0x0C
|
||||
|
||||
/*
|
||||
* nand_state_t - chip states
|
||||
* Enumeration for NAND flash chip state
|
||||
*/
|
||||
typedef enum {
|
||||
FL_READY,
|
||||
FL_READING,
|
||||
FL_WRITING,
|
||||
FL_ERASING,
|
||||
FL_SYNCING,
|
||||
FL_CACHEDPRG,
|
||||
FL_PM_SUSPENDED,
|
||||
} nand_state_t;
|
||||
|
||||
/* Keep gcc happy */
|
||||
struct nand_chip;
|
||||
|
||||
/* ONFI timing mode, used in both asynchronous and synchronous mode */
|
||||
#define ONFI_TIMING_MODE_0 (1 << 0)
|
||||
#define ONFI_TIMING_MODE_1 (1 << 1)
|
||||
#define ONFI_TIMING_MODE_2 (1 << 2)
|
||||
#define ONFI_TIMING_MODE_3 (1 << 3)
|
||||
#define ONFI_TIMING_MODE_4 (1 << 4)
|
||||
#define ONFI_TIMING_MODE_5 (1 << 5)
|
||||
#define ONFI_TIMING_MODE_UNKNOWN (1 << 6)
|
||||
|
||||
/* ONFI feature address */
|
||||
#define ONFI_FEATURE_ADDR_TIMING_MODE 0x1
|
||||
|
||||
/* ONFI subfeature parameters length */
|
||||
#define ONFI_SUBFEATURE_PARAM_LEN 4
|
||||
|
||||
struct nand_onfi_params {
|
||||
/* rev info and features block */
|
||||
/* 'O' 'N' 'F' 'I' */
|
||||
|
@ -287,75 +283,87 @@ struct nand_onfi_params {
|
|||
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
|
||||
* @lock: protection lock
|
||||
* @active: the mtd device which holds the controller currently
|
||||
* @wq: wait queue to sleep on if a NAND operation is in progress
|
||||
* used instead of the per chip wait queue when a hw controller is available
|
||||
* @wq: wait queue to sleep on if a NAND operation is in
|
||||
* progress used instead of the per chip wait queue
|
||||
* when a hw controller is available.
|
||||
*/
|
||||
struct nand_hw_control {
|
||||
struct nand_chip *active;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_ecc_ctrl - Control structure for ecc
|
||||
* @mode: ecc mode
|
||||
* @steps: number of ecc steps per page
|
||||
* @size: data bytes per ecc step
|
||||
* @bytes: ecc bytes per step
|
||||
* @total: total number of ecc bytes per page
|
||||
* @prepad: padding information for syndrome based ecc generators
|
||||
* @postpad: padding information for syndrome based ecc generators
|
||||
* struct nand_ecc_ctrl - Control structure for ECC
|
||||
* @mode: ECC mode
|
||||
* @steps: number of ECC steps per page
|
||||
* @size: data bytes per ECC step
|
||||
* @bytes: ECC bytes per step
|
||||
* @strength: max number of correctible bits per ECC step
|
||||
* @total: total number of ECC bytes per page
|
||||
* @prepad: padding information for syndrome based ECC generators
|
||||
* @postpad: padding information for syndrome based ECC generators
|
||||
* @layout: ECC layout control struct pointer
|
||||
* @hwctl: function to control hardware ecc generator. Must only
|
||||
* @priv: pointer to private ECC control data
|
||||
* @hwctl: function to control hardware ECC generator. Must only
|
||||
* be provided if an hardware ECC is available
|
||||
* @calculate: function for ecc calculation or readback from ecc hardware
|
||||
* @correct: function for ecc correction, matching to ecc generator (sw/hw)
|
||||
* @calculate: function for ECC calculation or readback from ECC hardware
|
||||
* @correct: function for ECC correction, matching to ECC generator (sw/hw)
|
||||
* @read_page_raw: function to read a raw page without ECC
|
||||
* @write_page_raw: function to write a raw page without ECC
|
||||
* @read_page: function to read a page according to the ecc generator requirements
|
||||
* @write_page: function to write a page according to the ecc generator requirements
|
||||
* @read_page: function to read a page according to the ECC generator
|
||||
* requirements; returns maximum number of bitflips corrected in
|
||||
* any single ECC step, 0 if bitflips uncorrectable, -EIO hw error
|
||||
* @read_subpage: function to read parts of the page covered by ECC;
|
||||
* returns same as read_page()
|
||||
* @write_subpage: function to write parts of the page covered by ECC.
|
||||
* @write_page: function to write a page according to the ECC generator
|
||||
* requirements.
|
||||
* @write_oob_raw: function to write chip OOB data without ECC
|
||||
* @read_oob_raw: function to read chip OOB data without ECC
|
||||
* @read_oob: function to read chip OOB data
|
||||
* @write_oob: function to write chip OOB data
|
||||
*/
|
||||
struct nand_ecc_ctrl {
|
||||
nand_ecc_modes_t mode;
|
||||
int steps;
|
||||
int size;
|
||||
int bytes;
|
||||
int total;
|
||||
int prepad;
|
||||
int postpad;
|
||||
nand_ecc_modes_t mode;
|
||||
int steps;
|
||||
int size;
|
||||
int bytes;
|
||||
int total;
|
||||
int strength;
|
||||
int prepad;
|
||||
int postpad;
|
||||
struct nand_ecclayout *layout;
|
||||
void (*hwctl)(struct mtd_info *mtd, int mode);
|
||||
int (*calculate)(struct mtd_info *mtd,
|
||||
const uint8_t *dat,
|
||||
uint8_t *ecc_code);
|
||||
int (*correct)(struct mtd_info *mtd, uint8_t *dat,
|
||||
uint8_t *read_ecc,
|
||||
uint8_t *calc_ecc);
|
||||
int (*read_page_raw)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf);
|
||||
void (*write_page_raw)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf);
|
||||
int (*read_page)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf);
|
||||
void (*write_page)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf);
|
||||
int (*read_oob)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
int page,
|
||||
int sndcmd);
|
||||
int (*write_oob)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
int page);
|
||||
void *priv;
|
||||
void (*hwctl)(struct mtd_info *mtd, int mode);
|
||||
int (*calculate)(struct mtd_info *mtd, const uint8_t *dat,
|
||||
uint8_t *ecc_code);
|
||||
int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
|
||||
uint8_t *calc_ecc);
|
||||
int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page);
|
||||
int (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required);
|
||||
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page);
|
||||
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offs, uint32_t len, uint8_t *buf);
|
||||
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, uint32_t data_len,
|
||||
const uint8_t *data_buf, int oob_required);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required);
|
||||
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page);
|
||||
int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page);
|
||||
int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page);
|
||||
int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_buffers - buffer structure for read/write
|
||||
* @ecccalc: buffer for calculated ecc
|
||||
* @ecccode: buffer for ecc read from flash
|
||||
* @ecccalc: buffer for calculated ECC
|
||||
* @ecccode: buffer for ECC read from flash
|
||||
* @databuf: buffer for data - dynamically sized
|
||||
*
|
||||
* Do not change the order of buffers. databuf and oobrbuf must be in
|
||||
|
@ -369,168 +377,234 @@ struct nand_buffers {
|
|||
|
||||
/**
|
||||
* struct nand_chip - NAND Private Flash Chip Data
|
||||
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
|
||||
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
|
||||
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the
|
||||
* flash device
|
||||
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
|
||||
* flash device.
|
||||
* @read_byte: [REPLACEABLE] read one byte from the chip
|
||||
* @read_word: [REPLACEABLE] read one word from the chip
|
||||
* @write_buf: [REPLACEABLE] write data from the buffer to the chip
|
||||
* @read_buf: [REPLACEABLE] read data from the chip into the buffer
|
||||
* @verify_buf: [REPLACEABLE] verify buffer contents against the chip data
|
||||
* @select_chip: [REPLACEABLE] select chip nr
|
||||
* @block_bad: [REPLACEABLE] check, if the block is bad
|
||||
* @block_markbad: [REPLACEABLE] mark the block bad
|
||||
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
|
||||
* ALE/CLE/nCE. Also used to write command and address
|
||||
* @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
|
||||
* If set to NULL no access to ready/busy is available and the ready/busy information
|
||||
* is read from the chip status register
|
||||
* @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip
|
||||
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready
|
||||
* @ecc: [BOARDSPECIFIC] ecc control ctructure
|
||||
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
|
||||
* mtd->oobsize, mtd->writesize and so on.
|
||||
* @id_data contains the 8 bytes values of NAND_CMD_READID.
|
||||
* Return with the bus width.
|
||||
* @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accessing
|
||||
* device ready/busy line. If set to NULL no access to
|
||||
* ready/busy is available and the ready/busy information
|
||||
* is read from the chip status register.
|
||||
* @cmdfunc: [REPLACEABLE] hardwarespecific function for writing
|
||||
* commands to the chip.
|
||||
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on
|
||||
* ready.
|
||||
* @ecc: [BOARDSPECIFIC] ECC control structure
|
||||
* @buffers: buffer structure for read/write
|
||||
* @hwcontrol: platform-specific hardware control structure
|
||||
* @ops: oob operation operands
|
||||
* @erase_cmd: [INTERN] erase command write function, selectable due to AND support
|
||||
* @erase_cmd: [INTERN] erase command write function, selectable due
|
||||
* to AND support.
|
||||
* @scan_bbt: [REPLACEABLE] function to scan bad block table
|
||||
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
|
||||
* @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress
|
||||
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring
|
||||
* data from array to read regs (tR).
|
||||
* @state: [INTERN] the current state of the NAND device
|
||||
* @oob_poi: poison value buffer
|
||||
* @page_shift: [INTERN] number of address bits in a page (column address bits)
|
||||
* @oob_poi: "poison value buffer," used for laying out OOB data
|
||||
* before writing
|
||||
* @page_shift: [INTERN] number of address bits in a page (column
|
||||
* address bits).
|
||||
* @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
|
||||
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
|
||||
* @chip_shift: [INTERN] number of address bits in one chip
|
||||
* @datbuf: [INTERN] internal buffer for one page + oob
|
||||
* @oobbuf: [INTERN] oob buffer for one eraseblock
|
||||
* @oobdirty: [INTERN] indicates that oob_buf must be reinitialized
|
||||
* @data_poi: [INTERN] pointer to a data buffer
|
||||
* @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
|
||||
* special functionality. See the defines for further explanation
|
||||
* @options: [BOARDSPECIFIC] various chip options. They can partly
|
||||
* be set to inform nand_scan about special functionality.
|
||||
* See the defines for further explanation.
|
||||
* @bbt_options: [INTERN] bad block specific options. All options used
|
||||
* here must come from bbm.h. By default, these options
|
||||
* will be copied to the appropriate nand_bbt_descr's.
|
||||
* @badblockpos: [INTERN] position of the bad block marker in the oob area
|
||||
* @badblockpos: [INTERN] position of the bad block marker in the oob
|
||||
* area.
|
||||
* @badblockbits: [INTERN] minimum number of set bits in a good block's
|
||||
* bad block marker position; i.e., BBM == 11110111b is
|
||||
* not bad when badblockbits == 7
|
||||
* @cellinfo: [INTERN] MLC/multichip data from chip ident
|
||||
* @numchips: [INTERN] number of physical chips
|
||||
* @chipsize: [INTERN] the size of one chip for multichip arrays
|
||||
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
||||
* @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf
|
||||
* @pagebuf: [INTERN] holds the pagenumber which is currently in
|
||||
* data_buf.
|
||||
* @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is
|
||||
* currently in data_buf.
|
||||
* @subpagesize: [INTERN] holds the subpagesize
|
||||
* @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
|
||||
* non 0 if ONFI supported.
|
||||
* @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is
|
||||
* supported, 0 otherwise.
|
||||
* @ecclayout: [REPLACEABLE] the default ecc placement scheme
|
||||
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
|
||||
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
|
||||
* @ecclayout: [REPLACEABLE] the default ECC placement scheme
|
||||
* @bbt: [INTERN] bad block table pointer
|
||||
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
|
||||
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
|
||||
* lookup.
|
||||
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor
|
||||
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
|
||||
* @controller: [REPLACEABLE] a pointer to a hardware controller structure
|
||||
* which is shared among multiple independend devices
|
||||
* @priv: [OPTIONAL] pointer to private chip date
|
||||
* @errstat: [OPTIONAL] hardware specific function to perform additional error status checks
|
||||
* (determine if errors are correctable)
|
||||
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial
|
||||
* bad block scan.
|
||||
* @controller: [REPLACEABLE] a pointer to a hardware controller
|
||||
* structure which is shared among multiple independent
|
||||
* devices.
|
||||
* @priv: [OPTIONAL] pointer to private chip data
|
||||
* @errstat: [OPTIONAL] hardware specific function to perform
|
||||
* additional error status checks (determine if errors are
|
||||
* correctable).
|
||||
* @write_page: [REPLACEABLE] High-level page write function
|
||||
*/
|
||||
|
||||
struct nand_chip {
|
||||
void __iomem *IO_ADDR_R;
|
||||
void __iomem *IO_ADDR_W;
|
||||
|
||||
void __iomem *IO_ADDR_R;
|
||||
void __iomem *IO_ADDR_W;
|
||||
uint8_t (*read_byte)(struct mtd_info *mtd);
|
||||
u16 (*read_word)(struct mtd_info *mtd);
|
||||
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
||||
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
||||
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
|
||||
u8 *id_data);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
|
||||
int page_addr);
|
||||
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
|
||||
void (*erase_cmd)(struct mtd_info *mtd, int page);
|
||||
int (*scan_bbt)(struct mtd_info *mtd);
|
||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
|
||||
int status, int page);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, int page, int cached, int raw);
|
||||
int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int feature_addr, uint8_t *subfeature_para);
|
||||
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int feature_addr, uint8_t *subfeature_para);
|
||||
|
||||
uint8_t (*read_byte)(struct mtd_info *mtd);
|
||||
u16 (*read_word)(struct mtd_info *mtd);
|
||||
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
|
||||
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
|
||||
unsigned int ctrl);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
|
||||
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
|
||||
void (*erase_cmd)(struct mtd_info *mtd, int page);
|
||||
int (*scan_bbt)(struct mtd_info *mtd);
|
||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int page, int cached, int raw);
|
||||
int (*set_buswidth)(struct mtd_info *mtd, struct nand_chip *this, int buswidth);
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
unsigned int bbt_options;
|
||||
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
unsigned int bbt_options;
|
||||
|
||||
int page_shift;
|
||||
int phys_erase_shift;
|
||||
int bbt_erase_shift;
|
||||
int chip_shift;
|
||||
int numchips;
|
||||
uint64_t chipsize;
|
||||
int pagemask;
|
||||
int pagebuf;
|
||||
int subpagesize;
|
||||
uint8_t cellinfo;
|
||||
int badblockpos;
|
||||
int page_shift;
|
||||
int phys_erase_shift;
|
||||
int bbt_erase_shift;
|
||||
int chip_shift;
|
||||
int numchips;
|
||||
uint64_t chipsize;
|
||||
int pagemask;
|
||||
int pagebuf;
|
||||
unsigned int pagebuf_bitflips;
|
||||
int subpagesize;
|
||||
uint8_t cellinfo;
|
||||
int badblockpos;
|
||||
int badblockbits;
|
||||
|
||||
int onfi_version;
|
||||
struct nand_onfi_params onfi_params;
|
||||
struct nand_onfi_params onfi_params;
|
||||
|
||||
nand_state_t state;
|
||||
flstate_t state;
|
||||
|
||||
uint8_t *oob_poi;
|
||||
struct nand_hw_control *controller;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
uint8_t *oob_poi;
|
||||
struct nand_hw_control *controller;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
|
||||
struct nand_ecc_ctrl ecc;
|
||||
struct nand_buffers *buffers;
|
||||
struct nand_hw_control hwcontrol;
|
||||
|
||||
struct mtd_oob_ops ops;
|
||||
uint8_t *bbt;
|
||||
struct nand_bbt_descr *bbt_td;
|
||||
struct nand_bbt_descr *bbt_md;
|
||||
|
||||
uint8_t *bbt;
|
||||
struct nand_bbt_descr *bbt_td;
|
||||
struct nand_bbt_descr *bbt_md;
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
|
||||
void *priv;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/*
|
||||
* NAND Flash Manufacturer ID Codes
|
||||
*/
|
||||
#define NAND_MFR_TOSHIBA 0x98
|
||||
#define NAND_MFR_SAMSUNG 0xec
|
||||
#define NAND_MFR_FUJITSU 0x04
|
||||
#define NAND_MFR_NATIONAL 0x8f
|
||||
#define NAND_MFR_RENESAS 0x07
|
||||
#define NAND_MFR_STMICRO 0x20
|
||||
#define NAND_MFR_HYNIX 0xad
|
||||
#define NAND_MFR_MICRON 0x2c
|
||||
#define NAND_MFR_AMD 0x01
|
||||
#define NAND_MFR_MACRONIX 0xc2
|
||||
#define NAND_MFR_EON 0x92
|
||||
#define NAND_MFR_TOSHIBA 0x98
|
||||
#define NAND_MFR_SAMSUNG 0xec
|
||||
#define NAND_MFR_FUJITSU 0x04
|
||||
#define NAND_MFR_NATIONAL 0x8f
|
||||
#define NAND_MFR_RENESAS 0x07
|
||||
#define NAND_MFR_STMICRO 0x20
|
||||
#define NAND_MFR_HYNIX 0xad
|
||||
#define NAND_MFR_MICRON 0x2c
|
||||
#define NAND_MFR_AMD 0x01
|
||||
#define NAND_MFR_MACRONIX 0xc2
|
||||
#define NAND_MFR_EON 0x92
|
||||
|
||||
/* The maximum expected count of bytes in the NAND ID sequence */
|
||||
#define NAND_MAX_ID_LEN 8
|
||||
|
||||
/*
|
||||
* A helper for defining older NAND chips where the second ID byte fully
|
||||
* defined the chip, including the geometry (chip size, eraseblock size, page
|
||||
* size). All these chips have 512 bytes NAND page size.
|
||||
*/
|
||||
#define LEGACY_ID_NAND(nm, devid, chipsz, erasesz, opts) \
|
||||
{ .name = (nm), {{ .dev_id = (devid) }}, .pagesize = 512, \
|
||||
.chipsize = (chipsz), .erasesize = (erasesz), .options = (opts) }
|
||||
|
||||
/*
|
||||
* A helper for defining newer chips which report their page size and
|
||||
* eraseblock size via the extended ID bytes.
|
||||
*
|
||||
* The real difference between LEGACY_ID_NAND and EXTENDED_ID_NAND is that with
|
||||
* EXTENDED_ID_NAND, manufacturers overloaded the same device ID so that the
|
||||
* device ID now only represented a particular total chip size (and voltage,
|
||||
* buswidth), and the page size, eraseblock size, and OOB size could vary while
|
||||
* using the same device ID.
|
||||
*/
|
||||
#define EXTENDED_ID_NAND(nm, devid, chipsz, opts) \
|
||||
{ .name = (nm), {{ .dev_id = (devid) }}, .chipsize = (chipsz), \
|
||||
.options = (opts) }
|
||||
|
||||
/**
|
||||
* struct nand_flash_dev - NAND Flash Device ID Structure
|
||||
* @name: Identify the device type
|
||||
* @id: device ID code
|
||||
* @pagesize: Pagesize in bytes. Either 256 or 512 or 0
|
||||
* If the pagesize is 0, then the real pagesize
|
||||
* and the eraseize are determined from the
|
||||
* extended id bytes in the chip
|
||||
* @erasesize: Size of an erase block in the flash device.
|
||||
* @chipsize: Total chipsize in Mega Bytes
|
||||
* @options: Bitfield to store chip relevant options
|
||||
* @name: a human-readable name of the NAND chip
|
||||
* @dev_id: the device ID (the second byte of the full chip ID array)
|
||||
* @mfr_id: manufecturer ID part of the full chip ID array (refers the same
|
||||
* memory address as @id[0])
|
||||
* @dev_id: device ID part of the full chip ID array (refers the same memory
|
||||
* address as @id[1])
|
||||
* @id: full device ID array
|
||||
* @pagesize: size of the NAND page in bytes; if 0, then the real page size (as
|
||||
* well as the eraseblock size) is determined from the extended NAND
|
||||
* chip ID array)
|
||||
* @chipsize: total chip size in MiB
|
||||
* @erasesize: eraseblock size in bytes (determined from the extended ID if 0)
|
||||
* @options: stores various chip bit options
|
||||
* @id_len: The valid length of the @id.
|
||||
* @oobsize: OOB size
|
||||
*/
|
||||
struct nand_flash_dev {
|
||||
char *name;
|
||||
int id;
|
||||
unsigned long pagesize;
|
||||
unsigned long chipsize;
|
||||
unsigned long erasesize;
|
||||
unsigned long options;
|
||||
union {
|
||||
struct {
|
||||
uint8_t mfr_id;
|
||||
uint8_t dev_id;
|
||||
};
|
||||
uint8_t id[NAND_MAX_ID_LEN];
|
||||
};
|
||||
unsigned int pagesize;
|
||||
unsigned int chipsize;
|
||||
unsigned int erasesize;
|
||||
unsigned int options;
|
||||
uint16_t id_len;
|
||||
uint16_t oobsize;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -540,7 +614,7 @@ struct nand_flash_dev {
|
|||
*/
|
||||
struct nand_manufacturers {
|
||||
int id;
|
||||
char * name;
|
||||
char *name;
|
||||
};
|
||||
|
||||
extern struct nand_flash_dev nand_flash_ids[];
|
||||
|
@ -553,13 +627,8 @@ extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
|
|||
extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
int allowbbt);
|
||||
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t * retlen, uint8_t * buf);
|
||||
size_t *retlen, uint8_t *buf);
|
||||
extern int add_mtd_nand_device(struct mtd_info *mtd, char *devname);
|
||||
/*
|
||||
* Constants for oob configuration
|
||||
*/
|
||||
#define NAND_SMALL_BADBLOCK_POS 5
|
||||
#define NAND_LARGE_BADBLOCK_POS 0
|
||||
|
||||
/**
|
||||
* struct platform_nand_chip - chip level device structure
|
||||
|
@ -569,40 +638,52 @@ extern int add_mtd_nand_device(struct mtd_info *mtd, char *devname);
|
|||
* @partitions: mtd partition list
|
||||
* @chip_delay: R/B delay value in us
|
||||
* @options: Option flags, e.g. 16bit buswidth
|
||||
* @ecclayout: ecc layout info structure
|
||||
* @bbt_options: BBT option flags, e.g. NAND_BBT_USE_FLASH
|
||||
* @ecclayout: ECC layout info structure
|
||||
* @part_probe_types: NULL-terminated array of probe types
|
||||
* @priv: hardware controller specific settings
|
||||
*/
|
||||
struct platform_nand_chip {
|
||||
int nr_chips;
|
||||
int chip_offset;
|
||||
int nr_partitions;
|
||||
struct mtd_partition *partitions;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
const char **part_probe_types;
|
||||
void *priv;
|
||||
int nr_chips;
|
||||
int chip_offset;
|
||||
int nr_partitions;
|
||||
struct mtd_partition *partitions;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
unsigned int bbt_options;
|
||||
const char **part_probe_types;
|
||||
};
|
||||
|
||||
/* Keep gcc happy */
|
||||
struct platform_device;
|
||||
|
||||
/**
|
||||
* struct platform_nand_ctrl - controller level device structure
|
||||
* @probe: platform specific function to probe/setup hardware
|
||||
* @remove: platform specific function to remove/teardown hardware
|
||||
* @hwcontrol: platform specific hardware control structure
|
||||
* @dev_ready: platform specific function to read ready/busy pin
|
||||
* @select_chip: platform specific chip select function
|
||||
* @cmd_ctrl: platform specific function for controlling
|
||||
* ALE/CLE/nCE. Also used to write command and address
|
||||
* @write_buf: platform specific function for write buffer
|
||||
* @read_buf: platform specific function for read buffer
|
||||
* @read_byte: platform specific function to read one byte from chip
|
||||
* @priv: private data to transport driver specific settings
|
||||
*
|
||||
* All fields are optional and depend on the hardware driver requirements
|
||||
*/
|
||||
struct platform_nand_ctrl {
|
||||
void (*hwcontrol)(struct mtd_info *mtd, int cmd);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
|
||||
unsigned int ctrl);
|
||||
void *priv;
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
void (*remove)(struct platform_device *pdev);
|
||||
void (*hwcontrol)(struct mtd_info *mtd, int cmd);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
||||
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
unsigned char (*read_byte)(struct mtd_info *mtd);
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -611,8 +692,8 @@ struct platform_nand_ctrl {
|
|||
* @ctrl: controller level device structure
|
||||
*/
|
||||
struct platform_nand_data {
|
||||
struct platform_nand_chip chip;
|
||||
struct platform_nand_ctrl ctrl;
|
||||
struct platform_nand_chip chip;
|
||||
struct platform_nand_ctrl ctrl;
|
||||
};
|
||||
|
||||
/* Some helpers to access the data structures */
|
||||
|
@ -624,6 +705,20 @@ struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd)
|
|||
return chip->priv;
|
||||
}
|
||||
|
||||
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
|
||||
/* return the supported asynchronous timing mode. */
|
||||
static inline int onfi_get_async_timing_mode(struct nand_chip *chip)
|
||||
{
|
||||
if (!chip->onfi_version)
|
||||
return ONFI_TIMING_MODE_UNKNOWN;
|
||||
return le16_to_cpu(chip->onfi_params.async_timing_mode);
|
||||
}
|
||||
|
||||
/* return the supported synchronous timing mode. */
|
||||
static inline int onfi_get_sync_timing_mode(struct nand_chip *chip)
|
||||
{
|
||||
if (!chip->onfi_version)
|
||||
return ONFI_TIMING_MODE_UNKNOWN;
|
||||
return le16_to_cpu(chip->onfi_params.src_sync_timing_mode);
|
||||
}
|
||||
|
||||
#endif /* __LINUX_MTD_NAND_H */
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.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.
|
||||
*
|
||||
* This file is the header for the NAND BCH ECC implementation.
|
||||
*/
|
||||
|
||||
#ifndef __MTD_NAND_BCH_H__
|
||||
#define __MTD_NAND_BCH_H__
|
||||
|
||||
struct mtd_info;
|
||||
struct nand_bch_control;
|
||||
|
||||
#if defined(CONFIG_NAND_ECC_BCH)
|
||||
|
||||
static inline int mtd_nand_has_bch(void) { return 1; }
|
||||
|
||||
/*
|
||||
* Calculate BCH ecc code
|
||||
*/
|
||||
int nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code);
|
||||
|
||||
/*
|
||||
* Detect and correct bit errors
|
||||
*/
|
||||
int nand_bch_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc,
|
||||
u_char *calc_ecc);
|
||||
/*
|
||||
* Initialize BCH encoder/decoder
|
||||
*/
|
||||
struct nand_bch_control *
|
||||
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize,
|
||||
unsigned int eccbytes, struct nand_ecclayout **ecclayout);
|
||||
/*
|
||||
* Release BCH encoder/decoder resources
|
||||
*/
|
||||
void nand_bch_free(struct nand_bch_control *nbc);
|
||||
|
||||
#else /* !CONFIG_MTD_NAND_ECC_BCH */
|
||||
|
||||
static inline int mtd_nand_has_bch(void) { return 0; }
|
||||
|
||||
static inline int
|
||||
nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline struct nand_bch_control *
|
||||
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize,
|
||||
unsigned int eccbytes, struct nand_ecclayout **ecclayout)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void nand_bch_free(struct nand_bch_control *nbc) {}
|
||||
|
||||
#endif /* CONFIG_MTD_NAND_ECC_BCH */
|
||||
|
||||
#endif /* __MTD_NAND_BCH_H__ */
|
|
@ -11,6 +11,9 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
@ -18,10 +21,12 @@
|
|||
#ifndef __LINUX_UBI_H__
|
||||
#define __LINUX_UBI_H__
|
||||
|
||||
/* #include <asm/ioctl.h> */
|
||||
#include <linux/types.h>
|
||||
#include <mtd/ubi-user.h>
|
||||
|
||||
/* All voumes/LEBs */
|
||||
#define UBI_ALL -1
|
||||
|
||||
/*
|
||||
* enum ubi_open_mode - UBI volume open mode constants.
|
||||
*
|
||||
|
@ -42,13 +47,13 @@ enum {
|
|||
* @size: how many physical eraseblocks are reserved for this volume
|
||||
* @used_bytes: how many bytes of data this volume contains
|
||||
* @used_ebs: how many physical eraseblocks of this volume actually contain any
|
||||
* data
|
||||
* data
|
||||
* @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
|
||||
* @corrupted: non-zero if the volume is corrupted (static volumes only)
|
||||
* @upd_marker: non-zero if the volume has update marker set
|
||||
* @alignment: volume alignment
|
||||
* @usable_leb_size: how many bytes are available in logical eraseblocks of
|
||||
* this volume
|
||||
* this volume
|
||||
* @name_len: volume name length
|
||||
* @name: volume name
|
||||
* @cdev: UBI volume character device major and minor numbers
|
||||
|
@ -84,7 +89,7 @@ enum {
|
|||
* physical eraseblock size and on how much bytes UBI headers consume. But
|
||||
* because of the volume alignment (@alignment), the usable size of logical
|
||||
* eraseblocks if a volume may be less. The following equation is true:
|
||||
* @usable_leb_size = LEB size - (LEB size mod @alignment),
|
||||
* @usable_leb_size = LEB size - (LEB size mod @alignment),
|
||||
* where LEB size is the logical eraseblock size defined by the UBI device.
|
||||
*
|
||||
* The alignment is multiple to the minimal flash input/output unit size or %1
|
||||
|
@ -106,27 +111,84 @@ struct ubi_volume_info {
|
|||
int usable_leb_size;
|
||||
int name_len;
|
||||
const char *name;
|
||||
struct cdev *cdev;
|
||||
dev_t cdev;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_device_info - UBI device description data structure.
|
||||
* @ubi_num: ubi device number
|
||||
* @leb_size: logical eraseblock size on this UBI device
|
||||
* @leb_start: starting offset of logical eraseblocks within physical
|
||||
* eraseblocks
|
||||
* @min_io_size: minimal I/O unit size
|
||||
* @max_write_size: maximum amount of bytes the underlying flash can write at a
|
||||
* time (MTD write buffer size)
|
||||
* @ro_mode: if this device is in read-only mode
|
||||
* @cdev: UBI character device major and minor numbers
|
||||
*
|
||||
* Note, @leb_size is the logical eraseblock size offered by the UBI device.
|
||||
* Volumes of this UBI device may have smaller logical eraseblock size if their
|
||||
* alignment is not equivalent to %1.
|
||||
*
|
||||
* The @max_write_size field describes flash write maximum write unit. For
|
||||
* example, NOR flash allows for changing individual bytes, so @min_io_size is
|
||||
* %1. However, it does not mean than NOR flash has to write data byte-by-byte.
|
||||
* Instead, CFI NOR flashes have a write-buffer of, e.g., 64 bytes, and when
|
||||
* writing large chunks of data, they write 64-bytes at a time. Obviously, this
|
||||
* improves write throughput.
|
||||
*
|
||||
* Also, the MTD device may have N interleaved (striped) flash chips
|
||||
* underneath, in which case @min_io_size can be physical min. I/O size of
|
||||
* single flash chip, while @max_write_size can be N * @min_io_size.
|
||||
*
|
||||
* The @max_write_size field is always greater or equivalent to @min_io_size.
|
||||
* E.g., some NOR flashes may have (@min_io_size = 1, @max_write_size = 64). In
|
||||
* contrast, NAND flashes usually have @min_io_size = @max_write_size = NAND
|
||||
* page size.
|
||||
*/
|
||||
struct ubi_device_info {
|
||||
int ubi_num;
|
||||
int leb_size;
|
||||
int leb_start;
|
||||
int min_io_size;
|
||||
int max_write_size;
|
||||
int ro_mode;
|
||||
struct cdev *cdev;
|
||||
dev_t cdev;
|
||||
};
|
||||
|
||||
/*
|
||||
* Volume notification types.
|
||||
* @UBI_VOLUME_ADDED: a volume has been added (an UBI device was attached or a
|
||||
* volume was created)
|
||||
* @UBI_VOLUME_REMOVED: a volume has been removed (an UBI device was detached
|
||||
* or a volume was removed)
|
||||
* @UBI_VOLUME_RESIZED: a volume has been re-sized
|
||||
* @UBI_VOLUME_RENAMED: a volume has been re-named
|
||||
* @UBI_VOLUME_UPDATED: data has been written to a volume
|
||||
*
|
||||
* These constants define which type of event has happened when a volume
|
||||
* notification function is invoked.
|
||||
*/
|
||||
enum {
|
||||
UBI_VOLUME_ADDED,
|
||||
UBI_VOLUME_REMOVED,
|
||||
UBI_VOLUME_RESIZED,
|
||||
UBI_VOLUME_RENAMED,
|
||||
UBI_VOLUME_UPDATED,
|
||||
};
|
||||
|
||||
/*
|
||||
* struct ubi_notification - UBI notification description structure.
|
||||
* @di: UBI device description object
|
||||
* @vi: UBI volume description object
|
||||
*
|
||||
* UBI notifiers are called with a pointer to an object of this type. The
|
||||
* object describes the notification. Namely, it provides a description of the
|
||||
* UBI device and UBI volume the notification informs about.
|
||||
*/
|
||||
struct ubi_notification {
|
||||
struct ubi_device_info di;
|
||||
struct ubi_volume_info vi;
|
||||
};
|
||||
|
||||
/* UBI descriptor given to users when they open UBI volumes */
|
||||
|
@ -138,17 +200,21 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc,
|
|||
struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode);
|
||||
struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
|
||||
int mode);
|
||||
struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode);
|
||||
|
||||
void ubi_close_volume(struct ubi_volume_desc *desc);
|
||||
int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
|
||||
int len, int check);
|
||||
int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
int offset, int len, int dtype);
|
||||
int offset, int len);
|
||||
int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
||||
int len, int dtype);
|
||||
int len);
|
||||
int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum);
|
||||
int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum);
|
||||
int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype);
|
||||
int ubi_leb_map(struct ubi_volume_desc *desc, int lnum);
|
||||
int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum);
|
||||
int ubi_sync(int ubi_num);
|
||||
int ubi_flush(int ubi_num, int vol_id, int lnum);
|
||||
|
||||
/*
|
||||
* This function is the same as the 'ubi_leb_read()' function, but it does not
|
||||
|
@ -159,25 +225,4 @@ static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, char *buf,
|
|||
{
|
||||
return ubi_leb_read(desc, lnum, buf, offset, len, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the same as the 'ubi_leb_write()' functions, but it does
|
||||
* not have the data type argument.
|
||||
*/
|
||||
static inline int ubi_write(struct ubi_volume_desc *desc, int lnum,
|
||||
const void *buf, int offset, int len)
|
||||
{
|
||||
return ubi_leb_write(desc, lnum, buf, offset, len, UBI_UNKNOWN);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the same as the 'ubi_leb_change()' functions, but it does
|
||||
* not have the data type argument.
|
||||
*/
|
||||
static inline int ubi_change(struct ubi_volume_desc *desc, int lnum,
|
||||
const void *buf, int len)
|
||||
{
|
||||
return ubi_leb_change(desc, lnum, buf, len, UBI_UNKNOWN);
|
||||
}
|
||||
|
||||
#endif /* !__LINUX_UBI_H__ */
|
||||
|
|
|
@ -106,6 +106,8 @@ extern char * skip_spaces(const char *);
|
|||
|
||||
extern char *strim(char *);
|
||||
|
||||
void *memchr_inv(const void *start, int c, size_t bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
* 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
|
||||
*
|
||||
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
||||
* Thomas Gleixner
|
||||
* Frank Haverkamp
|
||||
|
@ -145,10 +149,10 @@ enum {
|
|||
* The @image_seq field is used to validate a UBI image that has been prepared
|
||||
* for a UBI device. The @image_seq value can be any value, but it must be the
|
||||
* same on all eraseblocks. UBI will ensure that all new erase counter headers
|
||||
* also contain this value, and will check the value when scanning at start-up.
|
||||
* also contain this value, and will check the value when attaching the flash.
|
||||
* One way to make use of @image_seq is to increase its value by one every time
|
||||
* an image is flashed over an existing image, then, if the flashing does not
|
||||
* complete, UBI will detect the error when scanning.
|
||||
* complete, UBI will detect the error when attaching the media.
|
||||
*/
|
||||
struct ubi_ec_hdr {
|
||||
__be32 magic;
|
||||
|
@ -160,7 +164,7 @@ struct ubi_ec_hdr {
|
|||
__be32 image_seq;
|
||||
__u8 padding2[32];
|
||||
__be32 hdr_crc;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_vid_hdr - on-flash UBI volume identifier header.
|
||||
|
@ -279,7 +283,7 @@ struct ubi_vid_hdr {
|
|||
__u8 compat;
|
||||
__be32 vol_id;
|
||||
__be32 lnum;
|
||||
__be32 leb_ver;
|
||||
__u8 padding1[4];
|
||||
__be32 data_size;
|
||||
__be32 used_ebs;
|
||||
__be32 data_pad;
|
||||
|
@ -288,14 +292,14 @@ struct ubi_vid_hdr {
|
|||
__be64 sqnum;
|
||||
__u8 padding3[12];
|
||||
__be32 hdr_crc;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/* Internal UBI volumes count */
|
||||
#define UBI_INT_VOL_COUNT 1
|
||||
|
||||
/*
|
||||
* Starting ID of internal volumes. There is reserved room for 4096 internal
|
||||
* volumes.
|
||||
* Starting ID of internal volumes: 0x7fffefff.
|
||||
* There is reserved room for 4096 internal volumes.
|
||||
*/
|
||||
#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096)
|
||||
|
||||
|
@ -369,6 +373,143 @@ struct ubi_vtbl_record {
|
|||
__u8 flags;
|
||||
__u8 padding[23];
|
||||
__be32 crc;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/* UBI fastmap on-flash data structures */
|
||||
|
||||
#define UBI_FM_SB_VOLUME_ID (UBI_LAYOUT_VOLUME_ID + 1)
|
||||
#define UBI_FM_DATA_VOLUME_ID (UBI_LAYOUT_VOLUME_ID + 2)
|
||||
|
||||
/* fastmap on-flash data structure format version */
|
||||
#define UBI_FM_FMT_VERSION 1
|
||||
|
||||
#define UBI_FM_SB_MAGIC 0x7B11D69F
|
||||
#define UBI_FM_HDR_MAGIC 0xD4B82EF7
|
||||
#define UBI_FM_VHDR_MAGIC 0xFA370ED1
|
||||
#define UBI_FM_POOL_MAGIC 0x67AF4D08
|
||||
#define UBI_FM_EBA_MAGIC 0xf0c040a8
|
||||
|
||||
/* A fastmap supber block can be located between PEB 0 and
|
||||
* UBI_FM_MAX_START */
|
||||
#define UBI_FM_MAX_START 64
|
||||
|
||||
/* A fastmap can use up to UBI_FM_MAX_BLOCKS PEBs */
|
||||
#define UBI_FM_MAX_BLOCKS 32
|
||||
|
||||
/* 5% of the total number of PEBs have to be scanned while attaching
|
||||
* from a fastmap.
|
||||
* But the size of this pool is limited to be between UBI_FM_MIN_POOL_SIZE and
|
||||
* UBI_FM_MAX_POOL_SIZE */
|
||||
#define UBI_FM_MIN_POOL_SIZE 8
|
||||
#define UBI_FM_MAX_POOL_SIZE 256
|
||||
|
||||
#define UBI_FM_WL_POOL_SIZE 25
|
||||
|
||||
/**
|
||||
* struct ubi_fm_sb - UBI fastmap super block
|
||||
* @magic: fastmap super block magic number (%UBI_FM_SB_MAGIC)
|
||||
* @version: format version of this fastmap
|
||||
* @data_crc: CRC over the fastmap data
|
||||
* @used_blocks: number of PEBs used by this fastmap
|
||||
* @block_loc: an array containing the location of all PEBs of the fastmap
|
||||
* @block_ec: the erase counter of each used PEB
|
||||
* @sqnum: highest sequence number value at the time while taking the fastmap
|
||||
*
|
||||
*/
|
||||
struct ubi_fm_sb {
|
||||
__be32 magic;
|
||||
__u8 version;
|
||||
__u8 padding1[3];
|
||||
__be32 data_crc;
|
||||
__be32 used_blocks;
|
||||
__be32 block_loc[UBI_FM_MAX_BLOCKS];
|
||||
__be32 block_ec[UBI_FM_MAX_BLOCKS];
|
||||
__be64 sqnum;
|
||||
__u8 padding2[32];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_fm_hdr - header of the fastmap data set
|
||||
* @magic: fastmap header magic number (%UBI_FM_HDR_MAGIC)
|
||||
* @free_peb_count: number of free PEBs known by this fastmap
|
||||
* @used_peb_count: number of used PEBs known by this fastmap
|
||||
* @scrub_peb_count: number of to be scrubbed PEBs known by this fastmap
|
||||
* @bad_peb_count: number of bad PEBs known by this fastmap
|
||||
* @erase_peb_count: number of bad PEBs which have to be erased
|
||||
* @vol_count: number of UBI volumes known by this fastmap
|
||||
*/
|
||||
struct ubi_fm_hdr {
|
||||
__be32 magic;
|
||||
__be32 free_peb_count;
|
||||
__be32 used_peb_count;
|
||||
__be32 scrub_peb_count;
|
||||
__be32 bad_peb_count;
|
||||
__be32 erase_peb_count;
|
||||
__be32 vol_count;
|
||||
__u8 padding[4];
|
||||
} __packed;
|
||||
|
||||
/* struct ubi_fm_hdr is followed by two struct ubi_fm_scan_pool */
|
||||
|
||||
/**
|
||||
* struct ubi_fm_scan_pool - Fastmap pool PEBs to be scanned while attaching
|
||||
* @magic: pool magic numer (%UBI_FM_POOL_MAGIC)
|
||||
* @size: current pool size
|
||||
* @max_size: maximal pool size
|
||||
* @pebs: an array containing the location of all PEBs in this pool
|
||||
*/
|
||||
struct ubi_fm_scan_pool {
|
||||
__be32 magic;
|
||||
__be16 size;
|
||||
__be16 max_size;
|
||||
__be32 pebs[UBI_FM_MAX_POOL_SIZE];
|
||||
__be32 padding[4];
|
||||
} __packed;
|
||||
|
||||
/* ubi_fm_scan_pool is followed by nfree+nused struct ubi_fm_ec records */
|
||||
|
||||
/**
|
||||
* struct ubi_fm_ec - stores the erase counter of a PEB
|
||||
* @pnum: PEB number
|
||||
* @ec: ec of this PEB
|
||||
*/
|
||||
struct ubi_fm_ec {
|
||||
__be32 pnum;
|
||||
__be32 ec;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_fm_volhdr - Fastmap volume header
|
||||
* it identifies the start of an eba table
|
||||
* @magic: Fastmap volume header magic number (%UBI_FM_VHDR_MAGIC)
|
||||
* @vol_id: volume id of the fastmapped volume
|
||||
* @vol_type: type of the fastmapped volume
|
||||
* @data_pad: data_pad value of the fastmapped volume
|
||||
* @used_ebs: number of used LEBs within this volume
|
||||
* @last_eb_bytes: number of bytes used in the last LEB
|
||||
*/
|
||||
struct ubi_fm_volhdr {
|
||||
__be32 magic;
|
||||
__be32 vol_id;
|
||||
__u8 vol_type;
|
||||
__u8 padding1[3];
|
||||
__be32 data_pad;
|
||||
__be32 used_ebs;
|
||||
__be32 last_eb_bytes;
|
||||
__u8 padding2[8];
|
||||
} __packed;
|
||||
|
||||
/* struct ubi_fm_volhdr is followed by one struct ubi_fm_eba records */
|
||||
|
||||
/**
|
||||
* struct ubi_fm_eba - denotes an association beween a PEB and LEB
|
||||
* @magic: EBA table magic number
|
||||
* @reserved_pebs: number of table entries
|
||||
* @pnum: PEB number of LEB (LEB is the index)
|
||||
*/
|
||||
struct ubi_fm_eba {
|
||||
__be32 magic;
|
||||
__be32 reserved_pebs;
|
||||
__be32 pnum[0];
|
||||
} __packed;
|
||||
#endif /* !__UBI_MEDIA_H__ */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) International Business Machines Corp., 2006
|
||||
* Copyright © International Business Machines Corp., 2006
|
||||
*
|
||||
* 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
|
||||
|
@ -11,6 +11,9 @@
|
|||
* 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
|
||||
*
|
||||
* Author: Artem Bityutskiy (Битюцкий Артём)
|
||||
*/
|
||||
|
@ -18,6 +21,9 @@
|
|||
#ifndef __UBI_USER_H__
|
||||
#define __UBI_USER_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
/*
|
||||
* UBI device creation (the same as MTD device attachment)
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -37,30 +43,37 @@
|
|||
* UBI volume creation
|
||||
* ~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character
|
||||
* UBI volumes are created via the %UBI_IOCMKVOL ioctl command of UBI character
|
||||
* device. A &struct ubi_mkvol_req object has to be properly filled and a
|
||||
* pointer to it has to be passed to the IOCTL.
|
||||
* pointer to it has to be passed to the ioctl.
|
||||
*
|
||||
* UBI volume deletion
|
||||
* ~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character
|
||||
* To delete a volume, the %UBI_IOCRMVOL ioctl command of the UBI character
|
||||
* device should be used. A pointer to the 32-bit volume ID hast to be passed
|
||||
* to the IOCTL.
|
||||
* to the ioctl.
|
||||
*
|
||||
* UBI volume re-size
|
||||
* ~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character
|
||||
* To re-size a volume, the %UBI_IOCRSVOL ioctl command of the UBI character
|
||||
* device should be used. A &struct ubi_rsvol_req object has to be properly
|
||||
* filled and a pointer to it has to be passed to the IOCTL.
|
||||
* filled and a pointer to it has to be passed to the ioctl.
|
||||
*
|
||||
* UBI volumes re-name
|
||||
* ~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To re-name several volumes atomically at one go, the %UBI_IOCRNVOL command
|
||||
* of the UBI character device should be used. A &struct ubi_rnvol_req object
|
||||
* has to be properly filled and a pointer to it has to be passed to the ioctl.
|
||||
*
|
||||
* UBI volume update
|
||||
* ~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the
|
||||
* Volume update should be done via the %UBI_IOCVOLUP ioctl command of the
|
||||
* corresponding UBI volume character device. A pointer to a 64-bit update
|
||||
* size should be passed to the IOCTL. After this, UBI expects user to write
|
||||
* size should be passed to the ioctl. After this, UBI expects user to write
|
||||
* this number of bytes to the volume character device. The update is finished
|
||||
* when the claimed number of bytes is passed. So, the volume update sequence
|
||||
* is something like:
|
||||
|
@ -70,14 +83,58 @@
|
|||
* write(fd, buf, image_size);
|
||||
* close(fd);
|
||||
*
|
||||
* Atomic eraseblock change
|
||||
* Logical eraseblock erase
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL
|
||||
* command of the corresponding UBI volume character device. A pointer to
|
||||
* &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is
|
||||
* expected to write the requested amount of bytes. This is similar to the
|
||||
* "volume update" IOCTL.
|
||||
* To erase a logical eraseblock, the %UBI_IOCEBER ioctl command of the
|
||||
* corresponding UBI volume character device should be used. This command
|
||||
* unmaps the requested logical eraseblock, makes sure the corresponding
|
||||
* physical eraseblock is successfully erased, and returns.
|
||||
*
|
||||
* Atomic logical eraseblock change
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Atomic logical eraseblock change operation is called using the %UBI_IOCEBCH
|
||||
* ioctl command of the corresponding UBI volume character device. A pointer to
|
||||
* a &struct ubi_leb_change_req object has to be passed to the ioctl. Then the
|
||||
* user is expected to write the requested amount of bytes (similarly to what
|
||||
* should be done in case of the "volume update" ioctl).
|
||||
*
|
||||
* Logical eraseblock map
|
||||
* ~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To map a logical eraseblock to a physical eraseblock, the %UBI_IOCEBMAP
|
||||
* ioctl command should be used. A pointer to a &struct ubi_map_req object is
|
||||
* expected to be passed. The ioctl maps the requested logical eraseblock to
|
||||
* a physical eraseblock and returns. Only non-mapped logical eraseblocks can
|
||||
* be mapped. If the logical eraseblock specified in the request is already
|
||||
* mapped to a physical eraseblock, the ioctl fails and returns error.
|
||||
*
|
||||
* Logical eraseblock unmap
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To unmap a logical eraseblock to a physical eraseblock, the %UBI_IOCEBUNMAP
|
||||
* ioctl command should be used. The ioctl unmaps the logical eraseblocks,
|
||||
* schedules corresponding physical eraseblock for erasure, and returns. Unlike
|
||||
* the "LEB erase" command, it does not wait for the physical eraseblock being
|
||||
* erased. Note, the side effect of this is that if an unclean reboot happens
|
||||
* after the unmap ioctl returns, you may find the LEB mapped again to the same
|
||||
* physical eraseblock after the UBI is run again.
|
||||
*
|
||||
* Check if logical eraseblock is mapped
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To check if a logical eraseblock is mapped to a physical eraseblock, the
|
||||
* %UBI_IOCEBISMAP ioctl command should be used. It returns %0 if the LEB is
|
||||
* not mapped, and %1 if it is mapped.
|
||||
*
|
||||
* Set an UBI volume property
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* To set an UBI volume property the %UBI_IOCSETPROP ioctl command should be
|
||||
* used. A pointer to a &struct ubi_set_vol_prop_req object is expected to be
|
||||
* passed. The object describes which property should be set, and to which value
|
||||
* it should be set.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -91,56 +148,53 @@
|
|||
/* Maximum volume name length */
|
||||
#define UBI_MAX_VOLUME_NAME 127
|
||||
|
||||
/* IOCTL commands of UBI character devices */
|
||||
/* ioctl commands of UBI character devices */
|
||||
|
||||
#define UBI_IOC_MAGIC 'o'
|
||||
|
||||
/* Create an UBI volume */
|
||||
#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req)
|
||||
/* Remove an UBI volume */
|
||||
#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t)
|
||||
#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, __s32)
|
||||
/* Re-size an UBI volume */
|
||||
#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req)
|
||||
/* Re-name volumes */
|
||||
#define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req)
|
||||
|
||||
/* IOCTL commands of the UBI control character device */
|
||||
/* ioctl commands of the UBI control character device */
|
||||
|
||||
#define UBI_CTRL_IOC_MAGIC 'o'
|
||||
|
||||
/* Attach an MTD device */
|
||||
#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req)
|
||||
/* Detach an MTD device */
|
||||
#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t)
|
||||
#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, __s32)
|
||||
|
||||
/* IOCTL commands of UBI volume character devices */
|
||||
/* ioctl commands of UBI volume character devices */
|
||||
|
||||
#define UBI_VOL_IOC_MAGIC 'O'
|
||||
|
||||
/* Start UBI volume update */
|
||||
#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t)
|
||||
/* An eraseblock erasure command, used for debugging, disabled by default */
|
||||
#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t)
|
||||
/* An atomic eraseblock change command */
|
||||
#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t)
|
||||
#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, __s64)
|
||||
/* LEB erasure command, used for debugging, disabled by default */
|
||||
#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, __s32)
|
||||
/* Atomic LEB change command */
|
||||
#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, __s32)
|
||||
/* Map LEB command */
|
||||
#define UBI_IOCEBMAP _IOW(UBI_VOL_IOC_MAGIC, 3, struct ubi_map_req)
|
||||
/* Unmap LEB command */
|
||||
#define UBI_IOCEBUNMAP _IOW(UBI_VOL_IOC_MAGIC, 4, __s32)
|
||||
/* Check if LEB is mapped command */
|
||||
#define UBI_IOCEBISMAP _IOR(UBI_VOL_IOC_MAGIC, 5, __s32)
|
||||
/* Set an UBI volume property */
|
||||
#define UBI_IOCSETVOLPROP _IOW(UBI_VOL_IOC_MAGIC, 6, \
|
||||
struct ubi_set_vol_prop_req)
|
||||
|
||||
/* Maximum MTD device name length supported by UBI */
|
||||
#define MAX_UBI_MTD_NAME_LEN 127
|
||||
|
||||
/*
|
||||
* UBI data type hint constants.
|
||||
*
|
||||
* UBI_LONGTERM: long-term data
|
||||
* UBI_SHORTTERM: short-term data
|
||||
* UBI_UNKNOWN: data persistence is unknown
|
||||
*
|
||||
* These constants are used when data is written to UBI volumes in order to
|
||||
* help the UBI wear-leveling unit to find more appropriate physical
|
||||
* eraseblocks.
|
||||
*/
|
||||
enum {
|
||||
UBI_LONGTERM = 1,
|
||||
UBI_SHORTTERM = 2,
|
||||
UBI_UNKNOWN = 3,
|
||||
};
|
||||
/* Maximum amount of UBI volumes that can be re-named at one go */
|
||||
#define UBI_MAX_RNVOL 32
|
||||
|
||||
/*
|
||||
* UBI volume type constants.
|
||||
|
@ -153,6 +207,17 @@ enum {
|
|||
UBI_STATIC_VOLUME = 4,
|
||||
};
|
||||
|
||||
/*
|
||||
* UBI set volume property ioctl constants.
|
||||
*
|
||||
* @UBI_VOL_PROP_DIRECT_WRITE: allow (any non-zero value) or disallow (value 0)
|
||||
* user to directly write and erase individual
|
||||
* eraseblocks on dynamic volumes
|
||||
*/
|
||||
enum {
|
||||
UBI_VOL_PROP_DIRECT_WRITE = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubi_attach_req - attach MTD device request.
|
||||
* @ubi_num: UBI device number to create
|
||||
|
@ -173,20 +238,20 @@ enum {
|
|||
* it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages.
|
||||
*
|
||||
* But in rare cases, if this optimizes things, the VID header may be placed to
|
||||
* a different offset. For example, the boot-loader might do things faster if the
|
||||
* VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. As
|
||||
* the boot-loader would not normally need to read EC headers (unless it needs
|
||||
* UBI in RW mode), it might be faster to calculate ECC. This is weird example,
|
||||
* but it real-life example. So, in this example, @vid_hdr_offer would be
|
||||
* 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes
|
||||
* aligned, which is OK, as UBI is clever enough to realize this is 4th sub-page
|
||||
* of the first page and add needed padding.
|
||||
* a different offset. For example, the boot-loader might do things faster if
|
||||
* the VID header sits at the end of the first 2KiB NAND page with 4 sub-pages.
|
||||
* As the boot-loader would not normally need to read EC headers (unless it
|
||||
* needs UBI in RW mode), it might be faster to calculate ECC. This is weird
|
||||
* example, but it real-life example. So, in this example, @vid_hdr_offer would
|
||||
* be 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes
|
||||
* aligned, which is OK, as UBI is clever enough to realize this is 4th
|
||||
* sub-page of the first page and add needed padding.
|
||||
*/
|
||||
struct ubi_attach_req {
|
||||
int32_t ubi_num;
|
||||
int32_t mtd_num;
|
||||
int32_t vid_hdr_offset;
|
||||
uint8_t padding[12];
|
||||
__s32 ubi_num;
|
||||
__s32 mtd_num;
|
||||
__s32 vid_hdr_offset;
|
||||
__s8 padding[12];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -221,15 +286,15 @@ struct ubi_attach_req {
|
|||
* BLOBs, without caring about how to properly align them.
|
||||
*/
|
||||
struct ubi_mkvol_req {
|
||||
int32_t vol_id;
|
||||
int32_t alignment;
|
||||
int64_t bytes;
|
||||
int8_t vol_type;
|
||||
int8_t padding1;
|
||||
int16_t name_len;
|
||||
int8_t padding2[4];
|
||||
__s32 vol_id;
|
||||
__s32 alignment;
|
||||
__s64 bytes;
|
||||
__s8 vol_type;
|
||||
__s8 padding1;
|
||||
__s16 name_len;
|
||||
__s8 padding2[4];
|
||||
char name[UBI_MAX_VOLUME_NAME + 1];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_rsvol_req - a data structure used in volume re-size requests.
|
||||
|
@ -238,60 +303,109 @@ struct ubi_mkvol_req {
|
|||
*
|
||||
* Re-sizing is possible for both dynamic and static volumes. But while dynamic
|
||||
* volumes may be re-sized arbitrarily, static volumes cannot be made to be
|
||||
* smaller then the number of bytes they bear. To arbitrarily shrink a static
|
||||
* smaller than the number of bytes they bear. To arbitrarily shrink a static
|
||||
* volume, it must be wiped out first (by means of volume update operation with
|
||||
* zero number of bytes).
|
||||
*/
|
||||
struct ubi_rsvol_req {
|
||||
int64_t bytes;
|
||||
int32_t vol_id;
|
||||
} __attribute__ ((packed));
|
||||
__s64 bytes;
|
||||
__s32 vol_id;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_leb_change_req - a data structure used in atomic logical
|
||||
* eraseblock change requests.
|
||||
* struct ubi_rnvol_req - volumes re-name request.
|
||||
* @count: count of volumes to re-name
|
||||
* @padding1: reserved for future, not used, has to be zeroed
|
||||
* @vol_id: ID of the volume to re-name
|
||||
* @name_len: name length
|
||||
* @padding2: reserved for future, not used, has to be zeroed
|
||||
* @name: new volume name
|
||||
*
|
||||
* UBI allows to re-name up to %32 volumes at one go. The count of volumes to
|
||||
* re-name is specified in the @count field. The ID of the volumes to re-name
|
||||
* and the new names are specified in the @vol_id and @name fields.
|
||||
*
|
||||
* The UBI volume re-name operation is atomic, which means that should power cut
|
||||
* happen, the volumes will have either old name or new name. So the possible
|
||||
* use-cases of this command is atomic upgrade. Indeed, to upgrade, say, volumes
|
||||
* A and B one may create temporary volumes %A1 and %B1 with the new contents,
|
||||
* then atomically re-name A1->A and B1->B, in which case old %A and %B will
|
||||
* be removed.
|
||||
*
|
||||
* If it is not desirable to remove old A and B, the re-name request has to
|
||||
* contain 4 entries: A1->A, A->A1, B1->B, B->B1, in which case old A1 and B1
|
||||
* become A and B, and old A and B will become A1 and B1.
|
||||
*
|
||||
* It is also OK to request: A1->A, A1->X, B1->B, B->Y, in which case old A1
|
||||
* and B1 become A and B, and old A and B become X and Y.
|
||||
*
|
||||
* In other words, in case of re-naming into an existing volume name, the
|
||||
* existing volume is removed, unless it is re-named as well at the same
|
||||
* re-name request.
|
||||
*/
|
||||
struct ubi_rnvol_req {
|
||||
__s32 count;
|
||||
__s8 padding1[12];
|
||||
struct {
|
||||
__s32 vol_id;
|
||||
__s16 name_len;
|
||||
__s8 padding2[2];
|
||||
char name[UBI_MAX_VOLUME_NAME + 1];
|
||||
} ents[UBI_MAX_RNVOL];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubi_leb_change_req - a data structure used in atomic LEB change
|
||||
* requests.
|
||||
* @lnum: logical eraseblock number to change
|
||||
* @bytes: how many bytes will be written to the logical eraseblock
|
||||
* @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN)
|
||||
* @dtype: pass "3" for better compatibility with old kernels
|
||||
* @padding: reserved for future, not used, has to be zeroed
|
||||
*
|
||||
* The @dtype field used to inform UBI about what kind of data will be written
|
||||
* to the LEB: long term (value 1), short term (value 2), unknown (value 3).
|
||||
* UBI tried to pick a PEB with lower erase counter for short term data and a
|
||||
* PEB with higher erase counter for long term data. But this was not really
|
||||
* used because users usually do not know this and could easily mislead UBI. We
|
||||
* removed this feature in May 2012. UBI currently just ignores the @dtype
|
||||
* field. But for better compatibility with older kernels it is recommended to
|
||||
* set @dtype to 3 (unknown).
|
||||
*/
|
||||
struct ubi_leb_change_req {
|
||||
int32_t lnum;
|
||||
int32_t bytes;
|
||||
uint8_t dtype;
|
||||
uint8_t padding[7];
|
||||
} __attribute__ ((packed));
|
||||
__s32 lnum;
|
||||
__s32 bytes;
|
||||
__s8 dtype; /* obsolete, do not use! */
|
||||
__s8 padding[7];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* ubi_attach_mtd_dev - attach an MTD device.
|
||||
* @mtd_dev: MTD device description object
|
||||
* @ubi_num: number to assign to the new UBI device
|
||||
* @vid_hdr_offset: VID header offset
|
||||
*
|
||||
* This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number
|
||||
* to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in
|
||||
* which case this function finds a vacant device nubert and assings it
|
||||
* automatically. Returns the new UBI device number in case of success and a
|
||||
* negative error code in case of failure.
|
||||
*
|
||||
* This of course is originally not exported but is now part of the UBI
|
||||
* interface to barebox.
|
||||
* struct ubi_map_req - a data structure used in map LEB requests.
|
||||
* @dtype: pass "3" for better compatibility with old kernels
|
||||
* @lnum: logical eraseblock number to unmap
|
||||
* @padding: reserved for future, not used, has to be zeroed
|
||||
*/
|
||||
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset);
|
||||
struct ubi_map_req {
|
||||
__s32 lnum;
|
||||
__s8 dtype; /* obsolete, do not use! */
|
||||
__s8 padding[3];
|
||||
} __packed;
|
||||
|
||||
|
||||
/**
|
||||
* ubi_detach_mtd_dev - detach an MTD device.
|
||||
* @ubi_num: UBI device number to detach from
|
||||
* @anyway: detach MTD even if device reference count is not zero
|
||||
*
|
||||
* This function destroys an UBI device number @ubi_num and detaches the
|
||||
* underlying MTD device. Returns zero in case of success and %-EBUSY if the
|
||||
* UBI device is busy and cannot be destroyed, and %-EINVAL if it does not
|
||||
* exist.
|
||||
*
|
||||
* This of course is originally not exported but is now part of the UBI
|
||||
* interface to barebox.
|
||||
* struct ubi_set_vol_prop_req - a data structure used to set an UBI volume
|
||||
* property.
|
||||
* @property: property to set (%UBI_VOL_PROP_DIRECT_WRITE)
|
||||
* @padding: reserved for future, not used, has to be zeroed
|
||||
* @value: value to set
|
||||
*/
|
||||
int ubi_detach_mtd_dev(struct mtd_info *mtd, int anyway);
|
||||
struct ubi_set_vol_prop_req {
|
||||
__u8 property;
|
||||
__u8 padding[7];
|
||||
__u64 value;
|
||||
} __packed;
|
||||
|
||||
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
|
||||
int vid_hdr_offset, int max_beb_per1024);
|
||||
int ubi_detach_mtd_dev(int ubi_num, int anyway);
|
||||
|
||||
#endif /* __UBI_USER_H__ */
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
|
||||
*
|
||||
* OF helpers for mtd.
|
||||
*
|
||||
* This file is released under the GPLv2
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_OF_MTD_H
|
||||
#define __LINUX_OF_MTD_H
|
||||
|
||||
#include <of.h>
|
||||
|
||||
int of_get_nand_ecc_mode(struct device_node *np);
|
||||
int of_get_nand_bus_width(struct device_node *np);
|
||||
bool of_get_nand_on_flash_bbt(struct device_node *np);
|
||||
|
||||
#endif /* __LINUX_OF_MTD_H */
|
|
@ -10,7 +10,15 @@ unsigned int rand(void);
|
|||
void srand(unsigned int seed);
|
||||
|
||||
/* fill a buffer with pseudo-random data */
|
||||
void get_random_bytes(char *buf, int len);
|
||||
void get_random_bytes(void *buf, int len);
|
||||
|
||||
static inline u32 random32(void)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
get_random_bytes(&ret, 4);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* __STDLIB_H */
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* basic functions for devices following the "stmp" style register layout
|
||||
*
|
||||
* Copyright (C) 2011 Wolfram Sang, Pengutronix e.K.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __STMP_DEVICE_H__
|
||||
#define __STMP_DEVICE_H__
|
||||
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#define STMP_OFFSET_REG_SET 0x4
|
||||
#define STMP_OFFSET_REG_CLR 0x8
|
||||
#define STMP_OFFSET_REG_TOG 0xc
|
||||
|
||||
extern int stmp_reset_block(void __iomem *, int just_enable);
|
||||
#endif /* __STMP_DEVICE_H__ */
|
|
@ -47,6 +47,9 @@ config LIBUBIGEN
|
|||
config LIBMTD
|
||||
bool
|
||||
|
||||
config STMP_DEVICE
|
||||
bool
|
||||
|
||||
source lib/gui/Kconfig
|
||||
|
||||
source lib/bootstrap/Kconfig
|
||||
|
|
|
@ -43,3 +43,4 @@ obj-$(CONFIG_LIBMTD) += libmtd.o
|
|||
obj-y += gui/
|
||||
obj-$(CONFIG_XYMODEM) += xymodem.o
|
||||
obj-y += unlink-recursive.o
|
||||
obj-$(CONFIG_STMP_DEVICE) += stmp-device.o
|
||||
|
|
|
@ -18,8 +18,10 @@ void srand(unsigned int seed)
|
|||
random_seed = seed;
|
||||
}
|
||||
|
||||
void get_random_bytes(char *buf, int len)
|
||||
void get_random_bytes(void *_buf, int len)
|
||||
{
|
||||
char *buf = _buf;
|
||||
|
||||
while (len--)
|
||||
*buf++ = rand() % 256;
|
||||
}
|
||||
|
|
|
@ -14,46 +14,45 @@
|
|||
|
||||
#include <common.h>
|
||||
#include <io.h>
|
||||
#include <stmp-device.h>
|
||||
#include <errno.h>
|
||||
#include <clock.h>
|
||||
#include <mach/mxs.h>
|
||||
#include <mach/imx-regs.h>
|
||||
|
||||
#define MXS_IP_RESET_TIMEOUT (10 * MSECOND)
|
||||
#define STMP_IP_RESET_TIMEOUT (10 * MSECOND)
|
||||
|
||||
#define MXS_BLOCK_SFTRST (1 << 31)
|
||||
#define MXS_BLOCK_CLKGATE (1 << 30)
|
||||
#define STMP_BLOCK_SFTRST (1 << 31)
|
||||
#define STMP_BLOCK_CLKGATE (1 << 30)
|
||||
|
||||
int mxs_reset_block(void __iomem *reg, int just_enable)
|
||||
int stmp_reset_block(void __iomem *reg, int just_enable)
|
||||
{
|
||||
/* Clear SFTRST */
|
||||
writel(MXS_BLOCK_SFTRST, reg + BIT_CLR);
|
||||
writel(STMP_BLOCK_SFTRST, reg + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (wait_on_timeout(MXS_IP_RESET_TIMEOUT, !(readl(reg) & MXS_BLOCK_SFTRST)))
|
||||
if (wait_on_timeout(STMP_IP_RESET_TIMEOUT, !(readl(reg) & STMP_BLOCK_SFTRST)))
|
||||
goto timeout;
|
||||
|
||||
/* Clear CLKGATE */
|
||||
writel(MXS_BLOCK_CLKGATE, reg + BIT_CLR);
|
||||
writel(STMP_BLOCK_CLKGATE, reg + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (!just_enable) {
|
||||
/* Set SFTRST */
|
||||
writel(MXS_BLOCK_SFTRST, reg + BIT_SET);
|
||||
writel(STMP_BLOCK_SFTRST, reg + STMP_OFFSET_REG_SET);
|
||||
|
||||
/* Wait for CLKGATE being set */
|
||||
if (wait_on_timeout(MXS_IP_RESET_TIMEOUT, readl(reg) & MXS_BLOCK_CLKGATE))
|
||||
if (wait_on_timeout(STMP_IP_RESET_TIMEOUT, readl(reg) & STMP_BLOCK_CLKGATE))
|
||||
goto timeout;
|
||||
}
|
||||
|
||||
/* Clear SFTRST */
|
||||
writel(MXS_BLOCK_SFTRST, reg + BIT_CLR);
|
||||
writel(STMP_BLOCK_SFTRST, reg + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (wait_on_timeout(MXS_IP_RESET_TIMEOUT, !(readl(reg) & MXS_BLOCK_SFTRST)))
|
||||
if (wait_on_timeout(STMP_IP_RESET_TIMEOUT, !(readl(reg) & STMP_BLOCK_SFTRST)))
|
||||
goto timeout;
|
||||
|
||||
/* Clear CLKGATE */
|
||||
writel(MXS_BLOCK_CLKGATE, reg + BIT_CLR);
|
||||
writel(STMP_BLOCK_CLKGATE, reg + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (wait_on_timeout(MXS_IP_RESET_TIMEOUT, !(readl(reg) & MXS_BLOCK_CLKGATE)))
|
||||
if (wait_on_timeout(STMP_IP_RESET_TIMEOUT, !(readl(reg) & STMP_BLOCK_CLKGATE)))
|
||||
goto timeout;
|
||||
|
||||
return 0;
|
59
lib/string.c
59
lib/string.c
|
@ -666,3 +666,62 @@ char *strim(char *s)
|
|||
return s;
|
||||
}
|
||||
EXPORT_SYMBOL(strim);
|
||||
|
||||
static void *check_bytes8(const u8 *start, u8 value, unsigned int bytes)
|
||||
{
|
||||
while (bytes) {
|
||||
if (*start != value)
|
||||
return (void *)start;
|
||||
start++;
|
||||
bytes--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* memchr_inv - Find an unmatching character in an area of memory.
|
||||
* @start: The memory area
|
||||
* @c: Find a character other than c
|
||||
* @bytes: The size of the area.
|
||||
*
|
||||
* returns the address of the first character other than @c, or %NULL
|
||||
* if the whole buffer contains just @c.
|
||||
*/
|
||||
void *memchr_inv(const void *start, int c, size_t bytes)
|
||||
{
|
||||
u8 value = c;
|
||||
u64 value64;
|
||||
unsigned int words, prefix;
|
||||
|
||||
if (bytes <= 16)
|
||||
return check_bytes8(start, value, bytes);
|
||||
|
||||
value64 = value;
|
||||
value64 |= value64 << 8;
|
||||
value64 |= value64 << 16;
|
||||
value64 |= value64 << 32;
|
||||
|
||||
prefix = (unsigned long)start % 8;
|
||||
if (prefix) {
|
||||
u8 *r;
|
||||
|
||||
prefix = 8 - prefix;
|
||||
r = check_bytes8(start, value, prefix);
|
||||
if (r)
|
||||
return r;
|
||||
start += prefix;
|
||||
bytes -= prefix;
|
||||
}
|
||||
|
||||
words = bytes / 8;
|
||||
|
||||
while (words) {
|
||||
if (*(u64 *)start != value64)
|
||||
return check_bytes8(start, value, 8);
|
||||
start += 8;
|
||||
words--;
|
||||
}
|
||||
|
||||
return check_bytes8(start, value, bytes % 8);
|
||||
}
|
||||
EXPORT_SYMBOL(memchr_inv);
|
||||
|
|
Loading…
Reference in New Issue