[U-Boot] [PATCH] mflash u-boot support

unsik Kim donari75 at gmail.com
Mon Feb 2 09:32:52 CET 2009


Hello?

I fixed and added followings for your requests.

1. too long line length => fixed

2. not a linux coding style => fixed

3. add document (doc/README.mflash)

4. ARM only dependency and always init problem => fixed

5. msecs_to_hz function is changed.
In some ARM platform, CONFIG_SYS_HZ is not 1000 (samsung s3c24xx,
intel pxa25x, pxa27x)
and get_timer() just return elapsed tick of OS timer.
For the compatibility of these, I use #ifdef.

Any comments, advice will be appreciated.

Here is fixed patch.

unsik Kim

Signed-off-by: unsik Kim <donari75 at gmail.com>
---
 common/Makefile             |    2 +
 common/cmd_mgdisk.c         |   76 ++++++
 common/cmd_nvedit.c         |    8 +-
 common/env_mgdisk.c         |   90 ++++++
 disk/part.c                 |    8 +-
 disk/part_amiga.c           |    5 +-
 disk/part_dos.c             |    1 +
 disk/part_efi.c             |    1 +
 disk/part_iso.c             |    1 +
 disk/part_mac.c             |    1 +
 doc/README.mflash           |   93 +++++++
 drivers/block/Makefile      |    5 +-
 drivers/block/mg_disk.c     |  629 +++++++++++++++++++++++++++++++++++++++++++
 drivers/block/mg_disk_prv.h |  140 ++++++++++
 fs/fat/fat.c                |    2 +
 include/config_cmd_all.h    |    1 +
 include/environment.h       |   12 +
 include/mg_disk.h           |   51 ++++
 include/part.h              |    1 +
 19 files changed, 1119 insertions(+), 8 deletions(-)

diff --git a/common/Makefile b/common/Makefile
index 93e3963..3a85c2a 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -53,6 +53,7 @@ COBJS-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o
 COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o
 COBJS-y += env_embedded.o
 COBJS-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o
+COBJS-$(CONFIG_ENV_IS_IN_MG_DISK) += env_mgdisk.o
 COBJS-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o
 COBJS-$(CONFIG_ENV_IS_IN_NVRAM) += env_nvram.o
 COBJS-$(CONFIG_ENV_IS_IN_ONENAND) += env_onenand.o
@@ -104,6 +105,7 @@ COBJS-$(CONFIG_LOGBUFFER) += cmd_log.o
 COBJS-$(CONFIG_ID_EEPROM) += cmd_mac.o
 COBJS-$(CONFIG_CMD_MEMORY) += cmd_mem.o
 COBJS-$(CONFIG_CMD_MFSL) += cmd_mfsl.o
+COBJS-$(CONFIG_CMD_MG_DISK) += cmd_mgdisk.o
 COBJS-$(CONFIG_MII) += miiphyutil.o
 COBJS-$(CONFIG_CMD_MII) += miiphyutil.o
 COBJS-$(CONFIG_CMD_MII) += cmd_mii.o
diff --git a/common/cmd_mgdisk.c b/common/cmd_mgdisk.c
new file mode 100644
index 0000000..f2f5061
--- /dev/null
+++ b/common/cmd_mgdisk.c
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 2009 mGine co.
+ * unsik Kim <donari75 at gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <command.h>
+
+#if defined (CONFIG_CMD_MG_DISK)
+
+#include <mg_disk.h>
+
+int do_mg_disk_cmd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	u32 from, to, size;
+
+	switch (argc) {
+	case 2:
+		if (!strcmp(argv[1], "init"))
+			mg_disk_init();
+		else
+			return 1;
+		break;
+	case 4:
+		from = simple_strtoul(argv[2], NULL, 0);
+		to = simple_strtoul(argv[3], NULL, 0);
+		size = simple_strtoul(argv[4], NULL, 0);
+
+		if (!strcmp(argv[1], "read"))
+			mg_disk_read(from, (u8 *)to, size);
+		else if (!strcmp(argv[1], "write"))
+			mg_disk_write(to, (u8 *)from, size);
+		else if (!strcmp(argv[1], "readsec"))
+			mg_disk_read_sects((void *)to, from, size);
+		else if (!strcmp(argv[1], "writesec"))
+			mg_disk_write_sects((void *)from, to, size);
+		else
+			return 1;
+		break;
+	default:
+		printf("Usage:\n%s\n", cmdtp->usage);
+		return 1;
+	}
+	return 0;
+}
+
+U_BOOT_CMD(
+	mgd,	5,	0,	do_mg_disk_cmd,
+	"mgd     - mgine m[g]flash command\n",
+	": mgine mflash IO mode (disk) command\n"
+	"    - initialize : mgd init\n"
+	"    - random read : mgd read [from] [to] [size]\n"
+	"    - random write : mgd write [from] [to] [size]\n"
+	"    - sector read : mgd readsec [sector] [to] [counts]\n"
+	"    - sector write : mgd writesec [from] [sector] [counts]\n"
+);
+
+#endif
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c
index 1fcb4c9..649b23d 100644
--- a/common/cmd_nvedit.c
+++ b/common/cmd_nvedit.c
@@ -52,15 +52,17 @@

 DECLARE_GLOBAL_DATA_PTR;

-#if !defined(CONFIG_ENV_IS_IN_NVRAM)	&& \
-    !defined(CONFIG_ENV_IS_IN_EEPROM)	&& \
+#if !defined(CONFIG_ENV_IS_IN_EEPROM)	&& \
     !defined(CONFIG_ENV_IS_IN_FLASH)	&& \
     !defined(CONFIG_ENV_IS_IN_DATAFLASH)	&& \
+    !defined(CONFIG_ENV_IS_IN_MG_DISK)	&& \
     !defined(CONFIG_ENV_IS_IN_NAND)	&& \
+    !defined(CONFIG_ENV_IS_IN_NVRAM)	&& \
     !defined(CONFIG_ENV_IS_IN_ONENAND)	&& \
     !defined(CONFIG_ENV_IS_IN_SPI_FLASH)	&& \
     !defined(CONFIG_ENV_IS_NOWHERE)
-# error Define one of
CONFIG_ENV_IS_IN_{NVRAM|EEPROM|FLASH|DATAFLASH|ONENAND|SPI_FLASH|NOWHERE}
+# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|DATAFLASH|ONENAND|\
+SPI_FLASH|MG_DISK|NVRAM|NOWHERE}
 #endif

 #define XMK_STR(x)	#x
diff --git a/common/env_mgdisk.c b/common/env_mgdisk.c
new file mode 100644
index 0000000..2b4949f
--- /dev/null
+++ b/common/env_mgdisk.c
@@ -0,0 +1,90 @@
+/*
+ * (C) Copyright 2009 mGine co.
+ * unsik Kim <donari75 at gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <mg_disk.h>
+
+/* references to names in env_common.c */
+extern uchar default_environment[];
+extern int default_environment_size;
+
+char * env_name_spec = "MG_DISK";
+
+env_t *env_ptr = 0;
+
+DECLARE_GLOBAL_DATA_PTR;
+
+uchar env_get_char_spec(int index)
+{
+	return (*((uchar *) (gd->env_addr + index)));
+}
+
+void env_relocate_spec(void)
+{
+	unsigned int err;
+
+	err = mg_disk_init();
+	if (err) {
+		puts ("*** Warning - mg_disk_init error");
+		goto OUT;
+	}
+	err = mg_disk_read(CONFIG_ENV_ADDR, (u_char *)env_ptr, CONFIG_ENV_SIZE);
+	if (err) {
+		puts ("*** Warning - mg_disk_read error");
+		goto OUT;
+	}
+
+	if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) {
+		puts ("*** Warning - CRC error");
+		goto OUT;
+	}
+
+	return;
+
+OUT:
+	printf (", using default environment\n\n");
+	set_default_env();
+}
+
+int saveenv(void)
+{
+	unsigned int err;
+	env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE);
+	err = mg_disk_write(CONFIG_ENV_ADDR, (u_char *)env_ptr,
+			CONFIG_ENV_SIZE);
+	if (err)
+		puts ("*** Warning - mg_disk_write error\n\n");
+	return err;
+}
+
+int env_init(void)
+{
+	/* use default */
+	gd->env_addr = (ulong) & default_environment[0];
+	gd->env_valid = 1;
+
+	return 0;
+}
diff --git a/disk/part.c b/disk/part.c
index e353cee..fe299c7 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -35,6 +35,7 @@
 #endif

 #if (defined(CONFIG_CMD_IDE) || \
