U-Boot
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2000 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
September 2008
- 151 participants
- 458 discussions
Set the MQ Read Passing & MCIF Cycle limits to the recommended by AMCC
values. This fixes the occasional 440SPe hard locking issues when the 440SPe's
dedicated DMA engines are used (e.g. by the h/w accelerated RAID driver).
Previously the appropriate initialization had been made in Linux, by the
ppc440spe ADMA driver, which is wrong because modifying the MQ configuration
registers after normal operation has begun is not supported and could
have unpredictable results.
Signed-off-by: Yuri Tikhonov <yur(a)emcraft.com>
---
cpu/ppc4xx/44x_spd_ddr2.c | 10 ++++++----
include/asm-ppc/ppc4xx-sdram.h | 5 +++++
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/cpu/ppc4xx/44x_spd_ddr2.c b/cpu/ppc4xx/44x_spd_ddr2.c
index f1d7684..995d5fe 100644
--- a/cpu/ppc4xx/44x_spd_ddr2.c
+++ b/cpu/ppc4xx/44x_spd_ddr2.c
@@ -2261,10 +2261,12 @@ static void program_memory_queue(unsigned long *dimm_populated,
/*
* Set optimal value for Memory Queue HB/LL Configuration registers
*/
- mtdcr(SDRAM_CONF1HB, mfdcr(SDRAM_CONF1HB) | SDRAM_CONF1HB_AAFR |
- SDRAM_CONF1HB_RPEN | SDRAM_CONF1HB_RFTE);
- mtdcr(SDRAM_CONF1LL, mfdcr(SDRAM_CONF1LL) | SDRAM_CONF1LL_AAFR |
- SDRAM_CONF1LL_RPEN | SDRAM_CONF1LL_RFTE);
+ mtdcr(SDRAM_CONF1HB, (mfdcr(SDRAM_CONF1HB) & ~SDRAM_CONF1HB_MASK) |
+ SDRAM_CONF1HB_AAFR | SDRAM_CONF1HB_RPEN | SDRAM_CONF1HB_RFTE |
+ SDRAM_CONF1HB_RPLM | SDRAM_CONF1HB_WRCL);
+ mtdcr(SDRAM_CONF1LL, (mfdcr(SDRAM_CONF1LL) & ~SDRAM_CONF1LL_MASK) |
+ SDRAM_CONF1LL_AAFR | SDRAM_CONF1LL_RPEN | SDRAM_CONF1LL_RFTE |
+ SDRAM_CONF1LL_RPLM);
mtdcr(SDRAM_CONFPATHB, mfdcr(SDRAM_CONFPATHB) | SDRAM_CONFPATHB_TPEN);
#endif
}
diff --git a/include/asm-ppc/ppc4xx-sdram.h b/include/asm-ppc/ppc4xx-sdram.h
index 8efa557..98faced 100644
--- a/include/asm-ppc/ppc4xx-sdram.h
+++ b/include/asm-ppc/ppc4xx-sdram.h
@@ -272,8 +272,11 @@
#define SDRAM_CONF1HB_PRPD 0x00080000 /* PLB Read pipeline Disable - Bit 12 */
#define SDRAM_CONF1HB_PWPD 0x00040000 /* PLB Write pipeline Disable - Bit 13 */
#define SDRAM_CONF1HB_PRW 0x00020000 /* PLB Read Wait - Bit 14 */
+#define SDRAM_CONF1HB_RPLM 0x00001000 /* Read Passing Limit 1 - Bits 16..19 */
#define SDRAM_CONF1HB_RPEN 0x00000800 /* Read Passing Enable - Bit 20 */
#define SDRAM_CONF1HB_RFTE 0x00000400 /* Read Flow Through Enable - Bit 21 */
+#define SDRAM_CONF1HB_WRCL 0x00000080 /* MCIF Cycle Limit 1 - Bits 22..24 */
+#define SDRAM_CONF1HB_MASK 0x0000F380 /* RPLM & WRCL mask */
#define SDRAM_ERRSTATHB (SDRAMQ_DCR_BASE+0x7) /* error status HB */
#define SDRAM_ERRADDUHB (SDRAMQ_DCR_BASE+0x8) /* error address upper 32 HB */
@@ -284,8 +287,10 @@
#define SDRAM_CONF1LL_PRPD 0x00080000 /* PLB Read pipeline Disable - Bit 12 */
#define SDRAM_CONF1LL_PWPD 0x00040000 /* PLB Write pipeline Disable - Bit 13 */
#define SDRAM_CONF1LL_PRW 0x00020000 /* PLB Read Wait - Bit 14 */
+#define SDRAM_CONF1LL_RPLM 0x00001000 /* Read Passing Limit 1 - Bits 16..19 */
#define SDRAM_CONF1LL_RPEN 0x00000800 /* Read Passing Enable - Bit 20 */
#define SDRAM_CONF1LL_RFTE 0x00000400 /* Read Flow Through Enable - Bit 21 */
+#define SDRAM_CONF1LL_MASK 0x0000F000 /* RPLM mask */
#define SDRAM_ERRSTATLL (SDRAMQ_DCR_BASE+0xC) /* error status LL */
#define SDRAM_ERRADDULL (SDRAMQ_DCR_BASE+0xD) /* error address upper 32 LL */
--
1.5.6.1
Regards, Yuri
--
Yuri Tikhonov, Senior Software Engineer
Emcraft Systems, www.emcraft.com
5
15
Hi All,
Kindly let us know the comments and feedback on this patch.
With Regards
Gangheyamoorthy.A.P
-----Original Message-----
From: apgmoorthy [mailto:moorthy.apg@samsung.com]
Sent: Monday, September 22, 2008 11:59 AM
To: 'u-boot(a)lists.denx.de'
Cc: 'scottwood(a)freescale.com'
Subject: [U-Boot] [PATCH] Flex-OneNAND driver
Hi All,
This patch adds support for Samsung Flex-OneNAND devices.
Flex-OneNAND combines SLC and MLC technologies into a single
device. SLC area provides increased reliability and speed, suitable
for storing code and data, such as bootloader, kernel
and root file system. MLC area provides high density and is best used
for storing user data. Users can configure the size of SLC and MLC
regions through 'onenand setboundary' command.
Signed-off-by: Rohit Hagargundgi <h.rohit(a)samsung.com>
---
diff --git a/common/cmd_onenand.c b/common/cmd_onenand.c
index 8d87b78..7260017 100644
--- a/common/cmd_onenand.c
+++ b/common/cmd_onenand.c
@@ -20,9 +20,64 @@
extern struct mtd_info onenand_mtd;
extern struct onenand_chip onenand_chip;
+loff_t flexonenand_get_addr(int block)
+{
+ struct mtd_info *mtd = &onenand_mtd;
+ struct onenand_chip *this = mtd->priv;
+ loff_t ofs;
+ int die = 0, boundary;
+
+ ofs = 0;
+ if (this->dies == 2 && block >= this->density_mask) {
+ block -= this->density_mask;
+ die = 1;
+ ofs = this->diesize[0];
+ }
+ boundary = this->boundary[die];
+ ofs += block << (this->erase_shift - 1);
+ if (block > (boundary + 1))
+ ofs += (block - boundary - 1) << (this->erase_shift - 1);
+ return ofs;
+}
+
+static int do_erase(ulong start, ulong end)
+{
+ struct mtd_info *mtd = &onenand_mtd;
+ struct onenand_chip *this = mtd->priv;
+ struct erase_info instr = {
+ .callback = NULL,
+ };
+ int i, ret;
+ ulong block;
+
+ printf("Erase block from %lu to %lu\n", start, end);
+
+ for (block = start; block <= end; block++) {
+ if (FLEXONENAND(this))
+ instr.addr = flexonenand_get_addr(block);
+ else
+ instr.addr = block << onenand_chip.erase_shift;
+
+ if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) {
+ for (i = 0; i < mtd->numeraseregions &&
+ mtd->eraseregions[i].offset <= instr.addr;
i++)
+ ;
+ i--;
+ instr.len =
+ mtd->eraseregions[i].erasesize;
+ } else
+ instr.len = mtd->erasesize;
+ ret = onenand_erase(&onenand_mtd, &instr);
+ if (ret)
+ printf("erase failed %lu\n", block);
+ }
+ return 0;
+}
int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
+ struct mtd_info *mtd = &onenand_mtd;
+ struct onenand_chip *this = mtd->priv;
int ret = 0;
switch (argc) {
@@ -42,11 +97,7 @@
default:
/* At least 4 args */
if (strncmp(argv[1], "erase", 5) == 0) {
- struct erase_info instr = {
- .callback = NULL,
- };
ulong start, end;
- ulong block;
char *endtail;
if (strncmp(argv[2], "block", 5) == 0) {
@@ -57,28 +108,18 @@
start = simple_strtoul(argv[2], NULL, 10);
end = simple_strtoul(argv[3], NULL, 10);
- start >>= onenand_chip.erase_shift;
- end >>= onenand_chip.erase_shift;
+ start = onenand_get_block(&onenand_mtd,
+ start, NULL);
+ end = onenand_get_block(&onenand_mtd,
+ end, NULL);
/* Don't include the end block */
- end--;
+ if (end > 0)
+ end--;
}
if (!end || end < 0)
end = start;
-
- printf("Erase block from %lu to %lu\n", start, end);
-
- for (block = start; block <= end; block++) {
- instr.addr = block <<
onenand_chip.erase_shift;
- instr.len = 1 << onenand_chip.erase_shift;
- ret = onenand_erase(&onenand_mtd, &instr);
- if (ret) {
- printf("erase failed %lu\n", block);
- break;
- }
- }
-
- return 0;
+ return do_erase(start, end);
}
if (strncmp(argv[1], "read", 4) == 0) {
@@ -134,15 +175,18 @@
ops.mode = MTD_OOB_PLACE;
- ofs = block << onenand_chip.erase_shift;
+ if (FLEXONENAND(this))
+ ofs = flexonenand_get_addr(block);
+ else
+ ofs = block << onenand_chip.erase_shift;
if (page)
ofs += page << onenand_chip.page_shift;
if (!len) {
if (oob)
- ops.ooblen = 64;
+ ops.ooblen = FLEXONENAND(this) ? 128
: 64;
else
- ops.len = 512;
+ ops.len = FLEXONENAND(this) ? 4096 :
512;
}
if (oob) {
@@ -158,6 +202,39 @@
return 0;
}
+ if (strncmp(argv[1], "setboundary", 11) == 0) {
+ unsigned die = simple_strtoul(argv[2], NULL, 0);
+ unsigned bdry = simple_strtoul(argv[3], NULL, 0);
+ int lock = 0, old;
+
+ if (!FLEXONENAND(this)) {
+ printf("Flex-OneNAND not found.\n");
+ return -1;
+ }
+
+ if (argc == 5 && strncmp(argv[4], "LOCK", 4) == 0)
+ lock = 1;
+
+ if (die >= this->dies) {
+ printf("Invalid die index\n");
+ return -1;
+ }
+
+ if (!(bdry % 2)) {
+ printf("Attempt to set even boundary
value.\n");
+ bdry += 1;
+ printf("Setting boundary to %d\n", bdry);
+ }
+
+ old = this->boundary[die] + (die *
this->density_mask);
+ ret = flexonenand_set_boundary(mtd, die, bdry,
lock);
+ if (!ret) {
+ int new = this->boundary[die] +
+ (die * this->density_mask);
+ do_erase(min(old, new) + 1, max(old, new));
+ }
+ return 0;
+ }
break;
}
@@ -172,5 +249,7 @@
"onenand write addr ofs len - write data at ofs with len from
addr\n"
"onenand erase saddr eaddr - erase block start addr to end addr\n"
"onenand block[.oob] addr block [page] [len] - "
- "read data with (block [, page]) to addr"
+ "read data with (block [, page]) to addr\n"
+ "onenand setboundary DIE BOUNDARY [LOCK] - "
+ "Change SLC boundary of Flex-OneNAND"
);
diff --git a/common/env_onenand.c b/common/env_onenand.c
--- a/common/env_onenand.c
+++ b/common/env_onenand.c
@@ -58,11 +58,14 @@
void env_relocate_spec(void)
{
+ struct onenand_chip *this = &onenand_chip;
unsigned long env_addr;
int use_default = 0;
size_t retlen;
env_addr = CONFIG_ENV_ADDR;
+ if (FLEXONENAND(this))
+ env_addr = CONFIG_ENV_ADDR_FLEX;
/* Check OneNAND exist */
if (onenand_mtd.writesize)
@@ -89,6 +92,7 @@
int saveenv(void)
{
+ struct onenand_chip *this = &onenand_chip;
unsigned long env_addr = CONFIG_ENV_ADDR;
struct erase_info instr = {
.callback = NULL,
@@ -96,6 +100,12 @@
size_t retlen;
instr.len = CONFIG_ENV_SIZE;
+ if (FLEXONENAND(this)) {
+ env_addr = CONFIG_ENV_ADDR_FLEX;
+ instr.len = CONFIG_ENV_SIZE_FLEX;
+ instr.len <<= onenand_mtd.eraseregions[0].numblocks == 1 ?
+ 1 : 0;
+ }
instr.addr = env_addr;
if (onenand_erase(&onenand_mtd, &instr)) {
printf("OneNAND: erase failed at 0x%08lx\n", env_addr);
diff --git a/drivers/mtd/onenand/onenand_base.c
b/drivers/mtd/onenand/onenand_base.c
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -45,6 +45,14 @@
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */
};
/**
@@ -83,9 +91,11 @@
if (device & ONENAND_DEVICE_IS_DDP) {
/* Device Flash Core select, NAND Flash Block Address */
int dfs = 0, density, mask;
+ int flex = device & DEVICE_IS_FLEXONENAND;
density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
- mask = (1 << (density + 6));
+ density &= ONENAND_DEVICE_DENSITY_MASK;
+ mask = (1 << (density + (flex ? 4 : 6)));
if (block & mask)
dfs = 1;
@@ -109,9 +119,11 @@
if (device & ONENAND_DEVICE_IS_DDP) {
/* Device BufferRAM Select */
int dbs = 0, density, mask;
+ int flex = device & DEVICE_IS_FLEXONENAND;
density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
- mask = (1 << (density + 6));
+ density &= ONENAND_DEVICE_DENSITY_MASK;
+ mask = (1 << (density + (flex ? 4 : 6)));
if (block & mask)
dbs = 1;
@@ -168,6 +180,41 @@
return ((bsa << ONENAND_BSA_SHIFT) | bsc);
}
+unsigned onenand_get_block(struct mtd_info *mtd, loff_t addr,
+ unsigned *isblkslc)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned boundary, blk, die = 0;
+
+ if (!FLEXONENAND(this))
+ return addr >> this->erase_shift;
+
+ if (this->chipsize == 0) {
+ /* We have been called by read_boundary
+ * addr contains die index in this case
+ */
+ blk = addr * this->density_mask;
+ return blk;
+ }
+
+ if (addr >= this->diesize[0]) {
+ die = 1;
+ addr -= this->diesize[0];
+ }
+
+ boundary = this->boundary[die];
+
+ blk = addr >> (this->erase_shift - 1);
+ if (blk > boundary)
+ blk = (blk + boundary + 1) >> 1;
+
+ if (isblkslc)
+ *isblkslc = (blk <= boundary) ? 1 : 0;
+
+ blk += die ? this->density_mask : 0;
+ return blk;
+}
+
/**
* onenand_command - [DEFAULT] Send command to OneNAND device
* @param mtd MTD device structure
@@ -182,30 +229,36 @@
size_t len)
{
struct onenand_chip *this = mtd->priv;
- int value, readcmd = 0;
+ int value;
int block, page;
+ unsigned slc = 0;
+
/* Now we use page size operation */
- int sectors = 4, count = 4;
+ int sectors = 0, count = 0;
/* Address translation */
switch (cmd) {
case ONENAND_CMD_UNLOCK:
+ case ONENAND_CMD_UNLOCK_ALL:
case ONENAND_CMD_LOCK:
case ONENAND_CMD_LOCK_TIGHT:
block = -1;
page = -1;
break;
+ case FLEXONENAND_CMD_PI_ACCESS:
case ONENAND_CMD_ERASE:
case ONENAND_CMD_BUFFERRAM:
- block = (int)(addr >> this->erase_shift);
+ block = onenand_get_block(mtd, addr, NULL);
page = -1;
break;
default:
- block = (int)(addr >> this->erase_shift);
+ block = onenand_get_block(mtd, addr, &slc);
page = (int)(addr >> this->page_shift);
page &= this->page_mask;
+ if (FLEXONENAND(this) && slc)
+ page &= (this->page_mask >> 1);
break;
}
@@ -216,8 +269,11 @@
this->write_word(value,
this->base + ONENAND_REG_START_ADDRESS2);
- /* Switch to the next data buffer */
- ONENAND_SET_NEXT_BUFFERRAM(this);
+ if (ONENAND_IS_MLC(this))
+ ONENAND_SET_BUFFERRAM0(this);
+ else
+ /* Switch to the next data buffer */
+ ONENAND_SET_NEXT_BUFFERRAM(this);
return 0;
}
@@ -227,6 +283,10 @@
value = onenand_block_address(this->device_id, block);
this->write_word(value,
this->base + ONENAND_REG_START_ADDRESS1);
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this->device_id, block);
+ this->write_word(value,
+ this->base + ONENAND_REG_START_ADDRESS2);
}
if (page != -1) {
@@ -235,8 +295,11 @@
switch (cmd) {
case ONENAND_CMD_READ:
case ONENAND_CMD_READOOB:
- dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
- readcmd = 1;
+ if (ONENAND_IS_MLC(this))
+ dataram = ONENAND_SET_BUFFERRAM0(this);
+ else
+ /* Switch to the next data buffer */
+ dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
break;
default:
@@ -253,14 +316,6 @@
value = onenand_buffer_address(dataram, sectors, count);
this->write_word(value, this->base +
ONENAND_REG_START_BUFFER);
- if (readcmd) {
- /* Select DataRAM for DDP */
- value =
- onenand_bufferram_address(this->device_id,
block);
- this->write_word(value,
- this->base +
- ONENAND_REG_START_ADDRESS2);
- }
}
/* Interrupt clear */
@@ -272,6 +327,29 @@
}
/**
+ * onenand_get_ecc - return ecc status
+ * @param mtd MTD device structure
+ */
+int onenand_get_ecc(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ int ecc[4];
+ int i, result = 0;
+
+ for (i = 0; i < 4; i++) {
+ ecc[i] = this->read_word(this->base + ((0xFF00 + i) << 1));
+ if (!FLEXONENAND(this))
+ return ecc[i];
+ if (ecc[i] & FLEXONENAND_UNCORRECTABLE_ERROR) {
+ result = ONENAND_ECC_2BIT_ALL;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/**
* onenand_wait - [DEFAULT] wait until the command is done
* @param mtd MTD device structure
* @param state state to select the max. timeout value
@@ -295,6 +373,15 @@
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+ if (interrupt & ONENAND_INT_READ) {
+ ecc = onenand_get_ecc(mtd);
+ if (ecc & ONENAND_ECC_2BIT_ALL) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0,
+ "onenand_wait: ECC error = 0x%04x\n",
ecc);
+ return -EBADMSG;
+ }
+ }
+
if (ctrl & ONENAND_CTRL_ERROR) {
MTDDEBUG (MTD_DEBUG_LEVEL0,
"onenand_wait: controller error = 0x%04x\n",
ctrl);
@@ -307,15 +394,6 @@
return -EIO;
}
- if (interrupt & ONENAND_INT_READ) {
- ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
- if (ecc & ONENAND_ECC_2BIT_ALL) {
- MTDDEBUG (MTD_DEBUG_LEVEL0,
- "onenand_wait: ECC error = 0x%04x\n",
ecc);
- return -EBADMSG;
- }
- }
-
return 0;
}
@@ -433,10 +511,13 @@
struct onenand_chip *this = mtd->priv;
int block, page;
int i;
+ unsigned slc = 0;
- block = (int)(addr >> this->erase_shift);
+ block = onenand_get_block(mtd, addr, &slc);
page = (int)(addr >> this->page_shift);
page &= this->page_mask;
+ if (FLEXONENAND(this) && slc)
+ page &= (this->page_mask >> 1);
i = ONENAND_CURRENT_BUFFERRAM(this);
@@ -462,10 +543,13 @@
struct onenand_chip *this = mtd->priv;
int block, page;
int i;
+ unsigned slc = 0;
- block = (int)(addr >> this->erase_shift);
+ block = onenand_get_block(mtd, addr, &slc);
page = (int)(addr >> this->page_shift);
page &= this->page_mask;
+ if (FLEXONENAND(this) && slc)
+ page &= (this->page_mask >> 1);
/* Invalidate BufferRAM */
for (i = 0; i < MAX_BUFFERRAM; i++) {
@@ -573,6 +657,39 @@
}
/**
+ * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
+ * @param mtd MTD device structure
+ * @param addr address to recover
+ * @param status return value from onenand_wait
+ *
+ * Issue recovery command when read fails on MLC area.
+ */
+static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int
status)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned slc = 0;
+
+ /* check if we failed due to uncorrectable error */
+ if (status != (-EBADMSG) && status != (ONENAND_BBT_READ_ECC_ERROR))
+ return status;
+
+ /* check if address lies in MLC region */
+ onenand_get_block(mtd, addr, &slc);
+ if (slc)
+ return status;
+
+ /* We are attempting to reread, so decrement stats.failed
+ * which was incremented by onenand_wait due to read failure
+ */
+ printk(KERN_DEBUG "Attempting to recover from uncorrectable
read\n");
+ mtd->ecc_stats.failed--;
+
+ /* Issue the LSB page recovery command */
+ this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr,
this->writesize);
+ return this->wait(mtd, FL_READING);
+}
+
+/**
* onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or
out-of-band
* @param mtd MTD device structure
* @param from offset to read from
@@ -616,12 +733,15 @@
stats = mtd->ecc_stats;
/* Read-while-load method */
+ /* Note: We can't use this feature in MLC */
/* Do first load to bufferRAM */
if (read < len) {
if (!onenand_check_bufferram(mtd, from)) {
this->command(mtd, ONENAND_CMD_READ, from,
writesize);
ret = this->wait(mtd, FL_READING);
+ ret = (FLEXONENAND(this) && ret) ?
+ onenand_recover_lsb(mtd, from, ret) : ret;
onenand_update_bufferram(mtd, from, !ret);
if (ret == -EBADMSG)
ret = 0;
@@ -636,7 +756,7 @@
while (!ret) {
/* If there is more to load then start next load */
from += thislen;
- if (read + thislen < len) {
+ if (!ONENAND_IS_MLC(this) && read + thislen < len) {
this->command(mtd, ONENAND_CMD_READ, from,
writesize);
/*
* Chip boundary handling in DDP
@@ -669,6 +789,16 @@
oobcolumn = 0;
}
+ if (ONENAND_IS_MLC(this) && (read + thislen < len)) {
+ this->command(mtd, ONENAND_CMD_READ, from,
writesize);
+ ret = this->wait(mtd, FL_READING);
+ ret = (FLEXONENAND(this) && ret) ?
+ onenand_recover_lsb(mtd, from, ret) : ret;
+ onenand_update_bufferram(mtd, from, !ret);
+ if (ret == -EBADMSG)
+ ret = 0;
+ }
+
/* See if we are done */
read += thislen;
if (read == len)
@@ -676,16 +806,19 @@
/* Set up for next read from bufferRAM */
if (unlikely(boundary))
this->write_word(ONENAND_DDP_CHIP1, this->base +
ONENAND_REG_START_ADDRESS2);
- ONENAND_SET_NEXT_BUFFERRAM(this);
+ if (!ONENAND_IS_MLC(this))
+ ONENAND_SET_NEXT_BUFFERRAM(this);
buf += thislen;
thislen = min_t(int, writesize, len - read);
column = 0;
- /* Now wait for load */
- ret = this->wait(mtd, FL_READING);
- onenand_update_bufferram(mtd, from, !ret);
- if (ret == -EBADMSG)
- ret = 0;
+ if (!ONENAND_IS_MLC(this)) {
+ /* Now wait for load */
+ ret = this->wait(mtd, FL_READING);
+ onenand_update_bufferram(mtd, from, !ret);
+ if (ret == -EBADMSG)
+ ret = 0;
+ }
}
/*
@@ -722,7 +855,7 @@
size_t len = ops->ooblen;
mtd_oob_mode_t mode = ops->mode;
u_char *buf = ops->oobbuf;
- int ret = 0;
+ int ret = 0, readcmd;
from += ops->ooboffs;
@@ -755,15 +888,19 @@
stats = mtd->ecc_stats;
+ readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ :
ONENAND_CMD_READOOB;
+
while (read < len) {
thislen = oobsize - column;
thislen = min_t(int, thislen, len);
- this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+ this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0);
ret = this->wait(mtd, FL_READING);
+ ret = (FLEXONENAND(this) && ret) ?
+ onenand_recover_lsb(mtd, from, ret) : ret;
if (ret && ret != -EBADMSG) {
printk(KERN_ERR "onenand_read_oob_nolock: read
failed = 0x%x\n", ret);
break;
@@ -886,22 +1023,26 @@
interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
- /* Initial bad block case: 0x2400 or 0x0400 */
- if (ctrl & ONENAND_CTRL_ERROR) {
- printk(KERN_DEBUG "onenand_bbt_wait: controller error =
0x%04x\n", ctrl);
- return ONENAND_BBT_READ_ERROR;
- }
-
if (interrupt & ONENAND_INT_READ) {
- int ecc = this->read_word(this->base +
ONENAND_REG_ECC_STATUS);
- if (ecc & ONENAND_ECC_2BIT_ALL)
+ int ecc = onenand_get_ecc(mtd);
+ if (ecc & ONENAND_ECC_2BIT_ALL) {
+ printk(KERN_INFO "onenand_bbt_wait: ecc error =
0x%04x"
+ ", controller = 0x%04x\n", ecc, ctrl);
return ONENAND_BBT_READ_ERROR;
+ }
} else {
printk(KERN_ERR "onenand_bbt_wait: read timeout!"
"ctrl=0x%04x intr=0x%04x\n", ctrl,
interrupt);
return ONENAND_BBT_READ_FATAL_ERROR;
}
+ /* Initial bad block case: 0x2400 or 0x0400 */
+ if (ctrl & ONENAND_CTRL_ERROR) {
+ printk(KERN_DEBUG "onenand_bbt_wait: controller error"
+ " = 0x%04x\n", ctrl);
+ return ONENAND_BBT_READ_ERROR;
+ }
+
return 0;
}
@@ -918,7 +1059,7 @@
{
struct onenand_chip *this = mtd->priv;
int read = 0, thislen, column;
- int ret = 0;
+ int ret = 0, readcmd;
size_t len = ops->ooblen;
u_char *buf = ops->oobbuf;
@@ -926,6 +1067,8 @@
"onenand_bbt_read_oob: from = 0x%08x, len = %zi\n",
(unsigned int) from, len);
+ readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ :
ONENAND_CMD_READOOB;
+
/* Initialize return value */
ops->oobretlen = 0;
@@ -945,7 +1088,7 @@
thislen = mtd->oobsize - column;
thislen = min_t(int, thislen, len);
- this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+ this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0);
@@ -987,9 +1130,11 @@
{
struct onenand_chip *this = mtd->priv;
u_char *oob_buf = this->oob_buf;
- int status, i;
+ int status, i, readcmd;
+
+ readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ :
ONENAND_CMD_READOOB;
- this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
+ this->command(mtd, readcmd, to, mtd->oobsize);
onenand_update_bufferram(mtd, to, 0);
status = this->wait(mtd, FL_READING);
if (status)
@@ -1236,7 +1381,7 @@
{
struct onenand_chip *this = mtd->priv;
int column, ret = 0, oobsize;
- int written = 0;
+ int written = 0, oobcmd;
u_char *oobbuf;
size_t len = ops->ooblen;
const u_char *buf = ops->oobbuf;
@@ -1280,6 +1425,8 @@
oobbuf = this->oob_buf;
+ oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG :
ONENAND_CMD_PROGOOB;
+
/* Loop until all data write */
while (written < len) {
int thislen = min_t(int, oobsize, len - written);
@@ -1295,7 +1442,14 @@
memcpy(oobbuf + column, buf, thislen);
this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0,
mtd->oobsize);
- this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
+ if (ONENAND_IS_MLC(this)) {
+ /* Set main area of DataRAM to 0xff*/
+ memset(this->page_buf, 0xff, mtd->writesize);
+ this->write_bufferram(mtd, ONENAND_DATARAM,
+ this->page_buf, 0, mtd->writesize);
+ }
+
+ this->command(mtd, oobcmd, to, mtd->oobsize);
onenand_update_bufferram(mtd, to, 0);
if (ONENAND_IS_2PLANE(this)) {
@@ -1424,19 +1578,30 @@
unsigned int block_size;
loff_t addr;
int len;
- int ret = 0;
+ int ret = 0, i = 0;
MTDDEBUG (MTD_DEBUG_LEVEL3,
"onenand_erase: start = 0x%08x, len = %i\n",
(unsigned int)instr->addr, (unsigned int)ins tr->len);
- block_size = (1 << this->erase_shift);
+ if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) {
+ for (; i < mtd->numeraseregions &&
+ instr->addr >= mtd->eraseregions[i].offset; i++)
+ ;
+ i--;
+ block_size = mtd->eraseregions[i].erasesize;
+ } else
+ block_size = mtd->erasesize;
/* Start address must align on block boundary */
if (unlikely(instr->addr & (block_size - 1))) {
- MTDDEBUG (MTD_DEBUG_LEVEL0,
- "onenand_erase: Unaligned address\n");
- return -EINVAL;
+ /* We come here if boundary is even value. Just skip for
now.*/
+ if (!FLEXONENAND(this) ||
+ (instr->addr & ((block_size >> 1) - 1))) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
+ " Unaligned address\n");
+ return -EINVAL;
+ }
}
/* Length must align on block boundary */
@@ -1466,7 +1631,13 @@
while (len) {
- /* TODO Check badblock */
+ /* Check if we have a bad block, we do not erase bad blocks
*/
+ if (onenand_block_isbad_nolock(mtd, addr, 0)) {
+ printk(KERN_WARNING "onenand_erase: attempt to
erase"
+ "bad block at addr 0x%08x\n", (unsigned int)
addr);
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
@@ -1481,7 +1652,7 @@
else
MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase:
"
"Failed erase, block %d\n",
- (unsigned)(addr >>
this->erase_shift));
+ onenand_get_block(mtd, addr, NULL));
instr->state = MTD_ERASE_FAILED;
instr->fail_addr = addr;
goto erase_exit;
@@ -1489,6 +1660,12 @@
len -= block_size;
addr += block_size;
+ if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) {
+ if ((i < (mtd->numeraseregions - 1)) &&
+ (addr == mtd->eraseregions[i + 1].offset))
+ i++;
+ block_size = mtd->eraseregions[i].erasesize;
+ }
}
instr->state = MTD_ERASE_DONE;
@@ -1539,7 +1716,7 @@
return -EINVAL;
onenand_get_device(mtd, FL_READING);
- ret = onenand_block_isbad_nolock(mtd,ofs, 0);
+ ret = onenand_block_isbad_nolock(mtd, ofs, 0);
onenand_release_device(mtd);
return ret;
}
@@ -1581,8 +1758,8 @@
struct onenand_chip *this = mtd->priv;
int start, end, block, value, status;
- start = ofs >> this->erase_shift;
- end = len >> this->erase_shift;
+ start = onenand_get_block(mtd, ofs, NULL);
+ end = onenand_get_block(mtd, ofs + len, NULL) - 1;
/* Continuous lock scheme */
if (this->options & ONENAND_CONT_LOCK) {
@@ -1590,7 +1767,7 @@
this->write_word(start,
this->base +
ONENAND_REG_START_BLOCK_ADDRESS);
/* Set end block address */
- this->write_word(end - 1,
+ this->write_word(end,
this->base +
ONENAND_REG_END_BLOCK_ADDRESS);
/* Write unlock command */
this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
@@ -1612,12 +1789,23 @@
}
/* Block lock scheme */
- for (block = start; block < end; block++) {
+ for (block = start; block < end + 1; block++) {
+ /* Set block address */
+ value = onenand_block_address(this->device_id, block);
+ this->write_word(value,
+ this->base + ONENAND_REG_START_ADDRESS1);
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this->device_id, block);
+ this->write_word(value,
+ this->base + ONENAND_REG_START_ADDRESS2);
/* Set start block address */
this->write_word(block,
this->base +
ONENAND_REG_START_BLOCK_ADDRESS);
/* Write unlock command */
- this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
+ if (FLEXONENAND(this))
+ this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
+ else
+ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
/* There's no return value */
this->wait(mtd, FL_UNLOCKING);
@@ -1643,6 +1831,25 @@
}
/**
+ * flexonenand_unlock_all - [FlexOneNAND Interface] unlock all blocks
+ * @param mtd MTD device structure
+ *
+ * Unlock all blocks
+ */
+static int flexonenand_unlock_all(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ size_t len = mtd->erasesize;
+
+ if (mtd->numeraseregions > 1)
+ len >>= 1;
+ onenand_unlock(mtd, 0, len);
+ if (this->device_id & ONENAND_DEVICE_IS_DDP)
+ onenand_unlock(mtd, this->diesize[0], len);
+ return 0;
+}
+
+/**
* onenand_print_device_info - Print device ID
* @param device device ID
*
@@ -1650,15 +1857,18 @@
*/
char * onenand_print_device_info(int device)
{
- int vcc, demuxed, ddp, density;
+ int vcc, demuxed, ddp, density, flexonenand;
char *dev_info = malloc(80);
vcc = device & ONENAND_DEVICE_VCC_MASK;
demuxed = device & ONENAND_DEVICE_IS_DEMUX;
ddp = device & ONENAND_DEVICE_IS_DDP;
density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
- sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
- demuxed ? "" : "Muxed ",
+ density &= ONENAND_DEVICE_DENSITY_MASK;
+ flexonenand = device & DEVICE_IS_FLEXONENAND;
+ sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
+ flexonenand ? "Flex-" : "",
+ demuxed ? "" : "Mux",
ddp ? "(DDP)" : "",
(16 << density), vcc ? "2.65/3.3" : "1.8", device);
@@ -1694,6 +1904,174 @@
}
/**
+* flexonenand_get_boundary - Reads the SLC boundary
+* @param onenand_info onenand info structure
+**/
+static int flexonenand_get_boundary(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned die, bdry;
+ int ret, syscfg, unlocked;
+
+ /* Disable ECC */
+ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+ this->write_word((syscfg | 0x0100), this->base +
ONENAND_REG_SYS_CFG1);
+
+ for (die = 0; die < this->dies; die++) {
+ this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
+ this->wait(mtd, FL_SYNCING);
+
+ this->command(mtd, ONENAND_CMD_READ, die, 0);
+ ret = this->wait(mtd, FL_READING);
+
+ bdry = this->read_word(this->base + ONENAND_DATARAM);
+ unlocked = bdry >> FLEXONENAND_PI_UNLOCK_SHIFT;
+ unlocked = (unlocked == 0x3) ? 1 : 0;
+ this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
+
+ this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+ ret = this->wait(mtd, FL_RESETING);
+
+ printk(KERN_INFO "Die %d boundary: %d%s\n", die,
+ this->boundary[die],
+ unlocked ? "(Unlocked)" : "(Locked)");
+ }
+
+ /* Enable ECC */
+ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+ return 0;
+}
+
+/**
+ * get_flexonenand_size - Fill up fields in onenand_chip
+ * boundary[], diesize[], chipsize
+ * @param mtd - MTD device structure
+ */
+void get_flexonenand_size(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ int die, ofs, i, eraseshift;
+ unsigned blksperdie = 1024;
+ unsigned maxbdry = blksperdie - 1;
+
+ eraseshift = this->erase_shift - 1;
+
+ this->chipsize = 0;
+ mtd->numeraseregions = this->dies << 1;
+
+ /* This fills up the device boundary */
+ flexonenand_get_boundary(mtd);
+ die = ofs = 0;
+ i = -1;
+ for (; die < this->dies; die++) {
+ if (!die || this->boundary[die-1] != maxbdry) {
+ i++;
+ mtd->eraseregions[i].offset = ofs;
+ mtd->eraseregions[i].erasesize = 1 << eraseshift;
+ mtd->eraseregions[i].numblocks = this->boundary[die]
+ 1;
+ ofs += mtd->eraseregions[i].numblocks << eraseshift;
+ eraseshift++;
+ } else {
+ mtd->numeraseregions -= 1;
+ mtd->eraseregions[i].numblocks +=
this->boundary[die] + 1;
+ ofs += (this->boundary[die] + 1) << (eraseshift -
1);
+ }
+ if (this->boundary[die] != maxbdry) {
+ i++;
+ mtd->eraseregions[i].offset = ofs;
+ mtd->eraseregions[i].erasesize = 1 << eraseshift;
+ mtd->eraseregions[i].numblocks =
+ maxbdry ^ this->boundary[die];
+ ofs += mtd->eraseregions[i].numblocks << eraseshift;
+ eraseshift--;
+ } else
+ mtd->numeraseregions -= 1;
+ }
+
+ mtd->erasesize = 1 << (this->erase_shift);
+ if (mtd->numeraseregions == 1)
+ mtd->erasesize >>= 1;
+
+ printk(KERN_INFO "Device has %d eraseregions\n",
mtd->numeraseregions);
+ for (i = 0; i < mtd->numeraseregions; i++)
+ printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x,"
+ " numblocks: %04u]\n", mtd->eraseregions[i].offset,
+ mtd->eraseregions[i].erasesize,
+ mtd->eraseregions[i].numblocks);
+
+ eraseshift = this->erase_shift;
+ for (die = 0, mtd->size = 0; die < this->dies; die++) {
+ this->diesize[die] = (blksperdie << eraseshift);
+ this->diesize[die] -= (this->boundary[die] + 1)
+ << (eraseshift - 1);
+ mtd->size += this->diesize[die];
+ }
+ /* this->chipsize represents maximum possible chip size */
+ this->chipsize = (blksperdie << eraseshift) << (this->dies - 1);
+}
+
+/**
+* flexonenand_set_boundary - Writes the SLC boundary
+* @param onenand_info onenand info structure
+**/
+int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die,
+ int boundary, int lock)
+{
+ struct onenand_chip *this = mtd->priv;
+ int ret, density, blksperdie;
+ unsigned addr;
+
+ density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ density &= ONENAND_DEVICE_DENSITY_MASK;
+
+ blksperdie = ((16 << density) << 20) >> this->erase_shift;
+ blksperdie >>= (this->device_id & ONENAND_DEVICE_IS_DDP) ? 1 : 0;
+
+ addr = die ? this->diesize[0] : 0;
+
+ if (this->boundary[die] == boundary)
+ return -1;
+
+ printk(KERN_INFO "Changing boundary: %d%s\n", boundary, lock ?
+ "(Locked)" : "(Unlocked)");
+ if (boundary >= blksperdie) {
+ printk(KERN_ERR "Invalid boundary value.\n");
+ return -1;
+ }
+
+ boundary &= FLEXONENAND_PI_MASK;
+ boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
+
+ this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, addr, 0);
+ this->wait(mtd, FL_SYNCING);
+
+ this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
+ this->wait(mtd, FL_ERASING);
+
+ this->write_word(boundary, this->base + ONENAND_DATARAM);
+ this->command(mtd, ONENAND_CMD_PROG, addr, 0);
+ ret = this->wait(mtd, FL_WRITING);
+ if (ret) {
+ printk(KERN_ERR "Failed PI write for Die %d\n", die);
+ goto out;
+ }
+
+ this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, addr, 0);
+ ret = this->wait(mtd, FL_WRITING);
+ if (ret)
+ printk(KERN_ERR "Failed PI update for Die %d\n", die);
+ else
+ printk(KERN_INFO "Done\n");
+out:
+ this->write_word(ONENAND_CMD_RESET, this->base +
ONENAND_REG_COMMAND);
+ this->wait(mtd, FL_RESETING);
+ if (!ret)
+ /* Recalculate device size on boundary change*/
+ get_flexonenand_size(mtd);
+ return ret;
+}
+
+/**
* onenand_probe - [OneNAND Interface] Probe the OneNAND device
* @param mtd MTD device structure
*
@@ -1727,31 +2105,47 @@
/* Read manufacturer and device IDs from Register */
maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
+ this->technology = this->read_word(this->base +
ONENAND_REG_TECHNOLOGY);
/* Check OneNAND device */
if (maf_id != bram_maf_id || dev_id != bram_dev_id)
return -ENXIO;
- /* FIXME : Current OneNAND MTD doesn't support Flex-OneNAND */
- if (dev_id & (1 << 9)) {
- printk("Not yet support Flex-OneNAND\n");
- return -ENXIO;
- }
-
/* Flash device information */
mtd->name = onenand_print_device_info(dev_id);
this->device_id = dev_id;
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
- this->chipsize = (16 << density) << 20;
+ density &= ONENAND_DEVICE_DENSITY_MASK;
+ if (FLEXONENAND(this)) {
+ this->dies = (dev_id & ONENAND_DEVICE_IS_DDP) ? 2 : 1;
+ /* Maximum possible erase regions */
+ mtd->numeraseregions = this->dies << 1;
+ mtd->eraseregions = malloc(sizeof(struct
mtd_erase_region_info)
+ * (this->dies << 1));
+ if (!mtd->eraseregions)
+ return -ENOMEM;
+ }
+ this->chipsize = FLEXONENAND(this) ? 0 : (16 << density) << 20;
+ /* Set density mask. it is used for DDP */
+ if (dev_id & ONENAND_DEVICE_IS_DDP)
+ this->density_mask = (1 << (density + (FLEXONENAND(this) ?
+ 4 : 6)));
+ else
+ this->density_mask = 0;
/* OneNAND page size & block size */
/* The data buffer size is equal to page size */
mtd->writesize =
this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
+ /* We use the full BufferRAM */
+ if (ONENAND_IS_MLC(this))
+ mtd->writesize <<= 1;
+
mtd->oobsize = mtd->writesize >> 5;
/* Pagers per block is always 64 in OneNAND */
mtd->erasesize = mtd->writesize << 6;
+ mtd->erasesize <<= FLEXONENAND(this) ? 1 : 0;
this->erase_shift = ffs(mtd->erasesize) - 1;
this->page_shift = ffs(mtd->writesize) - 1;
@@ -1762,7 +2156,10 @@
/* REVIST: Multichip handling */
- mtd->size = this->chipsize;
+ if (FLEXONENAND(this))
+ get_flexonenand_size(mtd);
+ else
+ mtd->size = this->chipsize;
/* Version ID */
version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
@@ -1787,6 +2184,11 @@
mtd->block_isbad = onenand_block_isbad;
mtd->block_markbad = onenand_block_markbad;
+ if (FLEXONENAND(this)) {
+ this->options &= ~ONENAND_CONT_LOCK;
+ this->options |= ONENAND_UNLOCK_ALL;
+ }
+
return 0;
}
@@ -1850,7 +2252,8 @@
this->options |= ONENAND_OOBBUF_ALLOC;
}
- onenand_unlock(mtd, 0, mtd->size);
+ FLEXONENAND(this) ? flexonenand_unlock_all(mtd) :
+ onenand_unlock(mtd, 0, mtd->size);
return onenand_default_bbt(mtd);
}
diff --git a/drivers/mtd/onenand/onenand_bbt.c
b/drivers/mtd/onenand/onenand_bbt.c
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -66,6 +66,7 @@
struct bbm_info *bbm = this->bbm;
int i, j, numblocks, len, scanlen;
int startblock;
+ unsigned slc;
loff_t from;
size_t readlen, ooblen;
struct mtd_oob_ops ops;
@@ -82,7 +83,7 @@
/* Note that numblocks is 2 * (real numblocks) here;
* see i += 2 below as it makses shifting and masking less painful
*/
- numblocks = mtd->size >> (bbm->bbt_erase_shift - 1);
+ numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
startblock = 0;
from = 0;
@@ -115,7 +116,13 @@
}
}
i += 2;
- from += (1 << bbm->bbt_erase_shift);
+ if (FLEXONENAND(this)) {
+ onenand_get_block(mtd, from, &slc);
+ from += (1 << bbm->bbt_erase_shift) >> 1;
+ if (!slc)
+ from += (1 << bbm->bbt_erase_shift) >> 1;
+ } else
+ from += (1 << bbm->bbt_erase_shift);
}
return 0;
@@ -152,7 +159,7 @@
uint8_t res;
/* Get block number * 2 */
- block = (int)(offs >> (bbm->bbt_erase_shift - 1));
+ block = (int) (onenand_get_block(mtd, offs, NULL) << 1);
res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
MTDDEBUG (MTD_DEBUG_LEVEL2,
@@ -191,7 +198,7 @@
struct bbm_info *bbm = this->bbm;
int len, ret = 0;
- len = mtd->size >> (this->erase_shift + 2);
+ len = this->chipsize >> (this->erase_shift + 2);
/* Allocate memory (2bit per block) */
bbm->bbt = malloc(len);
if (!bbm->bbt) {
diff --git a/drivers/mtd/onenand/onenand_uboot.c
b/drivers/mtd/onenand/onenand_uboot.c
--- a/drivers/mtd/onenand/onenand_uboot.c
+++ b/drivers/mtd/onenand/onenand_uboot.c
@@ -31,6 +31,8 @@
onenand_scan(&onenand_mtd, 1);
+ if (onenand_chip.device_id & DEVICE_IS_FLEXONENAND)
+ puts("Flex-");
puts("OneNAND: ");
- print_size(onenand_mtd.size, "\n");
+ print_size(onenand_chip.chipsize, "\n");
}
diff --git a/include/configs/apollon.h b/include/configs/apollon.h
--- a/include/configs/apollon.h
+++ b/include/configs/apollon.h
@@ -73,6 +73,7 @@
* Size of malloc() pool
*/
#define CONFIG_ENV_SIZE SZ_128K /* Total Size of Environment Sector
*/
+#define CONFIG_ENV_SIZE_FLEX SZ_256K /* Can change to 512K */
#define CFG_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_128K)
#define CFG_GBL_DATA_SIZE 128 /* bytes reserved for
initial data */
@@ -227,5 +228,5 @@
#define CFG_ONENAND_BASE 0x00000000
#define CONFIG_ENV_IS_IN_ONENAND 1
#define CONFIG_ENV_ADDR 0x00020000
-
+#define CONFIG_ENV_ADDR_FLEX 0x00040000
#endif /* __CONFIG_H */
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -20,6 +20,7 @@
#include <linux/mtd/compat.h>
#include <linux/mtd/bbm.h>
+#define MAX_DIES 2
#define MAX_BUFFERRAM 2
#define MAX_ONENAND_PAGESIZE (2048 + 64)
@@ -43,6 +44,9 @@
/**
* struct onenand_chip - OneNAND Private Flash Chip Data
* @param base [BOARDSPECIFIC] address to access OneNAND
+ * @dies: [INTERN][FLEXONENAND] number of dies on chip
+ * @boundary: [INTERN][FLEXONENAND] Boundary of the dies
+ * @diesize: [INTERN][FLEXONENAND] Size of the dies
* @param chipsize [INTERN] the size of one chip for multichip arrays
* @param device_id [INTERN] device ID
* @param verstion_id [INTERN] version ID
@@ -67,8 +71,13 @@
*/
struct onenand_chip {
void __iomem *base;
+ unsigned int dies;
+ unsigned int boundary[MAX_DIES];
+ unsigned int diesize[MAX_DIES];
unsigned int chipsize;
unsigned int device_id;
+ unsigned int technology;
+ unsigned int density_mask;
unsigned int options;
unsigned int erase_shift;
@@ -116,6 +125,8 @@
#define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0)
#define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1)
+#define FLEXONENAND(this) (this->device_id & DEVICE_IS_FLEXONENAND)
+#define ONENAND_IS_MLC(this) (this->technology &
ONENAND_TECHNOLOGY_IS_MLC)
#define ONENAND_IS_DDP(this) \
(this->device_id & ONENAND_DEVICE_IS_DDP)
@@ -127,6 +138,7 @@
#define ONENAND_CONT_LOCK (0x0001)
#define ONENAND_PAGEBUF_ALLOC (0x1000)
#define ONENAND_OOBBUF_ALLOC (0x2000)
+#define ONENAND_UNLOCK_ALL (0x0002)
/*
* OneNAND Flash Manufacturer ID Codes
@@ -147,4 +159,8 @@
int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
+unsigned onenand_get_block(struct mtd_info *mtd, loff_t addr,
+ unsigned *isblkslc);
+int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die,
+ int bdry, int lock);
#endif /* __LINUX_MTD_ONENAND_H */
diff --git a/include/linux/mtd/onenand_regs.h
b/include/linux/mtd/onenand_regs.h
--- a/include/linux/mtd/onenand_regs.h
+++ b/include/linux/mtd/onenand_regs.h
@@ -67,6 +67,10 @@
/*
* Device ID Register F001h (R)
*/
+#define DEVICE_IS_FLEXONENAND (1 << 9)
+#define FLEXONENAND_PI_MASK (0x3ff)
+#define FLEXONENAND_PI_UNLOCK_SHIFT (14)
+#define ONENAND_DEVICE_DENSITY_MASK (0xf)
#define ONENAND_DEVICE_DENSITY_SHIFT (4)
#define ONENAND_DEVICE_IS_DDP (1 << 3)
#define ONENAND_DEVICE_IS_DEMUX (1 << 2)
@@ -80,6 +84,11 @@
#define ONENAND_VERSION_PROCESS_SHIFT (8)
/*
+ * Technology Register F006h (R)
+ */
+#define ONENAND_TECHNOLOGY_IS_MLC (1 << 0)
+
+/*
* Start Address 1 F100h (R/W)
*/
#define ONENAND_DDP_SHIFT (15)
@@ -89,7 +98,7 @@
/*
* Start Address 8 F107h (R/W)
*/
-#define ONENAND_FPA_MASK (0x3f)
+#define ONENAND_FPA_MASK (0x7f)
#define ONENAND_FPA_SHIFT (2)
#define ONENAND_FSA_MASK (0x03)
@@ -101,7 +110,7 @@
#define ONENAND_BSA_BOOTRAM (0 << 2)
#define ONENAND_BSA_DATARAM0 (2 << 2)
#define ONENAND_BSA_DATARAM1 (3 << 2)
-#define ONENAND_BSC_MASK (0x03)
+#define ONENAND_BSC_MASK (0x07)
/*
* Command Register F220h (R/W)
@@ -113,9 +122,14 @@
#define ONENAND_CMD_UNLOCK (0x23)
#define ONENAND_CMD_LOCK (0x2A)
#define ONENAND_CMD_LOCK_TIGHT (0x2C)
+#define ONENAND_CMD_UNLOCK_ALL (0x27)
#define ONENAND_CMD_ERASE (0x94)
#define ONENAND_CMD_RESET (0xF0)
#define ONENAND_CMD_READID (0x90)
+#define FLEXONENAND_CMD_RESET (0xF3)
+#define FLEXONENAND_CMD_PI_UPDATE (0x05)
+#define FLEXONENAND_CMD_PI_ACCESS (0x66)
+#define FLEXONENAND_CMD_RECOVER_LSB (0x05)
/* NOTE: Those are not *REAL* commands */
#define ONENAND_CMD_BUFFERRAM (0x1978)
@@ -179,5 +193,6 @@
#define ONENAND_ECC_1BIT (1 << 0)
#define ONENAND_ECC_2BIT (1 << 1)
#define ONENAND_ECC_2BIT_ALL (0xAAAA)
+#define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010)
#endif /* __ONENAND_REG_H */
3
2
Hello,
This patch series adds support for automatic software updates from a TFTP
server. First patch introduces some changes in the TFTP timeout handling that
are required by the feature proper, which is contained in the second patch.
Third patch provides minor enhancements to the iminfo output that can become
handy when using the new feature.
Please see newly added doc/README.au_tftp for more details.
Code has been compile-tested on several ppc, arm and mips targets. The feature
itself has been tested on TQM8555 and MPC8555CDS.
Regards,
Bartlomiej Sieka
11
50
Sub-command can benefit from using the same table and search functions
that top level commands have. Expose this functionality by refactoring
find_cmd() and introducing find_cmd_tbl() that sub-command processing
can call.
Signed-off-by: Kumar Gala <galak(a)kernel.crashing.org>
---
common/command.c | 14 ++++++++++----
include/command.h | 7 +++++++
2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/common/command.c b/common/command.c
index aca57b2..fc9d79c 100644
--- a/common/command.c
+++ b/common/command.c
@@ -341,10 +341,10 @@ cmd_tbl_t __u_boot_cmd_question_mark Struct_Section = {
/***************************************************************************
* find command table entry for a command
*/
-cmd_tbl_t *find_cmd (const char *cmd)
+cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
{
cmd_tbl_t *cmdtp;
- cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start; /*Init value */
+ cmd_tbl_t *cmdtp_temp = table; /*Init value */
const char *p;
int len;
int n_found = 0;
@@ -355,8 +355,8 @@ cmd_tbl_t *find_cmd (const char *cmd)
*/
len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
- for (cmdtp = &__u_boot_cmd_start;
- cmdtp != &__u_boot_cmd_end;
+ for (cmdtp = table;
+ cmdtp != table + table_len;
cmdtp++) {
if (strncmp (cmd, cmdtp->name, len) == 0) {
if (len == strlen (cmdtp->name))
@@ -373,6 +373,12 @@ cmd_tbl_t *find_cmd (const char *cmd)
return NULL; /* not found or ambiguous command */
}
+cmd_tbl_t *find_cmd (const char *cmd)
+{
+ int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
+ return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
+}
+
#ifdef CONFIG_AUTO_COMPLETE
int var_complete(int argc, char *argv[], char last_char, int maxv, char *cmdv[])
diff --git a/include/command.h b/include/command.h
index f92383d..78feea5 100644
--- a/include/command.h
+++ b/include/command.h
@@ -62,6 +62,7 @@ extern cmd_tbl_t __u_boot_cmd_end;
/* common/command.c */
cmd_tbl_t *find_cmd(const char *cmd);
+cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len);
#ifdef CONFIG_AUTO_COMPLETE
extern void install_auto_complete(void);
@@ -102,11 +103,17 @@ extern int cmd_get_data_size(char* arg, int default_size);
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
+#define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) \
+{#name, maxargs, rep, cmd, usage, help}
+
#else /* no long help info */
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
+#define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) \
+{#name, maxargs, rep, cmd, usage}
+
#endif /* CFG_LONGHELP */
#endif /* __COMMAND_H */
--
1.5.5.1
2
2

[U-Boot] [PATCH] 85xx: Using proper I2C source clock divider for MPC8544
by Wolfgang Grandegger 14 Oct '08
by Wolfgang Grandegger 14 Oct '08
14 Oct '08
Measurements with our MPC8544 board showed that the I2C bus frequency
is wrong by a factor of 1.5. Obviously, the interpretation of the
MPC85xx_PORDEVSR2_SEC_CFG bit of the cfg_sec_freq register is not
correct. There seems to be an error in the 8544 RM.
Signed-off-by: Wolfgang Grandegger <wg(a)grandegger.com>
---
cpu/mpc85xx/speed.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
Index: u-boot/cpu/mpc85xx/speed.c
===================================================================
--- u-boot.orig/cpu/mpc85xx/speed.c
+++ u-boot/cpu/mpc85xx/speed.c
@@ -101,9 +101,9 @@ int get_clocks (void)
* PORDEVSR2_SEC_CFG bit is 0 on all 85xx boards that are not an 8544.
*/
if (gur->pordevsr2 & MPC85xx_PORDEVSR2_SEC_CFG)
- gd->i2c1_clk = sys_info.freqSystemBus / 3;
- else
gd->i2c1_clk = sys_info.freqSystemBus / 2;
+ else
+ gd->i2c1_clk = sys_info.freqSystemBus / 3;
#else
/* Most 85xx SOCs use CCB/2, so this is the default behavior. */
gd->i2c1_clk = sys_info.freqSystemBus / 2;
2
9
Signed-off-by: Heiko Schocher <hs(a)denx.de>
---
board/keymile/mgsuvd/Makefile | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/board/keymile/mgsuvd/Makefile b/board/keymile/mgsuvd/Makefile
index af0d400..0326608 100644
--- a/board/keymile/mgsuvd/Makefile
+++ b/board/keymile/mgsuvd/Makefile
@@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
-COBJS = $(BOARD).o
+COBJS = $(BOARD).o ../common/common.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
--
1.5.6.1
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
2
1

14 Oct '08
The EEprom contains some Manufacturerinformation,
which are read from u-boot at boot time, and saved
in same Environmentvars.
Signed-off-by: Heiko Schocher <hs(a)denx.de>
---
board/keymile/common/common.c | 285 +++++++++++++++++++++++++++++++++++++++++
include/configs/mgcoge.h | 5 +
include/configs/mgsuvd.h | 5 +
lib_ppc/board.c | 6 +
4 files changed, 301 insertions(+), 0 deletions(-)
diff --git a/board/keymile/common/common.c b/board/keymile/common/common.c
index a6ed379..25c5900 100644
--- a/board/keymile/common/common.c
+++ b/board/keymile/common/common.c
@@ -38,6 +38,291 @@
extern int i2c_mgsuvd_read (void);
#endif
+int ivm_calc_crc (unsigned char *buf, int len)
+{
+ const unsigned short cCrc16Table[16] = {
+ 0x0000, 0xCC01, 0xD801, 0x1400,
+ 0xF001, 0x3C00, 0x2800, 0xE401,
+ 0xA001, 0x6C00, 0x7800, 0xB401,
+ 0x5000, 0x9C01, 0x8801, 0x4400};
+
+ unsigned short crc = 0; /* final result */
+ unsigned short r1 = 0; /* temp */
+ unsigned char byte = 0; /* input buffer */
+ int i;
+
+ /* calculate CRC from array data */
+ for (i = 0; i < len; i++) {
+ byte = buf[i];
+
+ /* lower 4 bits */
+ r1 = cCrc16Table[crc & 0xF];
+ crc = ((crc) >> 4) & 0x0FFF;
+ crc = crc ^ r1 ^ cCrc16Table[byte & 0xF];
+
+ /* upper 4 bits */
+ r1 = cCrc16Table[crc & 0xF];
+ crc = (crc >> 4) & 0x0FFF;
+ crc = crc ^ r1 ^ cCrc16Table[(byte >> 4) & 0xF];
+ }
+ return crc;
+}
+
+
+static int ivm_get_value (unsigned char *buf, int len, char *name, int off, int check)
+{
+ unsigned short val;
+ unsigned char valbuf[30];
+
+ if ((buf[off + 0] != buf[off + 2]) && (buf[off + 2] != buf[off + 4])) {
+ printf ("%s Error corrupted %s\n", __FUNCTION__, name);
+ val = -1;
+ } else {
+ val = buf[off + 0] + (buf[off + 1] << 8);
+ if ((val == 0) && (check == 1))
+ val = -1;
+ }
+ sprintf ((char *)valbuf, "%x", val);
+ setenv (name, (char *)valbuf);
+ return val;
+}
+
+#define BTChar unsigned char
+
+#define INVENTORYBLOCKSIZE 0x100
+#define INVENTORYDATAADDRESS 0x21
+#define INVENTORYDATASIZE (INVENTORYBLOCKSIZE - INVENTORYDATAADDRESS - 3)
+typedef enum {
+ eSymbolShortText = 0, /* Symbol;ShortText */
+ eManufId = 1, /* ManufacturerId */
+ eManufSerialNumber = 2, /* ManufacturerSerialNumber */
+ eManufPartNumber = 3, /* ManufacturerPartNumber */
+ eManufBuildState = 4, /* ManufacturerBuildState */
+ eSuplierPartNumber = 5, /* SuplierPartNumber */
+ eDeliveryDate = 6, /* DeliveryDate */
+ eSupplierBuildState = 7, /* SupplierBuildState */
+ eCustomerId = 8, /* CustomerId */
+ eCustomerProdId = 9, /* CustomerProdId */
+ eChangeHistory = 10, /* ChangeHistory (not used) */
+ eSymbolOnly
+} InventoryData;
+
+static char toAscii(char c)
+{
+ return (c<' ' || c>'~') ? '.' : c;
+}
+
+static int ivm_findInventoryString (InventoryData aType, BTChar* const aString,
+ unsigned long maxLength, unsigned char *buf)
+{
+ int xcode = 0;
+ BTChar cr = '\r';
+ /* Semikolon char */
+ BTChar sc = ';';
+ /* Number of CR found */
+ unsigned long crFound = 0;
+ /* Current address */
+ unsigned long address = INVENTORYDATAADDRESS;
+ /* String length */
+ unsigned long strSize = 0;
+ /* Number of CR to skip */
+ unsigned long nbrOfCR = aType;
+ /* Semicolon to end */
+ int endWithSemikolon = 0;
+
+ /* initialise string */
+ memset(aString, '\0', maxLength);
+
+ switch (aType)
+ {
+ case eSymbolOnly:
+ nbrOfCR = 0;
+ endWithSemikolon = 1;
+ break;
+ default:
+ nbrOfCR = aType;
+ endWithSemikolon = 0;
+ }
+
+ /* Look for the requested number of CR. */
+ while ((crFound != nbrOfCR) && (address < INVENTORYDATASIZE))
+ {
+ if ((buf[address] == cr)) {
+ crFound++;
+ }
+ address++;
+ }
+
+ /* the expected number of CR was found until the end of the IVM
+ * content --> fill string */
+ if (address < INVENTORYDATASIZE)
+ {
+ /* Copy the IVM string in the corresponding string */
+ for (; (buf[address] != cr) && /* not cr found */
+ ((buf[address] != sc) || (!endWithSemikolon)) && /* not semikolon found */
+ (strSize < (maxLength-1) &&
+ (address < INVENTORYDATASIZE)); address++)
+ {
+ strSize += sprintf((char *)aString + strSize, "%c", toAscii(buf[address]));
+ }
+
+ /* copy phase is done: check if everything is ok. If not, the inventory
+ * data is most probably corrupted: tell the world there is a problem! */
+ if (address == INVENTORYDATASIZE)
+ {
+ xcode = -1;
+ printf ("Error end of string not found\n");
+ } else if ((strSize >= (maxLength-1)) && (buf[address] != cr)) {
+ xcode = -1;
+ printf ("string too long till next CR\n");
+ }
+ } else {
+ /* some CR are missing... the inventory data is most probably corrupted */
+ xcode = -1;
+ printf ("not enough cr found\n");
+ }
+ return xcode;
+}
+
+#define GET_STRING(name, which, len) \
+ if (ivm_findInventoryString (which, valbuf, len, buf) == 0) { \
+ setenv (name, (char *)valbuf); \
+ }
+
+static int ivm_check_crc (unsigned char *buf, int block)
+{
+ unsigned long crc;
+ unsigned long crceeprom;
+
+ crc = ivm_calc_crc (buf, CFG_IVM_EEPROM_PAGE_LEN - 2);
+ crceeprom = (buf[CFG_IVM_EEPROM_PAGE_LEN - 1] + \
+ buf[CFG_IVM_EEPROM_PAGE_LEN - 2] * 256);
+ if (crc != crceeprom) {
+ printf ("Error CRC Block: %d EEprom: calculated: %lx EEprom: %lx\n",
+ block, crc, crceeprom);
+ return -1;
+ }
+ return 0;
+}
+
+static int ivm_analyse_block2 (unsigned char *buf, int len)
+{
+ unsigned char valbuf[CFG_IVM_EEPROM_PAGE_LEN];
+ unsigned long nbrsAdrs;
+
+ /* IVM_MacAddress */
+ sprintf ((char *)valbuf, "%02X:%02X:%02X:%02X:%02X:%02X",
+ buf[1],
+ buf[2],
+ buf[3],
+ buf[4],
+ buf[5],
+ buf[6]);
+ setenv ("IVM_MacAddress", (char *)valbuf);
+ if (getenv ("ethaddr") == NULL)
+ setenv ((char *)"ethaddr", (char *)valbuf);
+ /* IVM_MacCount */
+ nbrsAdrs = (buf[10] << 24) +
+ (buf[11] << 16) +
+ (buf[12] << 8) +
+ buf[13];
+ if (nbrsAdrs == 0xffffffff)
+ nbrsAdrs = 1;
+ sprintf ((char *)valbuf, "%lx", nbrsAdrs);
+ setenv ("IVM_MacCount", (char *)valbuf);
+ return 0;
+}
+
+int ivm_analyse_eeprom (unsigned char *buf, int len)
+{
+ unsigned short val;
+ unsigned char valbuf[CFG_IVM_EEPROM_PAGE_LEN];
+ unsigned char *tmp;
+
+ /* used Code from ivm/src/devsrv_baseproductdata.cpp */
+ if (ivm_check_crc (buf, 0) != 0)
+ return -1;
+
+ ivm_get_value (buf, CFG_IVM_EEPROM_PAGE_LEN, "IVM_BoardId", 0, 1);
+ val = ivm_get_value (buf, CFG_IVM_EEPROM_PAGE_LEN, "IVM_HWKey", 6, 1);
+ if (val != 0xffff) {
+ sprintf ((char *)valbuf, "%x", ((val /100) % 10));
+ setenv ("IVM_HWVariant", (char *)valbuf);
+ sprintf ((char *)valbuf, "%x", (val % 100));
+ setenv ("IVM_HWVersion", (char *)valbuf);
+ }
+ ivm_get_value (buf, CFG_IVM_EEPROM_PAGE_LEN, "IVM_Functions", 12, 0);
+
+ GET_STRING("IVM_Symbol", eSymbolOnly, 8)
+ GET_STRING("IVM_DeviceName", eSymbolShortText, 64)
+ tmp = (unsigned char *) getenv("IVM_DeviceName");
+ if (tmp) {
+ int len = strlen ((char *)tmp);
+ int i = 0;
+
+ while (i < len) {
+ if (tmp[i] == ';') {
+ setenv ("IVM_ShortText", (char *)&tmp[i + 1]);
+ break;
+ }
+ i++;
+ }
+ if (i >= len)
+ setenv ("IVM_ShortText", NULL);
+ } else {
+ setenv ("IVM_ShortText", NULL);
+ }
+ GET_STRING("IVM_ManufacturerID", eManufId, 32)
+ GET_STRING("IVM_ManufacturerSerialNumber", eManufSerialNumber, 20)
+ GET_STRING("IVM_ManufacturerPartNumber", eManufPartNumber, 32)
+ GET_STRING("IVM_ManufacturerBuildState", eManufBuildState, 32)
+ GET_STRING("IVM_SupplierPartNumber", eSuplierPartNumber, 32)
+ GET_STRING("IVM_DelieveryDate", eDeliveryDate, 32)
+ GET_STRING("IVM_SupplierBuildState", eSupplierBuildState, 32)
+ GET_STRING("IVM_CustomerID", eCustomerId, 32)
+ GET_STRING("IVM_CustomerProductID", eCustomerProdId, 32)
+
+ if (ivm_check_crc (&buf[CFG_IVM_EEPROM_PAGE_LEN * 2], 2) != 0)
+ return -2;
+ ivm_analyse_block2 (&buf[CFG_IVM_EEPROM_PAGE_LEN * 2], CFG_IVM_EEPROM_PAGE_LEN);
+
+ return 0;
+}
+
+int ivm_read_eeprom(void)
+{
+ I2C_MUX_DEVICE *dev = NULL;
+ uchar i2c_buffer[CFG_IVM_EEPROM_MAX_LEN];
+ uchar *buf;
+ unsigned dev_addr = CFG_IVM_EEPROM_ADR;
+
+ /* First init the Bus, select the Bus */
+#if defined(CFG_I2C_IVM_BUS)
+ dev = i2c_mux_ident_muxstring ((uchar *)CFG_I2C_IVM_BUS);
+#else
+ buf = (unsigned char *) getenv ("EEprom_ivm");
+ if (buf != NULL)
+ dev = i2c_mux_ident_muxstring (buf);
+#endif
+ if (dev == NULL) {
+ printf ("Error couldnt add Bus for IVM\n");
+ return -1;
+ }
+ i2c_set_bus_num (dev->busid);
+
+ buf = (unsigned char *) getenv ("EEprom_ivm_addr");
+ if (buf != NULL)
+ dev_addr = simple_strtoul ((char *)buf, NULL, 16);
+
+ if (eeprom_read (dev_addr, 0, i2c_buffer, CFG_IVM_EEPROM_MAX_LEN) != 0) {
+ printf ("Error reading EEprom\n");
+ return -2;
+ }
+
+ return ivm_analyse_eeprom (i2c_buffer, CFG_IVM_EEPROM_MAX_LEN);
+
+}
+
#if defined(CFG_I2C_INIT_BOARD)
#define DELAY_ABORT_SEQ 62
#define DELAY_HALF_PERIOD (500 / (CFG_I2C_SPEED / 1000))
diff --git a/include/configs/mgcoge.h b/include/configs/mgcoge.h
index 6564c15..b32eb80 100644
--- a/include/configs/mgcoge.h
+++ b/include/configs/mgcoge.h
@@ -212,6 +212,11 @@
#define CFG_EEPROM_PAGE_WRITE_BITS 3
#define CFG_EEPROM_PAGE_WRITE_DELAY_MS 10
+/* Support the IVM EEprom */
+#define CFG_IVM_EEPROM_ADR 0x50
+#define CFG_IVM_EEPROM_MAX_LEN 0x400
+#define CFG_IVM_EEPROM_PAGE_LEN 0x100
+
/* I2C SYSMON (LM75, AD7414 is almost compatible) */
#define CONFIG_DTT_LM75 1 /* ON Semi's LM75 */
#define CONFIG_DTT_SENSORS {0} /* Sensor addresses */
diff --git a/include/configs/mgsuvd.h b/include/configs/mgsuvd.h
index 79954fe..36c7f26 100644
--- a/include/configs/mgsuvd.h
+++ b/include/configs/mgsuvd.h
@@ -382,6 +382,11 @@
#define CFG_EEPROM_PAGE_WRITE_BITS 3
#define CFG_EEPROM_PAGE_WRITE_DELAY_MS 10
+/* Support the IVM EEprom */
+#define CFG_IVM_EEPROM_ADR 0x50
+#define CFG_IVM_EEPROM_MAX_LEN 0x400
+#define CFG_IVM_EEPROM_PAGE_LEN 0x100
+
/* I2C SYSMON (LM75, AD7414 is almost compatible) */
#define CONFIG_DTT_LM75 1 /* ON Semi's LM75 */
#define CONFIG_DTT_SENSORS {0, 2, 4, 6} /* Sensor addresses */
diff --git a/lib_ppc/board.c b/lib_ppc/board.c
index c02ac62..9b5ff41 100644
--- a/lib_ppc/board.c
+++ b/lib_ppc/board.c
@@ -79,6 +79,9 @@
extern int update_flash_size (int flash_size);
#endif
+#if defined(CONFIG_MGCOGE) || defined(CONFIG_MGSUVD)
+extern int ivm_read_eeprom(void);
+#endif
#if defined(CONFIG_SC3)
extern void sc3_read_eeprom(void);
#endif
@@ -869,6 +872,9 @@ void board_init_r (gd_t *id, ulong dest_addr)
#endif /* CONFIG_405GP, CONFIG_405EP */
#endif /* CFG_EXTBDINFO */
+#if defined(CONFIG_MGCOGE) || defined(CONFIG_MGSUVD)
+ ivm_read_eeprom();
+#endif
#if defined(CONFIG_SC3)
sc3_read_eeprom();
#endif
--
1.5.6.1
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
3
4

[U-Boot] [PATCH] I2C: adding new "i2c bus" Command to the I2C Subsystem.
by Heiko Schocher 14 Oct '08
by Heiko Schocher 14 Oct '08
14 Oct '08
With this Command it is possible to add new I2C Busses,
which are behind 1 .. n I2C Muxes. Details see README.
Signed-off-by: Heiko Schocher <hs(a)denx.de>
---
README | 47 ++++++++
common/cmd_i2c.c | 269 +++++++++++++++++++++++++++++++++++++++++++++-
cpu/mpc8260/i2c.c | 15 +++-
drivers/i2c/soft_i2c.c | 15 +++-
include/configs/mgcoge.h | 1 +
include/configs/mgsuvd.h | 1 +
include/i2c.h | 23 ++++
7 files changed, 368 insertions(+), 3 deletions(-)
diff --git a/README b/README
index ccd839c..363b68b 100644
--- a/README
+++ b/README
@@ -1426,6 +1426,53 @@ The following options need to be configured:
Define this option if you want to use Freescale's I2C driver in
drivers/i2c/fsl_i2c.c.
+ CONFIG_I2C_MUX
+
+ Define this option if you have I2C devices reached over 1 .. n
+ I2C Muxes like the pca9544a. This option addes a new I2C
+ Command "i2c bus [muxtype:muxaddr:muxchannel]" which adds a
+ new I2C Bus to the existing I2C Busses. If you select the
+ new Bus with "i2c dev", u-bbot sends first the commandos for
+ the muxes to activate this new "bus".
+
+ CONFIG_I2C_MULTI_BUS must be also defined, to use this
+ feature!
+
+ Example:
+ Adding a new I2C Bus reached over 2 pca9544a muxes
+ The First mux with address 70 and channel 6
+ The Second mux with address 71 and channel 4
+
+ => i2c bus pca9544a:70:6:pca9544a:71:4
+
+ Use the "i2c bus" command without parameter, to get a list
+ of I2C Busses with muxes:
+
+ => i2c bus
+ Busses reached over muxes:
+ Bus ID: 2
+ reached over Mux(es):
+ pca9544a@70 ch: 4
+ Bus ID: 3
+ reached over Mux(es):
+ pca9544a@70 ch: 6
+ pca9544a@71 ch: 4
+ =>
+
+ If you now switch to the new I2C Bus 3 with "i2c dev 3"
+ u-boot sends First the Commando to the mux@70 to enable
+ channel 6, and then the Commando to the mux@71 to enable
+ the channel 4.
+
+ After that, you can use the "normal" i2c commands as
+ usual, to communicate with your I2C devices behind
+ the 2 muxes.
+
+ This option is actually implemented for the bitbanging
+ algorithm in common/soft_i2c.c and for the Hardware I2C
+ Bus on the MPC8260. But it should be not so difficult
+ to add this option to other architectures.
+
- SPI Support: CONFIG_SPI
diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c
index 414c888..3710fc0 100644
--- a/common/cmd_i2c.c
+++ b/common/cmd_i2c.c
@@ -83,7 +83,9 @@
#include <common.h>
#include <command.h>
+#include <environment.h>
#include <i2c.h>
+#include <malloc.h>
#include <asm/byteorder.h>
/* Display values from last command.
@@ -125,6 +127,14 @@ static uchar i2c_no_probes[] = CFG_I2C_NOPROBES;
#define NUM_ELEMENTS_NOPROBE (sizeof(i2c_no_probes)/sizeof(i2c_no_probes[0]))
#endif
+#if defined(CONFIG_I2C_MUX)
+static I2C_MUX_DEVICE *i2c_mux_devices = NULL;
+static int i2c_mux_busid = CFG_MAX_I2C_BUS;
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#endif
+
static int
mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char *argv[]);
@@ -1188,6 +1198,37 @@ int do_i2c_res(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
return 0;
}
+#if defined(CONFIG_I2C_MUX)
+int do_i2c_add_bus(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
+{
+ int ret=0;
+
+ if (argc == 1) {
+ /* show all busses */
+ I2C_MUX *mux;
+ I2C_MUX_DEVICE *device = i2c_mux_devices;
+
+ printf ("Busses reached over muxes:\n");
+ while (device != NULL) {
+ printf ("Bus ID: %x\n", device->busid);
+ printf (" reached over Mux(es):\n");
+ mux = device->mux;
+ while (mux != NULL) {
+ printf (" %s@%x ch: %x\n", mux->name, mux->chip, mux->channel);
+ mux = mux->next;
+ }
+ device = device->next;
+ }
+ } else {
+ I2C_MUX_DEVICE *dev;
+
+ dev = i2c_mux_ident_muxstring ((uchar *)argv[1]);
+ ret = 0;
+ }
+ return ret;
+}
+#endif /* CONFIG_I2C_MUX */
+
#if defined(CONFIG_I2C_MULTI_BUS)
int do_i2c_bus_num(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
@@ -1226,6 +1267,10 @@ int do_i2c_bus_speed(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
int do_i2c(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
+#if defined(CONFIG_I2C_MUX)
+ if (!strncmp(argv[1], "bu", 2))
+ return do_i2c_add_bus(cmdtp, flag, --argc, ++argv);
+#endif /* CONFIG_I2C_MUX */
#if defined(CONFIG_I2C_MULTI_BUS)
if (!strncmp(argv[1], "de", 2))
return do_i2c_bus_num(cmdtp, flag, --argc, ++argv);
@@ -1264,8 +1309,11 @@ int do_i2c(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
U_BOOT_CMD(
i2c, 6, 1, do_i2c,
"i2c - I2C sub-system\n",
+#if defined(CONFIG_I2C_MUX)
+ "bus [muxtype:muxaddr:muxchannel] - add a new bus\n"
+#endif /* CONFIG_I2C_MUX */
#if defined(CONFIG_I2C_MULTI_BUS)
- "dev [dev] - show or set current I2C bus\n"
+ "i2c dev [dev] - show or set current I2C bus\n"
#endif /* CONFIG_I2C_MULTI_BUS */
"i2c speed [speed] - show or set I2C bus speed\n"
"i2c md chip address[.0, .1, .2] [# of objects] - read from I2C device\n"
@@ -1335,3 +1383,222 @@ U_BOOT_CMD(
" (valid chip values 50..57)\n"
);
#endif
+
+#if defined(CONFIG_I2C_MUX)
+
+int i2c_mux_add_device(I2C_MUX_DEVICE *dev)
+{
+ I2C_MUX_DEVICE *devtmp = i2c_mux_devices;
+
+ if (i2c_mux_devices == NULL) {
+ i2c_mux_devices = dev;
+ return 0;
+ }
+ while (devtmp->next != NULL)
+ devtmp = devtmp->next;
+
+ devtmp->next = dev;
+ return 0;
+}
+
+I2C_MUX_DEVICE *i2c_mux_search_device(int id)
+{
+ I2C_MUX_DEVICE *device = i2c_mux_devices;
+
+ while (device != NULL) {
+ if (device->busid == id)
+ return device;
+ device = device->next;
+ }
+ return NULL;
+}
+
+/* searches in the buf from *pos the next ':'.
+ * returns:
+ * 0 if found (with *pos = where)
+ * < 0 if an error occured
+ * > 0 if the end of buf is reached
+ */
+static int i2c_mux_search_next (int *pos, uchar *buf, int len)
+{
+ while ((buf[*pos] != ':') && (*pos < len)) {
+ *pos += 1;
+ }
+ if (*pos >= len)
+ return 1;
+ if (buf[*pos] != ':')
+ return -1;
+ return 0;
+}
+
+static int i2c_mux_get_busid (void)
+{
+ int tmp = i2c_mux_busid;
+
+ i2c_mux_busid ++;
+ return tmp;
+}
+
+/* Analyses a Muxstring and sends immediately the
+ Commands to the Muxes. Runs from Flash.
+ */
+int i2c_mux_ident_muxstring_f (uchar *buf)
+{
+ int pos = 0;
+ int oldpos;
+ int ret = 0;
+ int len = strlen((char *)buf);
+ int chip;
+ uchar channel;
+ int was = 0;
+
+ while (ret == 0) {
+ oldpos = pos;
+ /* search name */
+ ret = i2c_mux_search_next(&pos, buf, len);
+ if (ret != 0)
+ printf ("ERROR\n");
+ /* search address */
+ pos ++;
+ oldpos = pos;
+ ret = i2c_mux_search_next(&pos, buf, len);
+ if (ret != 0)
+ printf ("ERROR\n");
+ buf[pos] = 0;
+ chip = simple_strtoul((char *)&buf[oldpos], NULL, 16);
+ buf[pos] = ':';
+ /* search channel */
+ pos ++;
+ oldpos = pos;
+ ret = i2c_mux_search_next(&pos, buf, len);
+ if (ret < 0)
+ printf ("ERROR\n");
+ was = 0;
+ if (buf[pos] != 0) {
+ buf[pos] = 0;
+ was = 1;
+ }
+ channel = simple_strtoul((char *)&buf[oldpos], NULL, 16);
+ if (was)
+ buf[pos] = ':';
+ if (i2c_write(chip, 0, 0, &channel, 1) != 0) {
+ printf ("Error setting Mux: chip:%x channel: \
+ %x\n", chip, channel);
+ return -1;
+ }
+ pos ++;
+ oldpos = pos;
+
+ }
+
+ return 0;
+}
+
+/* Analyses a Muxstring and if this String is correct
+ * adds a new I2C Bus.
+ */
+I2C_MUX_DEVICE *i2c_mux_ident_muxstring (uchar *buf)
+{
+ I2C_MUX_DEVICE *device;
+ I2C_MUX *mux;
+ int pos = 0;
+ int oldpos;
+ int ret = 0;
+ int len = strlen((char *)buf);
+ int was = 0;
+
+ device = (I2C_MUX_DEVICE *)malloc (sizeof(I2C_MUX_DEVICE));
+ device->mux = NULL;
+ device->busid = i2c_mux_get_busid ();
+ device->next = NULL;
+ while (ret == 0) {
+ mux = (I2C_MUX *)malloc (sizeof(I2C_MUX));
+ mux->next = NULL;
+ /* search name of mux */
+ oldpos = pos;
+ ret = i2c_mux_search_next(&pos, buf, len);
+ if (ret != 0)
+ printf ("%s no name.\n", __FUNCTION__);
+ mux->name = (char *)malloc (pos - oldpos + 1);
+ memcpy (mux->name, &buf[oldpos], pos - oldpos);
+ mux->name[pos - oldpos] = 0;
+ /* search address */
+ pos ++;
+ oldpos = pos;
+ ret = i2c_mux_search_next(&pos, buf, len);
+ if (ret != 0)
+ printf ("%s no mux address.\n", __FUNCTION__);
+ buf[pos] = 0;
+ mux->chip = simple_strtoul((char *)&buf[oldpos], NULL, 16);
+ buf[pos] = ':';
+ /* search channel */
+ pos ++;
+ oldpos = pos;
+ ret = i2c_mux_search_next(&pos, buf, len);
+ if (ret < 0)
+ printf ("%s no mux channel.\n", __FUNCTION__);
+ was = 0;
+ if (buf[pos] != 0) {
+ buf[pos] = 0;
+ was = 1;
+ }
+ mux->channel = simple_strtoul((char *)&buf[oldpos], NULL, 16);
+ if (was)
+ buf[pos] = ':';
+ if (device->mux == NULL)
+ device->mux = mux;
+ else {
+ I2C_MUX *muxtmp = device->mux;
+ while (muxtmp->next != NULL) {
+ muxtmp = muxtmp->next;
+ }
+ muxtmp->next = mux;
+ }
+ pos ++;
+ oldpos = pos;
+ }
+ if (ret > 0) {
+ /* Add Device */
+ i2c_mux_add_device (device);
+ return device;
+ }
+
+ return NULL;
+}
+
+int i2x_mux_select_mux(int bus)
+{
+ I2C_MUX_DEVICE *dev;
+ I2C_MUX *mux;
+
+ if ((gd->flags & GD_FLG_RELOC) != GD_FLG_RELOC) {
+ /* select Default Mux Bus */
+#if defined(CFG_I2C_IVM_BUS)
+ i2c_mux_ident_muxstring_f ((uchar *)CFG_I2C_IVM_BUS);
+#else
+ {
+ unsigned char *buf;
+ buf = (unsigned char *) getenv("EEprom_ivm");
+ if (buf != NULL)
+ i2c_mux_ident_muxstring_f (buf);
+ }
+#endif
+ return 0;
+ }
+ dev = i2c_mux_search_device(bus);
+ if (dev == NULL)
+ return -1;
+
+ mux = dev->mux;
+ while (mux != NULL) {
+ if (i2c_write(mux->chip, 0, 0, &mux->channel, 1) != 0) {
+ printf ("Error setting Mux: chip:%x channel: \
+ %x\n", mux->chip, mux->channel);
+ return -1;
+ }
+ mux = mux->next;
+ }
+ return 0;
+}
+#endif /* CONFIG_I2C_MUX */
+
diff --git a/cpu/mpc8260/i2c.c b/cpu/mpc8260/i2c.c
index 335177f..a96fbf8 100644
--- a/cpu/mpc8260/i2c.c
+++ b/cpu/mpc8260/i2c.c
@@ -780,10 +780,23 @@ unsigned int i2c_get_bus_num(void)
int i2c_set_bus_num(unsigned int bus)
{
+#if defined(CONFIG_I2C_MUX)
+ if (bus < CFG_MAX_I2C_BUS) {
+ i2c_bus_num = bus;
+ } else {
+ int ret;
+
+ ret = i2x_mux_select_mux(bus);
+ if (ret == 0)
+ i2c_bus_num = bus;
+ else
+ return ret;
+ }
+#else
if (bus >= CFG_MAX_I2C_BUS)
return -1;
i2c_bus_num = bus;
-
+#endif
return 0;
}
/* TODO: add 100/400k switching */
diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
index 983b14b..f426bbb 100644
--- a/drivers/i2c/soft_i2c.c
+++ b/drivers/i2c/soft_i2c.c
@@ -249,10 +249,23 @@ unsigned int i2c_get_bus_num(void)
int i2c_set_bus_num(unsigned int bus)
{
+#if defined(CONFIG_I2C_MUX)
+ if (bus < CFG_MAX_I2C_BUS) {
+ i2c_bus_num = bus;
+ } else {
+ int ret;
+
+ ret = i2x_mux_select_mux(bus);
+ if (ret == 0)
+ i2c_bus_num = bus;
+ else
+ return ret;
+ }
+#else
if (bus >= CFG_MAX_I2C_BUS)
return -1;
i2c_bus_num = bus;
-
+#endif
return 0;
}
diff --git a/include/configs/mgcoge.h b/include/configs/mgcoge.h
index 398e092..6564c15 100644
--- a/include/configs/mgcoge.h
+++ b/include/configs/mgcoge.h
@@ -203,6 +203,7 @@
#define CONFIG_I2C_CMD_TREE 1
#define CFG_MAX_I2C_BUS 2
#define CFG_I2C_INIT_BOARD 1
+#define CONFIG_I2C_MUX 1
/* EEprom support */
#define CFG_I2C_EEPROM_ADDR_LEN 1
diff --git a/include/configs/mgsuvd.h b/include/configs/mgsuvd.h
index 17ad6b4..79954fe 100644
--- a/include/configs/mgsuvd.h
+++ b/include/configs/mgsuvd.h
@@ -373,6 +373,7 @@
#define CONFIG_I2C_CMD_TREE 1
#define CFG_MAX_I2C_BUS 2
#define CFG_I2C_INIT_BOARD 1
+#define CONFIG_I2C_MUX 1
/* EEprom support */
#define CFG_I2C_EEPROM_ADDR_LEN 1
diff --git a/include/i2c.h b/include/i2c.h
index a51c164..2ad2db0 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -76,6 +76,29 @@ void i2c_init(int speed, int slaveaddr);
void i2c_init_board(void);
#endif
+#if defined(CONFIG_I2C_MUX)
+
+typedef struct _mux {
+ uchar chip;
+ uchar channel;
+ char *name;
+ struct _mux *next;
+} I2C_MUX;
+
+typedef struct _mux_device {
+ int busid;
+ I2C_MUX *mux; /* List of muxes, to reach the device */
+ struct _mux_device *next;
+} I2C_MUX_DEVICE;
+
+int i2c_mux_add_device(I2C_MUX_DEVICE *dev);
+
+I2C_MUX_DEVICE *i2c_mux_search_device(int id);
+I2C_MUX_DEVICE *i2c_mux_ident_muxstring (uchar *buf);
+int i2x_mux_select_mux(int bus);
+int i2c_mux_ident_muxstring_f (uchar *buf);
+#endif
+
/*
* Probe the given I2C chip address. Returns 0 if a chip responded,
* not 0 on failure.
--
1.5.6.1
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
2
1

14 Oct '08
As Documented in README, adding a boardspecific Deblocking
mechansim via CFG_I2C_INIT_BOARD for the mgcoge and mgsuvd
board.
This code was originally written by keymile in association
with Anatech and Atmel in 1998. The Code toggels the SCL
until the SCA line goes to High (max. 16 times).
And after this, a start Condition is send.
Signed-off-by: Heiko Schocher <hs(a)denx.de>
---
board/keymile/common/common.c | 211 +++++++++++++++++++++++++++++++++++++++++
board/keymile/mgcoge/Makefile | 2 +-
drivers/i2c/soft_i2c.c | 11 ++
include/configs/mgcoge.h | 1 +
include/configs/mgsuvd.h | 1 +
5 files changed, 225 insertions(+), 1 deletions(-)
create mode 100644 board/keymile/common/common.c
diff --git a/board/keymile/common/common.c b/board/keymile/common/common.c
new file mode 100644
index 0000000..a6ed379
--- /dev/null
+++ b/board/keymile/common/common.c
@@ -0,0 +1,211 @@
+/*
+ * (C) Copyright 2008
+ * Heiko Schocher, DENX Software Engineering, hs(a)denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 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 <mpc8260.h>
+#include <ioports.h>
+#include <malloc.h>
+
+#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_OF_LIBFDT)
+#include <libfdt.h>
+#endif
+
+#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
+#include <i2c.h>
+#endif
+
+#if defined(CONFIG_MGSUVD)
+extern int i2c_mgsuvd_read (void);
+#endif
+
+#if defined(CFG_I2C_INIT_BOARD)
+#define DELAY_ABORT_SEQ 62
+#define DELAY_HALF_PERIOD (500 / (CFG_I2C_SPEED / 1000))
+
+#if defined(CONFIG_MGCOGE)
+#define SDA_MASK 0x00010000
+#define SCL_MASK 0x00020000
+static void set_pin (int state, unsigned long mask)
+{
+ volatile ioport_t *iop = ioport_addr ((immap_t *)CFG_IMMR, 3);
+
+ if (state)
+ iop->pdat |= (mask);
+ else
+ iop->pdat &= ~(mask);
+
+ iop->pdir |= (mask);
+}
+
+static int get_pin (unsigned long mask)
+{
+ volatile ioport_t *iop = ioport_addr ((immap_t *)CFG_IMMR, 3);
+
+ iop->pdir &= ~(mask);
+ return (0 != (iop->pdat & (mask)));
+}
+
+static void set_sda (int state)
+{
+ set_pin (state, SDA_MASK);
+}
+
+static void set_scl (int state)
+{
+ set_pin (state, SCL_MASK);
+}
+
+static int get_sda (void)
+{
+ return get_pin (SDA_MASK);
+}
+
+static int get_scl (void)
+{
+ return get_pin (SCL_MASK);
+}
+
+#if defined(CONFIG_HARD_I2C)
+static void setports (int gpio)
+{
+ volatile ioport_t *iop = ioport_addr ((immap_t *)CFG_IMMR, 3);
+
+ if (gpio) {
+ iop->ppar &= ~(SDA_MASK | SCL_MASK);
+ iop->podr &= ~(SDA_MASK | SCL_MASK);
+ } else {
+ iop->ppar |= (SDA_MASK | SCL_MASK);
+ iop->pdir &= ~(SDA_MASK | SCL_MASK);
+ iop->podr |= (SDA_MASK | SCL_MASK);
+ }
+}
+#endif
+#endif
+
+#if defined(CONFIG_MGSUVD)
+static void set_sda (int state)
+{
+ I2C_SDA(state);
+}
+
+static void set_scl (int state)
+{
+ I2C_SCL(state);
+}
+
+static int get_sda (void)
+{
+ return i2c_mgsuvd_read ();
+}
+
+static int get_scl (void)
+{
+ int val;
+
+ *(unsigned short *)(I2C_BASE_DIR) &= ~SCL_CONF;
+ udelay (1);
+ val = *(unsigned char *)(I2C_BASE_PORT);
+
+ return ((val & SCL_BIT) == SCL_BIT);
+}
+
+#endif
+
+static void writeStartSeq (void)
+{
+ set_sda (1);
+ udelay (DELAY_HALF_PERIOD);
+ set_scl (1);
+ udelay (DELAY_HALF_PERIOD);
+ set_sda (0);
+ udelay (DELAY_HALF_PERIOD);
+ set_scl (0);
+ udelay (DELAY_HALF_PERIOD);
+}
+
+/* I2C is a synchronous protocol and resets of the processor in the middle
+ of an access can block the I2C Bus until a powerdown of the full unit is
+ done. This function toggles the SCL until the SCL and SCA line are
+ released, but max. 16 times, after this a I2C start-sequence is sent.
+ This I2C Deblocking mechanism was developed by Keymile in association
+ with Anatech and Atmel in 1998.
+ */
+static int i2c_make_abort (void)
+{
+ int scl_state = 0;
+ int sda_state = 0;
+ int i = 0;
+ int ret = 0;
+
+ if (!get_sda ()) {
+ ret = -1;
+ while (i < 16) {
+ i++;
+ set_scl (0);
+ udelay (DELAY_ABORT_SEQ);
+ set_scl (1);
+ udelay (DELAY_ABORT_SEQ);
+ scl_state = get_scl ();
+ sda_state = get_sda ();
+ if (scl_state && sda_state) {
+ ret = 0;
+ break;
+ }
+ }
+ }
+ if (ret == 0) {
+ for (i =0; i < 5; i++) {
+ writeStartSeq ();
+ }
+ }
+ get_sda ();
+ return ret;
+}
+
+/**
+ * i2c_init_board - reset i2c bus. When the board is powercycled during a
+ * bus transfer it might hang; for details see doc/I2C_Edge_Conditions.
+ */
+void i2c_init_board(void)
+{
+#if defined(CONFIG_HARD_I2C)
+ volatile immap_t *immap = (immap_t *)CFG_IMMR ;
+ volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c;
+
+ /* disable I2C controller first, otherwhise it thinks we want to */
+ /* talk to the slave port... */
+ i2c->i2c_i2mod &= ~0x01;
+
+ /* Set the PortPins to GPIO */
+ setports (1);
+#endif
+
+ /* Now run the AbortSequence() */
+ i2c_make_abort ();
+
+#if defined(CONFIG_HARD_I2C)
+ /* Set the PortPins back to use for I2C */
+ setports (0);
+#endif
+}
+#endif
diff --git a/board/keymile/mgcoge/Makefile b/board/keymile/mgcoge/Makefile
index d4087cc..cbf7129 100644
--- a/board/keymile/mgcoge/Makefile
+++ b/board/keymile/mgcoge/Makefile
@@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
-COBJS := $(BOARD).o
+COBJS := $(BOARD).o ../common/common.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
index 57736da..983b14b 100644
--- a/drivers/i2c/soft_i2c.c
+++ b/drivers/i2c/soft_i2c.c
@@ -75,7 +75,9 @@ static unsigned int i2c_bus_num __attribute__ ((section ("data"))) = 0;
/*-----------------------------------------------------------------------
* Local functions
*/
+#if !defined(CFG_I2C_INIT_BOARD)
static void send_reset (void);
+#endif
static void send_start (void);
static void send_stop (void);
static void send_ack (int);
@@ -83,6 +85,7 @@ static int write_byte (uchar byte);
static uchar read_byte (int);
+#if !defined(CFG_I2C_INIT_BOARD)
/*-----------------------------------------------------------------------
* Send a reset sequence consisting of 9 clocks with the data signal high
* to clock any confused device back into an idle state. Also send a
@@ -115,6 +118,7 @@ static void send_reset(void)
send_stop();
I2C_TRISTATE;
}
+#endif
/*-----------------------------------------------------------------------
* START: High -> Low on SDA while SCL is High
@@ -311,6 +315,12 @@ static uchar read_byte(int ack)
*/
void i2c_init (int speed, int slaveaddr)
{
+#if defined(CFG_I2C_INIT_BOARD)
+ /* call board specific i2c bus reset routine before accessing the */
+ /* environment, which might be in a chip on that bus. For details */
+ /* about this problem see doc/I2C_Edge_Conditions. */
+ i2c_init_board();
+#else
/*
* WARNING: Do NOT save speed in a static variable: if the
* I2C routines are called before RAM is initialized (to read
@@ -318,6 +328,7 @@ void i2c_init (int speed, int slaveaddr)
* system will crash.
*/
send_reset ();
+#endif
}
/*-----------------------------------------------------------------------
diff --git a/include/configs/mgcoge.h b/include/configs/mgcoge.h
index bfbbd45..398e092 100644
--- a/include/configs/mgcoge.h
+++ b/include/configs/mgcoge.h
@@ -202,6 +202,7 @@
#define CONFIG_I2C_MULTI_BUS 1
#define CONFIG_I2C_CMD_TREE 1
#define CFG_MAX_I2C_BUS 2
+#define CFG_I2C_INIT_BOARD 1
/* EEprom support */
#define CFG_I2C_EEPROM_ADDR_LEN 1
diff --git a/include/configs/mgsuvd.h b/include/configs/mgsuvd.h
index 01433ab..17ad6b4 100644
--- a/include/configs/mgsuvd.h
+++ b/include/configs/mgsuvd.h
@@ -372,6 +372,7 @@
#define CONFIG_I2C_MULTI_BUS 1
#define CONFIG_I2C_CMD_TREE 1
#define CFG_MAX_I2C_BUS 2
+#define CFG_I2C_INIT_BOARD 1
/* EEprom support */
#define CFG_I2C_EEPROM_ADDR_LEN 1
--
1.5.6.1
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
2
2
Signed-off-by: Heiko Schocher <hs(a)denx.de>
---
include/configs/mgcoge.h | 9 +++++++++
include/configs/mgsuvd.h | 9 +++++++++
2 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/include/configs/mgcoge.h b/include/configs/mgcoge.h
index 8cff642..bfbbd45 100644
--- a/include/configs/mgcoge.h
+++ b/include/configs/mgcoge.h
@@ -81,6 +81,7 @@
*/
#include <config_cmd_default.h>
+#define CONFIG_CMD_DTT
#define CONFIG_CMD_ECHO
#define CONFIG_CMD_EEPROM
#define CONFIG_CMD_I2C
@@ -209,6 +210,14 @@
#define CFG_EEPROM_PAGE_WRITE_BITS 3
#define CFG_EEPROM_PAGE_WRITE_DELAY_MS 10
+/* I2C SYSMON (LM75, AD7414 is almost compatible) */
+#define CONFIG_DTT_LM75 1 /* ON Semi's LM75 */
+#define CONFIG_DTT_SENSORS {0} /* Sensor addresses */
+#define CFG_DTT_MAX_TEMP 70
+#define CFG_DTT_LOW_TEMP -30
+#define CFG_DTT_HYSTERESIS 3
+#define CFG_DTT_BUS_NUM (CFG_MAX_I2C_BUS)
+
#define CFG_IMMR 0xF0000000
#define CFG_INIT_RAM_ADDR CFG_IMMR
diff --git a/include/configs/mgsuvd.h b/include/configs/mgsuvd.h
index bf8f19e..01433ab 100644
--- a/include/configs/mgsuvd.h
+++ b/include/configs/mgsuvd.h
@@ -113,6 +113,7 @@
#define CONFIG_CMD_ASKENV
#define CONFIG_CMD_DHCP
+#define CONFIG_CMD_DTT
#define CONFIG_CMD_EEPROM
#define CONFIG_CMD_I2C
#define CONFIG_CMD_NFS
@@ -379,4 +380,12 @@
#define CFG_EEPROM_PAGE_WRITE_BITS 3
#define CFG_EEPROM_PAGE_WRITE_DELAY_MS 10
+/* I2C SYSMON (LM75, AD7414 is almost compatible) */
+#define CONFIG_DTT_LM75 1 /* ON Semi's LM75 */
+#define CONFIG_DTT_SENSORS {0, 2, 4, 6} /* Sensor addresses */
+#define CFG_DTT_MAX_TEMP 70
+#define CFG_DTT_LOW_TEMP -30
+#define CFG_DTT_HYSTERESIS 3
+#define CFG_DTT_BUS_NUM (CFG_MAX_I2C_BUS)
+
#endif /* __CONFIG_H */
--
1.5.6.1
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
2
1