+     defined(CONFIG_CMD_MG_DISK) || \
      defined(CONFIG_CMD_SATA) || \
      defined(CONFIG_CMD_SCSI) || \
      defined(CONFIG_CMD_USB) || \
@@ -65,6 +66,9 @@ static const struct block_drvr block_drvr[] = {
 #if defined(CONFIG_SYSTEMACE)
 	{ .name = "ace", .get_dev = systemace_get_dev, },
 #endif
+#if defined(CONFIG_CMD_MG_DISK)
+	{ .name = "mgd", .get_dev = mg_disk_get_dev, },
+#endif
 	{ },
 };

@@ -91,6 +95,7 @@ block_dev_desc_t *get_dev(char* ifname, int dev)
 #endif

 #if (defined(CONFIG_CMD_IDE) || \
+     defined(CONFIG_CMD_MG_DISK) || \
      defined(CONFIG_CMD_SATA) || \
      defined(CONFIG_CMD_SCSI) || \
      defined(CONFIG_CMD_USB) || \
@@ -203,11 +208,12 @@ void dev_print (block_dev_desc_t *dev_desc)
 #endif

 #if (defined(CONFIG_CMD_IDE) || \
+     defined(CONFIG_CMD_MG_DISK) || \
      defined(CONFIG_CMD_SATA) || \
      defined(CONFIG_CMD_SCSI) || \
      defined(CONFIG_CMD_USB) || \
      defined(CONFIG_MMC)		|| \
-     defined(CONFIG_SYSTEMACE)          )
+     defined(CONFIG_SYSTEMACE) )

 #if defined(CONFIG_MAC_PARTITION) || \
     defined(CONFIG_DOS_PARTITION) || \
diff --git a/disk/part_amiga.c b/disk/part_amiga.c
index 6c3d748..6f25173 100644
--- a/disk/part_amiga.c
+++ b/disk/part_amiga.c
@@ -27,6 +27,7 @@
 #include "part_amiga.h"

 #if (defined(CONFIG_CMD_IDE) || \
+     defined(CONFIG_CMD_MG_DISK) || \
      defined(CONFIG_CMD_SCSI) || \
      defined(CONFIG_CMD_USB) || \
      defined(CONFIG_MMC) || \
@@ -154,7 +155,7 @@ struct rigid_disk_block
*get_rdisk(block_dev_desc_t *dev_desc)

     s = getenv("amiga_scanlimit");
     if (s)
-	limit = atoi(s);
+	limit = simple_strtoul(s, NULL, 10);
     else
 	limit = AMIGA_BLOCK_LIMIT;

@@ -195,7 +196,7 @@ struct bootcode_block
*get_bootcode(block_dev_desc_t *dev_desc)

     s = getenv("amiga_scanlimit");
     if (s)
-	limit = atoi(s);
+	limit = simple_strtoul(s, NULL, 10);
     else
 	limit = AMIGA_BLOCK_LIMIT;

diff --git a/disk/part_dos.c b/disk/part_dos.c
index 4d778ec..845cdb6 100644
--- a/disk/part_dos.c
+++ b/disk/part_dos.c
@@ -36,6 +36,7 @@
 #include "part_dos.h"

 #if (defined(CONFIG_CMD_IDE) || \
+     defined(CONFIG_CMD_MG_DISK) || \
      defined(CONFIG_CMD_SATA) || \
      defined(CONFIG_CMD_SCSI) || \
      defined(CONFIG_CMD_USB) || \
diff --git a/disk/part_efi.c b/disk/part_efi.c
index d8a8111..4cf79fb 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -37,6 +37,7 @@
 #include "part_efi.h"

 #if (defined(CONFIG_CMD_IDE) || \
+     defined(CONFIG_CMD_MG_DISK) || \
      defined(CONFIG_CMD_SATA) || \
      defined(CONFIG_CMD_SCSI) || \
      defined(CONFIG_CMD_USB) || \
diff --git a/disk/part_iso.c b/disk/part_iso.c
index 72ff868..8348ce8 100644
--- a/disk/part_iso.c
+++ b/disk/part_iso.c
@@ -26,6 +26,7 @@
 #include "part_iso.h"

 #if (defined(CONFIG_CMD_IDE) || \
+     defined(CONFIG_CMD_MG_DISK) || \
      defined(CONFIG_CMD_SCSI) || \
      defined(CONFIG_CMD_SATA) || \
      defined(CONFIG_CMD_USB) || \
diff --git a/disk/part_mac.c b/disk/part_mac.c
index 1922fe5..fce4cc7 100644
--- a/disk/part_mac.c
+++ b/disk/part_mac.c
@@ -35,6 +35,7 @@
 #include "part_mac.h"

 #if (defined(CONFIG_CMD_IDE) || \
+     defined(CONFIG_CMD_MG_DISK) || \
      defined(CONFIG_CMD_SCSI) || \
      defined(CONFIG_CMD_SATA) || \
      defined(CONFIG_CMD_USB) || \
diff --git a/doc/README.mflash b/doc/README.mflash
new file mode 100644
index 0000000..fb74b90
--- /dev/null
+++ b/doc/README.mflash
@@ -0,0 +1,93 @@
+
+This document describes m[g]flash support in u-boot.
+
+Contents
+  1. Overview
+  2. Porting mflash driver
+  3. Mflash command
+  4. Misc.
+
+1. Overview
+Mflash and gflash are embedded flash drive. The only difference is mflash is
+MCP(Multi Chip Package) device. These two device operate exactly same way.
+So the rest mflash repersents mflash and gflash altogether.
+
+2. Porting mflash driver
+
+2-1. Board configuration
+* Mflash driver support
+#define CONFIG_CMD_MG_DISK
+
+* Environment variable support (optional)
+#define CONFIG_ENV_IS_IN_MG_DISK
+Also CONFIG_ENV_ADDR and CONFIG_ENV_SIZE should be defined.
+CONFIG_ENV_ADDR is byte offset starting from 0.
+
+Following example sets environment variable location to 0x80000 (1024'th
+sector) and size of 0x400 (1024 byte)
+#define CONFIG_ENV_ADDR		0x80000
+#define CONFIG_ENV_SIZE		0x400
+
+* Reserved size config (optional)
+If you want to use some reserved area for bootloader, environment variable or
+whatever, use CONFIG_MG_DISK_RES. The value should be multiple of
+MG_SECTOR_SIZE (512Byte). Mflash's block operation method use this value as
+start offset. So any u-boot's partition table parser and file system command
+work consistently. You can access this area by using mflash command.
+
+Following example sets 10MB of reserved area.
+#define CONFIG_MG_DISK_RES	10485760
+
+2-2. Porting mg_get_drv_data function
+Mflash is active device and need some gpio control for proper operation.
+This board dependency resolved by using mg_get_drv_data function.
+Port this function at your board init file. See include/mg_disk.h
+
+Here is some pseudo example.
+
+static void custom_hdrst_pin (u8 level)
+{
+	if (level)
+		/* set hard reset pin to high */
+	else
+		/* set hard reset pin to low */
+}
+
+static void custom_ctrl_pin_init (void)
+{
+	/* Set hard reset, write protect, deep power down pins
+	 * to gpio.
+	 * Set these pins to output high
+	 */
+}
+
+struct mg_drv_data* mg_get_drv_data (void)
+{
+	static struct mg_drv_data prv;
+
+	prv.base = /* base address of mflash */
+	prv.mg_ctrl_pin_init = custom_ctrl_pin_init;
+	prv.mg_hdrst_pin = custom_hdrst_pin;
+
+	return &prv;
+}
+
+3. Mflash command
+
+* initialize : mgd init
+* random read : mgd read [from] [to] [size]
+  ex) read 256 bytes from 0x300000 of mflash to 0xA0100000 of host memory
+      mgd read 0x300000 0xA0100000 256
+* random write : mgd write [from] [to] [size]
+* sector read : mgd readsec [sector] [to] [count]
+  ex) read 10 sectors starts from 400 sector to 0xA0100000
+      mgd readsec 400 0xA0100000 10
+* sector write : mgd writesec [from] [sector] [count]
+
+4. Misc.
+Mflash's device interface name for block driver is "mgd".
+Here is ext2 file system access example.
+
+ mgd init
+ ext2ls mgd 0:1 /boot
+ ext2load mgd 0:1 0xa0010000 /boot/uImage 1954156
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 642582b..32e134c 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -25,12 +25,13 @@ include $(TOPDIR)/config.mk

 LIB	:= $(obj)libblock.a

-COBJS-$(CONFIG_SCSI_AHCI) += ahci.o
 COBJS-$(CONFIG_ATA_PIIX) += ata_piix.o
+COBJS-$(CONFIG_CMD_MG_DISK) += mg_disk.o
 COBJS-$(CONFIG_FSL_SATA) += fsl_sata.o
+COBJS-$(CONFIG_IDE_SIL680) += sil680.o
 COBJS-$(CONFIG_LIBATA) += libata.o
 COBJS-$(CONFIG_SATA_SIL3114) += sata_sil3114.o
-COBJS-$(CONFIG_IDE_SIL680) += sil680.o
+COBJS-$(CONFIG_SCSI_AHCI) += ahci.o
 COBJS-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o
 COBJS-$(CONFIG_SYSTEMACE) += systemace.o

diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c
new file mode 100644
index 0000000..4454fca
--- /dev/null
+++ b/drivers/block/mg_disk.c
@@ -0,0 +1,629 @@
+/*
+ * (C) Copyright 2009 mGine co.
+ * unsik Kim <donari75 at gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <part.h>
+#include <ata.h>
+#include <asm/io.h>
+#include "mg_disk_prv.h"
+
+#ifdef CONFIG_CMD_MG_DISK
+
+#ifndef CONFIG_MG_DISK_RES
+#define CONFIG_MG_DISK_RES	0
+#endif
+
+#if (CONFIG_MG_DISK_RES % MG_SECTOR_SIZE != 0)
+#error "CONFIG_MG_DISK_RES should be MG_SECTOR_SIZE algined"
+#endif
+
+#define MG_DISK_RES ((CONFIG_MG_DISK_RES) / MG_SECTOR_SIZE)
+
+#define MG_BASE	(host.drv_data->base)
+
+static struct mg_host host;
+
+static block_dev_desc_t mg_disk_dev = {
+	.if_type = IF_TYPE_ATAPI,
+	.part_type = PART_TYPE_UNKNOWN,
+	.type = DEV_TYPE_HARDDISK,
+	.blksz = MG_SECTOR_SIZE,
+	.priv = &host };
+
+static void mg_dump_status (const char *msg, unsigned int stat, unsigned err)
+{
+	char *name = MG_DEV_NAME;
+
+	printf("%s: %s: status=0x%02x { ", name, msg, stat & 0xff);
+	if (stat & MG_REG_STATUS_BIT_BUSY)
+		printf("Busy ");
+	if (stat & MG_REG_STATUS_BIT_READY)
+		printf("DriveReady ");
+	if (stat & MG_REG_STATUS_BIT_WRITE_FAULT)
+		printf("WriteFault ");
+	if (stat & MG_REG_STATUS_BIT_SEEK_DONE)
+		printf("SeekComplete ");
+	if (stat & MG_REG_STATUS_BIT_DATA_REQ)
+		printf("DataRequest ");
+	if (stat & MG_REG_STATUS_BIT_CORRECTED_ERROR)
+		printf("CorrectedError ");
+	if (stat & MG_REG_STATUS_BIT_ERROR)
+		printf("Error ");
+	printf("}\n");
+
+	if ((stat & MG_REG_STATUS_BIT_ERROR)) {
+		printf("%s: %s: error=0x%02x { ", name, msg, err & 0xff);
+		if (err & MG_REG_ERR_BBK)
+			printf("BadSector ");
+		if (err & MG_REG_ERR_UNC)
+			printf("UncorrectableError ");
+		if (err & MG_REG_ERR_IDNF)
+			printf("SectorIdNotFound ");
+		if (err & MG_REG_ERR_ABRT)
+			printf("DriveStatusError ");
+		if (err & MG_REG_ERR_AMNF)
+			printf("AddrMarkNotFound ");
+		printf("}\n");
+	}
+}
+
+#if CONFIG_SYS_HZ == 1000
+#define msecs_to_hz(s) (s)
+#else
+static unsigned int msecs_to_hz (u32 msec)
+{
+	u32 hz = CONFIG_SYS_HZ / 1000 * msec;
+
+	if (!hz)
+		hz = 1;
+
+	return hz;
+}
+#endif
+
+/*
+ * copy src to dest, skipping leading and trailing blanks and null
+ * terminate the string
+ * "len" is the size of available memory including the terminating '\0'
+ */
+static void mg_ident_cpy (unsigned char *dst, unsigned char *src,
+		unsigned int len)
+{
+	unsigned char *end, *last;
+
+	last = dst;
+	end  = src + len - 1;
+
+	/* reserve space for '\0' */
+	if (len < 2)
+		goto OUT;
+
+	/* skip leading white space */
+	while ((*src) && (src<end) && (*src==' '))
+		++src;
+
+	/* copy string, omitting trailing white space */
+	while ((*src) && (src<end)) {
+		*dst++ = *src;
+		if (*src++ != ' ')
+			last = dst;
+	}
+OUT:
+	*last = '\0';
+}
+
+static unsigned int mg_wait (u32 expect, u32 msec)
+{
+	u8 status;
+	u32 from, cur, expire, err;
+
+	err = MG_ERR_NONE;
+	reset_timer();
+	from = get_timer(0);
+	expire = msecs_to_hz(msec);
+
+	status = readb(MG_BASE + MG_REG_STATUS);
+	do {
+		cur = get_timer(from);
+		if (status & MG_REG_STATUS_BIT_BUSY) {
+			if (expect == MG_REG_STATUS_BIT_BUSY)
+				break;
+		} else {
+			/* Check the error condition! */
+			if (status & MG_REG_STATUS_BIT_ERROR) {
+				err = readb(MG_BASE + MG_REG_ERROR);
+				mg_dump_status("mg_wait", status, err);
+				break;
+			}
+
+			if (expect == MG_STAT_READY)
+				if (MG_READY_OK(status))
+					break;
+
+			if (expect == MG_REG_STATUS_BIT_DATA_REQ)
+				if (status & MG_REG_STATUS_BIT_DATA_REQ)
+					break;
+		}
+		status = readb(MG_BASE + MG_REG_STATUS);
+	} while (cur < expire);
+
+	if (cur >= expire)
+		err = MG_ERR_TIMEOUT;
+
+	return err;
+}
+
+static int mg_get_disk_id (void)
+{
+	u32 iobuf[(MG_SECTOR_SIZE / sizeof(u32))];
+	hd_driveid_t *iop = (hd_driveid_t *)iobuf;
+	u32 i, err;
+	u16 *buff = (u16 *)iobuf;
+
+	writeb(MG_CMD_ID, MG_BASE + MG_REG_COMMAND);
+	err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000);
+	if (err)
+		return err;
+
+	for(i = 0; i < (MG_SECTOR_SIZE / sizeof(u32)) >> 1; i++)
+		buff[i] = readw(MG_BASE + MG_BUFF_OFFSET + i * 2);
+
+	writeb(MG_CMD_RD_CONF, MG_BASE + MG_REG_COMMAND);
+	err = mg_wait(MG_STAT_READY, 3000);
+	if (err)
+		return err;
+
+	if((iop->field_valid & 1) == 0)
+		return MG_ERR_TRANSLATION;
+
+	mg_ident_cpy((unsigned char*)mg_disk_dev.revision, iop->fw_rev,
+			sizeof(mg_disk_dev.revision));
+	mg_ident_cpy((unsigned char*)mg_disk_dev.vendor, iop->model,
+			sizeof(mg_disk_dev.vendor));
+	mg_ident_cpy((unsigned char*)mg_disk_dev.product, iop->serial_no,
+			sizeof(mg_disk_dev.product));
+#ifdef __LITTLE_ENDIAN
+	/*
+	 * firmware revision, model, and serial number have Big Endian Byte
+	 * order in Word. Convert all three to little endian.
+	 *
+	 * See CF+ and CompactFlash Specification Revision 2.0:
+	 * 6.2.1.6: Identify Drive, Table 39 for more details
+	 */
+
+	strswab(mg_disk_dev.revision);
+	strswab(mg_disk_dev.vendor);
+	strswab(mg_disk_dev.product);
+#endif /* __LITTLE_ENDIAN */
+
+#ifdef __BIG_ENDIAN
+	mg_disk_dev.lba = (iop->lba_capacity << 16) | (iop->lba_capacity >> 16);
+#else	/* ! __BIG_ENDIAN */
+	mg_disk_dev.lba = iop->lba_capacity;
+#endif /* __BIG_ENDIAN */
+
+	return MG_ERR_NONE;
+}
+
+static int mg_disk_reset (void)
+{
+	struct mg_drv_data *prv_data = host.drv_data;
+	s32 err;
+	u8 init_status;
+
+	/* hdd rst low */
+	prv_data->mg_hdrst_pin(0);
+	err = mg_wait(MG_REG_STATUS_BIT_BUSY, 300);
+	if(err)
+		return err;
+
+	/* hdd rst high */
+	prv_data->mg_hdrst_pin(1);
+	err = mg_wait(MG_STAT_READY, 3000);
+	if(err)
+		return err;
+
+	/* soft reset on */
+	writeb(MG_REG_CTRL_RESET | MG_REG_CTRL_INTR_DISABLE,
+		MG_BASE + MG_REG_DRV_CTRL);
+	err = mg_wait(MG_REG_STATUS_BIT_BUSY, 3000);
+	if(err)
+		return err;
+
+	/* soft reset off */
+	writeb(MG_REG_CTRL_INTR_DISABLE, MG_BASE + MG_REG_DRV_CTRL);
+	err = mg_wait(MG_STAT_READY, 3000);
+	if(err)
+		return err;
+
+	init_status = readb(MG_BASE + MG_REG_STATUS) & 0xf;
+
+	if (init_status == 0xf)
+		return MG_ERR_INIT_STAT;
+
+	return err;
+}
+
+
+static unsigned int mg_out(unsigned int sect_num,
+			unsigned int sect_cnt,
+			unsigned int cmd)
+{
+	u32 err = MG_ERR_NONE;
+
+	if ((err = mg_wait(MG_STAT_READY, 3000))) {
+		return err;
+	}
+
+	writeb((u8)sect_cnt, MG_BASE + MG_REG_SECT_CNT);
+	writeb((u8)sect_num, MG_BASE + MG_REG_SECT_NUM);
+	writeb((u8)(sect_num >> 8), MG_BASE + MG_REG_CYL_LOW);
+	writeb((u8)(sect_num >> 16), MG_BASE + MG_REG_CYL_HIGH);
+	writeb((u8)((sect_num >> 24) | MG_REG_HEAD_LBA_MODE),
+		MG_BASE + MG_REG_DRV_HEAD);
+	writeb(cmd, MG_BASE + MG_REG_COMMAND);
+	return err;
+}
+
+static unsigned int mg_do_read_sects(void *buff, u32 sect_num, u32 sect_cnt)
+{
+	u32 i, j, err;
+	u8 *buff_ptr = buff;
+
+	err = mg_out(sect_num, sect_cnt, MG_CMD_RD);
+	if (err)
+		return err;
+
+	for (i = 0; i < sect_cnt; i++) {
+		err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000);
+		if (err)
+			return err;
+
+		/* TODO : u16 unaligned case */
+		for(j = 0; j < MG_SECTOR_SIZE >> 1; j++) {
+			*(u16 *)buff_ptr =
+				readw(MG_BASE + MG_BUFF_OFFSET + (j << 1));
+			buff_ptr += 2;
+		}
+
+		writeb(MG_CMD_RD_CONF, MG_BASE + MG_REG_COMMAND);
+
+		MG_DBG("%u (0x%8.8x) sector read", sect_num + i,
+			(sect_num + i) * MG_SECTOR_SIZE);
+	}
+
+	return err;
+}
+
+unsigned int mg_disk_read_sects(void *buff, u32 sect_num, u32 sect_cnt)
+{
+	u32 quotient, residue, i, err;
+	u8 *buff_ptr = buff;
+
+	quotient = sect_cnt >> 8;
+	residue = sect_cnt % 256;
+
+	for (i = 0; i < quotient; i++) {
+		MG_DBG("sect num : %u buff : 0x%8.8x", sect_num, (u32)buff_ptr);
+		err = mg_do_read_sects(buff_ptr, sect_num, 256);
+		if (err)
+			return err;
+		sect_num += 256;
+		buff_ptr += 256 * MG_SECTOR_SIZE;
+	}
+
+	if (residue) {
+		MG_DBG("sect num : %u buff : %8.8x", sect_num, (u32)buff_ptr);
+		err = mg_do_read_sects(buff_ptr, sect_num, residue);
+	}
+
+	return err;
+}
+
+unsigned long mg_block_read (int dev, unsigned long start,
+		lbaint_t blkcnt, void *buffer)
+{
+	start += MG_DISK_RES;
+	if (! mg_disk_read_sects(buffer, start, blkcnt))
+		return blkcnt;
+	else
+		return 0;
+}
+
+unsigned int mg_disk_read (u32 addr, u8 *buff, u32 len)
+{
+	u8 *sect_buff, *buff_ptr = buff;
+	u32 cur_addr, next_sec_addr, end_addr, cnt, sect_num;
+	u32 err = MG_ERR_NONE;
+
+	/* TODO : sanity chk */
+	cnt = 0;
+	cur_addr = addr;
+	end_addr = addr + len;
+
+	sect_buff = malloc(MG_SECTOR_SIZE);
+
+	if (cur_addr & MG_SECTOR_SIZE_MASK) {
+		next_sec_addr = (cur_addr + MG_SECTOR_SIZE) &
+				~MG_SECTOR_SIZE_MASK;
+		sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
+		err = mg_disk_read_sects(sect_buff, sect_num, 1);
+		if (err)
+			goto mg_read_exit;
+
+		if (end_addr < next_sec_addr) {
+			memcpy(buff_ptr,
+				sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK),
+				end_addr - cur_addr);
+			MG_DBG("copies %u byte from sector offset 0x%8.8x",
+				end_addr - cur_addr, cur_addr);
+			cur_addr = end_addr;
+		} else {
+			memcpy(buff_ptr,
+				sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK),
+				next_sec_addr - cur_addr);
+			MG_DBG("copies %u byte from sector offset 0x%8.8x",
+				next_sec_addr - cur_addr, cur_addr);
+			buff_ptr += (next_sec_addr - cur_addr);
+			cur_addr = next_sec_addr;
+		}
+	}
+
+	if (cur_addr < end_addr) {
+		sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
+		next_sec_addr = cur_addr + MG_SECTOR_SIZE;
+
+		while (next_sec_addr <= end_addr) {
+			cnt++;
+			next_sec_addr += MG_SECTOR_SIZE;
+		}
+
+		if (cnt)
+			err = mg_disk_read_sects(buff_ptr, sect_num, cnt);
+		if (err)
+			goto mg_read_exit;
+
+		buff_ptr += cnt * MG_SECTOR_SIZE;
+		cur_addr += cnt * MG_SECTOR_SIZE;
+
+		if (cur_addr < end_addr) {
+			sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
+			err = mg_disk_read_sects(sect_buff, sect_num, 1);
+			if (err)
+				goto mg_read_exit;
+			memcpy(buff_ptr, sect_buff, end_addr - cur_addr);
+			MG_DBG("copies %u byte", end_addr - cur_addr);
+		}
+	}
+
+mg_read_exit:
+	free(sect_buff);
+
+	return err;
+}
+
+static int mg_do_write_sects(void *buff, u32 sect_num, u32 sect_cnt)
+{
+	u32 i, j, err;
+	u8 *buff_ptr = buff;
+
+	err = mg_out(sect_num, sect_cnt, MG_CMD_WR);
+	if (err)
+		return err;
+
+	for (i = 0; i < sect_cnt; i++) {
+		err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000);
+		if (err)
+			return err;
+
+		/* TODO : u16 unaligned case */
+		for(j = 0; j < MG_SECTOR_SIZE >> 1; j++) {
+			writew(*(u16 *)buff_ptr,
+				MG_BASE + MG_BUFF_OFFSET + (j << 1));
+			buff_ptr += 2;
+		}
+
+		writeb(MG_CMD_WR_CONF, MG_BASE + MG_REG_COMMAND);
+
+		MG_DBG("%u (0x%8.8x) sector write",
+			sect_num + i, (sect_num + i) * MG_SECTOR_SIZE);
+	}
+
+	return err;
+}
+
+unsigned int mg_disk_write_sects(void *buff, u32 sect_num, u32 sect_cnt)
+{
+	u32 quotient, residue, i;
+	u32 err = MG_ERR_NONE;
+	u8 *buff_ptr = buff;
+
+	quotient = sect_cnt >> 8;
+	residue = sect_cnt % 256;
+
+	for (i = 0; i < quotient; i++) {
+		MG_DBG("sect num : %u buff : %8.8x", sect_num, (u32)buff_ptr);
+		err = mg_do_write_sects(buff_ptr, sect_num, 256);
+		if (err)
+			return err;
+		sect_num += 256;
+		buff_ptr += 256 * MG_SECTOR_SIZE;
+	}
+
+	if (residue) {
+		MG_DBG("sect num : %u buff : %8.8x", sect_num, (u32)buff_ptr);
+		err = mg_do_write_sects(buff_ptr, sect_num, residue);
+	}
+
+	return err;
+}
+
+unsigned long mg_block_write (int dev, unsigned long start,
+		lbaint_t blkcnt, const void *buffer)
+{
+	start += MG_DISK_RES;
+	if (!mg_disk_write_sects((void *)buffer, start, blkcnt))
+		return blkcnt;
+	else
+		return 0;
+}
+
+unsigned int mg_disk_write(u32 addr, u8 *buff, u32 len)
+{
+	u8 *sect_buff, *buff_ptr = buff;
+	u32 cur_addr, next_sec_addr, end_addr, cnt, sect_num;
+	u32 err = MG_ERR_NONE;
+
+	/* TODO : sanity chk */
+	cnt = 0;
+	cur_addr = addr;
+	end_addr = addr + len;
+
+	sect_buff = malloc(MG_SECTOR_SIZE);
+
+	if (cur_addr & MG_SECTOR_SIZE_MASK) {
+
+		next_sec_addr = (cur_addr + MG_SECTOR_SIZE) &
+				~MG_SECTOR_SIZE_MASK;
+		sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
+		err = mg_disk_read_sects(sect_buff, sect_num, 1);
+		if (err)
+			goto mg_write_exit;
+
+		if (end_addr < next_sec_addr) {
+			memcpy(sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK),
+				buff_ptr, end_addr - cur_addr);
+			MG_DBG("copies %u byte to sector offset 0x%8.8x",
+				end_addr - cur_addr, cur_addr);
+			cur_addr = end_addr;
+		} else {
+			memcpy(sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK),
+				buff_ptr, next_sec_addr - cur_addr);
+			MG_DBG("copies %u byte to sector offset 0x%8.8x",
+				next_sec_addr - cur_addr, cur_addr);
+			buff_ptr += (next_sec_addr - cur_addr);
+			cur_addr = next_sec_addr;
+		}
+
+		err = mg_disk_write_sects(sect_buff, sect_num, 1);
+		if (err)
+			goto mg_write_exit;
+	}
+
+	if (cur_addr < end_addr) {
+
+		sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
+		next_sec_addr = cur_addr + MG_SECTOR_SIZE;
+
+		while (next_sec_addr <= end_addr) {
+			cnt++;
+			next_sec_addr += MG_SECTOR_SIZE;
+		}
+
+		if (cnt)
+			err = mg_disk_write_sects(buff_ptr, sect_num, cnt);
+		if (err)
+			goto mg_write_exit;
+
+		buff_ptr += cnt * MG_SECTOR_SIZE;
+		cur_addr += cnt * MG_SECTOR_SIZE;
+
+		if (cur_addr < end_addr) {
+			sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
+			err = mg_disk_read_sects(sect_buff, sect_num, 1);
+			if (err)
+				goto mg_write_exit;
+			memcpy(sect_buff, buff_ptr, end_addr - cur_addr);
+			MG_DBG("copies %u byte", end_addr - cur_addr);
+			err = mg_disk_write_sects(sect_buff, sect_num, 1);
+		}
+
+	}
+
+mg_write_exit:
+	free(sect_buff);
+
+	return err;
+}
+
+block_dev_desc_t *mg_disk_get_dev(int dev)
+{
+	return ((block_dev_desc_t *) & mg_disk_dev);
+}
+
+/* must override this function */
+struct mg_drv_data * __attribute__((weak)) mg_get_drv_data (void)
+{
+	puts ("### WARNING ### port mg_get_drv_data function\n");
+	return NULL;
+}
+
+unsigned int mg_disk_init (void)
+{
+	struct mg_drv_data *prv_data;
+	u32 err = MG_ERR_NONE;
+
+	prv_data = mg_get_drv_data();
+	if (! prv_data) {
+		printf("%s:%d fail (no driver_data)\n", __func__, __LINE__);
+		err = MG_ERR_NO_DRV_DATA;
+		return err;
+	}
+
+	((struct mg_host *)mg_disk_dev.priv)->drv_data = prv_data;
+
+	/* init ctrl pin */
+	if (prv_data->mg_ctrl_pin_init)
+		prv_data->mg_ctrl_pin_init();
+
+	if (! prv_data->mg_hdrst_pin) {
+		err = MG_ERR_CTRL_RST;
+		return err;
+	}
+
+	/* disk reset */
+	err = mg_disk_reset();
+	if (err) {
+		printf("%s:%d fail (err code : %d)\n", __func__, __LINE__, err);
+		return err;
+	}
+
+	/* get disk id */
+	err = mg_get_disk_id();
+	if (err) {
+		printf("%s:%d fail (err code : %d)\n", __func__, __LINE__, err);
+		return err;
+	}
+
+	mg_disk_dev.block_read = mg_block_read;
+	mg_disk_dev.block_write = mg_block_write;
+
+	init_part(&mg_disk_dev);
+
+	dev_print(&mg_disk_dev);
+
+	return err;
+}
+
+#endif /* CONFIG_CMD_MG_DISK */
diff --git a/drivers/block/mg_disk_prv.h b/drivers/block/mg_disk_prv.h
new file mode 100644
index 0000000..a6b7299
--- /dev/null
+++ b/drivers/block/mg_disk_prv.h
@@ -0,0 +1,140 @@
+/*
+ * (C) Copyright 2009 mGine co.
+ * unsik Kim <donari75 at gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __MG_DISK_PRV_H__
+#define __MG_DISK_PRV_H__
+
+#include <mg_disk.h>
+
+/* name for block device */
+#define MG_DISK_NAME "mgd"
+/* name for platform device */
+#define MG_DEV_NAME "mg_disk"
+
+#define MG_DISK_MAJ 240
+#define MG_DISK_MAX_PART 16
+#define MG_SECTOR_SIZE 512
+#define MG_SECTOR_SIZE_MASK (512 - 1)
+#define MG_SECTOR_SIZE_SHIFT (9)
+#define MG_MAX_SECTS 256
+
+/* Register offsets */
+#define MG_BUFF_OFFSET			0x8000
+#define MG_STORAGE_BUFFER_SIZE		0x200
+#define MG_REG_OFFSET			0xC000
+#define MG_REG_FEATURE			(MG_REG_OFFSET + 2)	/* write case */
+#define MG_REG_ERROR			(MG_REG_OFFSET + 2)	/* read case */
+#define MG_REG_SECT_CNT			(MG_REG_OFFSET + 4)
+#define MG_REG_SECT_NUM			(MG_REG_OFFSET + 6)
+#define MG_REG_CYL_LOW			(MG_REG_OFFSET + 8)
+#define MG_REG_CYL_HIGH			(MG_REG_OFFSET + 0xA)
+#define MG_REG_DRV_HEAD			(MG_REG_OFFSET + 0xC)
+#define MG_REG_COMMAND			(MG_REG_OFFSET + 0xE)	/* write case */
+#define MG_REG_STATUS			(MG_REG_OFFSET + 0xE)	/* read  case */
+#define MG_REG_DRV_CTRL			(MG_REG_OFFSET + 0x10)
+#define MG_REG_BURST_CTRL		(MG_REG_OFFSET + 0x12)
+
+/* "Drive Select/Head Register" bit values */
+#define MG_REG_HEAD_MUST_BE_ON		0xA0 /* These 2 bits are always on */
+#define MG_REG_HEAD_DRIVE_MASTER	(0x00 | MG_REG_HEAD_MUST_BE_ON)
+#define MG_REG_HEAD_DRIVE_SLAVE		(0x10 | MG_REG_HEAD_MUST_BE_ON)
+#define MG_REG_HEAD_LBA_MODE		(0x40 | MG_REG_HEAD_MUST_BE_ON)
+
+
+/* "Device Control Register" bit values */
+#define MG_REG_CTRL_INTR_ENABLE			0x0
+#define MG_REG_CTRL_INTR_DISABLE		(0x1 << 1)
+#define MG_REG_CTRL_RESET			(0x1 << 2)
+#define MG_REG_CTRL_INTR_POLA_ACTIVE_HIGH	0x0
+#define MG_REG_CTRL_INTR_POLA_ACTIVE_LOW	(0x1 << 4)
+#define MG_REG_CTRL_DPD_POLA_ACTIVE_LOW		0x0
+#define MG_REG_CTRL_DPD_POLA_ACTIVE_HIGH	(0x1 << 5)
+#define MG_REG_CTRL_DPD_DISABLE			0x0
+#define MG_REG_CTRL_DPD_ENABLE			(0x1 << 6)
+
+/* Status register bit */
+ /* error bit in status register */
+#define MG_REG_STATUS_BIT_ERROR			0x01
+ /* corrected error in status register */
+#define MG_REG_STATUS_BIT_CORRECTED_ERROR	0x04
+ /* data request bit in status register */
+#define MG_REG_STATUS_BIT_DATA_REQ		0x08
+ /* DSC - Drive Seek Complete */
+#define MG_REG_STATUS_BIT_SEEK_DONE		0x10
+ /* DWF - Drive Write Fault */
+#define MG_REG_STATUS_BIT_WRITE_FAULT		0x20
+#define MG_REG_STATUS_BIT_READY			0x40
+#define MG_REG_STATUS_BIT_BUSY			0x80
+
+/* handy status */
+#define MG_STAT_READY	(MG_REG_STATUS_BIT_READY | MG_REG_STATUS_BIT_SEEK_DONE)
+#define MG_READY_OK(s)	(((s) & (MG_STAT_READY | \
+				(MG_REG_STATUS_BIT_BUSY | \
+				 MG_REG_STATUS_BIT_WRITE_FAULT | \
+				 MG_REG_STATUS_BIT_ERROR))) == MG_STAT_READY)
+
+/* Error register */
+#define MG_REG_ERR_AMNF		0x01
+#define MG_REG_ERR_ABRT		0x04
+#define MG_REG_ERR_IDNF		0x10
+#define MG_REG_ERR_UNC		0x40
+#define MG_REG_ERR_BBK		0x80
+
+/* error code for others */
+#define MG_ERR_NONE		0
+#define MG_ERR_TIMEOUT		0x100
+#define MG_ERR_INIT_STAT	0x101
+#define MG_ERR_TRANSLATION	0x102
+#define MG_ERR_CTRL_RST		0x103
+#define MG_ERR_NO_DRV_DATA	0x104
+
+#define MG_MAX_ERRORS	16	/* Max read/write errors/sector */
+#define MG_RESET_FREQ	4	/* Reset controller every 4th retry */
+
+/* command */
+#define MG_CMD_RD	0x20
+#define MG_CMD_WR	0x30
+#define MG_CMD_SLEEP	0x99
+#define MG_CMD_WAKEUP	0xC3
+#define MG_CMD_ID	0xEC
+#define MG_CMD_WR_CONF	0x3C
+#define MG_CMD_RD_CONF	0x40
+
+/* main structure for mflash driver */
+struct mg_host {
+	struct mg_drv_data *drv_data;
+	/* for future use */
+};
+
+/*
+ * Debugging macro and defines
+ */
+#undef DO_MG_DEBUG
+#ifdef DO_MG_DEBUG
+# define MG_DBG(fmt, args...) printf("%s:%d "fmt"\n", __func__,
__LINE__,##args)
+#else /* CONFIG_MG_DEBUG */
+# define MG_DBG(fmt, args...) do { } while(0)
+#endif /* CONFIG_MG_DEBUG */
+
+#endif
+
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index 8081ee7..602edae 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -84,6 +84,7 @@ fat_register_device(block_dev_desc_t *dev_desc, int part_no)
 		return -1;
 	}
 #if (defined(CONFIG_CMD_IDE) || \
+     defined(CONFIG_CMD_MG_DISK) || \
      defined(CONFIG_CMD_SATA) || \
      defined(CONFIG_CMD_SCSI) || \
      defined(CONFIG_CMD_USB) || \
@@ -987,6 +988,7 @@ file_fat_detectfs(void)
 		return 1;
 	}
 #if defined(CONFIG_CMD_IDE) || \
+    defined(CONFIG_CMD_MG_DISK) || \
     defined(CONFIG_CMD_SATA) || \
     defined(CONFIG_CMD_SCSI) || \
     defined(CONFIG_CMD_USB) || \
diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h
index d771696..0ee2b58 100644
--- a/include/config_cmd_all.h
+++ b/include/config_cmd_all.h
@@ -83,5 +83,6 @@
 #define CONFIG_CMD_VFD		/* VFD support (TRAB)		*/
 #define CONFIG_CMD_XIMG		/* Load part of Multi Image	*/
 #define CONFIG_CMD_AT91_SPIMUX	/* AT91 MMC/SPI Mux Support     */
+#define CONFIG_CMD_MG_DISK	/* mGine m(g)flash IO node support */

 #endif	/* _CONFIG_CMD_ALL_H */
diff --git a/include/environment.h b/include/environment.h
index ea6b4d1..507e832 100644
--- a/include/environment.h
+++ b/include/environment.h
@@ -84,6 +84,18 @@
 # endif
 #endif /* CONFIG_ENV_IS_IN_NAND */

+#if defined(CONFIG_ENV_IS_IN_MG_DISK)
+# ifndef CONFIG_ENV_ADDR
+#  error "Need to define CONFIG_ENV_ADDR when using CONFIG_ENV_IS_IN_MG_DISK"
+# endif
+# ifndef CONFIG_ENV_SIZE
+#  error "Need to define CONFIG_ENV_SIZE when using CONFIG_ENV_IS_IN_MG_DISK"
+# endif
+# ifdef CONFIG_ENV_IS_EMBEDDED
+#  error "CONFIG_ENV_IS_EMBEDDED not supported when using
CONFIG_ENV_IS_IN_MG_DISK"
+# endif
+#endif /* CONFIG_ENV_IS_IN_MG_DISK */
+
 #ifdef USE_HOSTCC
 # include <stdint.h>
 #else
diff --git a/include/mg_disk.h b/include/mg_disk.h
new file mode 100644
index 0000000..bd767a1
--- /dev/null
+++ b/include/mg_disk.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 2009 mGine co.
+ * unsik Kim <donari75 at gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef MG_DISK_H_
+#define MG_DISK_H_
+
+#include <asm/types.h>
+
+/* private driver data */
+struct mg_drv_data {
+	/* base address of mflash */
+	u32 base;
+	/* Initialize hard reset, write protect, deep power down pins.
+	 * Set these pins to GPIO and output high
+	 */
+	void (*mg_ctrl_pin_init) (void);
+	/* Set hard reset pin for given level
+	 * level : logical level of hard reset pin (0 or 1)
+	 */
+	void (*mg_hdrst_pin) (u8 level);
+};
+
+struct mg_drv_data* mg_get_drv_data (void);
+
+unsigned int mg_disk_init (void);
+unsigned int mg_disk_read (u32 addr, u8 *buff, u32 len);
+unsigned int mg_disk_write(u32 addr, u8 *buff, u32 len);
+unsigned int mg_disk_write_sects(void *buff, u32 sect_num, u32 sect_cnt);
+unsigned int mg_disk_read_sects(void *buff, u32 sect_num, u32 sect_cnt);
+
+#endif /*MG_DISK_H_*/
diff --git a/include/part.h b/include/part.h
index 980fd04..3cdae02 100644
--- a/include/part.h
+++ b/include/part.h
@@ -100,6 +100,7 @@ block_dev_desc_t* scsi_get_dev(int dev);
 block_dev_desc_t* usb_stor_get_dev(int dev);
 block_dev_desc_t* mmc_get_dev(int dev);
 block_dev_desc_t* systemace_get_dev(int dev);
+block_dev_desc_t* mg_disk_get_dev(int dev);

 /* disk/part.c */
 int get_partition_info (block_dev_desc_t * dev_desc, int part,
disk_partition_t *info);

---

2009/1/28 Wolfgang Denk <wd at denx.de>:
> Dear Kim,
>
> In message <57afda040901052341g3b00f741r445c0ce8d33b7b71 at mail.gmail.com> you wrote:
>>
>> I wrote mflash IO mode block device driver for U-Boot.
>
> Thanks for your contribution. Here a few comments:
>
>> diff --git a/common/Makefile b/common/Makefile
>> index 93e3963..f93e575 100644
>> --- a/common/Makefile
>> +++ b/common/Makefile
>> @@ -57,6 +57,7 @@ COBJS-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o
>>  COBJS-$(CONFIG_ENV_IS_IN_NVRAM) += env_nvram.o
>>  COBJS-$(CONFIG_ENV_IS_IN_ONENAND) += env_onenand.o
>>  COBJS-$(CONFIG_ENV_IS_IN_SPI_FLASH) += env_sf.o
>> +COBJS-$(CONFIG_ENV_IS_IN_MG_DISK) += env_mgdisk.o
>>  COBJS-$(CONFIG_ENV_IS_NOWHERE) += env_nowhere.o
>>
>>  # command
>> @@ -138,6 +139,7 @@ endif
>>  COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o
>>  COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o
>>  COBJS-$(CONFIG_VFD) += cmd_vfd.o
>> +COBJS-$(CONFIG_CMD_MG_DISK) += cmd_mgdisk.o
>
> Please keep such lists sorted.
>
> ...
>> +U_BOOT_CMD(
>> +     mgd,    5,      0,      do_mg_disk_cmd,
>> +     "mgd     - mgine m[g]flash command\n",
>> +     ": mgine mflash IO mode (disk) command\n"
>> +     "\t- initialize : mgd init\n"
>> +     "\t- random read : mgd read [from] [to] [size]\n"
>> +     "\t\tbelow example read 256 bytes from 0x300000 of mflash\n"
>> +     "\t\tto 0xA0100000 of host memory\n"
>> +     "\t\tex) mgd read 0x300000 0xA0100000 256\n"
>> +     "\t- random write : mgd write [from] [to] [size]\n"
>> +     "\t\tex) mgd write 0xA0100000 0x300000 256\n"
>> +     "\t- sector read : mgd readsec [sector] [to] [counts]\n"
>> +     "\t\tbelow example read 10 sectors starts from 400 sector\n"
>> +     "\t\tto 0xA0100000\n"
>> +     "\t\tex) mgd readsec 400 0xA0100000 10\n"
>> +     "\t- sector write : mgd writesec [from] [sector] [counts]\n"
>> +);
>
> Please avoid using TAB characters here.
>
> Also,pleas ebe terse - don;t give examples here. Rather add a
> README.mflash file to the doc/ directory.
>
>> diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c
>> index 85025da..4945aeb 100644
>> --- a/common/cmd_nvedit.c
>> +++ b/common/cmd_nvedit.c
>> @@ -59,8 +59,9 @@ DECLARE_GLOBAL_DATA_PTR;
>>      !defined(CONFIG_ENV_IS_IN_NAND)  && \
>>      !defined(CONFIG_ENV_IS_IN_ONENAND)       && \
>>      !defined(CONFIG_ENV_IS_IN_SPI_FLASH)     && \
>> +    !defined(CONFIG_ENV_IS_IN_MG_DISK)       && \
>>      !defined(CONFIG_ENV_IS_NOWHERE)
>
> Please keep lists sorted.
>
>> -# error Define one of
>> CONFIG_ENV_IS_IN_{NVRAM|EEPROM|FLASH|DATAFLASH|ONENAND|SPI_FLASH|NOWHERE}
>> +# error Define one of
>> CONFIG_ENV_IS_IN_{NVRAM|EEPROM|FLASH|DATAFLASH|ONENAND|SPI_FLASH|MG_DISK|NOWHERE}
>
> Please keep lists sorted.
>
> Also note that your mailer wrapped long lines here, thus corrupting
> the patch. Please fix your mailer.
>
> And please avoid such long lines, too!
>
> ...
>> +void env_relocate_spec(void)
>> +{
>> +     unsigned int err;
>> +     err = mg_disk_read(CONFIG_ENV_ADDR, (u_char *) env_ptr, CONFIG_ENV_SIZE);
>> +     if (err) {
>> +             puts ("*** Warning - mg_disk_read error, using default environment\n\n");
>
> Line too long.
>
>
>> +             set_default_env();
>> +             return;
>> +     }
>> +
>> +     if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) {
>> +             puts ("*** Warning - CRC error, using default environment\n\n");
>> +             set_default_env();
>> +     }
>> +}
>> +
>> +int saveenv(void)
>> +{
>> +     unsigned int err;
>> +     env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE);
>> +     err = mg_disk_write(CONFIG_ENV_ADDR, (u_char *) env_ptr, CONFIG_ENV_SIZE);
>
> Line too long.
>
>> +     if (err)
>> +             puts ("*** Warning - mg_disk_write error\n\n");
>> +     return (int)err;
>
> The cast should not be needed here.
>
>> diff --git a/disk/part.c b/disk/part.c
>> index e353cee..c007060 100644
>> --- a/disk/part.c
>> +++ b/disk/part.c
>> @@ -39,7 +39,8 @@
>>       defined(CONFIG_CMD_SCSI) || \
>>       defined(CONFIG_CMD_USB) || \
>>       defined(CONFIG_MMC) || \
>> -     defined(CONFIG_SYSTEMACE) )
>> +     defined(CONFIG_SYSTEMACE) || \
>> +     defined(CONFIG_CMD_MG_DISK))
>
> Please keep lists sorted.
>
>> @@ -95,7 +99,8 @@ block_dev_desc_t *get_dev(char* ifname, int dev)
>>       defined(CONFIG_CMD_SCSI) || \
>>       defined(CONFIG_CMD_USB) || \
>>       defined(CONFIG_MMC) || \
>> -     defined(CONFIG_SYSTEMACE) )
>> +     defined(CONFIG_SYSTEMACE) || \
>> +     defined(CONFIG_CMD_MG_DISK))
>
> Ditto.
>
>> @@ -207,7 +212,8 @@ void dev_print (block_dev_desc_t *dev_desc)
>>       defined(CONFIG_CMD_SCSI) || \
>>       defined(CONFIG_CMD_USB) || \
>>       defined(CONFIG_MMC)             || \
>> -     defined(CONFIG_SYSTEMACE)          )
>> +     defined(CONFIG_SYSTEMACE) || \
>> +     defined(CONFIG_CMD_MG_DISK))
>
> Ditto.
>
>> diff --git a/disk/part_amiga.c b/disk/part_amiga.c
>> index 6c3d748..b4c2820 100644
>> --- a/disk/part_amiga.c
>> +++ b/disk/part_amiga.c
>> @@ -30,7 +30,8 @@
>>       defined(CONFIG_CMD_SCSI) || \
>>       defined(CONFIG_CMD_USB) || \
>>       defined(CONFIG_MMC) || \
>> -     defined(CONFIG_SYSTEMACE) ) && defined(CONFIG_AMIGA_PARTITION)
>> +     defined(CONFIG_SYSTEMACE) || \
>> +     defined(CONFIG_CMD_MG_DISK)) && defined(CONFIG_AMIGA_PARTITION)
>
> Ditto.
>
>> @@ -40,6 +41,8 @@
>>  #define PRINTF(fmt, args...)
>>  #endif
>>
>> +#define atoi(x)              simple_strtoul(x,NULL,10)
>
> Please avoid that.
>
>>  struct block_header
>>  {
>>      u32 id;
>> diff --git a/disk/part_dos.c b/disk/part_dos.c
>> index 4d778ec..30dc39f 100644
>> --- a/disk/part_dos.c
>> +++ b/disk/part_dos.c
>> @@ -40,7 +40,8 @@
>>       defined(CONFIG_CMD_SCSI) || \
>>       defined(CONFIG_CMD_USB) || \
>>       defined(CONFIG_MMC) || \
>> -     defined(CONFIG_SYSTEMACE) ) && defined(CONFIG_DOS_PARTITION)
>> +     defined(CONFIG_SYSTEMACE) || \
>> +     defined(CONFIG_CMD_MG_DISK)) && defined(CONFIG_DOS_PARTITION)
>
> more incorrect sort order, even more following below.
>
>> diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c
>> new file mode 100644
>> index 0000000..06c9bd8
>> --- /dev/null
>> +++ b/drivers/block/mg_disk.c
>> @@ -0,0 +1,595 @@
>> +/*
>> + * (C) Copyright 2009 mGine co.
>> + * unsik Kim <donari75 at gmail.com>
>> + *
>> + * See file CREDITS for list of people who contributed to this
>> + * project.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation; either version 2 of
>> + * the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
>> + * MA 02111-1307 USA
>> + */
>> +
>> +#include <common.h>
>> +#include <malloc.h>
>> +#include <part.h>
>> +#include <ata.h>
>> +#include "mg_disk_prv.h"
>> +
>> +#ifdef CONFIG_CMD_MG_DISK
>> +
>> +#undef printk
>> +#define printk printf
>
> Please don't.
>
>> +#undef KERN_ERR
>> +#define KERN_ERR
>> +#undef KERN_DEBUG
>> +#define KERN_DEBUG
>> +#undef KERN_INFO
>> +#define KERN_INFO
>
> Please don't.
>
>> +#undef inb
>> +#undef inw
>> +#undef outb
>> +#undef outw
>> +
>> +#define inb(a)       (*(volatile unsigned char *)(a))
>> +#define inw(a)       (*(volatile unsigned short *)(a))
>> +#define outb(v, a)   (*(volatile unsigned char *)(a) = (v))
>> +#define outw(v, a)   (*(volatile unsigned short *)(a) = (v))
>
> A strict NO, NO here. Please never do that.
>
> Please do use theproper accessor functions that are  needed  for  the
> respective architecture. PLain volatile pointer accesses are bound to
> fail. Never do that.
>
> ...
>> +     printk("%s: %s: status=0x%02x { ", name, msg, stat & 0xff);
>> +     if (stat & MG_REG_STATUS_BIT_BUSY)                      printk("Busy ");
>> +     if (stat & MG_REG_STATUS_BIT_READY)                     printk("DriveReady ");
>> +     if (stat & MG_REG_STATUS_BIT_WRITE_FAULT)               printk("WriteFault ");
>> +     if (stat & MG_REG_STATUS_BIT_SEEK_DONE)         printk("SeekComplete ");
>> +     if (stat & MG_REG_STATUS_BIT_DATA_REQ)          printk("DataRequest ");
>> +     if (stat & MG_REG_STATUS_BIT_CORRECTED_ERROR)   printk("CorrectedError ");
>> +     if (stat & MG_REG_STATUS_BIT_ERROR)                     printk("Error ");
>
> Bad coding style. Please reformat.
>
>> +     printk("}\n");
>> +     if ((stat & MG_REG_STATUS_BIT_ERROR)) {
>> +             printk("%s: %s: error=0x%02x { ", name, msg, err & 0xff);
>> +             if (err & MG_REG_ERR_BBK)       printk("BadSector ");
>> +             if (err & MG_REG_ERR_UNC)       printk("UncorrectableError ");
>> +             if (err & MG_REG_ERR_IDNF)      printk("SectorIdNotFound ");
>> +             if (err & MG_REG_ERR_ABRT)      printk("DriveStatusError ");
>> +             if (err & MG_REG_ERR_AMNF)      printk("AddrMarkNotFound ");
>
> Ditto.
>
>> +static unsigned int msecs_to_hz (u32 msec)
>> +{
>> +     u32 hz = CONFIG_SYS_HZ / 1000 * msec;
>> +
>> +     if (!hz)
>> +             hz = 1;
>> +
>> +     return hz;
>
> This makes no sense. CONFIG_SYS_HZ is always 1000. Please consider it
> a constant.
>
> ...
>> +     from = get_timer(0);
>> +     expire = from + msecs_to_hz(msec);
>
> I don;t understand y ou logic here. get_timer() is defined to operate
> in munits of millisconds. What's the msecs_to_hz() stuff gotta do
> here?
>
>> +     } while (cur < expire);
>
> You are aware of the overflow issues buried here, are you?
>
>> +             if (!err) {
>> +                     if((iop->field_valid & 1) == 0) {
>> +                             err = MG_ERR_TRANSLATION;
>> +                     } else {
>> +                             mg_ident_cpy((unsigned char*)mg_disk_dev.revision, iop->fw_rev,
>> sizeof(mg_disk_dev.revision));
>> +                             mg_ident_cpy((unsigned char*)mg_disk_dev.vendor, iop->model,
>> sizeof(mg_disk_dev.vendor));
>> +                             mg_ident_cpy((unsigned char*)mg_disk_dev.product, iop->serial_no,
>> sizeof(mg_disk_dev.product));
>
> Lines way too long, and wrapped by mailer.
>
>> +static int mg_disk_reset (void)
>> +{
>> +     struct mg_drv_data *prv_data = MG_HOST->drv_data;
>> +     s32 err;
>> +     u8 init_status;
>> +
>> +     /* hdd rst low */
>> +     prv_data->mg_hdrst_pin(0);
>> +     err = mg_wait(MG_REG_STATUS_BIT_BUSY, 300);
>> +     if(err) return err;
>
> Please refoemat according to CodingStyle requirements.
>
>> +     if ((err = mg_wait(MG_STAT_READY, 3000))) {
>> +             return err;
>> +     }
>
> No braces for one-liners, please.
>
>> +static unsigned int mg_do_read_sects(void *buff, u32 sect_num, u32 sect_cnt)
>> +{
>> +     u32 i, j, err;
>> +     u8 *buff_ptr = buff;
>> +
>> +     if ((err = mg_out(sect_num, sect_cnt, MG_CMD_RD))) {
>> +             return err;
>> +     }
>
> Ditto.
>
>> +     for (i = 0; i < sect_cnt; i++) {
>> +             if ((err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000))) {
>> +                     return err;
>> +             }
>
> Ditto. And so on.
>
>> +             MG_DBG("%u (0x%8.8x) sector read", sect_num + i, (sect_num + i) *
>> MG_SECTOR_SIZE);
>
> Please check all line lengths!
>
>> diff --git a/lib_arm/board.c b/lib_arm/board.c
>> index 2358beb..6a26bd2 100644
>> --- a/lib_arm/board.c
>> +++ b/lib_arm/board.c
>> @@ -48,6 +48,7 @@
>>  #include <serial.h>
>>  #include <nand.h>
>>  #include <onenand_uboot.h>
>> +#include <mg_disk.h>
>>
>>  #ifdef CONFIG_DRIVER_SMC91111
>>  #include "../drivers/net/smc91111.h"
>> @@ -348,6 +349,10 @@ void start_armboot (void)
>>       onenand_init();
>>  #endif
>>
>> +#if defined(CONFIG_CMD_MG_DISK)
>> +     mg_disk_init();
>> +#endif
>
> Please don't.
>
> First it's wrong to add it for ARM only - what about other
> architectures that want to use that technology?
>
> Second it's wrong to always call the init code. This shall be done
> only upon first access. See the longish discussion we had for example
> about how to handle the S-ATA init code - please see the archives.
>
> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
> What is wanted is not the will to believe,  but the will to find out,
> which is the exact opposite.
>                        -- Bertrand Russell, "Skeptical Essays", 1928
>


More information about the U-Boot mailing list