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
October 2008
- 175 participants
- 597 discussions

28 Oct '08
UBI (Latin: "where?") stands for "Unsorted Block Images". It is a volume management system for flash devices which manages multiple logical volumes on a single physical flash device and spreads the I/O load (i.e, wear-leveling) across the whole flash chip.
In a sense, UBI may be compared to the Logical Volume Manager (LVM). Whereas LVM maps logical sectors to physical sectors, UBI maps logical eraseblocks to physical eraseblocks. But besides the mapping, UBI implements global wear-leveling and I/O errors handling.
For more details, Please visit the following URL.
http://www.linux-mtd.infradead.org/doc/ubi.html
Signed-off-by: Kyungmin Park <kyungmin.park(a)samsung.com>
---
diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c
new file mode 100644
index 0000000..a6410bf
--- /dev/null
+++ b/drivers/mtd/ubi/misc.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/* Here we keep miscellaneous functions which are used all over the UBI code */
+
+#include <ubi_uboot.h>
+#include "ubi.h"
+
+/**
+ * calc_data_len - calculate how much real data is stored in a buffer.
+ * @ubi: UBI device description object
+ * @buf: a buffer with the contents of the physical eraseblock
+ * @length: the buffer length
+ *
+ * This function calculates how much "real data" is stored in @buf and returnes
+ * the length. Continuous 0xFF bytes at the end of the buffer are not
+ * considered as "real data".
+ */
+int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
+ int length)
+{
+ int i;
+
+ ubi_assert(!(length & (ubi->min_io_size - 1)));
+
+ for (i = length - 1; i >= 0; i--)
+ if (((const uint8_t *)buf)[i] != 0xFF)
+ break;
+
+ /* The resulting length must be aligned to the minimum flash I/O size */
+ length = ALIGN(i + 1, ubi->min_io_size);
+ return length;
+}
+
+/**
+ * ubi_check_volume - check the contents of a static volume.
+ * @ubi: UBI device description object
+ * @vol_id: ID of the volume to check
+ *
+ * This function checks if static volume @vol_id is corrupted by fully reading
+ * it and checking data CRC. This function returns %0 if the volume is not
+ * corrupted, %1 if it is corrupted and a negative error code in case of
+ * failure. Dynamic volumes are not checked and zero is returned immediately.
+ */
+int ubi_check_volume(struct ubi_device *ubi, int vol_id)
+{
+ void *buf;
+ int err = 0, i;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+
+ if (vol->vol_type != UBI_STATIC_VOLUME)
+ return 0;
+
+ buf = vmalloc(vol->usable_leb_size);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < vol->used_ebs; i++) {
+ int size;
+
+ if (i == vol->used_ebs - 1)
+ size = vol->last_eb_bytes;
+ else
+ size = vol->usable_leb_size;
+
+ err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1);
+ if (err) {
+ if (err == -EBADMSG)
+ err = 1;
+ break;
+ }
+ }
+
+ vfree(buf);
+ return err;
+}
+
+/**
+ * ubi_calculate_rsvd_pool - calculate how many PEBs must be reserved for bad
+ * eraseblock handling.
+ * @ubi: UBI device description object
+ */
+void ubi_calculate_reserved(struct ubi_device *ubi)
+{
+ ubi->beb_rsvd_level = ubi->good_peb_count/100;
+ ubi->beb_rsvd_level *= CONFIG_MTD_UBI_BEB_RESERVE;
+ if (ubi->beb_rsvd_level < MIN_RESEVED_PEBS)
+ ubi->beb_rsvd_level = MIN_RESEVED_PEBS;
+}
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
new file mode 100644
index 0000000..03ab247
--- /dev/null
+++ b/drivers/mtd/ubi/scan.c
@@ -0,0 +1,1365 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * UBI scanning unit.
+ *
+ * This unit is responsible for scanning the flash media, checking UBI
+ * headers and providing complete information about the UBI flash image.
+ *
+ * The scanning information is represented by a &struct ubi_scan_info' object.
+ * Information about found volumes is represented by &struct ubi_scan_volume
+ * objects which are kept in volume RB-tree with root at the @volumes field.
+ * The RB-tree is indexed by the volume ID.
+ *
+ * Found logical eraseblocks are represented by &struct ubi_scan_leb objects.
+ * These objects are kept in per-volume RB-trees with the root at the
+ * corresponding &struct ubi_scan_volume object. To put it differently, we keep
+ * an RB-tree of per-volume objects and each of these objects is the root of
+ * RB-tree of per-eraseblock objects.
+ *
+ * Corrupted physical eraseblocks are put to the @corr list, free physical
+ * eraseblocks are put to the @free list and the physical eraseblock to be
+ * erased are put to the @erase list.
+ */
+
+#ifdef UBI_LINUX
+#include <linux/err.h>
+#include <linux/crc32.h>
+#include <asm/div64.h>
+#endif
+
+#include <ubi_uboot.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si);
+#else
+#define paranoid_check_si(ubi, si) 0
+#endif
+
+/* Temporary variables used during scanning */
+static struct ubi_ec_hdr *ech;
+static struct ubi_vid_hdr *vidh;
+
+/**
+ * add_to_list - add physical eraseblock to a list.
+ * @si: scanning information
+ * @pnum: physical eraseblock number to add
+ * @ec: erase counter of the physical eraseblock
+ * @list: the list to add to
+ *
+ * This function adds physical eraseblock @pnum to free, erase, corrupted or
+ * alien lists. Returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int add_to_list(struct ubi_scan_info *si, int pnum, int ec,
+ struct list_head *list)
+{
+ struct ubi_scan_leb *seb;
+
+ if (list == &si->free)
+ dbg_bld("add to free: PEB %d, EC %d", pnum, ec);
+ else if (list == &si->erase)
+ dbg_bld("add to erase: PEB %d, EC %d", pnum, ec);
+ else if (list == &si->corr)
+ dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec);
+ else if (list == &si->alien)
+ dbg_bld("add to alien: PEB %d, EC %d", pnum, ec);
+ else
+ BUG();
+
+ seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
+ if (!seb)
+ return -ENOMEM;
+
+ seb->pnum = pnum;
+ seb->ec = ec;
+ list_add_tail(&seb->u.list, list);
+ return 0;
+}
+
+/**
+ * validate_vid_hdr - check that volume identifier header is correct and
+ * consistent.
+ * @vid_hdr: the volume identifier header to check
+ * @sv: information about the volume this logical eraseblock belongs to
+ * @pnum: physical eraseblock number the VID header came from
+ *
+ * This function checks that data stored in @vid_hdr is consistent. Returns
+ * non-zero if an inconsistency was found and zero if not.
+ *
+ * Note, UBI does sanity check of everything it reads from the flash media.
+ * Most of the checks are done in the I/O unit. Here we check that the
+ * information in the VID header is consistent to the information in other VID
+ * headers of the same volume.
+ */
+static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr,
+ const struct ubi_scan_volume *sv, int pnum)
+{
+ int vol_type = vid_hdr->vol_type;
+ int vol_id = be32_to_cpu(vid_hdr->vol_id);
+ int used_ebs = be32_to_cpu(vid_hdr->used_ebs);
+ int data_pad = be32_to_cpu(vid_hdr->data_pad);
+
+ if (sv->leb_count != 0) {
+ int sv_vol_type;
+
+ /*
+ * This is not the first logical eraseblock belonging to this
+ * volume. Ensure that the data in its VID header is consistent
+ * to the data in previous logical eraseblock headers.
+ */
+
+ if (vol_id != sv->vol_id) {
+ dbg_err("inconsistent vol_id");
+ goto bad;
+ }
+
+ if (sv->vol_type == UBI_STATIC_VOLUME)
+ sv_vol_type = UBI_VID_STATIC;
+ else
+ sv_vol_type = UBI_VID_DYNAMIC;
+
+ if (vol_type != sv_vol_type) {
+ dbg_err("inconsistent vol_type");
+ goto bad;
+ }
+
+ if (used_ebs != sv->used_ebs) {
+ dbg_err("inconsistent used_ebs");
+ goto bad;
+ }
+
+ if (data_pad != sv->data_pad) {
+ dbg_err("inconsistent data_pad");
+ goto bad;
+ }
+ }
+
+ return 0;
+
+bad:
+ ubi_err("inconsistent VID header at PEB %d", pnum);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ ubi_dbg_dump_sv(sv);
+ return -EINVAL;
+}
+
+/**
+ * add_volume - add volume to the scanning information.
+ * @si: scanning information
+ * @vol_id: ID of the volume to add
+ * @pnum: physical eraseblock number
+ * @vid_hdr: volume identifier header
+ *
+ * If the volume corresponding to the @vid_hdr logical eraseblock is already
+ * present in the scanning information, this function does nothing. Otherwise
+ * it adds corresponding volume to the scanning information. Returns a pointer
+ * to the scanning volume object in case of success and a negative error code
+ * in case of failure.
+ */
+static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id,
+ int pnum,
+ const struct ubi_vid_hdr *vid_hdr)
+{
+ struct ubi_scan_volume *sv;
+ struct rb_node **p = &si->volumes.rb_node, *parent = NULL;
+
+ ubi_assert(vol_id == be32_to_cpu(vid_hdr->vol_id));
+
+ /* Walk the volume RB-tree to look if this volume is already present */
+ while (*p) {
+ parent = *p;
+ sv = rb_entry(parent, struct ubi_scan_volume, rb);
+
+ if (vol_id == sv->vol_id)
+ return sv;
+
+ if (vol_id > sv->vol_id)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ /* The volume is absent - add it */
+ sv = kmalloc(sizeof(struct ubi_scan_volume), GFP_KERNEL);
+ if (!sv)
+ return ERR_PTR(-ENOMEM);
+
+ sv->highest_lnum = sv->leb_count = 0;
+ sv->vol_id = vol_id;
+ sv->root = RB_ROOT;
+ sv->used_ebs = be32_to_cpu(vid_hdr->used_ebs);
+ sv->data_pad = be32_to_cpu(vid_hdr->data_pad);
+ sv->compat = vid_hdr->compat;
+ sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME
+ : UBI_STATIC_VOLUME;
+ if (vol_id > si->highest_vol_id)
+ si->highest_vol_id = vol_id;
+
+ rb_link_node(&sv->rb, parent, p);
+ rb_insert_color(&sv->rb, &si->volumes);
+ si->vols_found += 1;
+ dbg_bld("added volume %d", vol_id);
+ return sv;
+}
+
+/**
+ * compare_lebs - find out which logical eraseblock is newer.
+ * @ubi: UBI device description object
+ * @seb: first logical eraseblock to compare
+ * @pnum: physical eraseblock number of the second logical eraseblock to
+ * compare
+ * @vid_hdr: volume identifier header of the second logical eraseblock
+ *
+ * This function compares 2 copies of a LEB and informs which one is newer. In
+ * case of success this function returns a positive value, in case of failure, a
+ * negative error code is returned. The success return codes use the following
+ * bits:
+ * o bit 0 is cleared: the first PEB (described by @seb) is newer then the
+ * second PEB (described by @pnum and @vid_hdr);
+ * o bit 0 is set: the second PEB is newer;
+ * o bit 1 is cleared: no bit-flips were detected in the newer LEB;
+ * o bit 1 is set: bit-flips were detected in the newer LEB;
+ * o bit 2 is cleared: the older LEB is not corrupted;
+ * o bit 2 is set: the older LEB is corrupted.
+ */
+static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb,
+ int pnum, const struct ubi_vid_hdr *vid_hdr)
+{
+ void *buf;
+ int len, err, second_is_newer, bitflips = 0, corrupted = 0;
+ uint32_t data_crc, crc;
+ struct ubi_vid_hdr *vh = NULL;
+ unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum);
+
+ if (seb->sqnum == 0 && sqnum2 == 0) {
+ long long abs, v1 = seb->leb_ver, v2 = be32_to_cpu(vid_hdr->leb_ver);
+
+ /*
+ * UBI constantly increases the logical eraseblock version
+ * number and it can overflow. Thus, we have to bear in mind
+ * that versions that are close to %0xFFFFFFFF are less then
+ * versions that are close to %0.
+ *
+ * The UBI WL unit guarantees that the number of pending tasks
+ * is not greater then %0x7FFFFFFF. So, if the difference
+ * between any two versions is greater or equivalent to
+ * %0x7FFFFFFF, there was an overflow and the logical
+ * eraseblock with lower version is actually newer then the one
+ * with higher version.
+ *
+ * FIXME: but this is anyway obsolete and will be removed at
+ * some point.
+ */
+ dbg_bld("using old crappy leb_ver stuff");
+
+ if (v1 == v2) {
+ ubi_err("PEB %d and PEB %d have the same version %lld",
+ seb->pnum, pnum, v1);
+ return -EINVAL;
+ }
+
+ abs = v1 - v2;
+ if (abs < 0)
+ abs = -abs;
+
+ if (abs < 0x7FFFFFFF)
+ /* Non-overflow situation */
+ second_is_newer = (v2 > v1);
+ else
+ second_is_newer = (v2 < v1);
+ } else
+ /* Obviously the LEB with lower sequence counter is older */
+ second_is_newer = sqnum2 > seb->sqnum;
+
+ /*
+ * Now we know which copy is newer. If the copy flag of the PEB with
+ * newer version is not set, then we just return, otherwise we have to
+ * check data CRC. For the second PEB we already have the VID header,
+ * for the first one - we'll need to re-read it from flash.
+ *
+ * FIXME: this may be optimized so that we wouldn't read twice.
+ */
+
+ if (second_is_newer) {
+ if (!vid_hdr->copy_flag) {
+ /* It is not a copy, so it is newer */
+ dbg_bld("second PEB %d is newer, copy_flag is unset",
+ pnum);
+ return 1;
+ }
+ } else {
+ pnum = seb->pnum;
+
+ vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
+ if (!vh)
+ return -ENOMEM;
+
+ err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0);
+ if (err) {
+ if (err == UBI_IO_BITFLIPS)
+ bitflips = 1;
+ else {
+ dbg_err("VID of PEB %d header is bad, but it "
+ "was OK earlier", pnum);
+ if (err > 0)
+ err = -EIO;
+
+ goto out_free_vidh;
+ }
+ }
+
+ if (!vh->copy_flag) {
+ /* It is not a copy, so it is newer */
+ dbg_bld("first PEB %d is newer, copy_flag is unset",
+ pnum);
+ err = bitflips << 1;
+ goto out_free_vidh;
+ }
+
+ vid_hdr = vh;
+ }
+
+ /* Read the data of the copy and check the CRC */
+
+ len = be32_to_cpu(vid_hdr->data_size);
+ buf = vmalloc(len);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out_free_vidh;
+ }
+
+ err = ubi_io_read_data(ubi, buf, pnum, 0, len);
+ if (err && err != UBI_IO_BITFLIPS)
+ goto out_free_buf;
+
+ data_crc = be32_to_cpu(vid_hdr->data_crc);
+ crc = crc32(UBI_CRC32_INIT, buf, len);
+ if (crc != data_crc) {
+ dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x",
+ pnum, crc, data_crc);
+ corrupted = 1;
+ bitflips = 0;
+ second_is_newer = !second_is_newer;
+ } else {
+ dbg_bld("PEB %d CRC is OK", pnum);
+ bitflips = !!err;
+ }
+
+ vfree(buf);
+ ubi_free_vid_hdr(ubi, vh);
+
+ if (second_is_newer)
+ dbg_bld("second PEB %d is newer, copy_flag is set", pnum);
+ else
+ dbg_bld("first PEB %d is newer, copy_flag is set", pnum);
+
+ return second_is_newer | (bitflips << 1) | (corrupted << 2);
+
+out_free_buf:
+ vfree(buf);
+out_free_vidh:
+ ubi_free_vid_hdr(ubi, vh);
+ return err;
+}
+
+/**
+ * ubi_scan_add_used - add information about a physical eraseblock to the
+ * scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @pnum: the physical eraseblock number
+ * @ec: erase counter
+ * @vid_hdr: the volume identifier header
+ * @bitflips: if bit-flips were detected when this physical eraseblock was read
+ *
+ * This function adds information about a used physical eraseblock to the
+ * 'used' tree of the corresponding volume. The function is rather complex
+ * because it has to handle cases when this is not the first physical
+ * eraseblock belonging to the same logical eraseblock, and the newer one has
+ * to be picked, while the older one has to be dropped. This function returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
+ int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
+ int bitflips)
+{
+ int err, vol_id, lnum;
+ uint32_t leb_ver;
+ unsigned long long sqnum;
+ struct ubi_scan_volume *sv;
+ struct ubi_scan_leb *seb;
+ struct rb_node **p, *parent = NULL;
+
+ vol_id = be32_to_cpu(vid_hdr->vol_id);
+ lnum = be32_to_cpu(vid_hdr->lnum);
+ sqnum = be64_to_cpu(vid_hdr->sqnum);
+ leb_ver = be32_to_cpu(vid_hdr->leb_ver);
+
+ dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d",
+ pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips);
+
+ sv = add_volume(si, vol_id, pnum, vid_hdr);
+ if (IS_ERR(sv) < 0)
+ return PTR_ERR(sv);
+
+ if (si->max_sqnum < sqnum)
+ si->max_sqnum = sqnum;
+
+ /*
+ * Walk the RB-tree of logical eraseblocks of volume @vol_id to look
+ * if this is the first instance of this logical eraseblock or not.
+ */
+ p = &sv->root.rb_node;
+ while (*p) {
+ int cmp_res;
+
+ parent = *p;
+ seb = rb_entry(parent, struct ubi_scan_leb, u.rb);
+ if (lnum != seb->lnum) {
+ if (lnum < seb->lnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ continue;
+ }
+
+ /*
+ * There is already a physical eraseblock describing the same
+ * logical eraseblock present.
+ */
+
+ dbg_bld("this LEB already exists: PEB %d, sqnum %llu, "
+ "LEB ver %u, EC %d", seb->pnum, seb->sqnum,
+ seb->leb_ver, seb->ec);
+
+ /*
+ * Make sure that the logical eraseblocks have different
+ * versions. Otherwise the image is bad.
+ */
+ if (seb->leb_ver == leb_ver && leb_ver != 0) {
+ ubi_err("two LEBs with same version %u", leb_ver);
+ ubi_dbg_dump_seb(seb, 0);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure that the logical eraseblocks have different
+ * sequence numbers. Otherwise the image is bad.
+ *
+ * FIXME: remove 'sqnum != 0' check when leb_ver is removed.
+ */
+ if (seb->sqnum == sqnum && sqnum != 0) {
+ ubi_err("two LEBs with same sequence number %llu",
+ sqnum);
+ ubi_dbg_dump_seb(seb, 0);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ return -EINVAL;
+ }
+
+ /*
+ * Now we have to drop the older one and preserve the newer
+ * one.
+ */
+ cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr);
+ if (cmp_res < 0)
+ return cmp_res;
+
+ if (cmp_res & 1) {
+ /*
+ * This logical eraseblock is newer then the one
+ * found earlier.
+ */
+ err = validate_vid_hdr(vid_hdr, sv, pnum);
+ if (err)
+ return err;
+
+ if (cmp_res & 4)
+ err = add_to_list(si, seb->pnum, seb->ec,
+ &si->corr);
+ else
+ err = add_to_list(si, seb->pnum, seb->ec,
+ &si->erase);
+ if (err)
+ return err;
+
+ seb->ec = ec;
+ seb->pnum = pnum;
+ seb->scrub = ((cmp_res & 2) || bitflips);
+ seb->sqnum = sqnum;
+ seb->leb_ver = leb_ver;
+
+ if (sv->highest_lnum == lnum)
+ sv->last_data_size =
+ be32_to_cpu(vid_hdr->data_size);
+
+ return 0;
+ } else {
+ /*
+ * This logical eraseblock is older then the one found
+ * previously.
+ */
+ if (cmp_res & 4)
+ return add_to_list(si, pnum, ec, &si->corr);
+ else
+ return add_to_list(si, pnum, ec, &si->erase);
+ }
+ }
+
+ /*
+ * We've met this logical eraseblock for the first time, add it to the
+ * scanning information.
+ */
+
+ err = validate_vid_hdr(vid_hdr, sv, pnum);
+ if (err)
+ return err;
+
+ seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
+ if (!seb)
+ return -ENOMEM;
+
+ seb->ec = ec;
+ seb->pnum = pnum;
+ seb->lnum = lnum;
+ seb->sqnum = sqnum;
+ seb->scrub = bitflips;
+ seb->leb_ver = leb_ver;
+
+ if (sv->highest_lnum <= lnum) {
+ sv->highest_lnum = lnum;
+ sv->last_data_size = be32_to_cpu(vid_hdr->data_size);
+ }
+
+ sv->leb_count += 1;
+ rb_link_node(&seb->u.rb, parent, p);
+ rb_insert_color(&seb->u.rb, &sv->root);
+ return 0;
+}
+
+/**
+ * ubi_scan_find_sv - find information about a particular volume in the
+ * scanning information.
+ * @si: scanning information
+ * @vol_id: the requested volume ID
+ *
+ * This function returns a pointer to the volume description or %NULL if there
+ * are no data about this volume in the scanning information.
+ */
+struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
+ int vol_id)
+{
+ struct ubi_scan_volume *sv;
+ struct rb_node *p = si->volumes.rb_node;
+
+ while (p) {
+ sv = rb_entry(p, struct ubi_scan_volume, rb);
+
+ if (vol_id == sv->vol_id)
+ return sv;
+
+ if (vol_id > sv->vol_id)
+ p = p->rb_left;
+ else
+ p = p->rb_right;
+ }
+
+ return NULL;
+}
+
+/**
+ * ubi_scan_find_seb - find information about a particular logical
+ * eraseblock in the volume scanning information.
+ * @sv: a pointer to the volume scanning information
+ * @lnum: the requested logical eraseblock
+ *
+ * This function returns a pointer to the scanning logical eraseblock or %NULL
+ * if there are no data about it in the scanning volume information.
+ */
+struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv,
+ int lnum)
+{
+ struct ubi_scan_leb *seb;
+ struct rb_node *p = sv->root.rb_node;
+
+ while (p) {
+ seb = rb_entry(p, struct ubi_scan_leb, u.rb);
+
+ if (lnum == seb->lnum)
+ return seb;
+
+ if (lnum > seb->lnum)
+ p = p->rb_left;
+ else
+ p = p->rb_right;
+ }
+
+ return NULL;
+}
+
+/**
+ * ubi_scan_rm_volume - delete scanning information about a volume.
+ * @si: scanning information
+ * @sv: the volume scanning information to delete
+ */
+void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv)
+{
+ struct rb_node *rb;
+ struct ubi_scan_leb *seb;
+
+ dbg_bld("remove scanning information about volume %d", sv->vol_id);
+
+ while ((rb = rb_first(&sv->root))) {
+ seb = rb_entry(rb, struct ubi_scan_leb, u.rb);
+ rb_erase(&seb->u.rb, &sv->root);
+ list_add_tail(&seb->u.list, &si->erase);
+ }
+
+ rb_erase(&sv->rb, &si->volumes);
+ kfree(sv);
+ si->vols_found -= 1;
+}
+
+/**
+ * ubi_scan_erase_peb - erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @pnum: physical eraseblock number to erase;
+ * @ec: erase counter value to write (%UBI_SCAN_UNKNOWN_EC if it is unknown)
+ *
+ * This function erases physical eraseblock 'pnum', and writes the erase
+ * counter header to it. This function should only be used on UBI device
+ * initialization stages, when the EBA unit had not been yet initialized. This
+ * function returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si,
+ int pnum, int ec)
+{
+ int err;
+ struct ubi_ec_hdr *ec_hdr;
+
+ if ((long long)ec >= UBI_MAX_ERASECOUNTER) {
+ /*
+ * Erase counter overflow. Upgrade UBI and use 64-bit
+ * erase counters internally.
+ */
+ ubi_err("erase counter overflow at PEB %d, EC %d", pnum, ec);
+ return -EINVAL;
+ }
+
+ ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+ if (!ec_hdr)
+ return -ENOMEM;
+
+ ec_hdr->ec = cpu_to_be64(ec);
+
+ err = ubi_io_sync_erase(ubi, pnum, 0);
+ if (err < 0)
+ goto out_free;
+
+ err = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr);
+
+out_free:
+ kfree(ec_hdr);
+ return err;
+}
+
+/**
+ * ubi_scan_get_free_peb - get a free physical eraseblock.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns a free physical eraseblock. It is supposed to be
+ * called on the UBI initialization stages when the wear-leveling unit is not
+ * initialized yet. This function picks a physical eraseblocks from one of the
+ * lists, writes the EC header if it is needed, and removes it from the list.
+ *
+ * This function returns scanning physical eraseblock information in case of
+ * success and an error code in case of failure.
+ */
+struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi,
+ struct ubi_scan_info *si)
+{
+ int err = 0, i;
+ struct ubi_scan_leb *seb;
+
+ if (!list_empty(&si->free)) {
+ seb = list_entry(si->free.next, struct ubi_scan_leb, u.list);
+ list_del(&seb->u.list);
+ dbg_bld("return free PEB %d, EC %d", seb->pnum, seb->ec);
+ return seb;
+ }
+
+ for (i = 0; i < 2; i++) {
+ struct list_head *head;
+ struct ubi_scan_leb *tmp_seb;
+
+ if (i == 0)
+ head = &si->erase;
+ else
+ head = &si->corr;
+
+ /*
+ * We try to erase the first physical eraseblock from the @head
+ * list and pick it if we succeed, or try to erase the
+ * next one if not. And so forth. We don't want to take care
+ * about bad eraseblocks here - they'll be handled later.
+ */
+ list_for_each_entry_safe(seb, tmp_seb, head, u.list) {
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+
+ err = ubi_scan_erase_peb(ubi, si, seb->pnum, seb->ec+1);
+ if (err)
+ continue;
+
+ seb->ec += 1;
+ list_del(&seb->u.list);
+ dbg_bld("return PEB %d, EC %d", seb->pnum, seb->ec);
+ return seb;
+ }
+ }
+
+ ubi_err("no eraseblocks found");
+ return ERR_PTR(-ENOSPC);
+}
+
+/**
+ * process_eb - read UBI headers, check them and add corresponding data
+ * to the scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @pnum: the physical eraseblock number
+ *
+ * This function returns a zero if the physical eraseblock was successfully
+ * handled and a negative error code in case of failure.
+ */
+static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum)
+{
+ long long uninitialized_var(ec);
+ int err, bitflips = 0, vol_id, ec_corr = 0;
+
+ dbg_bld("scan PEB %d", pnum);
+
+ /* Skip bad physical eraseblocks */
+ err = ubi_io_is_bad(ubi, pnum);
+ if (err < 0)
+ return err;
+ else if (err) {
+ /*
+ * FIXME: this is actually duty of the I/O unit to initialize
+ * this, but MTD does not provide enough information.
+ */
+ si->bad_peb_count += 1;
+ return 0;
+ }
+
+ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
+ if (err < 0)
+ return err;
+ else if (err == UBI_IO_BITFLIPS)
+ bitflips = 1;
+ else if (err == UBI_IO_PEB_EMPTY)
+ return add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, &si->erase);
+ else if (err == UBI_IO_BAD_EC_HDR) {
+ /*
+ * We have to also look at the VID header, possibly it is not
+ * corrupted. Set %bitflips flag in order to make this PEB be
+ * moved and EC be re-created.
+ */
+ ec_corr = 1;
+ ec = UBI_SCAN_UNKNOWN_EC;
+ bitflips = 1;
+ }
+
+ si->is_empty = 0;
+
+ if (!ec_corr) {
+ /* Make sure UBI version is OK */
+ if (ech->version != UBI_VERSION) {
+ ubi_err("this UBI version is %d, image version is %d",
+ UBI_VERSION, (int)ech->version);
+ return -EINVAL;
+ }
+
+ ec = be64_to_cpu(ech->ec);
+ if (ec > UBI_MAX_ERASECOUNTER) {
+ /*
+ * Erase counter overflow. The EC headers have 64 bits
+ * reserved, but we anyway make use of only 31 bit
+ * values, as this seems to be enough for any existing
+ * flash. Upgrade UBI and use 64-bit erase counters
+ * internally.
+ */
+ ubi_err("erase counter overflow, max is %d",
+ UBI_MAX_ERASECOUNTER);
+ ubi_dbg_dump_ec_hdr(ech);
+ return -EINVAL;
+ }
+ }
+
+ /* OK, we've done with the EC header, let's look at the VID header */
+
+ err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0);
+ if (err < 0)
+ return err;
+ else if (err == UBI_IO_BITFLIPS)
+ bitflips = 1;
+ else if (err == UBI_IO_BAD_VID_HDR ||
+ (err == UBI_IO_PEB_FREE && ec_corr)) {
+ /* VID header is corrupted */
+ err = add_to_list(si, pnum, ec, &si->corr);
+ if (err)
+ return err;
+ goto adjust_mean_ec;
+ } else if (err == UBI_IO_PEB_FREE) {
+ /* No VID header - the physical eraseblock is free */
+ err = add_to_list(si, pnum, ec, &si->free);
+ if (err)
+ return err;
+ goto adjust_mean_ec;
+ }
+
+ vol_id = be32_to_cpu(vidh->vol_id);
+ if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) {
+ int lnum = be32_to_cpu(vidh->lnum);
+
+ /* Unsupported internal volume */
+ switch (vidh->compat) {
+ case UBI_COMPAT_DELETE:
+ ubi_msg("\"delete\" compatible internal volume %d:%d"
+ " found, remove it", vol_id, lnum);
+ err = add_to_list(si, pnum, ec, &si->corr);
+ if (err)
+ return err;
+ break;
+
+ case UBI_COMPAT_RO:
+ ubi_msg("read-only compatible internal volume %d:%d"
+ " found, switch to read-only mode",
+ vol_id, lnum);
+ ubi->ro_mode = 1;
+ break;
+
+ case UBI_COMPAT_PRESERVE:
+ ubi_msg("\"preserve\" compatible internal volume %d:%d"
+ " found", vol_id, lnum);
+ err = add_to_list(si, pnum, ec, &si->alien);
+ if (err)
+ return err;
+ si->alien_peb_count += 1;
+ return 0;
+
+ case UBI_COMPAT_REJECT:
+ ubi_err("incompatible internal volume %d:%d found",
+ vol_id, lnum);
+ return -EINVAL;
+ }
+ }
+
+ /* Both UBI headers seem to be fine */
+ err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips);
+ if (err)
+ return err;
+
+adjust_mean_ec:
+ if (!ec_corr) {
+ si->ec_sum += ec;
+ si->ec_count += 1;
+ if (ec > si->max_ec)
+ si->max_ec = ec;
+ if (ec < si->min_ec)
+ si->min_ec = ec;
+ }
+
+ return 0;
+}
+
+/**
+ * ubi_scan - scan an MTD device.
+ * @ubi: UBI device description object
+ *
+ * This function does full scanning of an MTD device and returns complete
+ * information about it. In case of failure, an error code is returned.
+ */
+struct ubi_scan_info *ubi_scan(struct ubi_device *ubi)
+{
+ int err, pnum;
+ struct rb_node *rb1, *rb2;
+ struct ubi_scan_volume *sv;
+ struct ubi_scan_leb *seb;
+ struct ubi_scan_info *si;
+
+ si = kzalloc(sizeof(struct ubi_scan_info), GFP_KERNEL);
+ if (!si)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&si->corr);
+ INIT_LIST_HEAD(&si->free);
+ INIT_LIST_HEAD(&si->erase);
+ INIT_LIST_HEAD(&si->alien);
+ si->volumes = RB_ROOT;
+ si->is_empty = 1;
+
+ err = -ENOMEM;
+ ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+ if (!ech)
+ goto out_si;
+
+ vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
+ if (!vidh)
+ goto out_ech;
+
+ for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+ cond_resched();
+
+ dbg_msg("process PEB %d", pnum);
+ err = process_eb(ubi, si, pnum);
+ if (err < 0)
+ goto out_vidh;
+ }
+
+ dbg_msg("scanning is finished");
+
+ /* Calculate mean erase counter */
+ if (si->ec_count) {
+#ifdef UBI_LINUX
+ do_div(si->ec_sum, si->ec_count);
+#else
+ uint64_t tmp = si->ec_sum;
+ do_div(tmp, si->ec_count);
+ si->ec_sum = tmp;
+#endif
+ si->mean_ec = si->ec_sum;
+ }
+
+ if (si->is_empty)
+ ubi_msg("empty MTD device detected");
+
+ /*
+ * In case of unknown erase counter we use the mean erase counter
+ * value.
+ */
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb)
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+ }
+
+ list_for_each_entry(seb, &si->free, u.list) {
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+ }
+
+ list_for_each_entry(seb, &si->corr, u.list)
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+
+ list_for_each_entry(seb, &si->erase, u.list)
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+
+ err = paranoid_check_si(ubi, si);
+ if (err) {
+ if (err > 0)
+ err = -EINVAL;
+ goto out_vidh;
+ }
+
+ ubi_free_vid_hdr(ubi, vidh);
+ kfree(ech);
+
+ return si;
+
+out_vidh:
+ ubi_free_vid_hdr(ubi, vidh);
+out_ech:
+ kfree(ech);
+out_si:
+ ubi_scan_destroy_si(si);
+ return ERR_PTR(err);
+}
+
+/**
+ * destroy_sv - free the scanning volume information
+ * @sv: scanning volume information
+ *
+ * This function destroys the volume RB-tree (@sv->root) and the scanning
+ * volume information.
+ */
+static void destroy_sv(struct ubi_scan_volume *sv)
+{
+ struct ubi_scan_leb *seb;
+ struct rb_node *this = sv->root.rb_node;
+
+ while (this) {
+ if (this->rb_left)
+ this = this->rb_left;
+ else if (this->rb_right)
+ this = this->rb_right;
+ else {
+ seb = rb_entry(this, struct ubi_scan_leb, u.rb);
+ this = rb_parent(this);
+ if (this) {
+ if (this->rb_left == &seb->u.rb)
+ this->rb_left = NULL;
+ else
+ this->rb_right = NULL;
+ }
+
+ kfree(seb);
+ }
+ }
+ kfree(sv);
+}
+
+/**
+ * ubi_scan_destroy_si - destroy scanning information.
+ * @si: scanning information
+ */
+void ubi_scan_destroy_si(struct ubi_scan_info *si)
+{
+ struct ubi_scan_leb *seb, *seb_tmp;
+ struct ubi_scan_volume *sv;
+ struct rb_node *rb;
+
+ list_for_each_entry_safe(seb, seb_tmp, &si->alien, u.list) {
+ list_del(&seb->u.list);
+ kfree(seb);
+ }
+ list_for_each_entry_safe(seb, seb_tmp, &si->erase, u.list) {
+ list_del(&seb->u.list);
+ kfree(seb);
+ }
+ list_for_each_entry_safe(seb, seb_tmp, &si->corr, u.list) {
+ list_del(&seb->u.list);
+ kfree(seb);
+ }
+ list_for_each_entry_safe(seb, seb_tmp, &si->free, u.list) {
+ list_del(&seb->u.list);
+ kfree(seb);
+ }
+
+ /* Destroy the volume RB-tree */
+ rb = si->volumes.rb_node;
+ while (rb) {
+ if (rb->rb_left)
+ rb = rb->rb_left;
+ else if (rb->rb_right)
+ rb = rb->rb_right;
+ else {
+ sv = rb_entry(rb, struct ubi_scan_volume, rb);
+
+ rb = rb_parent(rb);
+ if (rb) {
+ if (rb->rb_left == &sv->rb)
+ rb->rb_left = NULL;
+ else
+ rb->rb_right = NULL;
+ }
+
+ destroy_sv(sv);
+ }
+ }
+
+ kfree(si);
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_si - check if the scanning information is correct and
+ * consistent.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero if the scanning information is all right, %1 if
+ * not and a negative error code if an error occurred.
+ */
+static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+ int pnum, err, vols_found = 0;
+ struct rb_node *rb1, *rb2;
+ struct ubi_scan_volume *sv;
+ struct ubi_scan_leb *seb, *last_seb;
+ uint8_t *buf;
+
+ /*
+ * At first, check that scanning information is OK.
+ */
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+ int leb_count = 0;
+
+ cond_resched();
+
+ vols_found += 1;
+
+ if (si->is_empty) {
+ ubi_err("bad is_empty flag");
+ goto bad_sv;
+ }
+
+ if (sv->vol_id < 0 || sv->highest_lnum < 0 ||
+ sv->leb_count < 0 || sv->vol_type < 0 || sv->used_ebs < 0 ||
+ sv->data_pad < 0 || sv->last_data_size < 0) {
+ ubi_err("negative values");
+ goto bad_sv;
+ }
+
+ if (sv->vol_id >= UBI_MAX_VOLUMES &&
+ sv->vol_id < UBI_INTERNAL_VOL_START) {
+ ubi_err("bad vol_id");
+ goto bad_sv;
+ }
+
+ if (sv->vol_id > si->highest_vol_id) {
+ ubi_err("highest_vol_id is %d, but vol_id %d is there",
+ si->highest_vol_id, sv->vol_id);
+ goto out;
+ }
+
+ if (sv->vol_type != UBI_DYNAMIC_VOLUME &&
+ sv->vol_type != UBI_STATIC_VOLUME) {
+ ubi_err("bad vol_type");
+ goto bad_sv;
+ }
+
+ if (sv->data_pad > ubi->leb_size / 2) {
+ ubi_err("bad data_pad");
+ goto bad_sv;
+ }
+
+ last_seb = NULL;
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) {
+ cond_resched();
+
+ last_seb = seb;
+ leb_count += 1;
+
+ if (seb->pnum < 0 || seb->ec < 0) {
+ ubi_err("negative values");
+ goto bad_seb;
+ }
+
+ if (seb->ec < si->min_ec) {
+ ubi_err("bad si->min_ec (%d), %d found",
+ si->min_ec, seb->ec);
+ goto bad_seb;
+ }
+
+ if (seb->ec > si->max_ec) {
+ ubi_err("bad si->max_ec (%d), %d found",
+ si->max_ec, seb->ec);
+ goto bad_seb;
+ }
+
+ if (seb->pnum >= ubi->peb_count) {
+ ubi_err("too high PEB number %d, total PEBs %d",
+ seb->pnum, ubi->peb_count);
+ goto bad_seb;
+ }
+
+ if (sv->vol_type == UBI_STATIC_VOLUME) {
+ if (seb->lnum >= sv->used_ebs) {
+ ubi_err("bad lnum or used_ebs");
+ goto bad_seb;
+ }
+ } else {
+ if (sv->used_ebs != 0) {
+ ubi_err("non-zero used_ebs");
+ goto bad_seb;
+ }
+ }
+
+ if (seb->lnum > sv->highest_lnum) {
+ ubi_err("incorrect highest_lnum or lnum");
+ goto bad_seb;
+ }
+ }
+
+ if (sv->leb_count != leb_count) {
+ ubi_err("bad leb_count, %d objects in the tree",
+ leb_count);
+ goto bad_sv;
+ }
+
+ if (!last_seb)
+ continue;
+
+ seb = last_seb;
+
+ if (seb->lnum != sv->highest_lnum) {
+ ubi_err("bad highest_lnum");
+ goto bad_seb;
+ }
+ }
+
+ if (vols_found != si->vols_found) {
+ ubi_err("bad si->vols_found %d, should be %d",
+ si->vols_found, vols_found);
+ goto out;
+ }
+
+ /* Check that scanning information is correct */
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+ last_seb = NULL;
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) {
+ int vol_type;
+
+ cond_resched();
+
+ last_seb = seb;
+
+ err = ubi_io_read_vid_hdr(ubi, seb->pnum, vidh, 1);
+ if (err && err != UBI_IO_BITFLIPS) {
+ ubi_err("VID header is not OK (%d)", err);
+ if (err > 0)
+ err = -EIO;
+ return err;
+ }
+
+ vol_type = vidh->vol_type == UBI_VID_DYNAMIC ?
+ UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME;
+ if (sv->vol_type != vol_type) {
+ ubi_err("bad vol_type");
+ goto bad_vid_hdr;
+ }
+
+ if (seb->sqnum != be64_to_cpu(vidh->sqnum)) {
+ ubi_err("bad sqnum %llu", seb->sqnum);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->vol_id != be32_to_cpu(vidh->vol_id)) {
+ ubi_err("bad vol_id %d", sv->vol_id);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->compat != vidh->compat) {
+ ubi_err("bad compat %d", vidh->compat);
+ goto bad_vid_hdr;
+ }
+
+ if (seb->lnum != be32_to_cpu(vidh->lnum)) {
+ ubi_err("bad lnum %d", seb->lnum);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->used_ebs != be32_to_cpu(vidh->used_ebs)) {
+ ubi_err("bad used_ebs %d", sv->used_ebs);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->data_pad != be32_to_cpu(vidh->data_pad)) {
+ ubi_err("bad data_pad %d", sv->data_pad);
+ goto bad_vid_hdr;
+ }
+
+ if (seb->leb_ver != be32_to_cpu(vidh->leb_ver)) {
+ ubi_err("bad leb_ver %u", seb->leb_ver);
+ goto bad_vid_hdr;
+ }
+ }
+
+ if (!last_seb)
+ continue;
+
+ if (sv->highest_lnum != be32_to_cpu(vidh->lnum)) {
+ ubi_err("bad highest_lnum %d", sv->highest_lnum);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->last_data_size != be32_to_cpu(vidh->data_size)) {
+ ubi_err("bad last_data_size %d", sv->last_data_size);
+ goto bad_vid_hdr;
+ }
+ }
+
+ /*
+ * Make sure that all the physical eraseblocks are in one of the lists
+ * or trees.
+ */
+ buf = kzalloc(ubi->peb_count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+ err = ubi_io_is_bad(ubi, pnum);
+ if (err < 0) {
+ kfree(buf);
+ return err;
+ } else if (err)
+ buf[pnum] = 1;
+ }
+
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb)
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb)
+ buf[seb->pnum] = 1;
+
+ list_for_each_entry(seb, &si->free, u.list)
+ buf[seb->pnum] = 1;
+
+ list_for_each_entry(seb, &si->corr, u.list)
+ buf[seb->pnum] = 1;
+
+ list_for_each_entry(seb, &si->erase, u.list)
+ buf[seb->pnum] = 1;
+
+ list_for_each_entry(seb, &si->alien, u.list)
+ buf[seb->pnum] = 1;
+
+ err = 0;
+ for (pnum = 0; pnum < ubi->peb_count; pnum++)
+ if (!buf[pnum]) {
+ ubi_err("PEB %d is not referred", pnum);
+ err = 1;
+ }
+
+ kfree(buf);
+ if (err)
+ goto out;
+ return 0;
+
+bad_seb:
+ ubi_err("bad scanning information about LEB %d", seb->lnum);
+ ubi_dbg_dump_seb(seb, 0);
+ ubi_dbg_dump_sv(sv);
+ goto out;
+
+bad_sv:
+ ubi_err("bad scanning information about volume %d", sv->vol_id);
+ ubi_dbg_dump_sv(sv);
+ goto out;
+
+bad_vid_hdr:
+ ubi_err("bad scanning information about volume %d", sv->vol_id);
+ ubi_dbg_dump_sv(sv);
+ ubi_dbg_dump_vid_hdr(vidh);
+
+out:
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h
new file mode 100644
index 0000000..46d444a
--- /dev/null
+++ b/drivers/mtd/ubi/scan.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_SCAN_H__
+#define __UBI_SCAN_H__
+
+/* The erase counter value for this physical eraseblock is unknown */
+#define UBI_SCAN_UNKNOWN_EC (-1)
+
+/**
+ * struct ubi_scan_leb - scanning information about a physical eraseblock.
+ * @ec: erase counter (%UBI_SCAN_UNKNOWN_EC if it is unknown)
+ * @pnum: physical eraseblock number
+ * @lnum: logical eraseblock number
+ * @scrub: if this physical eraseblock needs scrubbing
+ * @sqnum: sequence number
+ * @u: unions RB-tree or @list links
+ * @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects
+ * @u.list: link in one of the eraseblock lists
+ * @leb_ver: logical eraseblock version (obsolete)
+ *
+ * One object of this type is allocated for each physical eraseblock during
+ * scanning.
+ */
+struct ubi_scan_leb {
+ int ec;
+ int pnum;
+ int lnum;
+ int scrub;
+ unsigned long long sqnum;
+ union {
+ struct rb_node rb;
+ struct list_head list;
+ } u;
+ uint32_t leb_ver;
+};
+
+/**
+ * struct ubi_scan_volume - scanning information about a volume.
+ * @vol_id: volume ID
+ * @highest_lnum: highest logical eraseblock number in this volume
+ * @leb_count: number of logical eraseblocks in this volume
+ * @vol_type: volume type
+ * @used_ebs: number of used logical eraseblocks in this volume (only for
+ * static volumes)
+ * @last_data_size: amount of data in the last logical eraseblock of this
+ * volume (always equivalent to the usable logical eraseblock size in case of
+ * dynamic volumes)
+ * @data_pad: how many bytes at the end of logical eraseblocks of this volume
+ * are not used (due to volume alignment)
+ * @compat: compatibility flags of this volume
+ * @rb: link in the volume RB-tree
+ * @root: root of the RB-tree containing all the eraseblock belonging to this
+ * volume (&struct ubi_scan_leb objects)
+ *
+ * One object of this type is allocated for each volume during scanning.
+ */
+struct ubi_scan_volume {
+ int vol_id;
+ int highest_lnum;
+ int leb_count;
+ int vol_type;
+ int used_ebs;
+ int last_data_size;
+ int data_pad;
+ int compat;
+ struct rb_node rb;
+ struct rb_root root;
+};
+
+/**
+ * struct ubi_scan_info - UBI scanning information.
+ * @volumes: root of the volume RB-tree
+ * @corr: list of corrupted physical eraseblocks
+ * @free: list of free physical eraseblocks
+ * @erase: list of physical eraseblocks which have to be erased
+ * @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
+ * @bad_peb_count: count of bad physical eraseblocks
+ * those belonging to "preserve"-compatible internal volumes)
+ * @vols_found: number of volumes found during scanning
+ * @highest_vol_id: highest volume ID
+ * @alien_peb_count: count of physical eraseblocks in the @alien list
+ * @is_empty: flag indicating whether the MTD device is empty or not
+ * @min_ec: lowest erase counter value
+ * @max_ec: highest erase counter value
+ * @max_sqnum: highest sequence number value
+ * @mean_ec: mean erase counter value
+ * @ec_sum: a temporary variable used when calculating @mean_ec
+ * @ec_count: a temporary variable used when calculating @mean_ec
+ *
+ * This data structure contains the result of scanning and may be used by other
+ * UBI units to build final UBI data structures, further error-recovery and so
+ * on.
+ */
+struct ubi_scan_info {
+ struct rb_root volumes;
+ struct list_head corr;
+ struct list_head free;
+ struct list_head erase;
+ struct list_head alien;
+ int bad_peb_count;
+ int vols_found;
+ int highest_vol_id;
+ int alien_peb_count;
+ int is_empty;
+ int min_ec;
+ int max_ec;
+ unsigned long long max_sqnum;
+ int mean_ec;
+ int ec_sum;
+ int ec_count;
+};
+
+struct ubi_device;
+struct ubi_vid_hdr;
+
+/*
+ * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a
+ * list.
+ *
+ * @sv: volume scanning information
+ * @seb: scanning eraseblock infprmation
+ * @list: the list to move to
+ */
+static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv,
+ struct ubi_scan_leb *seb,
+ struct list_head *list)
+{
+ rb_erase(&seb->u.rb, &sv->root);
+ list_add_tail(&seb->u.list, list);
+}
+
+int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
+ int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
+ int bitflips);
+struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
+ int vol_id);
+struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv,
+ int lnum);
+void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv);
+struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi,
+ struct ubi_scan_info *si);
+int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si,
+ int pnum, int ec);
+struct ubi_scan_info *ubi_scan(struct ubi_device *ubi);
+void ubi_scan_destroy_si(struct ubi_scan_info *si);
+
+#endif /* !__UBI_SCAN_H__ */
diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h
new file mode 100644
index 0000000..c3185d9
--- /dev/null
+++ b/drivers/mtd/ubi/ubi-media.h
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Thomas Gleixner
+ * Frank Haverkamp
+ * Oliver Lohmann
+ * Andreas Arnez
+ */
+
+/*
+ * This file defines the layout of UBI headers and all the other UBI on-flash
+ * data structures.
+ */
+
+#ifndef __UBI_MEDIA_H__
+#define __UBI_MEDIA_H__
+
+#include <asm/byteorder.h>
+
+/* The version of UBI images supported by this implementation */
+#define UBI_VERSION 1
+
+/* The highest erase counter value supported by this implementation */
+#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF
+
+/* The initial CRC32 value used when calculating CRC checksums */
+#define UBI_CRC32_INIT 0xFFFFFFFFU
+
+/* Erase counter header magic number (ASCII "UBI#") */
+#define UBI_EC_HDR_MAGIC 0x55424923
+/* Volume identifier header magic number (ASCII "UBI!") */
+#define UBI_VID_HDR_MAGIC 0x55424921
+
+/*
+ * Volume type constants used in the volume identifier header.
+ *
+ * @UBI_VID_DYNAMIC: dynamic volume
+ * @UBI_VID_STATIC: static volume
+ */
+enum {
+ UBI_VID_DYNAMIC = 1,
+ UBI_VID_STATIC = 2
+};
+
+/*
+ * Volume flags used in the volume table record.
+ *
+ * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume
+ *
+ * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume
+ * table. UBI automatically re-sizes the volume which has this flag and makes
+ * the volume to be of largest possible size. This means that if after the
+ * initialization UBI finds out that there are available physical eraseblocks
+ * present on the device, it automatically appends all of them to the volume
+ * (the physical eraseblocks reserved for bad eraseblocks handling and other
+ * reserved physical eraseblocks are not taken). So, if there is a volume with
+ * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical
+ * eraseblocks will be zero after UBI is loaded, because all of them will be
+ * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared
+ * after the volume had been initialized.
+ *
+ * The auto-resize feature is useful for device production purposes. For
+ * example, different NAND flash chips may have different amount of initial bad
+ * eraseblocks, depending of particular chip instance. Manufacturers of NAND
+ * chips usually guarantee that the amount of initial bad eraseblocks does not
+ * exceed certain percent, e.g. 2%. When one creates an UBI image which will be
+ * flashed to the end devices in production, he does not know the exact amount
+ * of good physical eraseblocks the NAND chip on the device will have, but this
+ * number is required to calculate the volume sized and put them to the volume
+ * table of the UBI image. In this case, one of the volumes (e.g., the one
+ * which will store the root file system) is marked as "auto-resizable", and
+ * UBI will adjust its size on the first boot if needed.
+ *
+ * Note, first UBI reserves some amount of physical eraseblocks for bad
+ * eraseblock handling, and then re-sizes the volume, not vice-versa. This
+ * means that the pool of reserved physical eraseblocks will always be present.
+ */
+enum {
+ UBI_VTBL_AUTORESIZE_FLG = 0x01,
+};
+
+/*
+ * Compatibility constants used by internal volumes.
+ *
+ * @UBI_COMPAT_DELETE: delete this internal volume before anything is written
+ * to the flash
+ * @UBI_COMPAT_RO: attach this device in read-only mode
+ * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its
+ * physical eraseblocks, don't allow the wear-leveling unit to move them
+ * @UBI_COMPAT_REJECT: reject this UBI image
+ */
+enum {
+ UBI_COMPAT_DELETE = 1,
+ UBI_COMPAT_RO = 2,
+ UBI_COMPAT_PRESERVE = 4,
+ UBI_COMPAT_REJECT = 5
+};
+
+/* Sizes of UBI headers */
+#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr)
+#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr)
+
+/* Sizes of UBI headers without the ending CRC */
+#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32))
+#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32))
+
+/**
+ * struct ubi_ec_hdr - UBI erase counter header.
+ * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC)
+ * @version: version of UBI implementation which is supposed to accept this
+ * UBI image
+ * @padding1: reserved for future, zeroes
+ * @ec: the erase counter
+ * @vid_hdr_offset: where the VID header starts
+ * @data_offset: where the user data start
+ * @padding2: reserved for future, zeroes
+ * @hdr_crc: erase counter header CRC checksum
+ *
+ * The erase counter header takes 64 bytes and has a plenty of unused space for
+ * future usage. The unused fields are zeroed. The @version field is used to
+ * indicate the version of UBI implementation which is supposed to be able to
+ * work with this UBI image. If @version is greater then the current UBI
+ * version, the image is rejected. This may be useful in future if something
+ * is changed radically. This field is duplicated in the volume identifier
+ * header.
+ *
+ * The @vid_hdr_offset and @data_offset fields contain the offset of the the
+ * volume identifier header and user data, relative to the beginning of the
+ * physical eraseblock. These values have to be the same for all physical
+ * eraseblocks.
+ */
+struct ubi_ec_hdr {
+ __be32 magic;
+ __u8 version;
+ __u8 padding1[3];
+ __be64 ec; /* Warning: the current limit is 31-bit anyway! */
+ __be32 vid_hdr_offset;
+ __be32 data_offset;
+ __u8 padding2[36];
+ __be32 hdr_crc;
+} __attribute__ ((packed));
+
+/**
+ * struct ubi_vid_hdr - on-flash UBI volume identifier header.
+ * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC)
+ * @version: UBI implementation version which is supposed to accept this UBI
+ * image (%UBI_VERSION)
+ * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
+ * @copy_flag: if this logical eraseblock was copied from another physical
+ * eraseblock (for wear-leveling reasons)
+ * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE,
+ * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
+ * @vol_id: ID of this volume
+ * @lnum: logical eraseblock number
+ * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be
+ * removed, kept only for not breaking older UBI users)
+ * @data_size: how many bytes of data this logical eraseblock contains
+ * @used_ebs: total number of used logical eraseblocks in this volume
+ * @data_pad: how many bytes at the end of this physical eraseblock are not
+ * used
+ * @data_crc: CRC checksum of the data stored in this logical eraseblock
+ * @padding1: reserved for future, zeroes
+ * @sqnum: sequence number
+ * @padding2: reserved for future, zeroes
+ * @hdr_crc: volume identifier header CRC checksum
+ *
+ * The @sqnum is the value of the global sequence counter at the time when this
+ * VID header was created. The global sequence counter is incremented each time
+ * UBI writes a new VID header to the flash, i.e. when it maps a logical
+ * eraseblock to a new physical eraseblock. The global sequence counter is an
+ * unsigned 64-bit integer and we assume it never overflows. The @sqnum
+ * (sequence number) is used to distinguish between older and newer versions of
+ * logical eraseblocks.
+ *
+ * There are 2 situations when there may be more then one physical eraseblock
+ * corresponding to the same logical eraseblock, i.e., having the same @vol_id
+ * and @lnum values in the volume identifier header. Suppose we have a logical
+ * eraseblock L and it is mapped to the physical eraseblock P.
+ *
+ * 1. Because UBI may erase physical eraseblocks asynchronously, the following
+ * situation is possible: L is asynchronously erased, so P is scheduled for
+ * erasure, then L is written to,i.e. mapped to another physical eraseblock P1,
+ * so P1 is written to, then an unclean reboot happens. Result - there are 2
+ * physical eraseblocks P and P1 corresponding to the same logical eraseblock
+ * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the
+ * flash.
+ *
+ * 2. From time to time UBI moves logical eraseblocks to other physical
+ * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P
+ * to P1, and an unclean reboot happens before P is physically erased, there
+ * are two physical eraseblocks P and P1 corresponding to L and UBI has to
+ * select one of them when the flash is attached. The @sqnum field says which
+ * PEB is the original (obviously P will have lower @sqnum) and the copy. But
+ * it is not enough to select the physical eraseblock with the higher sequence
+ * number, because the unclean reboot could have happen in the middle of the
+ * copying process, so the data in P is corrupted. It is also not enough to
+ * just select the physical eraseblock with lower sequence number, because the
+ * data there may be old (consider a case if more data was added to P1 after
+ * the copying). Moreover, the unclean reboot may happen when the erasure of P
+ * was just started, so it result in unstable P, which is "mostly" OK, but
+ * still has unstable bits.
+ *
+ * UBI uses the @copy_flag field to indicate that this logical eraseblock is a
+ * copy. UBI also calculates data CRC when the data is moved and stores it at
+ * the @data_crc field of the copy (P1). So when UBI needs to pick one physical
+ * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is
+ * examined. If it is cleared, the situation* is simple and the newer one is
+ * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC
+ * checksum is correct, this physical eraseblock is selected (P1). Otherwise
+ * the older one (P) is selected.
+ *
+ * Note, there is an obsolete @leb_ver field which was used instead of @sqnum
+ * in the past. But it is not used anymore and we keep it in order to be able
+ * to deal with old UBI images. It will be removed at some point.
+ *
+ * There are 2 sorts of volumes in UBI: user volumes and internal volumes.
+ * Internal volumes are not seen from outside and are used for various internal
+ * UBI purposes. In this implementation there is only one internal volume - the
+ * layout volume. Internal volumes are the main mechanism of UBI extensions.
+ * For example, in future one may introduce a journal internal volume. Internal
+ * volumes have their own reserved range of IDs.
+ *
+ * The @compat field is only used for internal volumes and contains the "degree
+ * of their compatibility". It is always zero for user volumes. This field
+ * provides a mechanism to introduce UBI extensions and to be still compatible
+ * with older UBI binaries. For example, if someone introduced a journal in
+ * future, he would probably use %UBI_COMPAT_DELETE compatibility for the
+ * journal volume. And in this case, older UBI binaries, which know nothing
+ * about the journal volume, would just delete this volume and work perfectly
+ * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image
+ * - it just ignores the Ext3fs journal.
+ *
+ * The @data_crc field contains the CRC checksum of the contents of the logical
+ * eraseblock if this is a static volume. In case of dynamic volumes, it does
+ * not contain the CRC checksum as a rule. The only exception is when the
+ * data of the physical eraseblock was moved by the wear-leveling unit, then
+ * the wear-leveling unit calculates the data CRC and stores it in the
+ * @data_crc field. And of course, the @copy_flag is %in this case.
+ *
+ * The @data_size field is used only for static volumes because UBI has to know
+ * how many bytes of data are stored in this eraseblock. For dynamic volumes,
+ * this field usually contains zero. The only exception is when the data of the
+ * physical eraseblock was moved to another physical eraseblock for
+ * wear-leveling reasons. In this case, UBI calculates CRC checksum of the
+ * contents and uses both @data_crc and @data_size fields. In this case, the
+ * @data_size field contains data size.
+ *
+ * The @used_ebs field is used only for static volumes and indicates how many
+ * eraseblocks the data of the volume takes. For dynamic volumes this field is
+ * not used and always contains zero.
+ *
+ * The @data_pad is calculated when volumes are created using the alignment
+ * parameter. So, effectively, the @data_pad field reduces the size of logical
+ * eraseblocks of this volume. This is very handy when one uses block-oriented
+ * software (say, cramfs) on top of the UBI volume.
+ */
+struct ubi_vid_hdr {
+ __be32 magic;
+ __u8 version;
+ __u8 vol_type;
+ __u8 copy_flag;
+ __u8 compat;
+ __be32 vol_id;
+ __be32 lnum;
+ __be32 leb_ver; /* obsolete, to be removed, don't use */
+ __be32 data_size;
+ __be32 used_ebs;
+ __be32 data_pad;
+ __be32 data_crc;
+ __u8 padding1[4];
+ __be64 sqnum;
+ __u8 padding2[12];
+ __be32 hdr_crc;
+} __attribute__ ((packed));
+
+/* Internal UBI volumes count */
+#define UBI_INT_VOL_COUNT 1
+
+/*
+ * Starting ID of internal volumes. There is reserved room for 4096 internal
+ * volumes.
+ */
+#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096)
+
+/* The layout volume contains the volume table */
+
+#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START
+#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC
+#define UBI_LAYOUT_VOLUME_ALIGN 1
+#define UBI_LAYOUT_VOLUME_EBS 2
+#define UBI_LAYOUT_VOLUME_NAME "layout volume"
+#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT
+
+/* The maximum number of volumes per one UBI device */
+#define UBI_MAX_VOLUMES 128
+
+/* The maximum volume name length */
+#define UBI_VOL_NAME_MAX 127
+
+/* Size of the volume table record */
+#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record)
+
+/* Size of the volume table record without the ending CRC */
+#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32))
+
+/**
+ * struct ubi_vtbl_record - a record in the volume table.
+ * @reserved_pebs: how many physical eraseblocks are reserved for this volume
+ * @alignment: volume alignment
+ * @data_pad: how many bytes are unused at the end of the each physical
+ * eraseblock to satisfy the requested alignment
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @upd_marker: if volume update was started but not finished
+ * @name_len: volume name length
+ * @name: the volume name
+ * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG)
+ * @padding: reserved, zeroes
+ * @crc: a CRC32 checksum of the record
+ *
+ * The volume table records are stored in the volume table, which is stored in
+ * the layout volume. The layout volume consists of 2 logical eraseblock, each
+ * of which contains a copy of the volume table (i.e., the volume table is
+ * duplicated). The volume table is an array of &struct ubi_vtbl_record
+ * objects indexed by the volume ID.
+ *
+ * If the size of the logical eraseblock is large enough to fit
+ * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES
+ * records. Otherwise, it contains as many records as it can fit (i.e., size of
+ * logical eraseblock divided by sizeof(struct ubi_vtbl_record)).
+ *
+ * The @upd_marker flag is used to implement volume update. It is set to %1
+ * before update and set to %0 after the update. So if the update operation was
+ * interrupted, UBI knows that the volume is corrupted.
+ *
+ * The @alignment field is specified when the volume is created and cannot be
+ * later changed. It may be useful, for example, when a block-oriented file
+ * system works on top of UBI. The @data_pad field is calculated using the
+ * logical eraseblock size and @alignment. The alignment must be multiple to the
+ * minimal flash I/O unit. If @alignment is 1, all the available space of
+ * the physical eraseblocks is used.
+ *
+ * Empty records contain all zeroes and the CRC checksum of those zeroes.
+ */
+struct ubi_vtbl_record {
+ __be32 reserved_pebs;
+ __be32 alignment;
+ __be32 data_pad;
+ __u8 vol_type;
+ __u8 upd_marker;
+ __be16 name_len;
+ __u8 name[UBI_VOL_NAME_MAX+1];
+ __u8 flags;
+ __u8 padding[23];
+ __be32 crc;
+} __attribute__ ((packed));
+
+#endif /* !__UBI_MEDIA_H__ */
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
new file mode 100644
index 0000000..bf77a15
--- /dev/null
+++ b/drivers/mtd/ubi/ubi.h
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_UBI_H__
+#define __UBI_UBI_H__
+
+#ifdef UBI_LINUX
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/ubi.h>
+#endif
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/string.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/ubi.h>
+
+#include "ubi-media.h"
+#include "scan.h"
+#include "debug.h"
+
+/* Maximum number of supported UBI devices */
+#define UBI_MAX_DEVICES 32
+
+/* UBI name used for character devices, sysfs, etc */
+#define UBI_NAME_STR "ubi"
+
+/* Normal UBI messages */
+#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__)
+/* UBI warning messages */
+#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \
+ __func__, ##__VA_ARGS__)
+/* UBI error messages */
+#define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \
+ __func__, ##__VA_ARGS__)
+
+/* Lowest number PEBs reserved for bad PEB handling */
+#define MIN_RESEVED_PEBS 2
+
+/* Background thread name pattern */
+#define UBI_BGT_NAME_PATTERN "ubi_bgt%dd"
+
+/* This marker in the EBA table means that the LEB is um-mapped */
+#define UBI_LEB_UNMAPPED -1
+
+/*
+ * In case of errors, UBI tries to repeat the operation several times before
+ * returning error. The below constant defines how many times UBI re-tries.
+ */
+#define UBI_IO_RETRIES 3
+
+/*
+ * Error codes returned by the I/O unit.
+ *
+ * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only
+ * 0xFF bytes
+ * UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a
+ * valid erase counter header, and the rest are %0xFF bytes
+ * UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC)
+ * UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or
+ * CRC)
+ * UBI_IO_BITFLIPS: bit-flips were detected and corrected
+ */
+enum {
+ UBI_IO_PEB_EMPTY = 1,
+ UBI_IO_PEB_FREE,
+ UBI_IO_BAD_EC_HDR,
+ UBI_IO_BAD_VID_HDR,
+ UBI_IO_BITFLIPS
+};
+
+/**
+ * struct ubi_wl_entry - wear-leveling entry.
+ * @rb: link in the corresponding RB-tree
+ * @ec: erase counter
+ * @pnum: physical eraseblock number
+ *
+ * This data structure is used in the WL unit. Each physical eraseblock has a
+ * corresponding &struct wl_entry object which may be kept in different
+ * RB-trees. See WL unit for details.
+ */
+struct ubi_wl_entry {
+ struct rb_node rb;
+ int ec;
+ int pnum;
+};
+
+/**
+ * struct ubi_ltree_entry - an entry in the lock tree.
+ * @rb: links RB-tree nodes
+ * @vol_id: volume ID of the locked logical eraseblock
+ * @lnum: locked logical eraseblock number
+ * @users: how many tasks are using this logical eraseblock or wait for it
+ * @mutex: read/write mutex to implement read/write access serialization to
+ * the (@vol_id, @lnum) logical eraseblock
+ *
+ * This data structure is used in the EBA unit to implement per-LEB locking.
+ * When a logical eraseblock is being locked - corresponding
+ * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree).
+ * See EBA unit for details.
+ */
+struct ubi_ltree_entry {
+ struct rb_node rb;
+ int vol_id;
+ int lnum;
+ int users;
+ struct rw_semaphore mutex;
+};
+
+struct ubi_volume_desc;
+
+/**
+ * struct ubi_volume - UBI volume description data structure.
+ * @dev: device object to make use of the the Linux device model
+ * @cdev: character device object to create character device
+ * @ubi: reference to the UBI device description object
+ * @vol_id: volume ID
+ * @ref_count: volume reference count
+ * @readers: number of users holding this volume in read-only mode
+ * @writers: number of users holding this volume in read-write mode
+ * @exclusive: whether somebody holds this volume in exclusive mode
+ *
+ * @reserved_pebs: how many physical eraseblocks are reserved for this volume
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @usable_leb_size: logical eraseblock size without padding
+ * @used_ebs: how many logical eraseblocks in this volume contain data
+ * @last_eb_bytes: how many bytes are stored in the last logical eraseblock
+ * @used_bytes: how many bytes of data this volume contains
+ * @alignment: volume alignment
+ * @data_pad: how many bytes are not used at the end of physical eraseblocks to
+ * satisfy the requested alignment
+ * @name_len: volume name length
+ * @name: volume name
+ *
+ * @upd_ebs: how many eraseblocks are expected to be updated
+ * @ch_lnum: LEB number which is being changing by the atomic LEB change
+ * operation
+ * @ch_dtype: data persistency type which is being changing by the atomic LEB
+ * change operation
+ * @upd_bytes: how many bytes are expected to be received for volume update or
+ * atomic LEB change
+ * @upd_received: how many bytes were already received for volume update or
+ * atomic LEB change
+ * @upd_buf: update buffer which is used to collect update data or data for
+ * atomic LEB change
+ *
+ * @eba_tbl: EBA table of this volume (LEB->PEB mapping)
+ * @checked: %1 if this static volume was checked
+ * @corrupted: %1 if the volume is corrupted (static volumes only)
+ * @upd_marker: %1 if the update marker is set for this volume
+ * @updating: %1 if the volume is being updated
+ * @changing_leb: %1 if the atomic LEB change ioctl command is in progress
+ *
+ * @gluebi_desc: gluebi UBI volume descriptor
+ * @gluebi_refcount: reference count of the gluebi MTD device
+ * @gluebi_mtd: MTD device description object of the gluebi MTD device
+ *
+ * The @corrupted field indicates that the volume's contents is corrupted.
+ * Since UBI protects only static volumes, this field is not relevant to
+ * dynamic volumes - it is user's responsibility to assure their data
+ * integrity.
+ *
+ * The @upd_marker flag indicates that this volume is either being updated at
+ * the moment or is damaged because of an unclean reboot.
+ */
+struct ubi_volume {
+ struct device dev;
+ struct cdev cdev;
+ struct ubi_device *ubi;
+ int vol_id;
+ int ref_count;
+ int readers;
+ int writers;
+ int exclusive;
+
+ int reserved_pebs;
+ int vol_type;
+ int usable_leb_size;
+ int used_ebs;
+ int last_eb_bytes;
+ long long used_bytes;
+ int alignment;
+ int data_pad;
+ int name_len;
+ char name[UBI_VOL_NAME_MAX+1];
+
+ int upd_ebs;
+ int ch_lnum;
+ int ch_dtype;
+ long long upd_bytes;
+ long long upd_received;
+ void *upd_buf;
+
+ int *eba_tbl;
+ unsigned int checked:1;
+ unsigned int corrupted:1;
+ unsigned int upd_marker:1;
+ unsigned int updating:1;
+ unsigned int changing_leb:1;
+
+#ifdef CONFIG_MTD_UBI_GLUEBI
+ /*
+ * Gluebi-related stuff may be compiled out.
+ * TODO: this should not be built into UBI but should be a separate
+ * ubimtd driver which works on top of UBI and emulates MTD devices.
+ */
+ struct ubi_volume_desc *gluebi_desc;
+ int gluebi_refcount;
+ struct mtd_info gluebi_mtd;
+#endif
+};
+
+/**
+ * struct ubi_volume_desc - descriptor of the UBI volume returned when it is
+ * opened.
+ * @vol: reference to the corresponding volume description object
+ * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE)
+ */
+struct ubi_volume_desc {
+ struct ubi_volume *vol;
+ int mode;
+};
+
+struct ubi_wl_entry;
+
+/**
+ * struct ubi_device - UBI device description structure
+ * @dev: UBI device object to use the the Linux device model
+ * @cdev: character device object to create character device
+ * @ubi_num: UBI device number
+ * @ubi_name: UBI device name
+ * @vol_count: number of volumes in this UBI device
+ * @volumes: volumes of this UBI device
+ * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs,
+ * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count,
+ * @vol->readers, @vol->writers, @vol->exclusive,
+ * @vol->ref_count, @vol->mapping and @vol->eba_tbl.
+ * @ref_count: count of references on the UBI device
+ *
+ * @rsvd_pebs: count of reserved physical eraseblocks
+ * @avail_pebs: count of available physical eraseblocks
+ * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB
+ * handling
+ * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling
+ *
+ * @autoresize_vol_id: ID of the volume which has to be auto-resized at the end
+ * of UBI ititializetion
+ * @vtbl_slots: how many slots are available in the volume table
+ * @vtbl_size: size of the volume table in bytes
+ * @vtbl: in-RAM volume table copy
+ * @volumes_mutex: protects on-flash volume table and serializes volume
+ * changes, like creation, deletion, update, resize
+ *
+ * @max_ec: current highest erase counter value
+ * @mean_ec: current mean erase counter value
+ *
+ * @global_sqnum: global sequence number
+ * @ltree_lock: protects the lock tree and @global_sqnum
+ * @ltree: the lock tree
+ * @alc_mutex: serializes "atomic LEB change" operations
+ *
+ * @used: RB-tree of used physical eraseblocks
+ * @free: RB-tree of free physical eraseblocks
+ * @scrub: RB-tree of physical eraseblocks which need scrubbing
+ * @prot: protection trees
+ * @prot.pnum: protection tree indexed by physical eraseblock numbers
+ * @prot.aec: protection tree indexed by absolute erase counter value
+ * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from,
+ * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works
+ * fields
+ * @move_mutex: serializes eraseblock moves
+ * @wl_scheduled: non-zero if the wear-leveling was scheduled
+ * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any
+ * physical eraseblock
+ * @abs_ec: absolute erase counter
+ * @move_from: physical eraseblock from where the data is being moved
+ * @move_to: physical eraseblock where the data is being moved to
+ * @move_to_put: if the "to" PEB was put
+ * @works: list of pending works
+ * @works_count: count of pending works
+ * @bgt_thread: background thread description object
+ * @thread_enabled: if the background thread is enabled
+ * @bgt_name: background thread name
+ *
+ * @flash_size: underlying MTD device size (in bytes)
+ * @peb_count: count of physical eraseblocks on the MTD device
+ * @peb_size: physical eraseblock size
+ * @bad_peb_count: count of bad physical eraseblocks
+ * @good_peb_count: count of good physical eraseblocks
+ * @min_io_size: minimal input/output unit size of the underlying MTD device
+ * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers
+ * @ro_mode: if the UBI device is in read-only mode
+ * @leb_size: logical eraseblock size
+ * @leb_start: starting offset of logical eraseblocks within physical
+ * eraseblocks
+ * @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size
+ * @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size
+ * @vid_hdr_offset: starting offset of the volume identifier header (might be
+ * unaligned)
+ * @vid_hdr_aloffset: starting offset of the VID header aligned to
+ * @hdrs_min_io_size
+ * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset
+ * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or
+ * not
+ * @mtd: MTD device descriptor
+ *
+ * @peb_buf1: a buffer of PEB size used for different purposes
+ * @peb_buf2: another buffer of PEB size used for different purposes
+ * @buf_mutex: proptects @peb_buf1 and @peb_buf2
+ * @dbg_peb_buf: buffer of PEB size used for debugging
+ * @dbg_buf_mutex: proptects @dbg_peb_buf
+ */
+struct ubi_device {
+ struct cdev cdev;
+ struct device dev;
+ int ubi_num;
+ char ubi_name[sizeof(UBI_NAME_STR)+5];
+ int vol_count;
+ struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
+ spinlock_t volumes_lock;
+ int ref_count;
+
+ int rsvd_pebs;
+ int avail_pebs;
+ int beb_rsvd_pebs;
+ int beb_rsvd_level;
+
+ int autoresize_vol_id;
+ int vtbl_slots;
+ int vtbl_size;
+ struct ubi_vtbl_record *vtbl;
+ struct mutex volumes_mutex;
+
+ int max_ec;
+ /* TODO: mean_ec is not updated run-time, fix */
+ int mean_ec;
+
+ /* EBA unit's stuff */
+ unsigned long long global_sqnum;
+ spinlock_t ltree_lock;
+ struct rb_root ltree;
+ struct mutex alc_mutex;
+
+ /* Wear-leveling unit's stuff */
+ struct rb_root used;
+ struct rb_root free;
+ struct rb_root scrub;
+ struct {
+ struct rb_root pnum;
+ struct rb_root aec;
+ } prot;
+ spinlock_t wl_lock;
+ struct mutex move_mutex;
+ struct rw_semaphore work_sem;
+ int wl_scheduled;
+ struct ubi_wl_entry **lookuptbl;
+ unsigned long long abs_ec;
+ struct ubi_wl_entry *move_from;
+ struct ubi_wl_entry *move_to;
+ int move_to_put;
+ struct list_head works;
+ int works_count;
+ struct task_struct *bgt_thread;
+ int thread_enabled;
+ char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];
+
+ /* I/O unit's stuff */
+ long long flash_size;
+ int peb_count;
+ int peb_size;
+ int bad_peb_count;
+ int good_peb_count;
+ int min_io_size;
+ int hdrs_min_io_size;
+ int ro_mode;
+ int leb_size;
+ int leb_start;
+ int ec_hdr_alsize;
+ int vid_hdr_alsize;
+ int vid_hdr_offset;
+ int vid_hdr_aloffset;
+ int vid_hdr_shift;
+ int bad_allowed;
+ struct mtd_info *mtd;
+
+ void *peb_buf1;
+ void *peb_buf2;
+ struct mutex buf_mutex;
+ struct mutex ckvol_mutex;
+#ifdef CONFIG_MTD_UBI_DEBUG
+ void *dbg_peb_buf;
+ struct mutex dbg_buf_mutex;
+#endif
+};
+
+extern struct kmem_cache *ubi_wl_entry_slab;
+extern struct file_operations ubi_ctrl_cdev_operations;
+extern struct file_operations ubi_cdev_operations;
+extern struct file_operations ubi_vol_cdev_operations;
+extern struct class *ubi_class;
+extern struct mutex ubi_devices_mutex;
+
+/* vtbl.c */
+int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
+ struct ubi_vtbl_record *vtbl_rec);
+int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si);
+
+/* vmt.c */
+int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req);
+int ubi_remove_volume(struct ubi_volume_desc *desc);
+int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs);
+int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol);
+void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol);
+
+/* upd.c */
+int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
+ long long bytes);
+int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
+ const void __user *buf, int count);
+int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
+ const struct ubi_leb_change_req *req);
+int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
+ const void __user *buf, int count);
+
+/* misc.c */
+int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length);
+int ubi_check_volume(struct ubi_device *ubi, int vol_id);
+void ubi_calculate_reserved(struct ubi_device *ubi);
+
+/* gluebi.c */
+#ifdef CONFIG_MTD_UBI_GLUEBI
+int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol);
+int ubi_destroy_gluebi(struct ubi_volume *vol);
+void ubi_gluebi_updated(struct ubi_volume *vol);
+#else
+#define ubi_create_gluebi(ubi, vol) 0
+#define ubi_destroy_gluebi(vol) 0
+#define ubi_gluebi_updated(vol)
+#endif
+
+/* eba.c */
+int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
+ int lnum);
+int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
+ void *buf, int offset, int len, int check);
+int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
+ const void *buf, int offset, int len, int dtype);
+int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
+ int lnum, const void *buf, int len, int dtype,
+ int used_ebs);
+int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
+ int lnum, const void *buf, int len, int dtype);
+int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
+ struct ubi_vid_hdr *vid_hdr);
+int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
+void ubi_eba_close(const struct ubi_device *ubi);
+
+/* wl.c */
+int ubi_wl_get_peb(struct ubi_device *ubi, int dtype);
+int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture);
+int ubi_wl_flush(struct ubi_device *ubi);
+int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum);
+int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
+void ubi_wl_close(struct ubi_device *ubi);
+int ubi_thread(void *u);
+
+/* io.c */
+int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
+ int len);
+int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
+ int len);
+int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture);
+int ubi_io_is_bad(const struct ubi_device *ubi, int pnum);
+int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum);
+int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
+ struct ubi_ec_hdr *ec_hdr, int verbose);
+int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,
+ struct ubi_ec_hdr *ec_hdr);
+int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
+ struct ubi_vid_hdr *vid_hdr, int verbose);
+int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
+ struct ubi_vid_hdr *vid_hdr);
+
+/* build.c */
+int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset);
+int ubi_detach_mtd_dev(int ubi_num, int anyway);
+struct ubi_device *ubi_get_device(int ubi_num);
+void ubi_put_device(struct ubi_device *ubi);
+struct ubi_device *ubi_get_by_major(int major);
+int ubi_major2num(int major);
+
+/*
+ * ubi_rb_for_each_entry - walk an RB-tree.
+ * @rb: a pointer to type 'struct rb_node' to to use as a loop counter
+ * @pos: a pointer to RB-tree entry type to use as a loop counter
+ * @root: RB-tree's root
+ * @member: the name of the 'struct rb_node' within the RB-tree entry
+ */
+#define ubi_rb_for_each_entry(rb, pos, root, member) \
+ for (rb = rb_first(root), \
+ pos = (rb ? container_of(rb, typeof(*pos), member) : NULL); \
+ rb; \
+ rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member))
+
+/**
+ * ubi_zalloc_vid_hdr - allocate a volume identifier header object.
+ * @ubi: UBI device description object
+ * @gfp_flags: GFP flags to allocate with
+ *
+ * This function returns a pointer to the newly allocated and zero-filled
+ * volume identifier header object in case of success and %NULL in case of
+ * failure.
+ */
+static inline struct ubi_vid_hdr *
+ubi_zalloc_vid_hdr(const struct ubi_device *ubi, gfp_t gfp_flags)
+{
+ void *vid_hdr;
+
+ vid_hdr = kzalloc(ubi->vid_hdr_alsize, gfp_flags);
+ if (!vid_hdr)
+ return NULL;
+
+ /*
+ * VID headers may be stored at un-aligned flash offsets, so we shift
+ * the pointer.
+ */
+ return vid_hdr + ubi->vid_hdr_shift;
+}
+
+/**
+ * ubi_free_vid_hdr - free a volume identifier header object.
+ * @ubi: UBI device description object
+ * @vid_hdr: the object to free
+ */
+static inline void ubi_free_vid_hdr(const struct ubi_device *ubi,
+ struct ubi_vid_hdr *vid_hdr)
+{
+ void *p = vid_hdr;
+
+ if (!p)
+ return;
+
+ kfree(p - ubi->vid_hdr_shift);
+}
+
+/*
+ * This function is equivalent to 'ubi_io_read()', but @offset is relative to
+ * the beginning of the logical eraseblock, not to the beginning of the
+ * physical eraseblock.
+ */
+static inline int ubi_io_read_data(const struct ubi_device *ubi, void *buf,
+ int pnum, int offset, int len)
+{
+ ubi_assert(offset >= 0);
+ return ubi_io_read(ubi, buf, pnum, offset + ubi->leb_start, len);
+}
+
+/*
+ * This function is equivalent to 'ubi_io_write()', but @offset is relative to
+ * the beginning of the logical eraseblock, not to the beginning of the
+ * physical eraseblock.
+ */
+static inline int ubi_io_write_data(struct ubi_device *ubi, const void *buf,
+ int pnum, int offset, int len)
+{
+ ubi_assert(offset >= 0);
+ return ubi_io_write(ubi, buf, pnum, offset + ubi->leb_start, len);
+}
+
+/**
+ * ubi_ro_mode - switch to read-only mode.
+ * @ubi: UBI device description object
+ */
+static inline void ubi_ro_mode(struct ubi_device *ubi)
+{
+ if (!ubi->ro_mode) {
+ ubi->ro_mode = 1;
+ ubi_warn("switch to read-only mode");
+ }
+}
+
+/**
+ * vol_id2idx - get table index by volume ID.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ */
+static inline int vol_id2idx(const struct ubi_device *ubi, int vol_id)
+{
+ if (vol_id >= UBI_INTERNAL_VOL_START)
+ return vol_id - UBI_INTERNAL_VOL_START + ubi->vtbl_slots;
+ else
+ return vol_id;
+}
+
+/**
+ * idx2vol_id - get volume ID by table index.
+ * @ubi: UBI device description object
+ * @idx: table index
+ */
+static inline int idx2vol_id(const struct ubi_device *ubi, int idx)
+{
+ if (idx >= ubi->vtbl_slots)
+ return idx - ubi->vtbl_slots + UBI_INTERNAL_VOL_START;
+ else
+ return idx;
+}
+
+#endif /* !__UBI_UBI_H__ */
1
0

28 Oct '08
UBI (Latin: "where?") stands for "Unsorted Block Images". It is a volume management system for flash devices which manages multiple logical volumes on a single physical flash device and spreads the I/O load (i.e, wear-leveling) across the whole flash chip.
In a sense, UBI may be compared to the Logical Volume Manager (LVM). Whereas LVM maps logical sectors to physical sectors, UBI maps logical eraseblocks to physical eraseblocks. But besides the mapping, UBI implements global wear-leveling and I/O errors handling.
For more details, Please visit the following URL.
http://www.linux-mtd.infradead.org/doc/ubi.html
Signed-off-by: Kyungmin Park <kyungmin.park(a)samsung.com>
---
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
new file mode 100644
index 0000000..d320562
--- /dev/null
+++ b/drivers/mtd/ubi/eba.c
@@ -0,0 +1,1254 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * The UBI Eraseblock Association (EBA) unit.
+ *
+ * This unit is responsible for I/O to/from logical eraseblock.
+ *
+ * Although in this implementation the EBA table is fully kept and managed in
+ * RAM, which assumes poor scalability, it might be (partially) maintained on
+ * flash in future implementations.
+ *
+ * The EBA unit implements per-logical eraseblock locking. Before accessing a
+ * logical eraseblock it is locked for reading or writing. The per-logical
+ * eraseblock locking is implemented by means of the lock tree. The lock tree
+ * is an RB-tree which refers all the currently locked logical eraseblocks. The
+ * lock tree elements are &struct ubi_ltree_entry objects. They are indexed by
+ * (@vol_id, @lnum) pairs.
+ *
+ * EBA also maintains the global sequence counter which is incremented each
+ * time a logical eraseblock is mapped to a physical eraseblock and it is
+ * stored in the volume identifier header. This means that each VID header has
+ * a unique sequence number. The sequence number is only increased an we assume
+ * 64 bits is enough to never overflow.
+ */
+
+#ifdef UBI_LINUX
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/err.h>
+#endif
+
+#include <ubi_uboot.h>
+#include "ubi.h"
+
+/* Number of physical eraseblocks reserved for atomic LEB change operation */
+#define EBA_RESERVED_PEBS 1
+
+/**
+ * next_sqnum - get next sequence number.
+ * @ubi: UBI device description object
+ *
+ * This function returns next sequence number to use, which is just the current
+ * global sequence counter value. It also increases the global sequence
+ * counter.
+ */
+static unsigned long long next_sqnum(struct ubi_device *ubi)
+{
+ unsigned long long sqnum;
+
+ spin_lock(&ubi->ltree_lock);
+ sqnum = ubi->global_sqnum++;
+ spin_unlock(&ubi->ltree_lock);
+
+ return sqnum;
+}
+
+/**
+ * ubi_get_compat - get compatibility flags of a volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function returns compatibility flags for an internal volume. User
+ * volumes have no compatibility flags, so %0 is returned.
+ */
+static int ubi_get_compat(const struct ubi_device *ubi, int vol_id)
+{
+ if (vol_id == UBI_LAYOUT_VOLUME_ID)
+ return UBI_LAYOUT_VOLUME_COMPAT;
+ return 0;
+}
+
+/**
+ * ltree_lookup - look up the lock tree.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function returns a pointer to the corresponding &struct ubi_ltree_entry
+ * object if the logical eraseblock is locked and %NULL if it is not.
+ * @ubi->ltree_lock has to be locked.
+ */
+static struct ubi_ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id,
+ int lnum)
+{
+ struct rb_node *p;
+
+ p = ubi->ltree.rb_node;
+ while (p) {
+ struct ubi_ltree_entry *le;
+
+ le = rb_entry(p, struct ubi_ltree_entry, rb);
+
+ if (vol_id < le->vol_id)
+ p = p->rb_left;
+ else if (vol_id > le->vol_id)
+ p = p->rb_right;
+ else {
+ if (lnum < le->lnum)
+ p = p->rb_left;
+ else if (lnum > le->lnum)
+ p = p->rb_right;
+ else
+ return le;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * ltree_add_entry - add new entry to the lock tree.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function adds new entry for logical eraseblock (@vol_id, @lnum) to the
+ * lock tree. If such entry is already there, its usage counter is increased.
+ * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation
+ * failed.
+ */
+static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi,
+ int vol_id, int lnum)
+{
+ struct ubi_ltree_entry *le, *le1, *le_free;
+
+ le = kmalloc(sizeof(struct ubi_ltree_entry), GFP_NOFS);
+ if (!le)
+ return ERR_PTR(-ENOMEM);
+
+ le->users = 0;
+ init_rwsem(&le->mutex);
+ le->vol_id = vol_id;
+ le->lnum = lnum;
+
+ spin_lock(&ubi->ltree_lock);
+ le1 = ltree_lookup(ubi, vol_id, lnum);
+
+ if (le1) {
+ /*
+ * This logical eraseblock is already locked. The newly
+ * allocated lock entry is not needed.
+ */
+ le_free = le;
+ le = le1;
+ } else {
+ struct rb_node **p, *parent = NULL;
+
+ /*
+ * No lock entry, add the newly allocated one to the
+ * @ubi->ltree RB-tree.
+ */
+ le_free = NULL;
+
+ p = &ubi->ltree.rb_node;
+ while (*p) {
+ parent = *p;
+ le1 = rb_entry(parent, struct ubi_ltree_entry, rb);
+
+ if (vol_id < le1->vol_id)
+ p = &(*p)->rb_left;
+ else if (vol_id > le1->vol_id)
+ p = &(*p)->rb_right;
+ else {
+ ubi_assert(lnum != le1->lnum);
+ if (lnum < le1->lnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ }
+
+ rb_link_node(&le->rb, parent, p);
+ rb_insert_color(&le->rb, &ubi->ltree);
+ }
+ le->users += 1;
+ spin_unlock(&ubi->ltree_lock);
+
+ kfree(le_free);
+
+ return le;
+}
+
+/**
+ * leb_read_lock - lock logical eraseblock for reading.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for reading. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ struct ubi_ltree_entry *le;
+
+ le = ltree_add_entry(ubi, vol_id, lnum);
+ if (IS_ERR(le))
+ return PTR_ERR(le);
+ down_read(&le->mutex);
+ return 0;
+}
+
+/**
+ * leb_read_unlock - unlock logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ */
+static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ int _free = 0;
+ struct ubi_ltree_entry *le;
+
+ spin_lock(&ubi->ltree_lock);
+ le = ltree_lookup(ubi, vol_id, lnum);
+ le->users -= 1;
+ ubi_assert(le->users >= 0);
+ if (le->users == 0) {
+ rb_erase(&le->rb, &ubi->ltree);
+ _free = 1;
+ }
+ spin_unlock(&ubi->ltree_lock);
+
+ up_read(&le->mutex);
+ if (_free)
+ kfree(le);
+}
+
+/**
+ * leb_write_lock - lock logical eraseblock for writing.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for writing. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ struct ubi_ltree_entry *le;
+
+ le = ltree_add_entry(ubi, vol_id, lnum);
+ if (IS_ERR(le))
+ return PTR_ERR(le);
+ down_write(&le->mutex);
+ return 0;
+}
+
+/**
+ * leb_write_lock - lock logical eraseblock for writing.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for writing if there is no
+ * contention and does nothing if there is contention. Returns %0 in case of
+ * success, %1 in case of contention, and and a negative error code in case of
+ * failure.
+ */
+static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ int _free;
+ struct ubi_ltree_entry *le;
+
+ le = ltree_add_entry(ubi, vol_id, lnum);
+ if (IS_ERR(le))
+ return PTR_ERR(le);
+ if (down_write_trylock(&le->mutex))
+ return 0;
+
+ /* Contention, cancel */
+ spin_lock(&ubi->ltree_lock);
+ le->users -= 1;
+ ubi_assert(le->users >= 0);
+ if (le->users == 0) {
+ rb_erase(&le->rb, &ubi->ltree);
+ _free = 1;
+ } else
+ _free = 0;
+ spin_unlock(&ubi->ltree_lock);
+ if (_free)
+ kfree(le);
+
+ return 1;
+}
+
+/**
+ * leb_write_unlock - unlock logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ */
+static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ int _free;
+ struct ubi_ltree_entry *le;
+
+ spin_lock(&ubi->ltree_lock);
+ le = ltree_lookup(ubi, vol_id, lnum);
+ le->users -= 1;
+ ubi_assert(le->users >= 0);
+ if (le->users == 0) {
+ rb_erase(&le->rb, &ubi->ltree);
+ _free = 1;
+ } else
+ _free = 0;
+ spin_unlock(&ubi->ltree_lock);
+
+ up_write(&le->mutex);
+ if (_free)
+ kfree(le);
+}
+
+/**
+ * ubi_eba_unmap_leb - un-map logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ *
+ * This function un-maps logical eraseblock @lnum and schedules corresponding
+ * physical eraseblock for erasure. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
+ int lnum)
+{
+ int err, pnum, vol_id = vol->vol_id;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ err = leb_write_lock(ubi, vol_id, lnum);
+ if (err)
+ return err;
+
+ pnum = vol->eba_tbl[lnum];
+ if (pnum < 0)
+ /* This logical eraseblock is already unmapped */
+ goto out_unlock;
+
+ dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum);
+
+ vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED;
+ err = ubi_wl_put_peb(ubi, pnum, 0);
+
+out_unlock:
+ leb_write_unlock(ubi, vol_id, lnum);
+ return err;
+}
+
+/**
+ * ubi_eba_read_leb - read data.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ * @buf: buffer to store the read data
+ * @offset: offset from where to read
+ * @len: how many bytes to read
+ * @check: data CRC check flag
+ *
+ * If the logical eraseblock @lnum is unmapped, @buf is filled with 0xFF
+ * bytes. The @check flag only makes sense for static volumes and forces
+ * eraseblock data CRC checking.
+ *
+ * In case of success this function returns zero. In case of a static volume,
+ * if data CRC mismatches - %-EBADMSG is returned. %-EBADMSG may also be
+ * returned for any volume type if an ECC error was detected by the MTD device
+ * driver. Other negative error cored may be returned in case of other errors.
+ */
+int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
+ void *buf, int offset, int len, int check)
+{
+ int err, pnum, scrub = 0, vol_id = vol->vol_id;
+ struct ubi_vid_hdr *vid_hdr;
+ uint32_t uninitialized_var(crc);
+
+ err = leb_read_lock(ubi, vol_id, lnum);
+ if (err)
+ return err;
+
+ pnum = vol->eba_tbl[lnum];
+ if (pnum < 0) {
+ /*
+ * The logical eraseblock is not mapped, fill the whole buffer
+ * with 0xFF bytes. The exception is static volumes for which
+ * it is an error to read unmapped logical eraseblocks.
+ */
+ dbg_eba("read %d bytes from offset %d of LEB %d:%d (unmapped)",
+ len, offset, vol_id, lnum);
+ leb_read_unlock(ubi, vol_id, lnum);
+ ubi_assert(vol->vol_type != UBI_STATIC_VOLUME);
+ memset(buf, 0xFF, len);
+ return 0;
+ }
+
+ dbg_eba("read %d bytes from offset %d of LEB %d:%d, PEB %d",
+ len, offset, vol_id, lnum, pnum);
+
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+ check = 0;
+
+retry:
+ if (check) {
+ vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+ if (!vid_hdr) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
+ if (err && err != UBI_IO_BITFLIPS) {
+ if (err > 0) {
+ /*
+ * The header is either absent or corrupted.
+ * The former case means there is a bug -
+ * switch to read-only mode just in case.
+ * The latter case means a real corruption - we
+ * may try to recover data. FIXME: but this is
+ * not implemented.
+ */
+ if (err == UBI_IO_BAD_VID_HDR) {
+ ubi_warn("bad VID header at PEB %d, LEB"
+ "%d:%d", pnum, vol_id, lnum);
+ err = -EBADMSG;
+ } else
+ ubi_ro_mode(ubi);
+ }
+ goto out_free;
+ } else if (err == UBI_IO_BITFLIPS)
+ scrub = 1;
+
+ ubi_assert(lnum < be32_to_cpu(vid_hdr->used_ebs));
+ ubi_assert(len == be32_to_cpu(vid_hdr->data_size));
+
+ crc = be32_to_cpu(vid_hdr->data_crc);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ }
+
+ err = ubi_io_read_data(ubi, buf, pnum, offset, len);
+ if (err) {
+ if (err == UBI_IO_BITFLIPS) {
+ scrub = 1;
+ err = 0;
+ } else if (err == -EBADMSG) {
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+ goto out_unlock;
+ scrub = 1;
+ if (!check) {
+ ubi_msg("force data checking");
+ check = 1;
+ goto retry;
+ }
+ } else
+ goto out_unlock;
+ }
+
+ if (check) {
+ uint32_t crc1 = crc32(UBI_CRC32_INIT, buf, len);
+ if (crc1 != crc) {
+ ubi_warn("CRC error: calculated %#08x, must be %#08x",
+ crc1, crc);
+ err = -EBADMSG;
+ goto out_unlock;
+ }
+ }
+
+ if (scrub)
+ err = ubi_wl_scrub_peb(ubi, pnum);
+
+ leb_read_unlock(ubi, vol_id, lnum);
+ return err;
+
+out_free:
+ ubi_free_vid_hdr(ubi, vid_hdr);
+out_unlock:
+ leb_read_unlock(ubi, vol_id, lnum);
+ return err;
+}
+
+/**
+ * recover_peb - recover from write failure.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to recover
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data which was not written because of the write failure
+ * @offset: offset of the failed write
+ * @len: how many bytes should have been written
+ *
+ * This function is called in case of a write failure and moves all good data
+ * from the potentially bad physical eraseblock to a good physical eraseblock.
+ * This function also writes the data which was not written due to the failure.
+ * Returns new physical eraseblock number in case of success, and a negative
+ * error code in case of failure.
+ */
+static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
+ const void *buf, int offset, int len)
+{
+ int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0;
+ struct ubi_volume *vol = ubi->volumes[idx];
+ struct ubi_vid_hdr *vid_hdr;
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ mutex_lock(&ubi->buf_mutex);
+
+retry:
+ new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN);
+ if (new_pnum < 0) {
+ mutex_unlock(&ubi->buf_mutex);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return new_pnum;
+ }
+
+ ubi_msg("recover PEB %d, move data to PEB %d", pnum, new_pnum);
+
+ err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
+ if (err && err != UBI_IO_BITFLIPS) {
+ if (err > 0)
+ err = -EIO;
+ goto out_put;
+ }
+
+ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+ err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
+ if (err)
+ goto write_error;
+
+ data_size = offset + len;
+ memset(ubi->peb_buf1 + offset, 0xFF, len);
+
+ /* Read everything before the area where the write failure happened */
+ if (offset > 0) {
+ err = ubi_io_read_data(ubi, ubi->peb_buf1, pnum, 0, offset);
+ if (err && err != UBI_IO_BITFLIPS)
+ goto out_put;
+ }
+
+ memcpy(ubi->peb_buf1 + offset, buf, len);
+
+ err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size);
+ if (err)
+ goto write_error;
+
+ mutex_unlock(&ubi->buf_mutex);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+
+ vol->eba_tbl[lnum] = new_pnum;
+ ubi_wl_put_peb(ubi, pnum, 1);
+
+ ubi_msg("data was successfully recovered");
+ return 0;
+
+out_put:
+ mutex_unlock(&ubi->buf_mutex);
+ ubi_wl_put_peb(ubi, new_pnum, 1);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+
+write_error:
+ /*
+ * Bad luck? This physical eraseblock is bad too? Crud. Let's try to
+ * get another one.
+ */
+ ubi_warn("failed to write to PEB %d", new_pnum);
+ ubi_wl_put_peb(ubi, new_pnum, 1);
+ if (++tries > UBI_IO_RETRIES) {
+ mutex_unlock(&ubi->buf_mutex);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+ ubi_msg("try again");
+ goto retry;
+}
+
+/**
+ * ubi_eba_write_leb - write data to dynamic volume.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ * @buf: the data to write
+ * @offset: offset within the logical eraseblock where to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ *
+ * This function writes data to logical eraseblock @lnum of a dynamic volume
+ * @vol. Returns zero in case of success and a negative error code in case
+ * of failure. In case of error, it is possible that something was still
+ * written to the flash media, but may be some garbage.
+ */
+int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
+ const void *buf, int offset, int len, int dtype)
+{
+ int err, pnum, tries = 0, vol_id = vol->vol_id;
+ struct ubi_vid_hdr *vid_hdr;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ err = leb_write_lock(ubi, vol_id, lnum);
+ if (err)
+ return err;
+
+ pnum = vol->eba_tbl[lnum];
+ if (pnum >= 0) {
+ dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d",
+ len, offset, vol_id, lnum, pnum);
+
+ err = ubi_io_write_data(ubi, buf, pnum, offset, len);
+ if (err) {
+ ubi_warn("failed to write data to PEB %d", pnum);
+ if (err == -EIO && ubi->bad_allowed)
+ err = recover_peb(ubi, pnum, vol_id, lnum, buf,
+ offset, len);
+ if (err)
+ ubi_ro_mode(ubi);
+ }
+ leb_write_unlock(ubi, vol_id, lnum);
+ return err;
+ }
+
+ /*
+ * The logical eraseblock is not mapped. We have to get a free physical
+ * eraseblock and write the volume identifier header there first.
+ */
+ vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+ if (!vid_hdr) {
+ leb_write_unlock(ubi, vol_id, lnum);
+ return -ENOMEM;
+ }
+
+ vid_hdr->vol_type = UBI_VID_DYNAMIC;
+ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+ vid_hdr->vol_id = cpu_to_be32(vol_id);
+ vid_hdr->lnum = cpu_to_be32(lnum);
+ vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+ vid_hdr->data_pad = cpu_to_be32(vol->data_pad);
+
+retry:
+ pnum = ubi_wl_get_peb(ubi, dtype);
+ if (pnum < 0) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ leb_write_unlock(ubi, vol_id, lnum);
+ return pnum;
+ }
+
+ dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d",
+ len, offset, vol_id, lnum, pnum);
+
+ err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+ if (err) {
+ ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+ vol_id, lnum, pnum);
+ goto write_error;
+ }
+
+ if (len) {
+ err = ubi_io_write_data(ubi, buf, pnum, offset, len);
+ if (err) {
+ ubi_warn("failed to write %d bytes at offset %d of "
+ "LEB %d:%d, PEB %d", len, offset, vol_id,
+ lnum, pnum);
+ goto write_error;
+ }
+ }
+
+ vol->eba_tbl[lnum] = pnum;
+
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return 0;
+
+write_error:
+ if (err != -EIO || !ubi->bad_allowed) {
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ /*
+ * Fortunately, this is the first write operation to this physical
+ * eraseblock, so just put it and request a new one. We assume that if
+ * this physical eraseblock went bad, the erase code will handle that.
+ */
+ err = ubi_wl_put_peb(ubi, pnum, 1);
+ if (err || ++tries > UBI_IO_RETRIES) {
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+ ubi_msg("try another PEB");
+ goto retry;
+}
+
+/**
+ * ubi_eba_write_leb_st - write data to static volume.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ * @used_ebs: how many logical eraseblocks will this volume contain
+ *
+ * This function writes data to logical eraseblock @lnum of static volume
+ * @vol. The @used_ebs argument should contain total number of logical
+ * eraseblock in this static volume.
+ *
+ * When writing to the last logical eraseblock, the @len argument doesn't have
+ * to be aligned to the minimal I/O unit size. Instead, it has to be equivalent
+ * to the real data size, although the @buf buffer has to contain the
+ * alignment. In all other cases, @len has to be aligned.
+ *
+ * It is prohibited to write more then once to logical eraseblocks of static
+ * volumes. This function returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
+ int lnum, const void *buf, int len, int dtype,
+ int used_ebs)
+{
+ int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id;
+ struct ubi_vid_hdr *vid_hdr;
+ uint32_t crc;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ if (lnum == used_ebs - 1)
+ /* If this is the last LEB @len may be unaligned */
+ len = ALIGN(data_size, ubi->min_io_size);
+ else
+ ubi_assert(!(len & (ubi->min_io_size - 1)));
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ err = leb_write_lock(ubi, vol_id, lnum);
+ if (err) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+ vid_hdr->vol_id = cpu_to_be32(vol_id);
+ vid_hdr->lnum = cpu_to_be32(lnum);
+ vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+ vid_hdr->data_pad = cpu_to_be32(vol->data_pad);
+
+ crc = crc32(UBI_CRC32_INIT, buf, data_size);
+ vid_hdr->vol_type = UBI_VID_STATIC;
+ vid_hdr->data_size = cpu_to_be32(data_size);
+ vid_hdr->used_ebs = cpu_to_be32(used_ebs);
+ vid_hdr->data_crc = cpu_to_be32(crc);
+
+retry:
+ pnum = ubi_wl_get_peb(ubi, dtype);
+ if (pnum < 0) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ leb_write_unlock(ubi, vol_id, lnum);
+ return pnum;
+ }
+
+ dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d",
+ len, vol_id, lnum, pnum, used_ebs);
+
+ err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+ if (err) {
+ ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+ vol_id, lnum, pnum);
+ goto write_error;
+ }
+
+ err = ubi_io_write_data(ubi, buf, pnum, 0, len);
+ if (err) {
+ ubi_warn("failed to write %d bytes of data to PEB %d",
+ len, pnum);
+ goto write_error;
+ }
+
+ ubi_assert(vol->eba_tbl[lnum] < 0);
+ vol->eba_tbl[lnum] = pnum;
+
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return 0;
+
+write_error:
+ if (err != -EIO || !ubi->bad_allowed) {
+ /*
+ * This flash device does not admit of bad eraseblocks or
+ * something nasty and unexpected happened. Switch to read-only
+ * mode just in case.
+ */
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ err = ubi_wl_put_peb(ubi, pnum, 1);
+ if (err || ++tries > UBI_IO_RETRIES) {
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+ ubi_msg("try another PEB");
+ goto retry;
+}
+
+/*
+ * ubi_eba_atomic_leb_change - change logical eraseblock atomically.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ *
+ * This function changes the contents of a logical eraseblock atomically. @buf
+ * has to contain new logical eraseblock data, and @len - the length of the
+ * data, which has to be aligned. This function guarantees that in case of an
+ * unclean reboot the old contents is preserved. Returns zero in case of
+ * success and a negative error code in case of failure.
+ *
+ * UBI reserves one LEB for the "atomic LEB change" operation, so only one
+ * LEB change may be done at a time. This is ensured by @ubi->alc_mutex.
+ */
+int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
+ int lnum, const void *buf, int len, int dtype)
+{
+ int err, pnum, tries = 0, vol_id = vol->vol_id;
+ struct ubi_vid_hdr *vid_hdr;
+ uint32_t crc;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ if (len == 0) {
+ /*
+ * Special case when data length is zero. In this case the LEB
+ * has to be unmapped and mapped somewhere else.
+ */
+ err = ubi_eba_unmap_leb(ubi, vol, lnum);
+ if (err)
+ return err;
+ return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype);
+ }
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ mutex_lock(&ubi->alc_mutex);
+ err = leb_write_lock(ubi, vol_id, lnum);
+ if (err)
+ goto out_mutex;
+
+ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+ vid_hdr->vol_id = cpu_to_be32(vol_id);
+ vid_hdr->lnum = cpu_to_be32(lnum);
+ vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+ vid_hdr->data_pad = cpu_to_be32(vol->data_pad);
+
+ crc = crc32(UBI_CRC32_INIT, buf, len);
+ vid_hdr->vol_type = UBI_VID_DYNAMIC;
+ vid_hdr->data_size = cpu_to_be32(len);
+ vid_hdr->copy_flag = 1;
+ vid_hdr->data_crc = cpu_to_be32(crc);
+
+retry:
+ pnum = ubi_wl_get_peb(ubi, dtype);
+ if (pnum < 0) {
+ err = pnum;
+ goto out_leb_unlock;
+ }
+
+ dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d",
+ vol_id, lnum, vol->eba_tbl[lnum], pnum);
+
+ err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+ if (err) {
+ ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+ vol_id, lnum, pnum);
+ goto write_error;
+ }
+
+ err = ubi_io_write_data(ubi, buf, pnum, 0, len);
+ if (err) {
+ ubi_warn("failed to write %d bytes of data to PEB %d",
+ len, pnum);
+ goto write_error;
+ }
+
+ if (vol->eba_tbl[lnum] >= 0) {
+ err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1);
+ if (err)
+ goto out_leb_unlock;
+ }
+
+ vol->eba_tbl[lnum] = pnum;
+
+out_leb_unlock:
+ leb_write_unlock(ubi, vol_id, lnum);
+out_mutex:
+ mutex_unlock(&ubi->alc_mutex);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+
+write_error:
+ if (err != -EIO || !ubi->bad_allowed) {
+ /*
+ * This flash device does not admit of bad eraseblocks or
+ * something nasty and unexpected happened. Switch to read-only
+ * mode just in case.
+ */
+ ubi_ro_mode(ubi);
+ goto out_leb_unlock;
+ }
+
+ err = ubi_wl_put_peb(ubi, pnum, 1);
+ if (err || ++tries > UBI_IO_RETRIES) {
+ ubi_ro_mode(ubi);
+ goto out_leb_unlock;
+ }
+
+ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+ ubi_msg("try another PEB");
+ goto retry;
+}
+
+/**
+ * ubi_eba_copy_leb - copy logical eraseblock.
+ * @ubi: UBI device description object
+ * @from: physical eraseblock number from where to copy
+ * @to: physical eraseblock number where to copy
+ * @vid_hdr: VID header of the @from physical eraseblock
+ *
+ * This function copies logical eraseblock from physical eraseblock @from to
+ * physical eraseblock @to. The @vid_hdr buffer may be changed by this
+ * function. Returns:
+ * o %0 in case of success;
+ * o %1 if the operation was canceled and should be tried later (e.g.,
+ * because a bit-flip was detected at the target PEB);
+ * o %2 if the volume is being deleted and this LEB should not be moved.
+ */
+int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
+ struct ubi_vid_hdr *vid_hdr)
+{
+ int err, vol_id, lnum, data_size, aldata_size, idx;
+ struct ubi_volume *vol;
+ uint32_t crc;
+
+ vol_id = be32_to_cpu(vid_hdr->vol_id);
+ lnum = be32_to_cpu(vid_hdr->lnum);
+
+ dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to);
+
+ if (vid_hdr->vol_type == UBI_VID_STATIC) {
+ data_size = be32_to_cpu(vid_hdr->data_size);
+ aldata_size = ALIGN(data_size, ubi->min_io_size);
+ } else
+ data_size = aldata_size =
+ ubi->leb_size - be32_to_cpu(vid_hdr->data_pad);
+
+ idx = vol_id2idx(ubi, vol_id);
+ spin_lock(&ubi->volumes_lock);
+ /*
+ * Note, we may race with volume deletion, which means that the volume
+ * this logical eraseblock belongs to might be being deleted. Since the
+ * volume deletion unmaps all the volume's logical eraseblocks, it will
+ * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish.
+ */
+ vol = ubi->volumes[idx];
+ if (!vol) {
+ /* No need to do further work, cancel */
+ dbg_eba("volume %d is being removed, cancel", vol_id);
+ spin_unlock(&ubi->volumes_lock);
+ return 2;
+ }
+ spin_unlock(&ubi->volumes_lock);
+
+ /*
+ * We do not want anybody to write to this logical eraseblock while we
+ * are moving it, so lock it.
+ *
+ * Note, we are using non-waiting locking here, because we cannot sleep
+ * on the LEB, since it may cause deadlocks. Indeed, imagine a task is
+ * unmapping the LEB which is mapped to the PEB we are going to move
+ * (@from). This task locks the LEB and goes sleep in the
+ * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are
+ * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the
+ * LEB is already locked, we just do not move it and return %1.
+ */
+ err = leb_write_trylock(ubi, vol_id, lnum);
+ if (err) {
+ dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum);
+ return err;
+ }
+
+ /*
+ * The LEB might have been put meanwhile, and the task which put it is
+ * probably waiting on @ubi->move_mutex. No need to continue the work,
+ * cancel it.
+ */
+ if (vol->eba_tbl[lnum] != from) {
+ dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to "
+ "PEB %d, cancel", vol_id, lnum, from,
+ vol->eba_tbl[lnum]);
+ err = 1;
+ goto out_unlock_leb;
+ }
+
+ /*
+ * OK, now the LEB is locked and we can safely start moving iy. Since
+ * this function utilizes thie @ubi->peb1_buf buffer which is shared
+ * with some other functions, so lock the buffer by taking the
+ * @ubi->buf_mutex.
+ */
+ mutex_lock(&ubi->buf_mutex);
+ dbg_eba("read %d bytes of data", aldata_size);
+ err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size);
+ if (err && err != UBI_IO_BITFLIPS) {
+ ubi_warn("error %d while reading data from PEB %d",
+ err, from);
+ goto out_unlock_buf;
+ }
+
+ /*
+ * Now we have got to calculate how much data we have to to copy. In
+ * case of a static volume it is fairly easy - the VID header contains
+ * the data size. In case of a dynamic volume it is more difficult - we
+ * have to read the contents, cut 0xFF bytes from the end and copy only
+ * the first part. We must do this to avoid writing 0xFF bytes as it
+ * may have some side-effects. And not only this. It is important not
+ * to include those 0xFFs to CRC because later the they may be filled
+ * by data.
+ */
+ if (vid_hdr->vol_type == UBI_VID_DYNAMIC)
+ aldata_size = data_size =
+ ubi_calc_data_len(ubi, ubi->peb_buf1, data_size);
+
+ cond_resched();
+ crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size);
+ cond_resched();
+
+ /*
+ * It may turn out to me that the whole @from physical eraseblock
+ * contains only 0xFF bytes. Then we have to only write the VID header
+ * and do not write any data. This also means we should not set
+ * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc.
+ */
+ if (data_size > 0) {
+ vid_hdr->copy_flag = 1;
+ vid_hdr->data_size = cpu_to_be32(data_size);
+ vid_hdr->data_crc = cpu_to_be32(crc);
+ }
+ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi));
+
+ err = ubi_io_write_vid_hdr(ubi, to, vid_hdr);
+ if (err)
+ goto out_unlock_buf;
+
+ cond_resched();
+
+ /* Read the VID header back and check if it was written correctly */
+ err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1);
+ if (err) {
+ if (err != UBI_IO_BITFLIPS)
+ ubi_warn("cannot read VID header back from PEB %d", to);
+ else
+ err = 1;
+ goto out_unlock_buf;
+ }
+
+ if (data_size > 0) {
+ err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size);
+ if (err)
+ goto out_unlock_buf;
+
+ cond_resched();
+
+ /*
+ * We've written the data and are going to read it back to make
+ * sure it was written correctly.
+ */
+
+ err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size);
+ if (err) {
+ if (err != UBI_IO_BITFLIPS)
+ ubi_warn("cannot read data back from PEB %d",
+ to);
+ else
+ err = 1;
+ goto out_unlock_buf;
+ }
+
+ cond_resched();
+
+ if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) {
+ ubi_warn("read data back from PEB %d - it is different",
+ to);
+ goto out_unlock_buf;
+ }
+ }
+
+ ubi_assert(vol->eba_tbl[lnum] == from);
+ vol->eba_tbl[lnum] = to;
+
+out_unlock_buf:
+ mutex_unlock(&ubi->buf_mutex);
+out_unlock_leb:
+ leb_write_unlock(ubi, vol_id, lnum);
+ return err;
+}
+
+/**
+ * ubi_eba_init_scan - initialize the EBA unit using scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+ int i, j, err, num_volumes;
+ struct ubi_scan_volume *sv;
+ struct ubi_volume *vol;
+ struct ubi_scan_leb *seb;
+ struct rb_node *rb;
+
+ dbg_eba("initialize EBA unit");
+
+ spin_lock_init(&ubi->ltree_lock);
+ mutex_init(&ubi->alc_mutex);
+ ubi->ltree = RB_ROOT;
+
+ ubi->global_sqnum = si->max_sqnum + 1;
+ num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
+
+ for (i = 0; i < num_volumes; i++) {
+ vol = ubi->volumes[i];
+ if (!vol)
+ continue;
+
+ cond_resched();
+
+ vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int),
+ GFP_KERNEL);
+ if (!vol->eba_tbl) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ for (j = 0; j < vol->reserved_pebs; j++)
+ vol->eba_tbl[j] = UBI_LEB_UNMAPPED;
+
+ sv = ubi_scan_find_sv(si, idx2vol_id(ubi, i));
+ if (!sv)
+ continue;
+
+ ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
+ if (seb->lnum >= vol->reserved_pebs)
+ /*
+ * This may happen in case of an unclean reboot
+ * during re-size.
+ */
+ ubi_scan_move_to_list(sv, seb, &si->erase);
+ vol->eba_tbl[seb->lnum] = seb->pnum;
+ }
+ }
+
+ if (ubi->avail_pebs < EBA_RESERVED_PEBS) {
+ ubi_err("no enough physical eraseblocks (%d, need %d)",
+ ubi->avail_pebs, EBA_RESERVED_PEBS);
+ err = -ENOSPC;
+ goto out_free;
+ }
+ ubi->avail_pebs -= EBA_RESERVED_PEBS;
+ ubi->rsvd_pebs += EBA_RESERVED_PEBS;
+
+ if (ubi->bad_allowed) {
+ ubi_calculate_reserved(ubi);
+
+ if (ubi->avail_pebs < ubi->beb_rsvd_level) {
+ /* No enough free physical eraseblocks */
+ ubi->beb_rsvd_pebs = ubi->avail_pebs;
+ ubi_warn("cannot reserve enough PEBs for bad PEB "
+ "handling, reserved %d, need %d",
+ ubi->beb_rsvd_pebs, ubi->beb_rsvd_level);
+ } else
+ ubi->beb_rsvd_pebs = ubi->beb_rsvd_level;
+
+ ubi->avail_pebs -= ubi->beb_rsvd_pebs;
+ ubi->rsvd_pebs += ubi->beb_rsvd_pebs;
+ }
+
+ dbg_eba("EBA unit is initialized");
+ return 0;
+
+out_free:
+ for (i = 0; i < num_volumes; i++) {
+ if (!ubi->volumes[i])
+ continue;
+ kfree(ubi->volumes[i]->eba_tbl);
+ }
+ return err;
+}
+
+/**
+ * ubi_eba_close - close EBA unit.
+ * @ubi: UBI device description object
+ */
+void ubi_eba_close(const struct ubi_device *ubi)
+{
+ int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
+
+ dbg_eba("close EBA unit");
+
+ for (i = 0; i < num_volumes; i++) {
+ if (!ubi->volumes[i])
+ continue;
+ kfree(ubi->volumes[i]->eba_tbl);
+ }
+}
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
new file mode 100644
index 0000000..623ab76
--- /dev/null
+++ b/drivers/mtd/ubi/io.c
@@ -0,0 +1,1273 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * UBI input/output unit.
+ *
+ * This unit provides a uniform way to work with all kinds of the underlying
+ * MTD devices. It also implements handy functions for reading and writing UBI
+ * headers.
+ *
+ * We are trying to have a paranoid mindset and not to trust to what we read
+ * from the flash media in order to be more secure and robust. So this unit
+ * validates every single header it reads from the flash media.
+ *
+ * Some words about how the eraseblock headers are stored.
+ *
+ * The erase counter header is always stored at offset zero. By default, the
+ * VID header is stored after the EC header at the closest aligned offset
+ * (i.e. aligned to the minimum I/O unit size). Data starts next to the VID
+ * header at the closest aligned offset. But this default layout may be
+ * changed. For example, for different reasons (e.g., optimization) UBI may be
+ * asked to put the VID header at further offset, and even at an unaligned
+ * offset. Of course, if the offset of the VID header is unaligned, UBI adds
+ * proper padding in front of it. Data offset may also be changed but it has to
+ * be aligned.
+ *
+ * About minimal I/O units. In general, UBI assumes flash device model where
+ * there is only one minimal I/O unit size. E.g., in case of NOR flash it is 1,
+ * in case of NAND flash it is a NAND page, etc. This is reported by MTD in the
+ * @ubi->mtd->writesize field. But as an exception, UBI admits of using another
+ * (smaller) minimal I/O unit size for EC and VID headers to make it possible
+ * to do different optimizations.
+ *
+ * This is extremely useful in case of NAND flashes which admit of several
+ * write operations to one NAND page. In this case UBI can fit EC and VID
+ * headers at one NAND page. Thus, UBI may use "sub-page" size as the minimal
+ * I/O unit for the headers (the @ubi->hdrs_min_io_size field). But it still
+ * reports NAND page size (@ubi->min_io_size) as a minimal I/O unit for the UBI
+ * users.
+ *
+ * Example: some Samsung NANDs with 2KiB pages allow 4x 512-byte writes, so
+ * although the minimal I/O unit is 2K, UBI uses 512 bytes for EC and VID
+ * headers.
+ *
+ * Q: why not just to treat sub-page as a minimal I/O unit of this flash
+ * device, e.g., make @ubi->min_io_size = 512 in the example above?
+ *
+ * A: because when writing a sub-page, MTD still writes a full 2K page but the
+ * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing
+ * 4x512 sub-pages is 4 times slower then writing one 2KiB NAND page. Thus, we
+ * prefer to use sub-pages only for EV and VID headers.
+ *
+ * As it was noted above, the VID header may start at a non-aligned offset.
+ * For example, in case of a 2KiB page NAND flash with a 512 bytes sub-page,
+ * the VID header may reside at offset 1984 which is the last 64 bytes of the
+ * last sub-page (EC header is always at offset zero). This causes some
+ * difficulties when reading and writing VID headers.
+ *
+ * Suppose we have a 64-byte buffer and we read a VID header at it. We change
+ * the data and want to write this VID header out. As we can only write in
+ * 512-byte chunks, we have to allocate one more buffer and copy our VID header
+ * to offset 448 of this buffer.
+ *
+ * The I/O unit does the following trick in order to avoid this extra copy.
+ * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header
+ * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the
+ * VID header is being written out, it shifts the VID header pointer back and
+ * writes the whole sub-page.
+ */
+
+#ifdef UBI_LINUX
+#include <linux/crc32.h>
+#include <linux/err.h>
+#endif
+
+#include <ubi_uboot.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum);
+static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum);
+static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
+ const struct ubi_ec_hdr *ec_hdr);
+static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum);
+static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
+ const struct ubi_vid_hdr *vid_hdr);
+static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
+ int len);
+#else
+#define paranoid_check_not_bad(ubi, pnum) 0
+#define paranoid_check_peb_ec_hdr(ubi, pnum) 0
+#define paranoid_check_ec_hdr(ubi, pnum, ec_hdr) 0
+#define paranoid_check_peb_vid_hdr(ubi, pnum) 0
+#define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0
+#define paranoid_check_all_ff(ubi, pnum, offset, len) 0
+#endif
+
+/**
+ * ubi_io_read - read data from a physical eraseblock.
+ * @ubi: UBI device description object
+ * @buf: buffer where to store the read data
+ * @pnum: physical eraseblock number to read from
+ * @offset: offset within the physical eraseblock from where to read
+ * @len: how many bytes to read
+ *
+ * This function reads data from offset @offset of physical eraseblock @pnum
+ * and stores the read data in the @buf buffer. The following return codes are
+ * possible:
+ *
+ * o %0 if all the requested data were successfully read;
+ * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but
+ * correctable bit-flips were detected; this is harmless but may indicate
+ * that this eraseblock may become bad soon (but do not have to);
+ * o %-EBADMSG if the MTD subsystem reported about data integrity problems, for
+ * example it can be an ECC error in case of NAND; this most probably means
+ * that the data is corrupted;
+ * o %-EIO if some I/O error occurred;
+ * o other negative error codes in case of other errors.
+ */
+int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
+ int len)
+{
+ int err, retries = 0;
+ size_t read;
+ loff_t addr;
+
+ dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset);
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+ ubi_assert(offset >= 0 && offset + len <= ubi->peb_size);
+ ubi_assert(len > 0);
+
+ err = paranoid_check_not_bad(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+
+ addr = (loff_t)pnum * ubi->peb_size + offset;
+retry:
+ err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
+ if (err) {
+ if (err == -EUCLEAN) {
+ /*
+ * -EUCLEAN is reported if there was a bit-flip which
+ * was corrected, so this is harmless.
+ */
+ ubi_msg("fixable bit-flip detected at PEB %d", pnum);
+ ubi_assert(len == read);
+ return UBI_IO_BITFLIPS;
+ }
+
+ if (read != len && retries++ < UBI_IO_RETRIES) {
+ dbg_io("error %d while reading %d bytes from PEB %d:%d, "
+ "read only %zd bytes, retry",
+ err, len, pnum, offset, read);
+ yield();
+ goto retry;
+ }
+
+ ubi_err("error %d while reading %d bytes from PEB %d:%d, "
+ "read %zd bytes", err, len, pnum, offset, read);
+ ubi_dbg_dump_stack();
+
+ /*
+ * The driver should never return -EBADMSG if it failed to read
+ * all the requested data. But some buggy drivers might do
+ * this, so we change it to -EIO.
+ */
+ if (read != len && err == -EBADMSG) {
+ ubi_assert(0);
+ err = -EIO;
+ }
+ } else {
+ ubi_assert(len == read);
+
+ if (ubi_dbg_is_bitflip()) {
+ dbg_msg("bit-flip (emulated)");
+ err = UBI_IO_BITFLIPS;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * ubi_io_write - write data to a physical eraseblock.
+ * @ubi: UBI device description object
+ * @buf: buffer with the data to write
+ * @pnum: physical eraseblock number to write to
+ * @offset: offset within the physical eraseblock where to write
+ * @len: how many bytes to write
+ *
+ * This function writes @len bytes of data from buffer @buf to offset @offset
+ * of physical eraseblock @pnum. If all the data were successfully written,
+ * zero is returned. If an error occurred, this function returns a negative
+ * error code. If %-EIO is returned, the physical eraseblock most probably went
+ * bad.
+ *
+ * Note, in case of an error, it is possible that something was still written
+ * to the flash media, but may be some garbage.
+ */
+int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
+ int len)
+{
+ int err;
+ size_t written;
+ loff_t addr;
+
+ dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset);
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+ ubi_assert(offset >= 0 && offset + len <= ubi->peb_size);
+ ubi_assert(offset % ubi->hdrs_min_io_size == 0);
+ ubi_assert(len > 0 && len % ubi->hdrs_min_io_size == 0);
+
+ if (ubi->ro_mode) {
+ ubi_err("read-only mode");
+ return -EROFS;
+ }
+
+ /* The below has to be compiled out if paranoid checks are disabled */
+
+ err = paranoid_check_not_bad(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+
+ /* The area we are writing to has to contain all 0xFF bytes */
+ err = paranoid_check_all_ff(ubi, pnum, offset, len);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+
+ if (offset >= ubi->leb_start) {
+ /*
+ * We write to the data area of the physical eraseblock. Make
+ * sure it has valid EC and VID headers.
+ */
+ err = paranoid_check_peb_ec_hdr(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+ err = paranoid_check_peb_vid_hdr(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+ }
+
+ if (ubi_dbg_is_write_failure()) {
+ dbg_err("cannot write %d bytes to PEB %d:%d "
+ "(emulated)", len, pnum, offset);
+ ubi_dbg_dump_stack();
+ return -EIO;
+ }
+
+ addr = (loff_t)pnum * ubi->peb_size + offset;
+ err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf);
+ if (err) {
+ ubi_err("error %d while writing %d bytes to PEB %d:%d, written"
+ " %zd bytes", err, len, pnum, offset, written);
+ ubi_dbg_dump_stack();
+ } else
+ ubi_assert(written == len);
+
+ return err;
+}
+
+/**
+ * erase_callback - MTD erasure call-back.
+ * @ei: MTD erase information object.
+ *
+ * Note, even though MTD erase interface is asynchronous, all the current
+ * implementations are synchronous anyway.
+ */
+static void erase_callback(struct erase_info *ei)
+{
+ wake_up_interruptible((wait_queue_head_t *)ei->priv);
+}
+
+/**
+ * do_sync_erase - synchronously erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to erase
+ *
+ * This function synchronously erases physical eraseblock @pnum and returns
+ * zero in case of success and a negative error code in case of failure. If
+ * %-EIO is returned, the physical eraseblock most probably went bad.
+ */
+static int do_sync_erase(struct ubi_device *ubi, int pnum)
+{
+ int err, retries = 0;
+ struct erase_info ei;
+ wait_queue_head_t wq;
+
+ dbg_io("erase PEB %d", pnum);
+
+retry:
+ init_waitqueue_head(&wq);
+ memset(&ei, 0, sizeof(struct erase_info));
+
+ ei.mtd = ubi->mtd;
+ ei.addr = (loff_t)pnum * ubi->peb_size;
+ ei.len = ubi->peb_size;
+ ei.callback = erase_callback;
+ ei.priv = (unsigned long)&wq;
+
+ err = ubi->mtd->erase(ubi->mtd, &ei);
+ if (err) {
+ if (retries++ < UBI_IO_RETRIES) {
+ dbg_io("error %d while erasing PEB %d, retry",
+ err, pnum);
+ yield();
+ goto retry;
+ }
+ ubi_err("cannot erase PEB %d, error %d", pnum, err);
+ ubi_dbg_dump_stack();
+ return err;
+ }
+
+ err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE ||
+ ei.state == MTD_ERASE_FAILED);
+ if (err) {
+ ubi_err("interrupted PEB %d erasure", pnum);
+ return -EINTR;
+ }
+
+ if (ei.state == MTD_ERASE_FAILED) {
+ if (retries++ < UBI_IO_RETRIES) {
+ dbg_io("error while erasing PEB %d, retry", pnum);
+ yield();
+ goto retry;
+ }
+ ubi_err("cannot erase PEB %d", pnum);
+ ubi_dbg_dump_stack();
+ return -EIO;
+ }
+
+ err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+
+ if (ubi_dbg_is_erase_failure() && !err) {
+ dbg_err("cannot erase PEB %d (emulated)", pnum);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * check_pattern - check if buffer contains only a certain byte pattern.
+ * @buf: buffer to check
+ * @patt: the pattern to check
+ * @size: buffer size in bytes
+ *
+ * This function returns %1 in there are only @patt bytes in @buf, and %0 if
+ * something else was also found.
+ */
+static int check_pattern(const void *buf, uint8_t patt, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (((const uint8_t *)buf)[i] != patt)
+ return 0;
+ return 1;
+}
+
+/* Patterns to write to a physical eraseblock when torturing it */
+static uint8_t patterns[] = {0xa5, 0x5a, 0x0};
+
+/**
+ * torture_peb - test a supposedly bad physical eraseblock.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to test
+ *
+ * This function returns %-EIO if the physical eraseblock did not pass the
+ * test, a positive number of erase operations done if the test was
+ * successfully passed, and other negative error codes in case of other errors.
+ */
+static int torture_peb(struct ubi_device *ubi, int pnum)
+{
+ int err, i, patt_count;
+
+ patt_count = ARRAY_SIZE(patterns);
+ ubi_assert(patt_count > 0);
+
+ mutex_lock(&ubi->buf_mutex);
+ for (i = 0; i < patt_count; i++) {
+ err = do_sync_erase(ubi, pnum);
+ if (err)
+ goto out;
+
+ /* Make sure the PEB contains only 0xFF bytes */
+ err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size);
+ if (err)
+ goto out;
+
+ err = check_pattern(ubi->peb_buf1, 0xFF, ubi->peb_size);
+ if (err == 0) {
+ ubi_err("erased PEB %d, but a non-0xFF byte found",
+ pnum);
+ err = -EIO;
+ goto out;
+ }
+
+ /* Write a pattern and check it */
+ memset(ubi->peb_buf1, patterns[i], ubi->peb_size);
+ err = ubi_io_write(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size);
+ if (err)
+ goto out;
+
+ memset(ubi->peb_buf1, ~patterns[i], ubi->peb_size);
+ err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size);
+ if (err)
+ goto out;
+
+ err = check_pattern(ubi->peb_buf1, patterns[i], ubi->peb_size);
+ if (err == 0) {
+ ubi_err("pattern %x checking failed for PEB %d",
+ patterns[i], pnum);
+ err = -EIO;
+ goto out;
+ }
+ }
+
+ err = patt_count;
+
+out:
+ mutex_unlock(&ubi->buf_mutex);
+ if (err == UBI_IO_BITFLIPS || err == -EBADMSG) {
+ /*
+ * If a bit-flip or data integrity error was detected, the test
+ * has not passed because it happened on a freshly erased
+ * physical eraseblock which means something is wrong with it.
+ */
+ ubi_err("read problems on freshly erased PEB %d, must be bad",
+ pnum);
+ err = -EIO;
+ }
+ return err;
+}
+
+/**
+ * ubi_io_sync_erase - synchronously erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number to erase
+ * @torture: if this physical eraseblock has to be tortured
+ *
+ * This function synchronously erases physical eraseblock @pnum. If @torture
+ * flag is not zero, the physical eraseblock is checked by means of writing
+ * different patterns to it and reading them back. If the torturing is enabled,
+ * the physical eraseblock is erased more then once.
+ *
+ * This function returns the number of erasures made in case of success, %-EIO
+ * if the erasure failed or the torturing test failed, and other negative error
+ * codes in case of other errors. Note, %-EIO means that the physical
+ * eraseblock is bad.
+ */
+int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture)
+{
+ int err, ret = 0;
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ err = paranoid_check_not_bad(ubi, pnum);
+ if (err != 0)
+ return err > 0 ? -EINVAL : err;
+
+ if (ubi->ro_mode) {
+ ubi_err("read-only mode");
+ return -EROFS;
+ }
+
+ if (torture) {
+ ret = torture_peb(ubi, pnum);
+ if (ret < 0)
+ return ret;
+ }
+
+ err = do_sync_erase(ubi, pnum);
+ if (err)
+ return err;
+
+ return ret + 1;
+}
+
+/**
+ * ubi_io_is_bad - check if a physical eraseblock is bad.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ *
+ * This function returns a positive number if the physical eraseblock is bad,
+ * zero if not, and a negative error code if an error occurred.
+ */
+int ubi_io_is_bad(const struct ubi_device *ubi, int pnum)
+{
+ struct mtd_info *mtd = ubi->mtd;
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ if (ubi->bad_allowed) {
+ int ret;
+
+ ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size);
+ if (ret < 0)
+ ubi_err("error %d while checking if PEB %d is bad",
+ ret, pnum);
+ else if (ret)
+ dbg_io("PEB %d is bad", pnum);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ubi_io_mark_bad - mark a physical eraseblock as bad.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to mark
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum)
+{
+ int err;
+ struct mtd_info *mtd = ubi->mtd;
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ if (ubi->ro_mode) {
+ ubi_err("read-only mode");
+ return -EROFS;
+ }
+
+ if (!ubi->bad_allowed)
+ return 0;
+
+ err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size);
+ if (err)
+ ubi_err("cannot mark PEB %d bad, error %d", pnum, err);
+ return err;
+}
+
+/**
+ * validate_ec_hdr - validate an erase counter header.
+ * @ubi: UBI device description object
+ * @ec_hdr: the erase counter header to check
+ *
+ * This function returns zero if the erase counter header is OK, and %1 if
+ * not.
+ */
+static int validate_ec_hdr(const struct ubi_device *ubi,
+ const struct ubi_ec_hdr *ec_hdr)
+{
+ long long ec;
+ int vid_hdr_offset, leb_start;
+
+ ec = be64_to_cpu(ec_hdr->ec);
+ vid_hdr_offset = be32_to_cpu(ec_hdr->vid_hdr_offset);
+ leb_start = be32_to_cpu(ec_hdr->data_offset);
+
+ if (ec_hdr->version != UBI_VERSION) {
+ ubi_err("node with incompatible UBI version found: "
+ "this UBI version is %d, image version is %d",
+ UBI_VERSION, (int)ec_hdr->version);
+ goto bad;
+ }
+
+ if (vid_hdr_offset != ubi->vid_hdr_offset) {
+ ubi_err("bad VID header offset %d, expected %d",
+ vid_hdr_offset, ubi->vid_hdr_offset);
+ goto bad;
+ }
+
+ if (leb_start != ubi->leb_start) {
+ ubi_err("bad data offset %d, expected %d",
+ leb_start, ubi->leb_start);
+ goto bad;
+ }
+
+ if (ec < 0 || ec > UBI_MAX_ERASECOUNTER) {
+ ubi_err("bad erase counter %lld", ec);
+ goto bad;
+ }
+
+ return 0;
+
+bad:
+ ubi_err("bad EC header");
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+/**
+ * ubi_io_read_ec_hdr - read and check an erase counter header.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to read from
+ * @ec_hdr: a &struct ubi_ec_hdr object where to store the read erase counter
+ * header
+ * @verbose: be verbose if the header is corrupted or was not found
+ *
+ * This function reads erase counter header from physical eraseblock @pnum and
+ * stores it in @ec_hdr. This function also checks CRC checksum of the read
+ * erase counter header. The following codes may be returned:
+ *
+ * o %0 if the CRC checksum is correct and the header was successfully read;
+ * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected
+ * and corrected by the flash driver; this is harmless but may indicate that
+ * this eraseblock may become bad soon (but may be not);
+ * o %UBI_IO_BAD_EC_HDR if the erase counter header is corrupted (a CRC error);
+ * o %UBI_IO_PEB_EMPTY if the physical eraseblock is empty;
+ * o a negative error code in case of failure.
+ */
+int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
+ struct ubi_ec_hdr *ec_hdr, int verbose)
+{
+ int err, read_err = 0;
+ uint32_t crc, magic, hdr_crc;
+
+ dbg_io("read EC header from PEB %d", pnum);
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+ if (UBI_IO_DEBUG)
+ verbose = 1;
+
+ err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE);
+ if (err) {
+ if (err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ return err;
+
+ /*
+ * We read all the data, but either a correctable bit-flip
+ * occurred, or MTD reported about some data integrity error,
+ * like an ECC error in case of NAND. The former is harmless,
+ * the later may mean that the read data is corrupted. But we
+ * have a CRC check-sum and we will detect this. If the EC
+ * header is still OK, we just report this as there was a
+ * bit-flip.
+ */
+ read_err = err;
+ }
+
+ magic = be32_to_cpu(ec_hdr->magic);
+ if (magic != UBI_EC_HDR_MAGIC) {
+ /*
+ * The magic field is wrong. Let's check if we have read all
+ * 0xFF. If yes, this physical eraseblock is assumed to be
+ * empty.
+ *
+ * But if there was a read error, we do not test it for all
+ * 0xFFs. Even if it does contain all 0xFFs, this error
+ * indicates that something is still wrong with this physical
+ * eraseblock and we anyway cannot treat it as empty.
+ */
+ if (read_err != -EBADMSG &&
+ check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) {
+ /* The physical eraseblock is supposedly empty */
+
+ /*
+ * The below is just a paranoid check, it has to be
+ * compiled out if paranoid checks are disabled.
+ */
+ err = paranoid_check_all_ff(ubi, pnum, 0,
+ ubi->peb_size);
+ if (err)
+ return err > 0 ? UBI_IO_BAD_EC_HDR : err;
+
+ if (verbose)
+ ubi_warn("no EC header found at PEB %d, "
+ "only 0xFF bytes", pnum);
+ return UBI_IO_PEB_EMPTY;
+ }
+
+ /*
+ * This is not a valid erase counter header, and these are not
+ * 0xFF bytes. Report that the header is corrupted.
+ */
+ if (verbose) {
+ ubi_warn("bad magic number at PEB %d: %08x instead of "
+ "%08x", pnum, magic, UBI_EC_HDR_MAGIC);
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ }
+ return UBI_IO_BAD_EC_HDR;
+ }
+
+ crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
+ hdr_crc = be32_to_cpu(ec_hdr->hdr_crc);
+
+ if (hdr_crc != crc) {
+ if (verbose) {
+ ubi_warn("bad EC header CRC at PEB %d, calculated %#08x,"
+ " read %#08x", pnum, crc, hdr_crc);
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ }
+ return UBI_IO_BAD_EC_HDR;
+ }
+
+ /* And of course validate what has just been read from the media */
+ err = validate_ec_hdr(ubi, ec_hdr);
+ if (err) {
+ ubi_err("validation failed for PEB %d", pnum);
+ return -EINVAL;
+ }
+
+ return read_err ? UBI_IO_BITFLIPS : 0;
+}
+
+/**
+ * ubi_io_write_ec_hdr - write an erase counter header.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to write to
+ * @ec_hdr: the erase counter header to write
+ *
+ * This function writes erase counter header described by @ec_hdr to physical
+ * eraseblock @pnum. It also fills most fields of @ec_hdr before writing, so
+ * the caller do not have to fill them. Callers must only fill the @ec_hdr->ec
+ * field.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If %-EIO is returned, the physical eraseblock most probably
+ * went bad.
+ */
+int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,
+ struct ubi_ec_hdr *ec_hdr)
+{
+ int err;
+ uint32_t crc;
+
+ dbg_io("write EC header to PEB %d", pnum);
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ ec_hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC);
+ ec_hdr->version = UBI_VERSION;
+ ec_hdr->vid_hdr_offset = cpu_to_be32(ubi->vid_hdr_offset);
+ ec_hdr->data_offset = cpu_to_be32(ubi->leb_start);
+ crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
+ ec_hdr->hdr_crc = cpu_to_be32(crc);
+
+ err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr);
+ if (err)
+ return -EINVAL;
+
+ err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize);
+ return err;
+}
+
+/**
+ * validate_vid_hdr - validate a volume identifier header.
+ * @ubi: UBI device description object
+ * @vid_hdr: the volume identifier header to check
+ *
+ * This function checks that data stored in the volume identifier header
+ * @vid_hdr. Returns zero if the VID header is OK and %1 if not.
+ */
+static int validate_vid_hdr(const struct ubi_device *ubi,
+ const struct ubi_vid_hdr *vid_hdr)
+{
+ int vol_type = vid_hdr->vol_type;
+ int copy_flag = vid_hdr->copy_flag;
+ int vol_id = be32_to_cpu(vid_hdr->vol_id);
+ int lnum = be32_to_cpu(vid_hdr->lnum);
+ int compat = vid_hdr->compat;
+ int data_size = be32_to_cpu(vid_hdr->data_size);
+ int used_ebs = be32_to_cpu(vid_hdr->used_ebs);
+ int data_pad = be32_to_cpu(vid_hdr->data_pad);
+ int data_crc = be32_to_cpu(vid_hdr->data_crc);
+ int usable_leb_size = ubi->leb_size - data_pad;
+
+ if (copy_flag != 0 && copy_flag != 1) {
+ dbg_err("bad copy_flag");
+ goto bad;
+ }
+
+ if (vol_id < 0 || lnum < 0 || data_size < 0 || used_ebs < 0 ||
+ data_pad < 0) {
+ dbg_err("negative values");
+ goto bad;
+ }
+
+ if (vol_id >= UBI_MAX_VOLUMES && vol_id < UBI_INTERNAL_VOL_START) {
+ dbg_err("bad vol_id");
+ goto bad;
+ }
+
+ if (vol_id < UBI_INTERNAL_VOL_START && compat != 0) {
+ dbg_err("bad compat");
+ goto bad;
+ }
+
+ if (vol_id >= UBI_INTERNAL_VOL_START && compat != UBI_COMPAT_DELETE &&
+ compat != UBI_COMPAT_RO && compat != UBI_COMPAT_PRESERVE &&
+ compat != UBI_COMPAT_REJECT) {
+ dbg_err("bad compat");
+ goto bad;
+ }
+
+ if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) {
+ dbg_err("bad vol_type");
+ goto bad;
+ }
+
+ if (data_pad >= ubi->leb_size / 2) {
+ dbg_err("bad data_pad");
+ goto bad;
+ }
+
+ if (vol_type == UBI_VID_STATIC) {
+ /*
+ * Although from high-level point of view static volumes may
+ * contain zero bytes of data, but no VID headers can contain
+ * zero at these fields, because they empty volumes do not have
+ * mapped logical eraseblocks.
+ */
+ if (used_ebs == 0) {
+ dbg_err("zero used_ebs");
+ goto bad;
+ }
+ if (data_size == 0) {
+ dbg_err("zero data_size");
+ goto bad;
+ }
+ if (lnum < used_ebs - 1) {
+ if (data_size != usable_leb_size) {
+ dbg_err("bad data_size");
+ goto bad;
+ }
+ } else if (lnum == used_ebs - 1) {
+ if (data_size == 0) {
+ dbg_err("bad data_size at last LEB");
+ goto bad;
+ }
+ } else {
+ dbg_err("too high lnum");
+ goto bad;
+ }
+ } else {
+ if (copy_flag == 0) {
+ if (data_crc != 0) {
+ dbg_err("non-zero data CRC");
+ goto bad;
+ }
+ if (data_size != 0) {
+ dbg_err("non-zero data_size");
+ goto bad;
+ }
+ } else {
+ if (data_size == 0) {
+ dbg_err("zero data_size of copy");
+ goto bad;
+ }
+ }
+ if (used_ebs != 0) {
+ dbg_err("bad used_ebs");
+ goto bad;
+ }
+ }
+
+ return 0;
+
+bad:
+ ubi_err("bad VID header");
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+/**
+ * ubi_io_read_vid_hdr - read and check a volume identifier header.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number to read from
+ * @vid_hdr: &struct ubi_vid_hdr object where to store the read volume
+ * identifier header
+ * @verbose: be verbose if the header is corrupted or wasn't found
+ *
+ * This function reads the volume identifier header from physical eraseblock
+ * @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read
+ * volume identifier header. The following codes may be returned:
+ *
+ * o %0 if the CRC checksum is correct and the header was successfully read;
+ * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected
+ * and corrected by the flash driver; this is harmless but may indicate that
+ * this eraseblock may become bad soon;
+ * o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted (a CRC
+ * error detected);
+ * o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID
+ * header there);
+ * o a negative error code in case of failure.
+ */
+int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
+ struct ubi_vid_hdr *vid_hdr, int verbose)
+{
+ int err, read_err = 0;
+ uint32_t crc, magic, hdr_crc;
+ void *p;
+
+ dbg_io("read VID header from PEB %d", pnum);
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+ if (UBI_IO_DEBUG)
+ verbose = 1;
+
+ p = (char *)vid_hdr - ubi->vid_hdr_shift;
+ err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
+ ubi->vid_hdr_alsize);
+ if (err) {
+ if (err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ return err;
+
+ /*
+ * We read all the data, but either a correctable bit-flip
+ * occurred, or MTD reported about some data integrity error,
+ * like an ECC error in case of NAND. The former is harmless,
+ * the later may mean the read data is corrupted. But we have a
+ * CRC check-sum and we will identify this. If the VID header is
+ * still OK, we just report this as there was a bit-flip.
+ */
+ read_err = err;
+ }
+
+ magic = be32_to_cpu(vid_hdr->magic);
+ if (magic != UBI_VID_HDR_MAGIC) {
+ /*
+ * If we have read all 0xFF bytes, the VID header probably does
+ * not exist and the physical eraseblock is assumed to be free.
+ *
+ * But if there was a read error, we do not test the data for
+ * 0xFFs. Even if it does contain all 0xFFs, this error
+ * indicates that something is still wrong with this physical
+ * eraseblock and it cannot be regarded as free.
+ */
+ if (read_err != -EBADMSG &&
+ check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) {
+ /* The physical eraseblock is supposedly free */
+
+ /*
+ * The below is just a paranoid check, it has to be
+ * compiled out if paranoid checks are disabled.
+ */
+ err = paranoid_check_all_ff(ubi, pnum, ubi->leb_start,
+ ubi->leb_size);
+ if (err)
+ return err > 0 ? UBI_IO_BAD_VID_HDR : err;
+
+ if (verbose)
+ ubi_warn("no VID header found at PEB %d, "
+ "only 0xFF bytes", pnum);
+ return UBI_IO_PEB_FREE;
+ }
+
+ /*
+ * This is not a valid VID header, and these are not 0xFF
+ * bytes. Report that the header is corrupted.
+ */
+ if (verbose) {
+ ubi_warn("bad magic number at PEB %d: %08x instead of "
+ "%08x", pnum, magic, UBI_VID_HDR_MAGIC);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ }
+ return UBI_IO_BAD_VID_HDR;
+ }
+
+ crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC);
+ hdr_crc = be32_to_cpu(vid_hdr->hdr_crc);
+
+ if (hdr_crc != crc) {
+ if (verbose) {
+ ubi_warn("bad CRC at PEB %d, calculated %#08x, "
+ "read %#08x", pnum, crc, hdr_crc);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ }
+ return UBI_IO_BAD_VID_HDR;
+ }
+
+ /* Validate the VID header that we have just read */
+ err = validate_vid_hdr(ubi, vid_hdr);
+ if (err) {
+ ubi_err("validation failed for PEB %d", pnum);
+ return -EINVAL;
+ }
+
+ return read_err ? UBI_IO_BITFLIPS : 0;
+}
+
+/**
+ * ubi_io_write_vid_hdr - write a volume identifier header.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to write to
+ * @vid_hdr: the volume identifier header to write
+ *
+ * This function writes the volume identifier header described by @vid_hdr to
+ * physical eraseblock @pnum. This function automatically fills the
+ * @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates
+ * header CRC checksum and stores it at vid_hdr->hdr_crc.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If %-EIO is returned, the physical eraseblock probably went
+ * bad.
+ */
+int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
+ struct ubi_vid_hdr *vid_hdr)
+{
+ int err;
+ uint32_t crc;
+ void *p;
+
+ dbg_io("write VID header to PEB %d", pnum);
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ err = paranoid_check_peb_ec_hdr(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL: err;
+
+ vid_hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC);
+ vid_hdr->version = UBI_VERSION;
+ crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC);
+ vid_hdr->hdr_crc = cpu_to_be32(crc);
+
+ err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr);
+ if (err)
+ return -EINVAL;
+
+ p = (char *)vid_hdr - ubi->vid_hdr_shift;
+ err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
+ ubi->vid_hdr_alsize);
+ return err;
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_not_bad - ensure that a physical eraseblock is not bad.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number to check
+ *
+ * This function returns zero if the physical eraseblock is good, a positive
+ * number if it is bad and a negative error code if an error occurred.
+ */
+static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
+{
+ int err;
+
+ err = ubi_io_is_bad(ubi, pnum);
+ if (!err)
+ return err;
+
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_dbg_dump_stack();
+ return err;
+}
+
+/**
+ * paranoid_check_ec_hdr - check if an erase counter header is all right.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number the erase counter header belongs to
+ * @ec_hdr: the erase counter header to check
+ *
+ * This function returns zero if the erase counter header contains valid
+ * values, and %1 if not.
+ */
+static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
+ const struct ubi_ec_hdr *ec_hdr)
+{
+ int err;
+ uint32_t magic;
+
+ magic = be32_to_cpu(ec_hdr->magic);
+ if (magic != UBI_EC_HDR_MAGIC) {
+ ubi_err("bad magic %#08x, must be %#08x",
+ magic, UBI_EC_HDR_MAGIC);
+ goto fail;
+ }
+
+ err = validate_ec_hdr(ubi, ec_hdr);
+ if (err) {
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+/**
+ * paranoid_check_peb_ec_hdr - check that the erase counter header of a
+ * physical eraseblock is in-place and is all right.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ *
+ * This function returns zero if the erase counter header is all right, %1 if
+ * not, and a negative error code if an error occurred.
+ */
+static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
+{
+ int err;
+ uint32_t crc, hdr_crc;
+ struct ubi_ec_hdr *ec_hdr;
+
+ ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
+ if (!ec_hdr)
+ return -ENOMEM;
+
+ err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE);
+ if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ goto exit;
+
+ crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
+ hdr_crc = be32_to_cpu(ec_hdr->hdr_crc);
+ if (hdr_crc != crc) {
+ ubi_err("bad CRC, calculated %#08x, read %#08x", crc, hdr_crc);
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ ubi_dbg_dump_stack();
+ err = 1;
+ goto exit;
+ }
+
+ err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr);
+
+exit:
+ kfree(ec_hdr);
+ return err;
+}
+
+/**
+ * paranoid_check_vid_hdr - check that a volume identifier header is all right.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number the volume identifier header belongs to
+ * @vid_hdr: the volume identifier header to check
+ *
+ * This function returns zero if the volume identifier header is all right, and
+ * %1 if not.
+ */
+static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
+ const struct ubi_vid_hdr *vid_hdr)
+{
+ int err;
+ uint32_t magic;
+
+ magic = be32_to_cpu(vid_hdr->magic);
+ if (magic != UBI_VID_HDR_MAGIC) {
+ ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x",
+ magic, pnum, UBI_VID_HDR_MAGIC);
+ goto fail;
+ }
+
+ err = validate_vid_hdr(ubi, vid_hdr);
+ if (err) {
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ goto fail;
+ }
+
+ return err;
+
+fail:
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ ubi_dbg_dump_stack();
+ return 1;
+
+}
+
+/**
+ * paranoid_check_peb_vid_hdr - check that the volume identifier header of a
+ * physical eraseblock is in-place and is all right.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ *
+ * This function returns zero if the volume identifier header is all right,
+ * %1 if not, and a negative error code if an error occurred.
+ */
+static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
+{
+ int err;
+ uint32_t crc, hdr_crc;
+ struct ubi_vid_hdr *vid_hdr;
+ void *p;
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ p = (char *)vid_hdr - ubi->vid_hdr_shift;
+ err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
+ ubi->vid_hdr_alsize);
+ if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ goto exit;
+
+ crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC);
+ hdr_crc = be32_to_cpu(vid_hdr->hdr_crc);
+ if (hdr_crc != crc) {
+ ubi_err("bad VID header CRC at PEB %d, calculated %#08x, "
+ "read %#08x", pnum, crc, hdr_crc);
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ ubi_dbg_dump_stack();
+ err = 1;
+ goto exit;
+ }
+
+ err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr);
+
+exit:
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+}
+
+/**
+ * paranoid_check_all_ff - check that a region of flash is empty.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ * @offset: the starting offset within the physical eraseblock to check
+ * @len: the length of the region to check
+ *
+ * This function returns zero if only 0xFF bytes are present at offset
+ * @offset of the physical eraseblock @pnum, %1 if not, and a negative error
+ * code if an error occurred.
+ */
+static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
+ int len)
+{
+ size_t read;
+ int err;
+ loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
+
+ mutex_lock(&ubi->dbg_buf_mutex);
+ err = ubi->mtd->read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf);
+ if (err && err != -EUCLEAN) {
+ ubi_err("error %d while reading %d bytes from PEB %d:%d, "
+ "read %zd bytes", err, len, pnum, offset, read);
+ goto error;
+ }
+
+ err = check_pattern(ubi->dbg_peb_buf, 0xFF, len);
+ if (err == 0) {
+ ubi_err("flash region at PEB %d:%d, length %d does not "
+ "contain all 0xFF bytes", pnum, offset, len);
+ goto fail;
+ }
+ mutex_unlock(&ubi->dbg_buf_mutex);
+
+ return 0;
+
+fail:
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ dbg_msg("hex dump of the %d-%d region", offset, offset + len);
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
+ ubi->dbg_peb_buf, len, 1);
+ err = 1;
+error:
+ ubi_dbg_dump_stack();
+ mutex_unlock(&ubi->dbg_buf_mutex);
+ return err;
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
1
0
Dear Wolfgang,
The following changes since commit d9d8c7c696dec370ca714c03beb6e79d4c90bd5e:
Wolfgang Denk (1):
Fix strmhz(): avoid printing negative fractions
are available in the git repository at:
git://git.denx.de/u-boot-video.git master
Haavard Skinnemoen (5):
atmel_lcdfb: Eliminate unneeded #include <asm/arch/hardware.h>
atmel_lcdfb: Straighten out funky vl_sync logic
lcd: Implement lcd_printf()
lcd: Set lcd_is_enabled before clearing the screen
lcd: Let the board code show board-specific info
board/atmel/at91cap9adk/at91cap9adk.c | 29 ++++++++
board/atmel/at91sam9261ek/at91sam9261ek.c | 29 ++++++++
board/atmel/at91sam9263ek/at91sam9263ek.c | 29 ++++++++
board/atmel/at91sam9rlek/at91sam9rlek.c | 29 ++++++++
board/lwmon/lwmon.c | 29 ++++++++
board/tqc/tqm8xx/tqm8xx.c | 26 ++++++++
common/lcd.c | 100 ++++++-----------------------
drivers/video/atmel_lcdfb.c | 6 +--
include/asm-arm/arch-at91/gpio.h | 1 +
include/lcd.h | 2 +
10 files changed, 195 insertions(+), 85 deletions(-)
Best regards,
Anatolij
2
1
fdt_add_mem_rsv() requires space for a struct fdt_reserve_entry
(16 bytes), so make sure that fdt_resize atleast adds that much
padding, no matter what the location or size of the fdt is.
Signed-off-by: Peter Korsgaard <jacmet(a)sunsite.dk>
---
common/fdt_support.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/common/fdt_support.c b/common/fdt_support.c
index 8ceeb0f..e2197ed 100644
--- a/common/fdt_support.c
+++ b/common/fdt_support.c
@@ -575,9 +575,10 @@ int fdt_resize(void *blob)
}
}
- /* Calculate the actual size of the fdt */
+ /* Calculate the actual size of the fdt
+ + size needed for fdt_add_mem_rsv */
actualsize = fdt_off_dt_strings(blob) +
- fdt_size_dt_strings(blob);
+ fdt_size_dt_strings(blob) + sizeof(struct fdt_reserve_entry);
/* Make it so the fdt ends on a page boundary */
actualsize = ALIGN(actualsize, 0x1000);
--
1.5.6.5
2
2
Patch to fix buffer allocation size and alignment. Buffer needs to be u32 aligned and
PKTSIZE_ALIGN bytes long.
Acked-by: Michal Simek <monstr(a)monstr.eu>
---
drivers/net/xilinx_emaclite.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c
index 88cd0f9..0e96ef1 100644
--- a/drivers/net/xilinx_emaclite.c
+++ b/drivers/net/xilinx_emaclite.c
@@ -70,7 +70,7 @@ typedef struct {
static xemaclite emaclite;
-static char etherrxbuff[PKTSIZE_ALIGN/4]; /* Receive buffer */
+static u32 etherrxbuff[PKTSIZE_ALIGN/4]; /* Receive buffer */
/* hardcoded MAC address for the Xilinx EMAC Core when env is nowhere*/
#ifdef CONFIG_ENV_IS_NOWHERE
--
1.5.6.4
2
1
From: Frank Haverkamp <haver(a)vnet.ibm.com>
http://tools.ietf.org/html/rfc2348 describes the TFTP block size option
which allows larger packtes than the 512 byte default. This reduces the
number of TFTP ACKs significantly and improves performance.
To get the most benefit out of the tftp block size option the support
of defragementation of IP/UDP packet is helpful. The current implemenation
should work even with packets received out of order. To enable the large
packet size the user should set "tftp_block_size" so a value like 16352.
We experimented with different packet sizes and found that more than those
16KiB do not contribute much to the performance anymore. Therefor I limited
the defragmentation buffer to 16KiB no too waste memory.
Signed-off-by: Frank Haverkamp <haver(a)vnet.ibm.com>
Signed-off-by: Josh Boyer <jwboyer(a)linux.vnet.ibm.com>
---
include/net.h | 17 ++++++
net/net.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
net/tftp.c | 22 ++++++++
net/tftp.h | 10 +++
4 files changed, 185 insertions(+), 20 deletions(-)
--- u-boot.git.orig/include/net.h
+++ u-boot.git/include/net.h
@@ -200,6 +200,13 @@ typedef struct {
ushort udp_xsum; /* Checksum */
} IP_t;
+#define IP_OFFS 0x1FFF /* ip offset *= 8 */
+#define IP_OFFS_SHIFT 3 /* in 8 byte steps */
+#define IP_FLAGS 0xE000 /* first 3 bits */
+#define IP_FLAGS_RES 0x8000 /* reserved */
+#define IP_FLAGS_DFRAG 0x4000 /* don't fragments */
+#define IP_FLAGS_MFRAG 0x2000 /* more fragments */
+
#define IP_HDR_SIZE_NO_UDP (sizeof (IP_t) - 8)
#define IP_HDR_SIZE (sizeof (IP_t))
@@ -282,6 +289,16 @@ typedef struct icmphdr {
#define PKTSIZE_ALIGN 1536
/*#define PKTSIZE 608*/
+ /*
+ * IP/UDP Fragmentation support
+ * See: http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly
+ * MAX possible UDP packet size is 64 KiB, if there is memory available.
+ */
+#define NET_ETH_MTU 1500
+#define NET_FRAG_BUF_SIZE (16 * 1024) /* MAX is 64 KiB */
+#define NET_UDP_FRAG_SIZE (NET_ETH_MTU - IP_HDR_SIZE_NO_UDP) /* 1480 */
+#define NET_FRAG_BUF_USED (NET_FRAG_BUF_SIZE / NET_UDP_FRAG_SIZE + 1)
+
/*
* Maximum receive ring size; that is, the number of packets
* we can buffer before overflow happens. Basically, this just
--- u-boot.git.orig/net/net.c
+++ u-boot.git/net/net.c
@@ -192,6 +192,15 @@ volatile uchar PktBuf[(PKTBUFSRX+1) * PK
volatile uchar *NetRxPackets[PKTBUFSRX]; /* Receive packets */
+/* Packet fragmentation support */
+static uint16_t ip_id = 0; /* sequence number */
+static uint16_t udp_len = 0;
+static uint16_t udp_src = 0;
+static uint16_t udp_dst = 0;
+static int max_idx = 0;
+static uchar NetFragBuf[NET_FRAG_BUF_SIZE];
+static char NetFragBufUsed[NET_FRAG_BUF_USED] = { 0, };
+
static rxhand_f *packetHandler; /* Current RX packet handler */
static thand_f *timeHandler; /* Current timeout handler */
static ulong timeStart; /* Time base value */
@@ -288,6 +297,13 @@ NetLoop(proto_t protocol)
{
bd_t *bd = gd->bd;
+ /* Packet fragmentation support */
+ ip_id = udp_len = udp_src = udp_dst = max_idx = 0;
+ memset(NetFragBuf, 0xFF, sizeof(NetFragBuf));
+ memset(NetFragBufUsed, 0, sizeof(NetFragBufUsed));
+ printf("NetFragBuf @ %08x max tftp_block_size=%d udp_frag_size=%d\n",
+ NetFragBuf, TFTP_BLOCK_SIZE_MAX, NET_UDP_FRAG_SIZE);
+
#ifdef CONFIG_NET_MULTI
NetRestarted = 0;
NetDevExists = 0;
@@ -1150,6 +1166,39 @@ static void CDPStart(void)
}
#endif
+#ifdef CONFIG_UDP_CHECKSUM
+/*
+ * @sumptr: Points to UDP data
+ * @sumlen: Size of UDP data
+ * @xsum: UDP checksum across IP source, destination address, protocol and size
+ *
+ * Returns 0 when checksum is correct and 1 if it is not.
+ */
+static int udp_checksum(ushort *sumptr, ushort sumlen, ulong xsum)
+{
+ while (sumlen > 1) {
+ ushort sumdata;
+
+ sumdata = *sumptr++;
+ xsum += ntohs(sumdata);
+ sumlen -= 2;
+ }
+ if (sumlen > 0) {
+ ushort sumdata;
+
+ sumdata = *(unsigned char *) sumptr;
+ sumdata = (sumdata << 8) & 0xff00;
+ xsum += sumdata;
+ }
+ while ((xsum >> 16) != 0) {
+ xsum = (xsum & 0x0000ffff) + ((xsum >> 16) & 0x0000ffff);
+ }
+ if ((xsum != 0x00000000) && (xsum != 0x0000ffff))
+ return 1;
+
+ return 0;
+}
+#endif /* CONFIG_UDP_CHECKSUM */
void
NetReceive(volatile uchar * inpkt, int len)
@@ -1164,6 +1213,7 @@ NetReceive(volatile uchar * inpkt, int l
int iscdp;
#endif
ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;
+ uint32_t off; /* ip_off for fragmentation */
#ifdef ET_DEBUG
printf("packet received\n");
@@ -1404,9 +1454,11 @@ NetReceive(volatile uchar * inpkt, int l
if ((ip->ip_hl_v & 0xf0) != 0x40) {
return;
}
+#if 0 /* Obsolete after adding the fragmentation support */
if (ip->ip_off & htons(0x1fff)) { /* Can't deal w/ fragments */
return;
}
+#endif
/* can't deal with headers > 20 bytes */
if ((ip->ip_hl_v & 0x0f) > 0x05) {
return;
@@ -1422,6 +1474,88 @@ NetReceive(volatile uchar * inpkt, int l
#endif
return;
}
+
+ /*
+ * Fragmentation support. We need to check the ip_id
+ * and if all fragments were received correctly.
+ */
+ off = (ntohs(ip->ip_off) & IP_OFFS) << IP_OFFS_SHIFT;
+ if ((off != 0) || (ip->ip_off & htons(IP_FLAGS_MFRAG))) {
+ int size, idx, complete;
+ char *start;
+
+ /* New fragmented packet arrived, clear data. */
+ if (ntohs(ip->ip_id) != ip_id) {
+ ip_id = ntohs(ip->ip_id);
+ memset(NetFragBufUsed, 0, sizeof(NetFragBufUsed));
+ udp_len = udp_src = udp_dst = max_idx = 0;
+ }
+
+ idx = off / NET_UDP_FRAG_SIZE;
+
+ /* Packet does not fit into IP/UDP fragmentation buf */
+ if (idx >= NET_FRAG_BUF_USED) {
+ return;
+ }
+
+ NetFragBufUsed[idx] = 1;
+
+ /* Copy the UDP hdr with the data for 1st
+ fragment, else copy just payload */
+ if (off == 0) {
+ udp_len = ntohs(ip->udp_len);
+ udp_src = ntohs(ip->udp_src);
+ udp_dst = ntohs(ip->udp_dst);
+ }
+ size = ntohs(ip->ip_len) - IP_HDR_SIZE_NO_UDP;
+ start = (char *)ip + IP_HDR_SIZE_NO_UDP;
+ memcpy(NetFragBuf + off, start, size);
+
+ /*
+ * When last fragement has been received we
+ * know the number of fragments we expect. If
+ * all have arrived we process the packet.
+ */
+ if (((off != 0) && !(ip->ip_off & htons(IP_FLAGS_MFRAG))))
+ max_idx = idx;
+
+ if (max_idx == 0)
+ return;
+
+ complete = 1;
+ for (idx = 0; idx < max_idx; idx++) {
+ if (NetFragBufUsed[idx] == 0) {
+ complete = 0;
+ break;
+ }
+ }
+ if (!complete)
+ return;
+#ifdef CONFIG_UDP_CHECKSUM
+ if (ip->udp_xsum != 0) {
+ ulong xsum = ip->ip_p;
+ uint16_t *sumptr;
+
+ xsum += udp_len;
+ xsum += (ntohl(ip->ip_src) >> 16) & 0xffff;
+ xsum += (ntohl(ip->ip_src) >> 0) & 0xffff;
+ xsum += (ntohl(ip->ip_dst) >> 16) & 0xffff;
+ xsum += (ntohl(ip->ip_dst) >> 0) & 0xffff;
+ sumptr = (ushort *)NetFragBuf;
+
+ if (udp_checksum(sumptr, udp_len, xsum)) {
+ putc('U');
+ return;
+ }
+ }
+#endif /* CONFIG_UDP_CHECKSUM */
+ (*packetHandler)(NetFragBuf + 8,
+ udp_dst,
+ udp_src,
+ udp_len - 8);
+ return;
+ }
+
/*
* watch for ICMP host redirects
*
@@ -1502,26 +1636,8 @@ NetReceive(volatile uchar * inpkt, int l
sumlen = ntohs(ip->udp_len);
sumptr = (ushort *) &(ip->udp_src);
- while (sumlen > 1) {
- ushort sumdata;
-
- sumdata = *sumptr++;
- xsum += ntohs(sumdata);
- sumlen -= 2;
- }
- if (sumlen > 0) {
- ushort sumdata;
-
- sumdata = *(unsigned char *) sumptr;
- sumdata = (sumdata << 8) & 0xff00;
- xsum += sumdata;
- }
- while ((xsum >> 16) != 0) {
- xsum = (xsum & 0x0000ffff) + ((xsum >> 16) & 0x0000ffff);
- }
- if ((xsum != 0x00000000) && (xsum != 0x0000ffff)) {
- printf(" UDP wrong checksum %08lx %08x\n",
- xsum, ntohs(ip->udp_xsum));
+ if (udp_checksum(sumptr, sumlen, xsum)) {
+ putc('U');
return;
}
}
--- u-boot.git.orig/net/tftp.c
+++ u-boot.git/net/tftp.c
@@ -456,6 +456,7 @@ TftpTimeout (void)
void
TftpStart (void)
{
+ char *s, *err;
#ifdef CONFIG_TFTP_PORT
char *ep; /* Environment pointer */
#endif
@@ -518,6 +519,27 @@ TftpStart (void)
puts ("Loading: *\b");
+ /* Get alternate tftp_block_size */
+ if ((s = getenv("tftp_block_size")) != NULL) {
+ err = NULL;
+
+ TftpBlkSizeOption = simple_strtoul(s, &err, 10);
+ if (*err) {
+ printf("ERR: \"tftp_block_size\" is not a number\n");
+ TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+ }
+ /*
+ * Reject values which require extensive handling.
+ * block size of 1428 octets (Ethernet MTU, less
+ * the TFTP, UDP and IP header lengths).
+ */
+ if (TftpBlkSizeOption > TFTP_BLOCK_SIZE_MAX) {
+ printf("ERR: tftp_block_sizes larger than %d not "
+ "supported\n", TFTP_BLOCK_SIZE_MAX);
+ TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+ }
+ }
+
NetSetTimeout (TIMEOUT * CFG_HZ, TftpTimeout);
NetSetHandler (TftpHandler);
--- u-boot.git.orig/net/tftp.h
+++ u-boot.git/net/tftp.h
@@ -8,11 +8,21 @@
#ifndef __TFTP_H__
#define __TFTP_H__
+#include <net.h>
+
/**********************************************************************/
/*
* Global functions and variables.
*/
+/*
+ * Maximum TFTP block size bound to max size of fragmented IP/UDP
+ * packets minus TFTP and UDP/IP overhead. TFTP overhead is 2 byte
+ * opcode and 2 byte block-number.
+ */
+#define TFTP_BLOCK_SIZE_MAX (NET_FRAG_BUF_SIZE - sizeof(IP_t) - 4)
+
+
/* tftp.c */
extern void TftpStart (void); /* Begin TFTP get */
4
7
The MPC8572 has a 4-bit wide PORDEVSR IO_SEL field. Other MPC85xx
processors have a 3-bit wide IO_SEL field but have the most
significant bit is wired to 0 so this change should not affect
them.
Signed-off-by: Peter Tyser <ptyser(a)xes-inc.com>
---
include/asm-ppc/immap_85xx.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/include/asm-ppc/immap_85xx.h b/include/asm-ppc/immap_85xx.h
index ad30099..4892d8b 100644
--- a/include/asm-ppc/immap_85xx.h
+++ b/include/asm-ppc/immap_85xx.h
@@ -1568,7 +1568,7 @@ typedef struct ccsr_gur {
#define MPC85xx_PORDEVSR_SGMII3_DIS 0x08000000
#define MPC85xx_PORDEVSR_SGMII4_DIS 0x04000000
#define MPC85xx_PORDEVSR_SRDS2_IO_SEL 0x38000000
-#define MPC85xx_PORDEVSR_IO_SEL 0x00380000
+#define MPC85xx_PORDEVSR_IO_SEL 0x00780000
#define MPC85xx_PORDEVSR_PCI2_ARB 0x00040000
#define MPC85xx_PORDEVSR_PCI1_ARB 0x00020000
#define MPC85xx_PORDEVSR_PCI1_PCI32 0x00010000
--
1.6.0.2.GIT
2
1

28 Oct '08
Split to meet mailing list size limit
Initial addition of eNET files - builds clean but will not run until
additional i386 code changes are made
Signed-off-by: Graeme Russ <graeme.russ(a)gmail.com>
--
diff --git a/board/eNET/fpga.c b/board/eNET/fpga.c
new file mode 100644
index 0000000..a3e4677
--- /dev/null
+++ b/board/eNET/fpga.c
@@ -0,0 +1,149 @@
+/*
+ * (C) Copyright 2002
+ * Wolfgang Grandegger, DENX Software Engineering, wg(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 <command.h>
+#include <linux/ctype.h>
+#include <asm/ic/sc520.h>
+#include <asm/ic/ssi.h>
+#include <watchdog.h>
+#include <asm/io.h>
+
+#include "fpga.h"
+
+static u8 fpga_init(void);
+static u8 fpga_write(void *buf, size_t bsize);
+static u8 fpga_finalise(void);
+static void fpga_close(void);
+
+
+int fpga_load(int devnum, void *buf, size_t bsize )
+{
+ u8 ret = FPGA_SUCCESS;
+
+ WATCHDOG_RESET();
+
+ ret = fpga_init();
+
+ if (ret == FPGA_SUCCESS) {
+ ret = fpga_write(buf, bsize);
+ }
+
+ if (ret == FPGA_SUCCESS) {
+ ret = fpga_finalise();
+ }
+
+ fpga_close();
+
+ WATCHDOG_RESET();
+
+ return ret;
+}
+
+
+static u8 fpga_init(void)
+{
+ u8 ret = FPGA_FAIL_INIT;
+ u16 state = 0x0000;
+
+ /*
+ * Drop then raise the FPGA's program bit
+ */
+ writew(CFG_FPGA_PROGRAM_PIO_BIT, CFG_FPGA_PIO_CLR);
+ udelay(CFG_FPGA_PROGRAM_BIT_DROP_TIME * 1000);
+ writew(CFG_FPGA_PROGRAM_PIO_BIT, CFG_FPGA_PIO_SET);
+
+ reset_timer();
+
+ while (get_timer(0) < CFG_FPGA_MAX_INIT_TIME) {
+ /*
+ * Check if the FPGA has raised its initialized bit
+ */
+ state = readw(CFG_FPGA_PIO_DATA);
+
+ if (state & CFG_FPGA_INIT_PIO_BIT) {
+ ret = FPGA_SUCCESS;
+ goto Done;
+ }
+
+ udelay (10);
+ }
+
+Done:
+ return ret;
+}
+
+
+static u8 fpga_write(void *buf, size_t bsize)
+{
+ u8 *ptr = (u8 *) buf;
+
+ /*
+ * Stream the buffer to the FPGA using the SSI
+ */
+ ssi_set_interface(CFG_FPGA_SSI_DATA_RATE, 0, 0, 0);
+
+ /*
+ * TODO: Can ssi_tx_byte() fail (port busy)?
+ */
+ while (bsize--)
+ ssi_tx_byte (*ptr);
+
+ return FPGA_SUCCESS;
+}
+
+
+static u8 fpga_finalise(void)
+{
+ u8 ret = FPGA_FAIL_FINALISE;
+ u16 state = 0x0000;
+
+ reset_timer();
+
+ while (get_timer(0) < CFG_FPGA_MAX_FINALISE_TIME) {
+ state = readw(CFG_FPGA_PIO_DATA);
+
+ if (state & CFG_FPGA_DONE_PIO_BIT) {
+ ret = FPGA_SUCCESS;
+ goto Done;
+ }
+
+ udelay (10);
+ }
+
+Done:
+ return ret;
+}
+
+static void fpga_close(void)
+{
+ u16 dirs = readw(CFG_FPGA_PIO_DIRECTION);
+
+ /*
+ * Set the program pin of the FPGA to be an input (high impedance)
+ */
+ dirs &= ~CFG_FPGA_PROGRAM_PIO_BIT;
+
+ writew(dirs, CFG_FPGA_PIO_DIRECTION);
+}
diff --git a/board/eNET/fpga.h b/board/eNET/fpga.h
new file mode 100644
index 0000000..a4db321
--- /dev/null
+++ b/board/eNET/fpga.h
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 2008
+ * Graeme Russ, graeme.russ(a)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 _FPGA_H_
+#define _FPGA_H_
+
+#define FPGA_SUCCESS 0
+#define FPGA_FAIL_INIT 1
+#define FPGA_FAIL_FINALISE 2
+#define FPGA_FAIL_START 3
+
+
+
+int fpga_load( int devnum, void *buf, size_t bsize );
+
+#endif /* _FPGA_H_ */
diff --git a/board/eNET/hardware.h b/board/eNET/hardware.h
new file mode 100644
index 0000000..eab612c
--- /dev/null
+++ b/board/eNET/hardware.h
@@ -0,0 +1,13 @@
+/*
+ * hardware.h
+ *
+ * Created on: 17/09/2008
+ * Author: graeme
+ */
+
+#ifndef HARDWARE_H_
+#define HARDWARE_H_
+
+#include "hardware_defs.h"
+
+#endif /* HARDWARE_H_ */
diff --git a/board/eNET/hardware_defs.h b/board/eNET/hardware_defs.h
new file mode 100644
index 0000000..2ffa008
--- /dev/null
+++ b/board/eNET/hardware_defs.h
@@ -0,0 +1,19 @@
+/*
+ * hardware.h
+ *
+ * Created on: 11/09/2008
+ * Author: graeme
+ */
+
+#ifndef HARDWARE_DEFS_H_
+#define HARDWARE_DEFS_H_
+
+#define LED_LATCH_ADDRESS 0x1002
+#define LED_RUN_BITMASK 0x01
+#define LED_1_BITMASK 0x02
+#define LED_2_BITMASK 0x04
+#define LED_RX_BITMASK 0x08
+#define LED_TX_BITMASK 0x10
+#define LED_ERR_BITMASK 0x20
+
+#endif /* HARDWARE_H_ */
diff --git a/board/eNET/u-boot.lds b/board/eNET/u-boot.lds
new file mode 100644
index 0000000..7855c0b
--- /dev/null
+++ b/board/eNET/u-boot.lds
@@ -0,0 +1,90 @@
+/*
+ * (C) Copyright 2002
+ * Daniel Engstr�m, Omicron Ceti AB, daniel(a)omicron.se.
+ *
+ * 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
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+ . = 0x38040000; /* Where bootcode in the flash is mapped */
+ .text : { *(.text); }
+
+ . = ALIGN(4);
+ .rodata : { *(.rodata) *(.rodata.str1.1) *(.rodata.str1.32) }
+
+ _i386boot_text_size = SIZEOF(.text) + SIZEOF(.rodata);
+
+ . = 0x03FF0000; /* Ram data segment to use */
+ _i386boot_romdata_dest = ABSOLUTE(.);
+ .data : AT ( LOADADDR(.rodata) + SIZEOF(.rodata) ) { *(.data) }
+ _i386boot_romdata_start = LOADADDR(.data);
+
+ . = ALIGN(4);
+ .got : AT ( LOADADDR(.data) + SIZEOF(.data) ) { *(.got) }
+
+ . = ALIGN(4);
+ __u_boot_cmd_start = .;
+ .u_boot_cmd : { *(.u_boot_cmd) }
+ __u_boot_cmd_end = .;
+ _i386boot_cmd_start = LOADADDR(.u_boot_cmd);
+
+ _i386boot_romdata_size = SIZEOF(.data) + SIZEOF(.got) + SIZEOF(.u_boot_cmd);
+
+ . = ALIGN(4);
+ _i386boot_bss_start = ABSOLUTE(.);
+ .bss (NOLOAD) : { *(.bss) }
+ _i386boot_bss_size = SIZEOF(.bss);
+
+ /* 16bit realmode trampoline code */
+ .realmode 0x7c0 : AT ( LOADADDR(.got) + SIZEOF(.got) + SIZEOF(.u_boot_cmd)) { *(.realmode) }
+
+ _i386boot_realmode = LOADADDR(.realmode);
+ _i386boot_realmode_size = SIZEOF(.realmode);
+
+ /* 16bit BIOS emulation code (just enough to boot Linux) */
+ .bios 0 : AT ( LOADADDR(.realmode) + SIZEOF(.realmode) ) { *(.bios) }
+
+ _i386boot_bios = LOADADDR(.bios);
+ _i386boot_bios_size = SIZEOF(.bios);
+
+ /* The load addresses below assumes that the flash
+ * will be mapped so that 0x387f0000 == 0xffff0000
+ * at reset time
+ *
+ * The fe00 and ff00 offsets of the start32 and start16
+ * segments are arbitrary, the just have to be mapped
+ * at reset and the code have to fit.
+ * The fff0 offset of reset is important, however.
+ */
+
+ . = 0xfffffe00;
+ .start32 : AT (0x3807fe00) { *(.start32); }
+
+ . = 0xf800;
+ .start16 : AT (0x3807f800) { *(.start16); }
+
+ . = 0xfff0;
+ .reset : AT (0x3807fff0) { *(.reset); }
+ _i386boot_end = (LOADADDR(.reset) + SIZEOF(.reset) );
+}
diff --git a/cpu/i386/sc520.c b/cpu/i386/sc520.c
index 640b255..a5724b6 100644
--- a/cpu/i386/sc520.c
+++ b/cpu/i386/sc520.c
@@ -32,7 +32,7 @@
#include <config.h>
#include <pci.h>
#ifdef CONFIG_SC520_SSI
-#include <ssi.h>
+#include <asm/ic/ssi.h>
#endif
#include <asm/io.h>
#include <asm/pci.h>
diff --git a/examples/82559_eeprom.c b/examples/82559_eeprom.c
index d99af26..047d3aa 100644
--- a/examples/82559_eeprom.c
+++ b/examples/82559_eeprom.c
@@ -19,7 +19,7 @@
*/
#define _PPC_STRING_H_ /* avoid unnecessary str/mem functions */
-#define _LINUX_STRING_H_ /* avoid unnecessary str/mem functions */
+/* #define _LINUX_STRING_H_ */ /* avoid unnecessary str/mem functions */
#include <common.h>
#include <exports.h>
diff --git a/include/asm-i386/ic/sc520.h b/include/asm-i386/ic/sc520.h
index 0f7e7a5..2da01ea 100644
--- a/include/asm-i386/ic/sc520.h
+++ b/include/asm-i386/ic/sc520.h
@@ -312,7 +312,10 @@ extern int sc520_pci_ints[];
void init_sc520(void);
unsigned long init_sc520_dram(void);
+
+#ifdef CONFIG_PCI
void pci_sc520_init(struct pci_controller *hose);
int pci_sc520_set_irq(int pci_pin, int irq);
+#endif
#endif
diff --git a/include/asm-i386/ic/sc520_defs.h b/include/asm-i386/ic/sc520_defs.h
new file mode 100644
index 0000000..c8f6311
--- /dev/null
+++ b/include/asm-i386/ic/sc520_defs.h
@@ -0,0 +1,1489 @@
+/*
+ * sc520_defs.h
+ *
+ * Created on: 17/09/2008
+ * Author: graeme
+ */
+
+#ifndef _ASM_IC_SC520_DEFS_H_
+#define _ASM_IC_SC520_DEFS_H_
+
+/* Memory mapped configuration registers, MMCR */
+#define SC520_REVID 0x0000 /* ElanSC520 Microcontroller Revision ID Register */
+#define SC520_CPUCTL 0x0002 /* Am5x86 CPU Control Register */
+#define SC520_DRCCTL 0x0010 /* SDRAM Control Register */
+#define SC520_DRCTMCTL 0x0012 /* SDRAM Timing Control Register */
+#define SC520_DRCCFG 0x0014 /* SDRAM Bank Configuration Register*/
+#define SC520_DRCBENDADR 0x0018 /* SDRAM Bank 0-3 Ending Address Register*/
+#define SC520_ECCCTL 0x0020 /* ECC Control Register */
+#define SC520_ECCSTA 0x0021 /* ECC Status Register */
+#define SC520_ECCCKBPOS 0x0022 /* ECC Check Bit Position Register */
+#define SC520_ECCSBADD 0x0024 /* ECC Single-Bit Error Address Register */
+#define SC520_DBCTL 0x0040 /* SDRAM Buffer Control Register */
+#define SC520_BOOTCSCTL 0x0050 /* /BOOTCS Control Register */
+#define SC520_ROMCS1CTL 0x0054 /* /ROMCS1 Control Register */
+#define SC520_ROMCS2CTL 0x0056 /* /ROMCS2 Control Register */
+#define SC520_HBCTL 0x0060 /* Host Bridge Control Register */
+#define SC520_HBTGTIRQCTL 0x0062 /* Host Bridge Target Interrupt Control Register */
+#define SC520_HBTGTIRQSTA 0x0064 /* Host Bridge Target Interrupt Status Register */
+#define SC520_HBMSTIRQCTL 0x0066 /* Host Bridge Target Interrupt Control Register */
+#define SC520_HBMSTIRQSTA 0x0068 /* Host Bridge Master Interrupt Status Register */
+#define SC520_MSTINTADD 0x006c /* Host Bridge Master Interrupt Address Register */
+#define SC520_SYSARBCTL 0x0070 /* System Arbiter Control Register */
+#define SC520_PCIARBSTA 0x0071 /* PCI Bus Arbiter Status Register */
+#define SC520_SYSARBMENB 0x0072 /* System Arbiter Master Enable Register */
+#define SC520_ARBPRICTL 0x0074 /* Arbiter Priority Control Register */
+#define SC520_ADDDECCTL 0x0080 /* Address Decode Control Register */
+#define SC520_WPVSTA 0x0082 /* Write-Protect Violation Status Register */
+#define SC520_PAR0 0x0088 /* Programmable Address Region 0 Register */
+#define SC520_PAR1 0x008c /* Programmable Address Region 1 Register */
+#define SC520_PAR2 0x0090 /* Programmable Address Region 2 Register */
+#define SC520_PAR3 0x0094 /* Programmable Address Region 3 Register */
+#define SC520_PAR4 0x0098 /* Programmable Address Region 4 Register */
+#define SC520_PAR5 0x009c /* Programmable Address Region 5 Register */
+#define SC520_PAR6 0x00a0 /* Programmable Address Region 6 Register */
+#define SC520_PAR7 0x00a4 /* Programmable Address Region 7 Register */
+#define SC520_PAR8 0x00a8 /* Programmable Address Region 8 Register */
+#define SC520_PAR9 0x00ac /* Programmable Address Region 9 Register */
+#define SC520_PAR10 0x00b0 /* Programmable Address Region 10 Register */
+#define SC520_PAR11 0x00b4 /* Programmable Address Region 11 Register */
+#define SC520_PAR12 0x00b8 /* Programmable Address Region 12 Register */
+#define SC520_PAR13 0x00bc /* Programmable Address Region 13 Register */
+#define SC520_PAR14 0x00c0 /* Programmable Address Region 14 Register */
+#define SC520_PAR15 0x00c4 /* Programmable Address Region 15 Register */
+#define SC520_GPECHO 0x0c00 /* GP Echo Mode Register */
+#define SC520_GPCSDW 0x0c01 /* GP Chip Select Data Width Register */
+#define SC520_GPCSQUAL 0x0c02 /* GP Chip Select Qualification Register */
+#define SC520_GPCSRT 0x0c08 /* GP Chip Select Recovery Time Register */
+#define SC520_GPCSPW 0x0c09 /* GP Chip Select Pulse Width Register */
+#define SC520_GPCSOFF 0x0c0a /* GP Chip Select Offset Register */
+#define SC520_GPRDW 0x0c0b /* GP Read Pulse Width Register */
+#define SC520_GPRDOFF 0x0c0c /* GP Read Offset Register */
+#define SC520_GPWRW 0x0c0d /* GP Write Pulse Width Register */
+#define SC520_GPWROFF 0x0c0e /* GP Write Offset Register */
+#define SC520_GPALEW 0x0c0f /* GP ALE Pulse Width Register */
+#define SC520_GPALEOFF 0x0c10 /* GP ALE Offset Register */
+#define SC520_PIOPFS15_0 0x0c20 /* PIO15-PIO0 Pin Function Select */
+#define SC520_PIOPFS31_16 0x0c22 /* PIO31-PIO16 Pin Function Select */
+#define SC520_CSPFS 0x0c24 /* Chip Select Pin Function Select */
+#define SC520_CLKSEL 0x0c26 /* Clock Select */
+#define SC520_DSCTL 0x0c28 /* Drive Strength Control */
+#define SC520_PIODIR15_0 0x0c2a /* PIO15-PIO0 Direction */
+#define SC520_PIODIR31_16 0x0c2c /* PIO31-PIO16 Direction */
+#define SC520_PIODATA15_0 0x0c30 /* PIO15-PIO0 Data */
+#define SC520_PIODATA31_16 0x0c32 /* PIO31-PIO16 Data */
+#define SC520_PIOSET15_0 0x0c34 /* PIO15-PIO0 Set */
+#define SC520_PIOSET31_16 0x0c36 /* PIO31-PIO16 Set */
+#define SC520_PIOCLR15_0 0x0c38 /* PIO15-PIO0 Clear */
+#define SC520_PIOCLR31_16 0x0c3a /* PIO31-PIO16 Clear */
+#define SC520_SWTMRMILLI 0x0c60 /* Software Timer Millisecond Count */
+#define SC520_SWTMRMICRO 0x0c62 /* Software Timer Microsecond Count */
+#define SC520_SWTMRCFG 0x0c64 /* Software Timer Configuration */
+#define SC520_GPTMRSTA 0x0c70 /* GP Timers Status Register */
+#define SC520_GPTMR0CTL 0x0c72 /* GP Timer 0 Mode/Control Register */
+#define SC520_GPTMR0CNT 0x0c74 /* GP Timer 0 Count Register */
+#define SC520_GPTMR0MAXCMPA 0x0c76 /* GP Timer 0 Maxcount Compare A Register */
+#define SC520_GPTMR0MAXCMPB 0x0c78 /* GP Timer 0 Maxcount Compare B Register */
+#define SC520_GPTMR1CTL 0x0c7a /* GP Timer 1 Mode/Control Register */
+#define SC520_GPTMR1CNT 0x0c7c /* GP Timer 1 Count Register */
+#define SC520_GPTMR1MAXCMPA 0x0c7e /* GP Timer 1 Maxcount Compare Register A */
+#define SC520_GPTMR1MAXCMPB 0x0c80 /* GP Timer 1 Maxcount Compare B Register */
+#define SC520_GPTMR2CTL 0x0c82 /* GP Timer 2 Mode/Control Register */
+#define SC520_GPTMR2CNT 0x0c84 /* GP Timer 2 Count Register */
+#define SC520_GPTMR2MAXCMPA 0x0c8e /* GP Timer 2 Maxcount Compare A Register */
+#define SC520_WDTMRCTL 0x0cb0 /* Watchdog Timer Control Register */
+#define SC520_WDTMRCNTL 0x0cb2 /* Watchdog Timer Count Low Register */
+#define SC520_WDTMRCNTH 0x0cb4 /* Watchdog Timer Count High Register */
+#define SC520_UART1CTL 0x0cc0 /* UART 1 General Control Register */
+#define SC520_UART1STA 0x0cc1 /* UART 1 General Status Register */
+#define SC520_UART1FCRSHAD 0x0cc2 /* UART 1 FIFO Control Shadow Register */
+#define SC520_UART2CTL 0x0cc4 /* UART 2 General Control Register */
+#define SC520_UART2STA 0x0cc5 /* UART 2 General Status Register */
+#define SC520_UART2FCRSHAD 0x0cc6 /* UART 2 FIFO Control Shadow Register */
+#define SC520_SSICTL 0x0cd0 /* SSI Control */
+#define SC520_SSIXMIT 0x0cd1 /* SSI Transmit */
+#define SC520_SSICMD 0x0cd2 /* SSI Command */
+#define SC520_SSISTA 0x0cd3 /* SSI Status */
+#define SC520_SSIRCV 0x0cd4 /* SSI Receive */
+#define SC520_PICICR 0x0d00 /* Interrupt Control Register */
+#define SC520_MPICMODE 0x0d02 /* Master PIC Interrupt Mode Register */
+#define SC520_SL1PICMODE 0x0d03 /* Slave 1 PIC Interrupt Mode Register */
+#define SC520_SL2PICMODE 0x0d04 /* Slave 2 PIC Interrupt Mode Register */
+#define SC520_SWINT16_1 0x0d08 /* Software Interrupt 16-1 Control Register */
+#define SC520_SWINT22_17 0x0d0a /* Software Interrupt 22-17/NMI Control Register */
+#define SC520_INTPINPOL 0x0d10 /* Interrupt Pin Polarity Register */
+#define SC520_PCIHOSTMAP 0x0d14 /* PCI Host Bridge Interrupt Mappin Register */
+#define SC520_ECCMAP 0x0d18 /* ECC Interrupt Mapping Register */
+#define SC520_GPTMR0MAP 0x0d1a /* GP Timer 0 Interrupt Mapping Register */
+#define SC520_GPTMR1MAP 0x0d1b /* GP Timer 1 Interrupt Mapping Register */
+#define SC520_GPTMR2MAP 0x0d1c /* GP Timer 2 Interrupt Mapping Register */
+#define SC520_PIT0MAP 0x0d20 /* PIT0 Interrupt Mapping Register */
+#define SC520_PIT1MAP 0x0d21 /* PIT1 Interrupt Mapping Register */
+#define SC520_PIT2MAP 0x0d22 /* PIT2 Interrupt Mapping Register */
+#define SC520_UART1MAP 0x0d28 /* UART 1 Interrupt Mapping Register */
+#define SC520_UART2MAP 0x0d29 /* UART 2 Interrupt Mapping Register */
+#define SC520_PCIINTAMAP 0x0d30 /* PCI Interrupt A Mapping Register */
+#define SC520_PCIINTBMAP 0x0d31 /* PCI Interrupt B Mapping Register */
+#define SC520_PCIINTCMAP 0x0d32 /* PCI Interrupt C Mapping Register */
+#define SC520_PCIINTDMAP 0x0d33 /* PCI Interrupt D Mapping Register */
+#define SC520_DMABCINTMAP 0x0d40 /* DMA Buffer Chaining Interrupt Mapping Register */
+#define SC520_SSIMAP 0x0d41 /* SSI Interrupt Mapping Register */
+#define SC520_WDTMAP 0x0d42 /* Watchdog Timer Interrupt Mapping Register */
+#define SC520_RTCMAP 0x0d43 /* RTC Interrupt Mapping Register */
+#define SC520_WPVMAP 0x0d44 /* Write-Protect Interrupt Mapping Register */
+#define SC520_ICEMAP 0x0d45 /* AMDebug JTAG RX/TX Interrupt Mapping Register */
+#define SC520_FERRMAP 0x0d46 /* Floating Point Error Interrupt Mapping Register */
+#define SC520_GP0IMAP 0x0d50 /* GPIRQ0 Interrupt Mapping Register */
+#define SC520_GP1IMAP 0x0d51 /* GPIRQ1 Interrupt Mapping Register */
+#define SC520_GP2IMAP 0x0d52 /* GPIRQ2 Interrupt Mapping Register */
+#define SC520_GP3IMAP 0x0d53 /* GPIRQ3 Interrupt Mapping Register */
+#define SC520_GP4IMAP 0x0d54 /* GPIRQ4 Interrupt Mapping Register */
+#define SC520_GP5IMAP 0x0d55 /* GPIRQ5 Interrupt Mapping Register */
+#define SC520_GP6IMAP 0x0d56 /* GPIRQ6 Interrupt Mapping Register */
+#define SC520_GP7IMAP 0x0d57 /* GPIRQ7 Interrupt Mapping Register */
+#define SC520_GP8IMAP 0x0d58 /* GPIRQ8 Interrupt Mapping Register */
+#define SC520_GP9IMAP 0x0d59 /* GPIRQ9 Interrupt Mapping Register */
+#define SC520_GP10IMAP 0x0d5a /* GPIRQ10 Interrupt Mapping Register */
+#define SC520_SYSINFO 0x0d70 /* System Board Information Register */
+#define SC520_RESCFG 0x0d72 /* Reset Configuration Register */
+#define SC520_RESSTA 0x0d74 /* Reset Status Register */
+#define SC520_GPDMAMMIO 0x0d81 /* GP-DMA Memory-Mapped I/O Register */
+#define SC520_GPDMAEXTCHMAPA 0x0d82 /* GP-DMA Resource Channel Map A */
+#define SC520_GPDMAEXTCHMAPB 0x0d84 /* GP-DMA Resource Channel Map B */
+#define SC520_GPDMAEXTPG0 0x0d86 /* GP-DMA Channel 0 Extended Page */
+#define SC520_GPDMAEXTPG1 0x0d87 /* GP-DMA Channel 1 Extended Page */
+#define SC520_GPDMAEXTPG2 0x0d88 /* GP-DMA Channel 2 Extended Page */
+#define SC520_GPDMAEXTPG3 0x0d89 /* GP-DMA Channel 3 Extended Page */
+#define SC520_GPDMAEXTPG5 0x0d8a /* GP-DMA Channel 5 Extended Page */
+#define SC520_GPDMAEXTPG6 0x0d8b /* GP-DMA Channel 6 Extended Page */
+#define SC520_GPDMAEXTPG7 0x0d8c /* GP-DMA Channel 7 Extended Page */
+#define SC520_GPDMAEXTTC3 0x0d90 /* GP-DMA Channel 3 Extender Transfer count */
+#define SC520_GPDMAEXTTC5 0x0d91 /* GP-DMA Channel 5 Extender Transfer count */
+#define SC520_GPDMAEXTTC6 0x0d92 /* GP-DMA Channel 6 Extender Transfer count */
+#define SC520_GPDMAEXTTC7 0x0d93 /* GP-DMA Channel 7 Extender Transfer count */
+#define SC520_GPDMABCCTL 0x0d98 /* Buffer Chaining Control */
+#define SC520_GPDMABCSTA 0x0d99 /* Buffer Chaining Status */
+#define SC520_GPDMABSINTENB 0x0d9a /* Buffer Chaining Interrupt Enable */
+#define SC520_GPDMABCVAL 0x0d9b /* Buffer Chaining Valid */
+#define SC520_GPDMANXTADDL3 0x0da0 /* GP-DMA Channel 3 Next Address Low */
+#define SC520_GPDMANXTADDH3 0x0da2 /* GP-DMA Channel 3 Next Address High */
+#define SC520_GPDMANXTADDL5 0x0da4 /* GP-DMA Channel 5 Next Address Low */
+#define SC520_GPDMANXTADDH5 0x0da6 /* GP-DMA Channel 5 Next Address High */
+#define SC520_GPDMANXTADDL6 0x0da8 /* GP-DMA Channel 6 Next Address Low */
+#define SC520_GPDMANXTADDH6 0x0daa /* GP-DMA Channel 6 Next Address High */
+#define SC520_GPDMANXTADDL7 0x0dac /* GP-DMA Channel 7 Next Address Low */
+#define SC520_GPDMANXTADDH7 0x0dae /* GP-DMA Channel 7 Next Address High */
+#define SC520_GPDMANXTTCL3 0x0db0 /* GP-DMA Channel 3 Next Transfer Count Low */
+#define SC520_GPDMANXTTCH3 0x0db2 /* GP-DMA Channel 3 Next Transfer Count High */
+#define SC520_GPDMANXTTCL5 0x0db4 /* GP-DMA Channel 5 Next Transfer Count Low */
+#define SC520_GPDMANXTTCH5 0x0db6 /* GP-DMA Channel 5 Next Transfer Count High */
+#define SC520_GPDMANXTTCL6 0x0db8 /* GP-DMA Channel 6 Next Transfer Count Low */
+#define SC520_GPDMANXTTCH6 0x0dba /* GP-DMA Channel 6 Next Transfer Count High */
+#define SC520_GPDMANXTTCL7 0x0dbc /* GP-DMA Channel 7 Next Transfer Count Low */
+#define SC520_GPDMANXTTCH7 0x0dbe /* GP-DMA Channel 7 Next Transfer Count High */
+
+/* MMCR Register bits (not all of them :) ) */
+
+/* SSI Stuff */
+#define CTL_CLK_SEL_4 0x00 /* Nominal Bit Rate = 8 MHz */
+#define CTL_CLK_SEL_8 0x10 /* Nominal Bit Rate = 4 MHz */
+#define CTL_CLK_SEL_16 0x20 /* Nominal Bit Rate = 2 MHz */
+#define CTL_CLK_SEL_32 0x30 /* Nominal Bit Rate = 1 MHz */
+#define CTL_CLK_SEL_64 0x40 /* Nominal Bit Rate = 512 KHz */
+#define CTL_CLK_SEL_128 0x50 /* Nominal Bit Rate = 256 KHz */
+#define CTL_CLK_SEL_256 0x60 /* Nominal Bit Rate = 128 KHz */
+#define CTL_CLK_SEL_512 0x70 /* Nominal Bit Rate = 64 KHz */
+
+#define TC_INT_ENB 0x08 /* Transaction Complete Interrupt Enable */
+#define PHS_INV_ENB 0x04 /* SSI Inverted Phase Mode Enable */
+#define CLK_INV_ENB 0x02 /* SSI Inverted Clock Mode Enable */
+#define MSBF_ENB 0x01 /* SSI Most Significant Bit First Mode Enable */
+
+#define SSICMD_CMD_SEL_XMITRCV 0x03 /* Simultaneous Transmit / Receive Transaction */
+#define SSICMD_CMD_SEL_RCV 0x02 /* Receive Transaction */
+#define SSICMD_CMD_SEL_XMIT 0x01 /* Transmit Transaction */
+#define SSISTA_BSY 0x02 /* SSI Busy */
+#define SSISTA_TC_INT 0x01 /* SSI Transaction Complete Interrupt */
+
+
+/* BITS for SC520_ADDDECCTL: */
+#define WPV_INT_ENB 0x80 /* Write-Protect Violation Interrupt Enable */
+#define IO_HOLE_DEST_PCI 0x10 /* I/O Hole Access Destination */
+#define RTC_DIS 0x04 /* RTC Disable */
+#define UART2_DIS 0x02 /* UART2 Disable */
+#define UART1_DIS 0x01 /* UART1 Disable */
+
+/* bus mapping constants (used for PCI core initialization) */ /* bus mapping constants */
+#define SC520_REG_ADDR 0x00000cf8
+#define SC520_REG_DATA 0x00000cfc
+
+
+#define SC520_ISA_MEM_PHYS 0x00000000
+#define SC520_ISA_MEM_BUS 0x00000000
+#define SC520_ISA_MEM_SIZE 0x01000000
+
+#define SC520_ISA_IO_PHYS 0x00000000
+#define SC520_ISA_IO_BUS 0x00000000
+#define SC520_ISA_IO_SIZE 0x00001000
+
+/* PCI I/O space from 0x1000 to 0xdfff
+ * (make 0xe000-0xfdff available for stuff like PCCard boot) */
+#define SC520_PCI_IO_PHYS 0x00001000
+#define SC520_PCI_IO_BUS 0x00001000
+#define SC520_PCI_IO_SIZE 0x0000d000
+
+/* system memory from 0x00000000 to 0x0fffffff */
+#define SC520_PCI_MEMORY_PHYS 0x00000000
+#define SC520_PCI_MEMORY_BUS 0x00000000
+#define SC520_PCI_MEMORY_SIZE 0x10000000
+
+/* PCI bus memory from 0x10000000 to 0x26ffffff
+ * (make 0x27000000 - 0x27ffffff available for stuff like PCCard boot) */
+#define SC520_PCI_MEM_PHYS 0x10000000
+#define SC520_PCI_MEM_BUS 0x10000000
+#define SC520_PCI_MEM_SIZE 0x17000000
+
+/* 0x28000000 - 0x3fffffff is used by the flash banks */
+
+/* 0x40000000 - 0xffffffff is not adressable by the SC520 */
+
+/* priority numbers used for interrupt channel mappings */
+#define SC520_IRQ_DISABLED 0
+#define SC520_IRQ0 1
+#define SC520_IRQ1 2
+#define SC520_IRQ2 4 /* same as IRQ9 */
+#define SC520_IRQ3 11
+#define SC520_IRQ4 12
+#define SC520_IRQ5 13
+#define SC520_IRQ6 21
+#define SC520_IRQ7 22
+#define SC520_IRQ8 3
+#define SC520_IRQ9 4
+#define SC520_IRQ10 5
+#define SC520_IRQ11 6
+#define SC520_IRQ12 7
+#define SC520_IRQ13 8
+#define SC520_IRQ14 9
+#define SC520_IRQ15 10
+
+
+/* pin number used for PCI interrupt mappings */
+#define SC520_PCI_INTA 0
+#define SC520_PCI_INTB 1
+#define SC520_PCI_INTC 2
+#define SC520_PCI_INTD 3
+#define SC520_PCI_GPIRQ0 4
+#define SC520_PCI_GPIRQ1 5
+#define SC520_PCI_GPIRQ2 6
+#define SC520_PCI_GPIRQ3 7
+#define SC520_PCI_GPIRQ4 8
+#define SC520_PCI_GPIRQ5 9
+#define SC520_PCI_GPIRQ6 10
+#define SC520_PCI_GPIRQ7 11
+#define SC520_PCI_GPIRQ8 12
+#define SC520_PCI_GPIRQ9 13
+#define SC520_PCI_GPIRQ10 14
+
+
+
+/* PIC I/O mapped registers */
+
+#define MPICIR 0x20 /* Master PIC Interrupt Request Register */
+#define MPICISR 0x20 /* Master PIC In-Service Register */
+#define MPICICW1 0x20 /* Master PIC Initialization Control Word 1 Register */
+#define MPICOCW2 0x20 /* Master PIC Operation Control Word 2 Register */
+#define MPICOCW3 0x20 /* Master PIC Operation Control Word 3 Register */
+
+#define MPICICW2 0x21 /* Master PIC Initialization Control Word 2 Register */
+#define MPICICW3 0x21 /* Master PIC Initialization Control Word 3 Register */
+#define MPICICW4 0x21 /* Master PIC Initialization Control Word 4 Register */
+#define MPICINTMSK 0x21 /* Master PIC Interrupt Mask Register */
+
+#define S2PICIR 0x24 /* Slave 2 PIC Interrupt Request Register */
+#define S2PICISR 0x24 /* Slave 2 PIC In-Service Register */
+#define S2PICICW1 0x24 /* Slave 2 PIC Initialization Control Word 1 Register */
+#define S2PICOCW2 0x24 /* Slave 2 PIC Operation Control Word 2 Register */
+#define S2PICOCW3 0x24 /* Slave 2 PIC Operation Control Word 3 Register */
+
+#define S2PICICW2 0x25 /* Slave 2 PIC Initialization Control Word 2 Register */
+#define S2PICICW3 0x25 /* Slave 2 PIC Initialization Control Word 3 Register */
+#define S2PICICW4 0x25 /* Slave 2 PIC Initialization Control Word 4 Register */
+#define S2PICINTMSK 0x25 /* Slave 2 PIC Interrupt Mask Register */
+
+#define S1PICIR 0xa0 /* Slave 1 PIC Interrupt Request Register */
+#define S1PICISR 0xa0 /* Slave 1 PIC In-Service Register */
+#define S1PICICW1 0xa0 /* Slave 1 PIC Initialization Control Word 1 Register */
+#define S1PICOCW2 0xa0 /* Slave 1 PIC Operation Control Word 2 Register */
+#define S1PICOCW3 0xa0 /* Slave 1 PIC Operation Control Word 3 Register */
+
+#define S1PICICW2 0xa1 /* Slave 1 PIC Initialization Control Word 2 Register */
+#define S1PICICW3 0xa1 /* Slave 1 PIC Initialization Control Word 3 Register */
+#define S1PICICW4 0xa1 /* Slave 1 PIC Initialization Control Word 4 Register */
+#define S1PICINTMSK 0xa1 /* Slave 1 PIC Interrupt Mask Register */
+
+/*
+Programmable Interrupt Controller Register Bit Definitions
+*/
+
+/* Interrupt Control Register Bit Definitions */
+
+#define NMI_DONE 0x80 /* NMI Routine Done */
+#define NMI_ENB 0x40 /* Master NMI Done */
+#define S2_GINT_MODE 0x04 /* Slave 2 PIC Global Interrupt Mode Enable */
+#define S1_GINT_MODE 0x02 /* Slave 1 PIC Global Interrupt Mode Enable */
+#define M_GINT_MODE 0x01 /* Master PIC Global Interrupt Mode Enable */
+
+/* Master , SLAVEs 1&2 PIC Interrupt Mode Register Bit Definitions */
+
+#define CH7_INT_MODE 0x80 /* PIC Channel 7 Interrupt Mode 0-edge 1-level */
+#define CH6_INT_MODE 0x40 /* PIC Channel 6 Interrupt Mode 0-edge 1-level */
+#define CH5_INT_MODE 0x20 /* PIC Channel 5 Interrupt Mode 0-edge 1-level */
+#define CH4_INT_MODE 0x10 /* PIC Channel 4 Interrupt Mode 0-edge 1-level */
+#define CH3_INT_MODE 0x08 /* PIC Channel 3 Interrupt Mode 0-edge 1-level */
+#define CH2_INT_MODE 0x04 /* PIC Channel 2 Interrupt Mode 0-edge 1-level */
+#define CH1_INT_MODE 0x02 /* PIC Channel 1 Interrupt Mode 0-edge 1-level */
+#define CH0_INT_MODE 0x01 /* PIC Channel 0 Interrupt Mode 0-edge 1-level */
+
+/* Software Interrupt 16-1 Control Register Bit Definitions */
+
+#define SW_P16_TRIG 0x8000 /* Directly Trigger Priority Level P16 0-don't assert int, 1-assert int*/
+#define SW_P15_TRIG 0x4000 /* Directly Trigger Priority Level P15 */
+#define SW_P14_TRIG 0x2000 /* Directly Trigger Priority Level P14 */
+#define SW_P13_TRIG 0x1000 /* Directly Trigger Priority Level P13 */
+#define SW_P12_TRIG 0x0800 /* Directly Trigger Priority Level P12 */
+#define SW_P11_TRIG 0x0400 /* Directly Trigger Priority Level P11 */
+#define SW_P10_TRIG 0x0200 /* Directly Trigger Priority Level P10 */
+#define SW_P9_TRIG 0x0100 /* Directly Trigger Priority Level P9 */
+#define SW_P8_TRIG 0x0080 /* Directly Trigger Priority Level P8 */
+#define SW_P7_TRIG 0x0040 /* Directly Trigger Priority Level P7 */
+#define SW_P6_TRIG 0x0020 /* Directly Trigger Priority Level P6 */
+#define SW_P5_TRIG 0x0010 /* Directly Trigger Priority Level P5 */
+#define SW_P4_TRIG 0x0008 /* Directly Trigger Priority Level P4 */
+#define SW_P3_TRIG 0x0004 /* Directly Trigger Priority Level P3 */
+#define SW_P2_TRIG 0x0002 /* Directly Trigger Priority Level P2 */
+#define SW_P1_TRIG 0x0001 /* Directly Trigger Priority Level P1 */
+
+/* Software Interrupt 22-17/NMI Control Register Bit Defintions */
+
+#define NMI_TRIG 0x0040 /* Software NMI Source */
+#define SW_P22_TRIG 0x0020 /* Directly Trigger Priority Level P22 */
+#define SW_P21_TRIG 0x0010 /* Directly Trigger Priority Level P21 */
+#define SW_P20_TRIG 0x0008 /* Directly Trigger Priority Level P20 */
+#define SW_P19_TRIG 0x0004 /* Directly Trigger Priority Level P19 */
+#define SW_P18_TRIG 0x0002 /* Directly Trigger Priority Level P18 */
+#define SW_P17_TRIG 0x0001 /* Directly Trigger Priority Level P17 */
+
+/* Interrupt Pin Polarity Register Bit Definitions */
+
+#define INTD_POL 0x8000 /* PCI Interrupt Request /INTD Polarity */
+#define INTC_POL 0x4000 /* PCI Interrupt Request /INTC Polarity */
+#define INTB_POL 0x2000 /* PCI Interrupt Request /INTB Polarity */
+#define INTA_POL 0x1000 /* PCI Interrupt Request /INTA Polarity */
+
+#define GPINT10_POL 0x0400 /* General-Purpose Interrupt Request GPIRQ10 Polarity 0 - high to low, 1 low to high */
+#define GPINT9_POL 0x0200 /* General-Purpose Interrupt Request GPIRQ9 Polarity 0 - high to low, 1 low to high */
+#define GPINT8_POL 0x0100 /* General-Purpose Interrupt Request GPIRQ8 Polarity 0 - high to low, 1 low to high */
+#define GPINT7_POL 0x0080 /* General-Purpose Interrupt Request GPIRQ7 Polarity 0 - high to low, 1 low to high */
+#define GPINT6_POL 0x0040 /* General-Purpose Interrupt Request GPIRQ6 Polarity 0 - high to low, 1 low to high */
+#define GPINT5_POL 0x0020 /* General-Purpose Interrupt Request GPIRQ5 Polarity 0 - high to low, 1 low to high */
+#define GPINT4_POL 0x0010 /* General-Purpose Interrupt Request GPIRQ4 Polarity 0 - high to low, 1 low to high */
+#define GPINT3_POL 0x0008 /* General-Purpose Interrupt Request GPIRQ3 Polarity 0 - high to low, 1 low to high */
+#define GPINT2_POL 0x0004 /* General-Purpose Interrupt Request GPIRQ2 Polarity 0 - high to low, 1 low to high */
+#define GPINT1_POL 0x0002 /* General-Purpose Interrupt Request GPIRQ1 Polarity 0 - high to low, 1 low to high */
+#define GPINT0_POL 0x0001 /* General-Purpose Interrupt Request GPIRQ0 Polarity 0 - high to low, 1 low to high */
+
+/* PCI Host Bridge Interrupt Mapping Register Bit Definitions */
+
+#define PCI_NMI_ENB 0x0010 /* PCI Host Bridge NMI Enable */
+
+#define PCI_IRQ_MAP_P0 0x0000 /* PCI Host Bridge Interrupt Mapping: Disable PCI interrupt from reaching PIC */
+#define PCI_IRQ_MAP_P1 0x0001 /* PCI Host Bridge Interrupt Mapping: Priority P1 (Master PIC IR0) (highest priority)*/
+#define PCI_IRQ_MAP_P2 0x0002 /* PCI Host Bridge Interrupt Mapping: Priority P2 (Master PIC IR1)*/
+#define PCI_IRQ_MAP_P3 0x0003 /* PCI Host Bridge Interrupt Mapping: Priority P3 (Slave PIC IR0/Master PIC IR2)*/
+#define PCI_IRQ_MAP_P4 0x0004 /* PCI Host Bridge Interrupt Mapping: Priority P4 (Slave 1 PIC IR1)*/
+#define PCI_IRQ_MAP_P5 0x0005 /* PCI Host Bridge Interrupt Mapping: Priority P5 (Slave 1 PIC IR2)*/
+#define PCI_IRQ_MAP_P6 0x0006 /* PCI Host Bridge Interrupt Mapping: Priority P6 (Slave 1 PIC IR3)*/
+#define PCI_IRQ_MAP_P7 0x0007 /* PCI Host Bridge Interrupt Mapping: Priority P7 (Slave 1 PIC IR4)*/
+#define PCI_IRQ_MAP_P8 0x0008 /* PCI Host Bridge Interrupt Mapping: Priority P8 (Slave 1 PIC IR5)*/
+#define PCI_IRQ_MAP_P9 0x0009 /* PCI Host Bridge Interrupt Mapping: Priority P9 (Slave 1 PIC IR6)*/
+#define PCI_IRQ_MAP_P10 0x000a /* PCI Host Bridge Interrupt Mapping: Priority P10 (Slave 1 PIC IR7)*/
+#define PCI_IRQ_MAP_P11 0x000b /* PCI Host Bridge Interrupt Mapping: Priority P11 (Master PIC IR3)*/
+#define PCI_IRQ_MAP_P12 0x000c /* PCI Host Bridge Interrupt Mapping: Priority P12 (Master PIC IR4)*/
+#define PCI_IRQ_MAP_P13 0x000d /* PCI Host Bridge Interrupt Mapping: Priority P13 (Slave 2 PIC IR0/Master PIC IR5)*/
+#define PCI_IRQ_MAP_P14 0x000e /* PCI Host Bridge Interrupt Mapping: Priority P14 (Slave 2 PIC IR1)*/
+#define PCI_IRQ_MAP_P15 0x000f /* PCI Host Bridge Interrupt Mapping: Priority P15 (Slave 2 PIC IR2)*/
+#define PCI_IRQ_MAP_P16 0x0010 /* PCI Host Bridge Interrupt Mapping: Priority P16 (Slave 2 PIC IR3)*/
+#define PCI_IRQ_MAP_P17 0x0011 /* PCI Host Bridge Interrupt Mapping: Priority P17 (Slave 2 PIC IR4)*/
+#define PCI_IRQ_MAP_P18 0x0012 /* PCI Host Bridge Interrupt Mapping: Priority P18 (Slave 2 PIC IR5)*/
+#define PCI_IRQ_MAP_P19 0x0013 /* PCI Host Bridge Interrupt Mapping: Priority P19 (Slave 2 PIC IR6)*/
+#define PCI_IRQ_MAP_P20 0x0014 /* PCI Host Bridge Interrupt Mapping: Priority P20 (Slave 2 PIC IR7)*/
+#define PCI_IRQ_MAP_P21 0x0015 /* PCI Host Bridge Interrupt Mapping: Priority P21 (Master PIC IR6)*/
+#define PCI_IRQ_MAP_P22 0x0016 /* PCI Host Bridge Interrupt Mapping: Priority P22 (Master PIC IR7)(lowest priority) */
+#define PCI_IRQ_MAP_DA 0x0017 /* PCI Host Bridge Interrupt Mapping: disable internal-interrupt from reaching PIC*/
+#define PCI_IRQ_MAP_NMI 0x001F /* PCI Host Bridge Interrupt Mapping: NMI Source*/
+
+/* ECC Interrupt Mapping Register Bit Definitions */
+
+#define ECC_NMI_ENB 0x0100 /* ECC NMI Enable */
+
+#define ECC_IRQ_MAP_P0 0x0000 /* SDRAM ECC Interrupt Mapping: Disable PCI interrupt from reaching PIC */
+#define ECC_IRQ_MAP_P1 0x0001 /* SDRAM ECC Interrupt Mapping: Priority P1 (Master PIC IR0) (highest priority)*/
+#define ECC_IRQ_MAP_P2 0x0002 /* SDRAM ECC Interrupt Mapping: Priority P2 (Master PIC IR1)*/
+#define ECC_IRQ_MAP_P3 0x0003 /* SDRAM ECC Interrupt Mapping: Priority P3 (Slave PIC IR0/Master PIC IR2)*/
+#define ECC_IRQ_MAP_P4 0x0004 /* SDRAM ECC Interrupt Mapping: Priority P4 (Slave 1 PIC IR1)*/
+#define ECC_IRQ_MAP_P5 0x0005 /* SDRAM ECC Interrupt Mapping: Priority P5 (Slave 1 PIC IR2)*/
+#define ECC_IRQ_MAP_P6 0x0006 /* SDRAM ECC Interrupt Mapping: Priority P6 (Slave 1 PIC IR3)*/
+#define ECC_IRQ_MAP_P7 0x0007 /* SDRAM ECC Interrupt Mapping: Priority P7 (Slave 1 PIC IR4)*/
+#define ECC_IRQ_MAP_P8 0x0008 /* SDRAM ECC Interrupt Mapping: Priority P8 (Slave 1 PIC IR5)*/
+#define ECC_IRQ_MAP_P9 0x0009 /* SDRAM ECC Interrupt Mapping: Priority P9 (Slave 1 PIC IR6)*/
+#define ECC_IRQ_MAP_P10 0x000a /* SDRAM ECC Interrupt Mapping: Priority P10 (Slave 1 PIC IR7)*/
+#define ECC_IRQ_MAP_P11 0x000b /* SDRAM ECC Interrupt Mapping: Priority P11 (Master PIC IR3)*/
+#define ECC_IRQ_MAP_P12 0x000c /* SDRAM ECC Interrupt Mapping: Priority P12 (Master PIC IR4)*/
+#define ECC_IRQ_MAP_P13 0x000d /* SDRAM ECC Interrupt Mapping: Priority P13 (Slave 2 PIC IR0/Master PIC IR5)*/
+#define ECC_IRQ_MAP_P14 0x000e /* SDRAM ECC Interrupt Mapping: Priority P14 (Slave 2 PIC IR1)*/
+#define ECC_IRQ_MAP_P15 0x000f /* SDRAM ECC Interrupt Mapping: Priority P15 (Slave 2 PIC IR2)*/
+#define ECC_IRQ_MAP_P16 0x0010 /* SDRAM ECC Interrupt Mapping: Priority P16 (Slave 2 PIC IR3)*/
+#define ECC_IRQ_MAP_P17 0x0011 /* SDRAM ECC Interrupt Mapping: Priority P17 (Slave 2 PIC IR4)*/
+#define ECC_IRQ_MAP_P18 0x0012 /* SDRAM ECC Interrupt Mapping: Priority P18 (Slave 2 PIC IR5)*/
+#define ECC_IRQ_MAP_P19 0x0013 /* SDRAM ECC Interrupt Mapping: Priority P19 (Slave 2 PIC IR6)*/
+#define ECC_IRQ_MAP_P20 0x0014 /* SDRAM ECC Interrupt Mapping: Priority P20 (Slave 2 PIC IR7)*/
+#define ECC_IRQ_MAP_P21 0x0015 /* SDRAM ECC Interrupt Mapping: Priority P21 (Master PIC IR6)*/
+#define ECC_IRQ_MAP_P22 0x0016 /* SDRAM ECC Interrupt Mapping: Priority P22 (Master PIC IR7)(lowest priority) */
+#define ECC_IRQ_MAP_DA 0x0017 /* SDRAM ECC Interrupt Mapping: disable internal-interrupt from reaching PIC*/
+
+/* Interrupt Mappings for GP TIMER 0
+GP TImer 1
+GP Timer 2
+PIT0
+PIT1
+PIT2
+UART 1
+UART 2
+PCI A
+PCI B
+PCI C
+PCI D
+DMA Buffer Chaining
+SSI
+WDT
+RTC
+Write-Protection Violation
+AMDebug JTAG RX/TX
+Floating Point Error
+GPIRQ0 - QPIRQ10
+*/
+
+#define INT_MAP_P0 0x0000 /* Interrupt Mapping: Disable PCI interrupt from reaching PIC */
+#define INT_MAP_P1 0x0001 /* Interrupt Mapping: Priority P1 (Master PIC IR0) (highest priority)*/
+#define INT_MAP_P2 0x0002 /* Interrupt Mapping: Priority P2 (Master PIC IR1)*/
+#define INT_MAP_P3 0x0003 /* Interrupt Mapping: Priority P3 (Slave PIC IR0/Master PIC IR2)*/
+#define INT_MAP_P4 0x0004 /* Interrupt Mapping: Priority P4 (Slave 1 PIC IR1)*/
+#define INT_MAP_P5 0x0005 /* Interrupt Mapping: Priority P5 (Slave 1 PIC IR2)*/
+#define INT_MAP_P6 0x0006 /* Interrupt Mapping: Priority P6 (Slave 1 PIC IR3)*/
+#define INT_MAP_P7 0x0007 /* Interrupt Mapping: Priority P7 (Slave 1 PIC IR4)*/
+#define INT_MAP_P8 0x0008 /* Interrupt Mapping: Priority P8 (Slave 1 PIC IR5)*/
+#define INT_MAP_P9 0x0009 /* Interrupt Mapping: Priority P9 (Slave 1 PIC IR6)*/
+#define INT_MAP_P10 0x000a /* Interrupt Mapping: Priority P10 (Slave 1 PIC IR7)*/
+#define INT_MAP_P11 0x000b /* Interrupt Mapping: Priority P11 (Master PIC IR3)*/
+#define INT_MAP_P12 0x000c /* Interrupt Mapping: Priority P12 (Master PIC IR4)*/
+#define INT_MAP_P13 0x000d /* Interrupt Mapping: Priority P13 (Slave 2 PIC IR0/Master PIC IR5)*/
+#define INT_MAP_P14 0x000e /* Interrupt Mapping: Priority P14 (Slave 2 PIC IR1)*/
+#define INT_MAP_P15 0x000f /* Interrupt Mapping: Priority P15 (Slave 2 PIC IR2)*/
+#define INT_MAP_P16 0x0010 /* Interrupt Mapping: Priority P16 (Slave 2 PIC IR3)*/
+#define INT_MAP_P17 0x0011 /* Interrupt Mapping: Priority P17 (Slave 2 PIC IR4)*/
+#define INT_MAP_P18 0x0012 /* Interrupt Mapping: Priority P18 (Slave 2 PIC IR5)*/
+#define INT_MAP_P19 0x0013 /* Interrupt Mapping: Priority P19 (Slave 2 PIC IR6)*/
+#define INT_MAP_P20 0x0014 /* Interrupt Mapping: Priority P20 (Slave 2 PIC IR7)*/
+#define INT_MAP_P21 0x0015 /* Interrupt Mapping: Priority P21 (Master PIC IR6)*/
+#define INT_MAP_P22 0x0016 /* Interrupt Mapping: Priority P22 (Master PIC IR7)(lowest priority) */
+#define INT_MAP_DA 0x0017 /* Interrupt Mapping: disable internal-interrupt from reaching PIC*/
+#define INT_MAP_NMI 0x001F /* Interrupt Mapping: NMI Source*/
+
+/* Master, SLAVE 1,2 PIC Interrupt Request Register Bit Definitions */
+
+#define IR7 0x80 /* Interrupt Request 7 */
+#define IR6 0x40 /* Interrupt Request 6 */
+#define IR5 0x20 /* Interrupt Request 5 */
+#define IR4 0x10 /* Interrupt Request 4 */
+#define IR3 0x08 /* Interrupt Request 3 */
+#define IR2 0x04 /* Interrupt Request 2 */
+#define IR1 0x02 /* Interrupt Request 1 */
+#define IR0 0x01 /* Interrupt Request 0 */
+
+/* Master, SLAVE 1,2 PIC In-Service Register Bit Definitions */
+
+#define IS7 0x80 /* Interrupt Request 7 In-Service */
+#define IS6 0x40 /* Interrupt Request 6 In-Service */
+#define IS5 0x20 /* Interrupt Request 5 In-Service */
+#define IS4 0x10 /* Interrupt Request 4 In-Service */
+#define IS3 0x08 /* Interrupt Request 3 In-Service */
+#define IS2 0x04 /* Interrupt Request 2 In-Service */
+#define IS1 0x02 /* Interrupt Request 1 In-Service */
+#define IS0 0x01 /* Interrupt Request 0 In-Service */
+
+/* Master PIC Initilization Control Word 1 Register Bit Definitions */
+
+#define SLCT_ICW1 0x10 /* Select ICW1 */
+#define LTIM 0x08 /* Level-Triggered Interrupt Mode */
+#define ADI 0x04 /* Address Interval */
+#define SNGL 0x02 /* Single PIC */
+#define IC4 0x01 /* Initilization Control Word 4 */
+
+/* Master PIC Operation Control Word 2 Register Bit Definitions */
+
+#define R_SL_EOI_RAEOIC 0x00 /* Interrupt Request EOI and Priority Rotation Controls: Rotate in auto EOI mode (clear)*/
+#define R_SL_EOI_NEOI 0x20 /* Interrupt Request EOI and Priority Rotation Controls: Non-specific EOI */
+#define R_SL_EOI_NOP 0x40 /* Interrupt Request EOI and Priority Rotation Controls: NOP */
+#define R_SL_EOI_SEOI 0x60 /* Interrupt Request EOI and Priority Rotation Controls: Specific EOI */
+#define R_SL_EOI_RAEOIS 0x80 /* Interrupt Request EOI and Priority Rotation Controls: Rotate in auto EOI mode (set)*/
+#define R_SL_EOI_RONEOI 0xA0 /* Interrupt Request EOI and Priority Rotation Controls: Rotate on non-specific EOI command */
+#define R_SL_EOI_SPC 0xC0 /* Interrupt Request EOI and Priority Rotation Controls: Set Priority Command */
+#define R_SL_EOI_ROEOIC 0xE0 /* Interrupt Request EOI and Priority Rotation Controls: Rotate on specific EOI command */
+
+#define IS_OCW3 0x08 /* Access is OCW3 */
+
+#define LS_IR0 0x00 /* Specific EOI Level Select: IR0 */
+#define LS_IR1 0x01 /* Specific EOI Level Select: IR1 */
+#define LS_IR2 0x02 /* Specific EOI Level Select: IR2 */
+#define LS_IR3 0x03 /* Specific EOI Level Select: IR3 */
+#define LS_IR4 0x04 /* Specific EOI Level Select: IR4 */
+#define LS_IR5 0x05 /* Specific EOI Level Select: IR5 */
+#define LS_IR6 0x06 /* Specific EOI Level Select: IR6 */
+#define LS_IR7 0x07 /* Specific EOI Level Select: IR7 */
+
+/* Master PIC Operation Control Word 3 */
+
+#define ESMMSMM_NOP 0x00 /* Special Mask Mode: NOP */
+#define ESMMSMM_RSM 0x40 /* Special Mask Mode: Reset Special mask */
+#define ESMMSMM_SSM 0x60 /* Special Mask Mode: Set Special mask */
+
+#define P 0x04 /* PIC Poll Command */
+
+#define RRRIS_NC 0x00 /* Status Register Select: No change from last state */
+#define RRRIS_MPICIR 0x02 /* Status Register Select: Next Port 0020h read returns MPICIR register contents */
+#define RRRIS_MPICISR 0x03 /* Status Register Select: Next Port 0020h read returns MPICISR register contents */
+
+/* Master PIC Intialization Control Word 2 Register Masks */
+
+#define T7_T3 0xF8 /* Bits 7-3 of Base Interrupt Vector Number for this PIC */
+#define A10_A8 0x07 /* A10-A8 of Interrupt Vector */
+
+/* Master PIC Intilization Control Word 3 Register Bit Definitions */
+
+#define S7 0x80 /* Channel 7 Slave Cascade Select */
+#define S6 0x40 /* Channel 6 Slave Cascade Select */
+#define S5 0x20 /* Channel 5 Slave Cascade Select */
+#define S4 0x10 /* Channel 4 Slave Cascade Select */
+#define S3 0x08 /* Channel 3 Slave Cascade Select */
+#define S2 0x04 /* Channel 2 Slave Cascade Select */
+#define S1 0x02 /* Channel 1 Slave Cascade Select */
+#define S0 0x01 /* Channel 0 Slave Cascade Select */
+
+/* Master PIC Initilization Control Word 4 Register Bit Definitions */
+
+#define SFNM 0x10 /* Special Fully Nested Mode Enable */
+#define BUFMS_NBM 0x00 /* Buffered Mode and Master/Slave Select: Non-buffered mode */
+#define BUFMS_BMS 0x08 /* Buffered Mode and Master/Slave Select: Buffered Mode/slave */
+#define BUFMS_BMM 0x0C /* Buffered Mode and Master/Slave Select: Buffered mode/master */
+
+#define AEOI 0x02 /* Automatic EOI Mode */
+#define PM 0x01 /* Microprocessor Mode */
+
+/* Master, SLAVE 1,2 PIC Interrupt Mask Register Bit Definitions */
+
+#define IM7 0x80 /* IR7 Mask */
+#define IM6 0x40 /* IR6 Mask */
+#define IM5 0x20 /* IR5 Mask */
+#define IM4 0x10 /* IR4 Mask */
+#define IM3 0x08 /* IR3 Mask */
+#define IM2 0x04 /* IR2 Mask */
+#define IM1 0x02 /* IR1 Mask */
+#define IM0 0x01 /* IR0 Mask */
+
+/* other SLAVE1 and SLAVE2 PIC definitions have already been previously defined
+ just use the name of the bit as specified in the Register set manual */
+
+/* Slave 1 PIC Initilization Control Word 3 Register Masks */
+
+#define ID2_ID0 0x07 /* Slave 1 PIC ID 2-0 */
+
+/**********************************************
+* Reset Generation Registers *
+**********************************************/
+
+/* MMCR Registers */
+
+#define OFFS_SYSINFO 0x0D70 /* System Board Information Register */
+#define OFFS_RESCFG 0x0D72 /* Reset Configuration Register */
+#define OFFS_RESSTA 0x0D74 /* Reset Status Register */
+
+#define SYSINFO (MMCR + OFFS_SYSINFO) /* System Board Information Register */
+#define RESCFG (MMCR + OFFS_RESCFG) /* Reset Configuration Register */
+#define RESSTA (MMCR + OFFS_RESSTA) /* Reset Status Register */
+
+/* I/O Mapped Registers */
+
+#define SCPDATA 0x60 /* SCP DATA Port */
+#define SCPCMD 0x64 /* SCP Command Port */
+#define SYSCTLA 0x92 /* System Control Port A */
+#define FPUERRCLR 0xF0 /* FPU Error Interrupt Clear */
+
+/*
+Reset Generation Register Bit Definitions
+*/
+
+/* System Board Information Register Masks */
+
+#define RST_ID 0xFF /* Reset Latched Input Data */
+
+/* Reset Configuration Register Bit Definitions */
+
+#define ICE_ON_RST 0x08 /* Enter AMDebug Mode on Next Reset */
+#define PRG_RST_ENB 0x04 /* Programmable Reset Enable */
+#define GP_RST 0x02 /* Software GP Bus Reset */
+#define SYS_RST 0x01 /* Software System Reset */
+
+/* Reset Status Register Bit Definitions */
+
+#define SCP_RST_DET 0x40 /* SCP Reset Detect */
+#define ICE_HRST_DET 0x20 /* AMDebug Utiliity Hard Reset Detect */
+#define ICE_SRST_DET 0x10 /* AMDebug Utility Sytem Reset Detect */
+#define WDT_RST_DET 0x08 /* WDT Reset Detect */
+#define SD_RST_DET 0x04 /* CPU Shutdown Reset Detect */
+#define PRGRST_DET 0x02 /* PRGRESET Detect */
+#define PWRGOOD_DET 0x01 /* POWERGOOD Reset Detect */
+
+/* SCP Data Port Register Masks */
+
+#define SCP_DATA 0xFF /* System Control Processor Data */
+
+/* SCP Data Port Register Bit Definitions */
+
+#define A20_GATE 0x02 /* A20 Gate Data */
+#define CPU_RST 0x01 /* CPU Reset Control */
+
+/* SCP Command Port Register Masks */
+
+#define SCP_CMD 0xFF /* SCP Command */
+
+/* System Control Port A Register Bit Definitions */
+
+#define A20G_CTL 0x02 /* A20 Gate Control */
+/* CPU_RST - Alternate CPU Core Reset Control, already defined */
+
+/* Floating Point Error Interrupt Clear Register Mask */
+
+#define FPUERR_RST 0xFF /* Clear FPU Error Interrupt Request */
+
+/**********************************
+* GP Bus DMA Controller Registers *
+**********************************/
+
+/* GP-DMA MMCR Registers */
+
+#define OFFS_GPDMACTL 0x0D80 /* GP-DMa Control Register */
+#define OFFS_GPDMAMMIO 0x0D81 /* GP-DMA Memory-Mapped I/O Register */
+#define OFFS_GPDMAEXTCHMAPA 0x0D82 /* GP-DMA Resource Channel Map A */
+#define OFFS_GPDMAEXTCHMAPB 0x0D84 /* GP-DMA Resource Channel Map B */
+#define OFFS_GPDMAEXTPG0 0x0D86 /* GP-DMA Channel 0 Extended Page */
+#define OFFS_GPDMAEXTPG1 0x0D87 /* GP-DMA Channel 1 Extended Page */
+#define OFFS_GPDMAEXTPG2 0x0D88 /* GP-DMA Channel 2 Extended Page */
+#define OFFS_GPDMAEXTPG3 0x0D89 /* GP-DMA Channel 3 Extended Page */
+#define OFFS_GPDMAEXTPG5 0x0D8a /* GP-DMA Channel 5 Extended Page */
+#define OFFS_GPDMAEXTPG6 0x0D8b /* GP-DMA Channel 6 Extended Page */
+#define OFFS_GPDMAEXTPG7 0x0D8c /* GP-DMA Channel 7 Extended Page */
+#define OFFS_GPDMAEXTTC3 0x0D90 /* GP-DMA Channel 3 Extender Transfer count */
+#define OFFS_GPDMAEXTTC5 0x0D91 /* GP-DMA Channel 5 Extender Transfer count */
+#define OFFS_GPDMAEXTTC6 0x0D92 /* GP-DMA Channel 6 Extender Transfer count */
+#define OFFS_GPDMAEXTTC7 0x0D93 /* GP-DMA Channel 7 Extender Transfer count */
+#define OFFS_GPDMABCCTL 0x0D98 /* Buffer Chaining Control */
+#define OFFS_GPDMABCSTA 0x0D99 /* Buffer Chaining Status */
+#define OFFS_GPDMABSINTENB 0x0D9A /* Buffer Chaining Interrupt Enable */
+#define OFFS_GPDMABCVAL 0x0D9B /* Buffer Chaining Valid */
+#define OFFS_GPDMANXTADDL3 0x0DA0 /* GP-DMA Channel 3 Next Address Low */
+#define OFFS_GPDMANXTADDH3 0x0DA2 /* GP-DMA Channel 3 Next Address High */
+#define OFFS_GPDMANXTADDL5 0x0DA4 /* GP-DMA Channel 5 Next Address Low */
+#define OFFS_GPDMANXTADDH5 0x0DA6 /* GP-DMA Channel 5 Next Address High */
+#define OFFS_GPDMANXTADDL6 0x0DA8 /* GP-DMA Channel 6 Next Address Low */
+#define OFFS_GPDMANXTADDH6 0x0DAA /* GP-DMA Channel 6 Next Address High */
+#define OFFS_GPDMANXTADDL7 0x0DAC /* GP-DMA Channel 7 Next Address Low */
+#define OFFS_GPDMANXTADDH7 0x0DAE /* GP-DMA Channel 7 Next Address High */
+#define OFFS_GPDMANXTTCL3 0x0DB0 /* GP-DMA Channel 3 Next Transfer Count Low */
+#define OFFS_GPDMANXTTCH3 0x0DB2 /* GP-DMA Channel 3 Next Transfer Count High */
+#define OFFS_GPDMANXTTCL5 0x0DB4 /* GP-DMA Channel 5 Next Transfer Count Low */
+#define OFFS_GPDMANXTTCH5 0x0DB6 /* GP-DMA Channel 5 Next Transfer Count High */
+#define OFFS_GPDMANXTTCL6 0x0DB8 /* GP-DMA Channel 6 Next Transfer Count Low */
+#define OFFS_GPDMANXTTCH6 0x0DBA /* GP-DMA Channel 6 Next Transfer Count High */
+#define OFFS_GPDMANXTTCL7 0x0DBC /* GP-DMA Channel 7 Next Transfer Count Low */
+#define OFFS_GPDMANXTTCH7 0x0DBE /* GP-DMA Channel 7 Next Transfer Count High */
+
+#define GPDMACTL (MMCR + OFFS_GPDMACTL) /* GP-DMa Control Register */
+#define GPDMAMMIO (MMCR + OFFS_GPDMAMMIO) /* GP-DMA Memory-Mapped I/O Register */
+#define GPDMAEXTCHMAPA (MMCR + OFFS_GPDMAEXTCHMAPA)/* GP-DMA Resource Channel Map A */
+#define GPDMAEXTCHMAPB (MMCR + OFFS_GPDMAEXTCHMAPB)/* GP-DMA Resource Channel Map B */
+#define GPDMAEXTPG0 (MMCR + OFFS_GPDMAEXTPG0) /* GP-DMA Channel 0 Extended Page */
+#define GPDMAEXTPG1 (MMCR + OFFS_GPDMAEXTPG1) /* GP-DMA Channel 1 Extended Page */
+#define GPDMAEXTPG2 (MMCR + OFFS_GPDMAEXTPG2) /* GP-DMA Channel 2 Extended Page */
+#define GPDMAEXTPG3 (MMCR + OFFS_GPDMAEXTPG3) /* GP-DMA Channel 3 Extended Page */
+#define GPDMAEXTPG5 (MMCR + OFFS_GPDMAEXTPG5) /* GP-DMA Channel 5 Extended Page */
+#define GPDMAEXTPG6 (MMCR + OFFS_GPDMAEXTPG6) /* GP-DMA Channel 6 Extended Page */
+#define GPDMAEXTPG7 (MMCR + OFFS_GPDMAEXTPG7) /* GP-DMA Channel 7 Extended Page */
+#define GPDMAEXTTC3 (MMCR + OFFS_GPDMAEXTTC3) /* GP-DMA Channel 3 Extender Transfer count */
+#define GPDMAEXTTC5 (MMCR + OFFS_GPDMAEXTTC5) /* GP-DMA Channel 5 Extender Transfer count */
+#define GPDMAEXTTC6 (MMCR + OFFS_GPDMAEXTTC6) /* GP-DMA Channel 6 Extender Transfer count */
+#define GPDMAEXTTC7 (MMCR + OFFS_GPDMAEXTTC7) /* GP-DMA Channel 7 Extender Transfer count */
+#define GPDMABCCTL (MMCR + OFFS_GPDMABCCTL) /* Buffer Chaining Control */
+#define GPDMABCSTA (MMCR + OFFS_GPDMABCSTA) /* Buffer Chaining Status */
+#define GPDMABSINTENB (MMCR + OFFS_GPDMABSINTENB) /* Buffer Chaining Interrupt Enable */
+#define GPDMABCVAL (MMCR + OFFS_GPDMABCVAL) /* Buffer Chaining Valid */
+#define GPDMANXTADDL3 (MMCR + OFFS_GPDMANXTADDL3) /* GP-DMA Channel 3 Next Address Low */
+#define GPDMANXTADDH3 (MMCR + OFFS_GPDMANXTADDH3) /* GP-DMA Channel 3 Next Address High */
+#define GPDMANXTADDL5 (MMCR + OFFS_GPDMANXTADDL5) /* GP-DMA Channel 5 Next Address Low */
+#define GPDMANXTADDH5 (MMCR + OFFS_GPDMANXTADDH5) /* GP-DMA Channel 5 Next Address High */
+#define GPDMANXTADDL6 (MMCR + OFFS_GPDMANXTADDL6) /* GP-DMA Channel 6 Next Address Low */
+#define GPDMANXTADDH6 (MMCR + OFFS_GPDMANXTADDH6) /* GP-DMA Channel 6 Next Address High */
+#define GPDMANXTADDL7 (MMCR + OFFS_GPDMANXTADDL7) /* GP-DMA Channel 7 Next Address Low */
+#define GPDMANXTADDH7 (MMCR + OFFS_GPDMANXTADDH7) /* GP-DMA Channel 7 Next Address High */
+#define GPDMANXTTCL3 (MMCR + OFFS_GPDMANXTTCL3) /* GP-DMA Channel 3 Next Transfer Count Low */
+#define GPDMANXTTCH3 (MMCR + OFFS_GPDMANXTTCH3) /* GP-DMA Channel 3 Next Transfer Count High */
+#define GPDMANXTTCL5 (MMCR + OFFS_GPDMANXTTCL5) /* GP-DMA Channel 5 Next Transfer Count Low */
+#define GPDMANXTTCH5 (MMCR + OFFS_GPDMANXTTCH5) /* GP-DMA Channel 5 Next Transfer Count High */
+#define GPDMANXTTCL6 (MMCR + OFFS_GPDMANXTTCL6) /* GP-DMA Channel 6 Next Transfer Count Low */
+#define GPDMANXTTCH6 (MMCR + OFFS_GPDMANXTTCH6) /* GP-DMA Channel 6 Next Transfer Count High */
+#define GPDMANXTTCL7 (MMCR + OFFS_GPDMANXTTCL7) /* GP-DMA Channel 7 Next Transfer Count Low */
+#define GPDMANXTTCH7 (MMCR + OFFS_GPDMANXTTCH7) /* GP-DMA Channel 7 Next Transfer Count High */
+
+/* GP-DMA Direct-Mapped Registers */
+
+#define GPDMA0MAR 0x0000 /* Slave DMA Channel 0 Memory Address */
+#define GPDMA0TC 0x0001 /* Slave DMA Channel 0 Transfer Count */
+#define GPDMA1MAR 0x0002 /* Slave DMA Channel 1 Memory Address */
+#define GPDMA1TC 0x0003 /* Slave DMA Channel 1 Transfer Count */
+#define GPDMA2MAR 0x0004 /* Slave DMA Channel 2 Memory Address */
+#define GPDMA2TC 0x0005 /* Slave DMA Channel 2 Transfer Count */
+#define GPDMA3MAR 0x0006 /* Slave DMA Channel 3 Memory Address */
+#define GPDMA3TC 0x0007 /* Slave DMA Channel 3 Transfer Count */
+#define SLDMASTA 0x0008 /* Slave DMA Channel 0-3 Status */
+#define SLDMACTL 0x0008 /* Slave DMA Channel 0-3 Control */
+#define SLDMASWREQ 0x0009 /* Slave Software DRQ(n) Request */
+#define SLDMAMSK 0x000A /* Slave DMA Channel 0-3 Mask */
+#define SLDMAMODE 0x000B /* Slave DMA Channel 0-3 Mode */
+#define SLDMACBP 0x000C /* Slave DMA Clear Byte Pointer */
+#define SLDMARST 0x000D /* Slave DMA Controller Reset */
+#define SLDMATMP 0x000D /* Slave DMA Controller Temporary */
+#define SLDMAMSKRST 0x000E /* Slave DMA Mask Reset */
+#define SLDMAGENMSK 0x000F /* Slave DMA General Mask */
+#define GPDMAGR0 0x0080 /* General 0 */
+#define GPDMA2PG 0x0081 /* Slave DMA Channel 2 Page */
+#define GPDMA3PG 0x0082 /* Slave DMA Channel 3 Page */
+#define GPDMA1PG 0x0083 /* Slave DMA Channel 1 Page */
+#define GPDMAGR1 0x0084 /* General 1 */
+#define GPDMAGR2 0x0085 /* General 2 */
+#define GPDMAGR3 0x0086 /* General 3 */
+#define GPDMA0PG 0x0087 /* Slave DMA Channel 0 Page */
+#define GPDMAGR4 0x0088 /* General 4 */
+#define GPDMA6PG 0x0089 /* Master DMA Channel 6 Page */
+#define GPDMA7PG 0x008a /* Master DMA Channel 7 Page */
+#define GPDMA5PG 0x008b /* Master DMA Channel 5 Page */
+#define GPDMAGR5 0x008c /* General 5 */
+#define GPDMAGR6 0x008d /* General 6 */
+#define GPDMAGR7 0x008e /* General 7 */
+#define GPDMAGR8 0x008f /* General 8 */
+#define GPDMA4MAR 0x00c0 /* Master DMA Channel 4 Memory Address */
+#define GPDMA4TC 0x00c2 /* Master DMA Channel 4 Transfer Count */
+#define GPDMA5MAR 0x00c4 /* Master DMA Channel 5 Memory Address */
+#define GPDMA5TC 0x00c6 /* Master DMA Channel 5 Transfer Count */
+#define GPDMA6MAR 0x00c8 /* Master DMA Channel 6 Memory Address */
+#define GPDMA6TC 0x00cc /* Master DMA Channel 6 Transfer Count */
+#define GPDMA7MAR 0x00ce /* Master DMA Channel 7 Memory Address */
+#define GPDMA7TC 0x00c2 /* Master DMA Channel 7 Transfer Count */
+#define MSTDMASTA 0x00d0 /* Master DMA Channel 4-7 Status */
+#define MSTDMACTL 0x00d0 /* Master DMA Channel 4-7 Control */
+#define MSTDMASWREQ 0x00d2 /* Master Software DRQ(n) Request */
+#define MSTDMAMSK 0x00d4 /* Master DMA Channel 4-7 Mask */
+#define MSTDMAMODE 0x00d6 /* Master DMA Channel 4-7 mode */
+#define MSTDMACBP 0x00d8 /* Master DMA Clear Byte Pointer */
+#define MSTDMARST 0x00da /* Master DMA Controller Reset */
+#define MSTDMATMP 0x00da /* Master DMA Temporary */
+#define MSTDMAMSKRST 0x00dc /* Master DMA Mask Reset */
+#define MSTDMAGENMSK 0x00de /* Master DMA General Mask */
+
+/*
+GP Bus DMA Controller Register Bit Definitions
+*/
+
+/* GP-DMA Control Register Bit Definitions */
+
+#define CH7_ALT_SIZE 0x80 /* Alternate Size for Channel 7 */
+#define CH6_ALT_SIZE 0x40 /* Alternate Size for Channel 6 */
+#define CH5_ALT_SIZE 0x20 /* Alternate Size for Channel 5 */
+#define CH3_ALT_SIZE 0x10 /* Alternate Size for Channel 3 */
+
+#define CLK_MODE_4MHZ 0x00 /* Clock Mode: GP Bus Controller at 4Mhz */
+#define CLK_MODE_8MHZ 0x04 /* Clock Mode: GP Bus Controller at 8Mhz */
+#define CLK_MODE_16MHZ 0x08 /* Clock Mode: GP Bus Controller at 16Mhz */
+
+#define ENH_MODE_ENB 0x01 /* Enhanced Mode Enable */
+
+/* GP-DMA Memory-Mapped I/O Register Bit Definitions */
+
+#define DMA7_MMAP 0x80 /* Memory-Mapped Device for DMA Channel 7 */
+#define DMA6_MMAP 0x40 /* Memory-Mapped Device for DMA Channel 6 */
+#define DMA5_MMAP 0x20 /* Memory-Mapped Device for DMA Channel 5 */
+#define DMA3_MMAP 0x08 /* Memory-Mapped Device for DMA Channel 3 */
+#define DMA2_MMAP 0x04 /* Memory-Mapped Device for DMA Channel 2 */
+#define DMA1_MMAP 0x02 /* Memory-Mapped Device for DMA Channel 1 */
+#define DMA0_MMAP 0x01 /* Memory-Mapped Device for DMA Channel 0 */
+
+/* GP-DMA Resource Channel Map A Register Bit Definitions */
+
+#define GPDRQ3_CHSEL_0 0x0000 /* GPDRQ3 Channel Mapping: Channel 0 */
+#define GPDRQ3_CHSEL_1 0x1000 /* GPDRQ3 Channel Mapping: Channel 1 */
+#define GPDRQ3_CHSEL_2 0x2000 /* GPDRQ3 Channel Mapping: Channel 2 */
+#define GPDRQ3_CHSEL_3 0x3000 /* GPDRQ3 Channel Mapping: Channel 3 */
+#define GPDRQ3_CHSEL_5 0x5000 /* GPDRQ3 Channel Mapping: Channel 5 */
+#define GPDRQ3_CHSEL_6 0x6000 /* GPDRQ3 Channel Mapping: Channel 6 */
+#define GPDRQ3_CHSEL_7 0x7000 /* GPDRQ3 Channel Mapping: Channel 7 */
+
+#define GPDRQ2_CHSEL_0 0x0000 /* GPDRQ2 Channel Mapping: Channel 0 */
+#define GPDRQ2_CHSEL_1 0x0100 /* GPDRQ2 Channel Mapping: Channel 1 */
+#define GPDRQ2_CHSEL_2 0x0200 /* GPDRQ2 Channel Mapping: Channel 2 */
+#define GPDRQ2_CHSEL_3 0x0300 /* GPDRQ2 Channel Mapping: Channel 3 */
+#define GPDRQ2_CHSEL_5 0x0500 /* GPDRQ2 Channel Mapping: Channel 5 */
+#define GPDRQ2_CHSEL_6 0x0600 /* GPDRQ2 Channel Mapping: Channel 6 */
+#define GPDRQ2_CHSEL_7 0x0700 /* GPDRQ2 Channel Mapping: Channel 7 */
+
+#define GPDRQ1_CHSEL_0 0x0000 /* GPDRQ1 Channel Mapping: Channel 0 */
+#define GPDRQ1_CHSEL_1 0x0010 /* GPDRQ1 Channel Mapping: Channel 1 */
+#define GPDRQ1_CHSEL_2 0x0020 /* GPDRQ1 Channel Mapping: Channel 2 */
+#define GPDRQ1_CHSEL_3 0x0030 /* GPDRQ1 Channel Mapping: Channel 3 */
+#define GPDRQ1_CHSEL_5 0x0050 /* GPDRQ1 Channel Mapping: Channel 5 */
+#define GPDRQ1_CHSEL_6 0x0060 /* GPDRQ1 Channel Mapping: Channel 6 */
+#define GPDRQ1_CHSEL_7 0x0070 /* GPDRQ1 Channel Mapping: Channel 7 */
+
+#define GPDRQ0_CHSEL_0 0x0000 /* GPDRQ0 Channel Mapping: Channel 0 */
+#define GPDRQ0_CHSEL_1 0x0001 /* GPDRQ0 Channel Mapping: Channel 1 */
+#define GPDRQ0_CHSEL_2 0x0002 /* GPDRQ0 Channel Mapping: Channel 2 */
+#define GPDRQ0_CHSEL_3 0x0003 /* GPDRQ0 Channel Mapping: Channel 3 */
+#define GPDRQ0_CHSEL_5 0x0005 /* GPDRQ0 Channel Mapping: Channel 5 */
+#define GPDRQ0_CHSEL_6 0x0006 /* GPDRQ0 Channel Mapping: Channel 6 */
+#define GPDRQ0_CHSEL_7 0x0007 /* GPDRQ0 Channel Mapping: Channel 7 */
+
+/* GP-DMA Resource Channel Map B Register Bit Definitions */
+
+#define TXDRQ3_CHSEL_0 0x0000 /* TXDRQ3 Channel Mapping: Channel 0 */
+#define TXDRQ3_CHSEL_1 0x1000 /* TXDRQ3 Channel Mapping: Channel 1 */
+#define TXDRQ3_CHSEL_2 0x2000 /* TXDRQ3 Channel Mapping: Channel 2 */
+#define TXDRQ3_CHSEL_3 0x3000 /* TXDRQ3 Channel Mapping: Channel 3 */
+
+#define TXDRQ2_CHSEL_0 0x0000 /* TXDRQ2 Channel Mapping: Channel 0 */
+#define TXDRQ2_CHSEL_1 0x0100 /* TXDRQ2 Channel Mapping: Channel 1 */
+#define TXDRQ2_CHSEL_2 0x0200 /* TXDRQ2 Channel Mapping: Channel 2 */
+#define TXDRQ2_CHSEL_3 0x0300 /* TXDRQ2 Channel Mapping: Channel 3 */
+
+#define TXDRQ1_CHSEL_0 0x0000 /* TXDRQ1 Channel Mapping: Channel 0 */
+#define TXDRQ1_CHSEL_1 0x0010 /* TXDRQ1 Channel Mapping: Channel 1 */
+#define TXDRQ1_CHSEL_2 0x0020 /* TXDRQ1 Channel Mapping: Channel 2 */
+#define TXDRQ1_CHSEL_3 0x0030 /* TXDRQ1 Channel Mapping: Channel 3 */
+
+#define TXDRQ0_CHSEL_0 0x0000 /* TXDRQ0 Channel Mapping: Channel 0 */
+#define TXDRQ0_CHSEL_1 0x0001 /* TXDRQ0 Channel Mapping: Channel 1 */
+#define TXDRQ0_CHSEL_2 0x0002 /* TXDRQ0 Channel Mapping: Channel 2 */
+#define TXDRQ0_CHSEL_3 0x0003 /* TXDRQ0 Channel Mapping: Channel 3 */
+
+/* GP-DMA Channel 0 Extended Page Register Masks */
+
+#define DMA0ADR 0x0F /* GP-DMA Channel 0 Extended Page Address */
+
+/* GP-DMA Channel 1 Extended Page Register Masks */
+
+#define DMA1ADR 0x0F /* GP-DMA Channel 1 Extended Page Address */
+
+/* GP-DMA Channel 2 Extended Page Register Masks */
+
+#define DMA2ADR 0x0F /* GP-DMA Channel 2 Extended Page Address */
+
+/* GP-DMA Channel 3 Extended Page Register Masks */
+
+#define DMA3ADR 0x0F /* GP-DMA Channel 3 Extended Page Address */
+
+/* GP-DMA Channel 5 Extended Page Register Masks */
+
+#define DMA5ADR 0x0F /* GP-DMA Channel 5 Extended Page Address */
+
+/* GP-DMA Channel 6 Extended Page Register Masks */
+
+#define DMA6ADR 0x0F /* GP-DMA Channel 6 Extended Page Address */
+
+/* GP-DMA Channel 7 Extended Page Register Masks */
+
+#define DMA7ADR 0x0F /* GP-DMA Channel 7 Extended Page Address */
+
+/* GP-DMA Channel 3 Extended Transfer Count Register Masks */
+
+#define DMA3TC 0xFF /* GP-DMA Channel 3 Transfer Count Extension */
+
+/* GP-DMA Channel 5 Extended Transfer Count Register Masks */
+
+#define DMA5TC 0xFF /* GP-DMA Channel 5 Transfer Count Extension */
+
+/* GP-DMA Channel 6 Extended Transfer Count Register Masks */
+
+#define DMA6TC 0xFF /* GP-DMA Channel 6 Transfer Count Extension */
+
+/* GP-DMA Channel 7 Extended Transfer Count Register Masks */
+
+#define DMA7TC 0xFF /* GP-DMA Channel 7 Transfer Count Extension */
+
+/* Buffer Chaining Control Register Bit Definitions */
+
+#define CH7_BCHN_ENB 0x08 /* Buffer Chaining Enable for Channel 7 */
+#define CH6_BCHN_ENB 0x04 /* Buffer Chaining Enable for Channel 6 */
+#define CH5_BCHN_ENB 0x02 /* Buffer Chaining Enable for Channel 5 */
+#define CH3_BCHN_ENB 0x01 /* Buffer Chaining Enable for Channel 3 */
+
+/* Buffer Chaining Status Register Bit Definitions */
+
+#define CH7_EOB_STA 0x08 /* End of Current Buffer in Channel 7 */
+#define CH6_EOB_STA 0x04 /* End of Current Buffer in Channel 6 */
+#define CH5_EOB_STA 0x02 /* End of Current Buffer in Channel 5 */
+#define CH3_EOB_STA 0x01 /* End of Current Buffer in Channel 3 */
+
+/* Buffer Chaining Interrupt Enable Register Bit Definitions */
+
+#define CH7_INT_ENB 0x08 /* Interrupt Enable for Channel 7 */
+#define CH6_INT_ENB 0x04 /* Interrupt Enable for Channel 6 */
+#define CH5_INT_ENB 0x02 /* Interrupt Enable for Channel 5 */
+#define CH3_INT_ENB 0x01 /* Interrupt Enable for Channel 3 */
+
+/* Buffer Chaining Valid Register Bit Definitions */
+
+#define CH7_CBUF_VAL 0x08 /* Chaining Buffer Valid for Channel 7 */
+#define CH6_CBUF_VAL 0x04 /* Chaining Buffer Valid for Channel 6 */
+#define CH5_CBUF_VAL 0x02 /* Chaining Buffer Valid for Channel 5 */
+#define CH3_CBUF_VAL 0x01 /* Chaining Buffer Valid for Channel 3 */
+
+/* GP-DMA Channel 3 Next Address Low Register Masks */
+
+#define DMA3_NXT_ADRL 0xFFFF /* GP-DMA Channel 3 Next Address Low*/
+
+/* GP-DMA Channel 3 Next Address High Register Masks */
+
+#define DMA3_NXT_ADRH 0x0FFF /* GP-DMA Channel 3 Next Address High */
+
+/* GP-DMA Channel 5 Next Address Low Register Masks */
+
+#define DMA5_NXT_ADRL 0xFFFF /* GP-DMA Channel 5 Next Address Low */
+
+/* GP-DMA Channel 5 Next Address High Register Masks */
+
+#define DMA5_NXT_ADRH 0x0FFF /* GP-DMA Channel 5 Next Address High */
+
+/* GP-DMA Channel 6 Next Address Low Register Masks */
+
+#define DMA6_NXT_ADRL 0xFFFF /* GP-DMA Channel 6 Next Address Low */
+
+/* GP-DMA Channel 6 Next Address High Register Masks */
+
+#define DMA6_NXT_ADRH 0x0FFF /* GP-DMA Channel 6 Next Address High */
+
+/* GP-DMA Channel 7 Next Address Low Register Masks */
+
+#define DMA7_NXT_ADRL 0xFFFF /* GP-DMA Channel 7 Next Address Low */
+
+/* GP-DMA Channel 7 Next Address High Register Masks */
+
+#define DMA7_NXT_ADRH 0x0FFF /* GP-DMA Channel 7 Next Address High */
+
+
+/* GP-DMA Channel 3 Next Transfer Count Low Register Masks */
+
+#define DMA3_NXT_TCL 0xFFFF /* GP-DMA Channel 3 Next Transfer Count Low*/
+
+/* GP-DMA Channel 3 Next Transfer Count High Register Masks */
+
+#define DMA3_NXT_TCH 0xFF /* GP-DMA Channel 3 Next Transfer Count High*/
+
+/* GP-DMA Channel 5 Next Transfer Count Low Register Masks */
+
+#define DMA5_NXT_TCL 0xFFFF /* GP-DMA Channel 5 Next Transfer Count Low */
+
+/* GP-DMA Channel 5 Next Transfer Count High Register Masks */
+
+#define DMA5_NXT_TCH 0xFF /* GP-DMA Channel 5 Next Transfer Count High*/
+
+/* GP-DMA Channel 6 Next Transfer Count Low Register Masks */
+
+#define DMA6_NXT_TCL 0xFFFF /* GP-DMA Channel 6 Next Transfer Count Low */
+
+/* GP-DMA Channel 6 Next Transfer Count High Register Masks */
+
+#define DMA6_NXT_TCH 0xFF /* GP-DMA Channel 6 Next Transfer Count High*/
+
+/* GP-DMA Channel 7 Next Transfer Count Low Register Masks */
+
+#define DMA7_NXT_TCL 0xFFFF /* GP-DMA Channel 7 Next Transfer Count Low */
+
+/* GP-DMA Channel 7 Next Transfer Count High Register Masks */
+
+#define DMA7_NXT_TCH 0xFF /* GP-DMA Channel 7 Next Transfer Count High*/
+
+/* Slave DMA Channel 0 Memory Address Register Masks */
+
+#define DMA0MAR 0xFF /* Lower 16 Bits of DMA Channel 0 Memory Address */
+
+/* Slave DMA Channel 0 Transfer Count Register Masks */
+
+#define DMA0TC 0xFF /* DMA Channel 0 Transfer Count */
+
+/* Slave DMA Channel 1 Memory Address Register Masks */
+
+#define DMA1MAR 0xFF /* Lower 16 Bits of DMA Channel 1 Memory Address */
+
+/* Slave DMA Channel 1 Transfer Count Register Masks */
+
+#define DMA1TC 0xFF /* DMA Channel 1 Transfer Count */
+
+/* Slave DMA Channel 2 Memory Address Register Masks */
+
+#define DMA2MAR 0xFF /* Lower 16 Bits of DMA Channel 2 Memory Address */
+
+/* Slave DMA Channel 2 Transfer Count Register Masks */
+
+#define DMA2TC 0xFF /* DMA Channel 2 Transfer Count */
+
+/* Slave DMA Channel 3 Memory Address Register Masks */
+
+#define DMA3MAR 0xFF /* Lower 16 Bits of DMA Channel 3 Memory Address */
+
+/* Slave DMA Channel 3 Transfer Count Register Masks */
+
+#define DMA3TC 0xFF /* DMA Channel 3 Transfer Count */
+
+/* Slave DMA Channel 0-3 Status Register Bit Definitions */
+
+#define DMAR3 0x80 /* Channel 3 DMA Request */
+#define DMAR2 0x40 /* Channel 2 DMA Request */
+#define DMAR1 0x20 /* Channel 1 DMA Request */
+#define DMAR0 0x10 /* Channel 0 DMA Request */
+
+#define TC3 0x08 /* Channel 3 Terminal Count */
+#define TC2 0x04 /* Channel 2 Terminal Count */
+#define TC1 0x02 /* Channel 1 Terminal Count */
+#define TC0 0x01 /* Channel 0 Terminal Count */
+
+/* Slave DMA Channel 0-3 Control Register Bit Definitions */
+
+#define DAKSEN 0x80 /* Internal /DACKX Sense */
+#define DRQSEN 0x40 /* Internal drqx Sense */
+#define WRTSEL 0x20 /* Write Selection Control */
+#define PRITYPE 0x10 /* Priority Type */
+#define COMPTIM 0x08 /* Compressed Timing */
+#define DMA_DIS 0x04 /* Disable DMA Controller */
+
+/* Slave Software DRQ(n) Request Register Bit Definitions */
+
+#define REQDMA 0x04 /* Software DMA Request */
+
+#define REQSEL_CH0 0x00 /* DMA Channel Select: channel 0 */
+#define REQSEL_CH1 0x01 /* DMA Channel Select: channel 1 */
+#define REQSEL_CH2 0x02 /* DMA Channel Select: channel 2 */
+#define REQSEL_CH3 0x03 /* DMA Channel Select: channel 3 */
+
+/* Slave DMA Channel 0-3 Mask Register Bit Definitions */
+
+#define CHMASK 0x40 /* DMA Channel Mask */
+
+#define MSKSEL_CH0 0x00 /* DMA Channel Mask Select: channel 0 */
+#define MSKSEL_CH1 0x01 /* DMA Channel Mask Select: channel 1 */
+#define MSKSEL_CH2 0x02 /* DMA Channel Mask Select: channel 2 */
+#define MSKSEL_CH3 0x03 /* DMA Channel Mask Select: channel 3 */
+
+/* Slave DMA Channel 0-3 Mode Register Bit Definitions */
+
+#define TRNMOD_DTM 0x00 /* Transfer Mode: Demand transfer */
+#define TRNMOD_STM 0x40 /* Transfer Mode: Single transfer */
+#define TRNMOD_BTM 0x80 /* Transfer Mode: Block transfer */
+#define TRNMOD_CM 0xC0 /* Transfer Mode: Cascade Mode */
+
+#define ADDDEC 0x20 /* Address Decrement */
+#define AINIT 0x10 /* Automatic Initilization Control */
+
+#define OPSEL_VM 0x00 /* Operation Select: Verify Mode */
+#define OPSEL_WT 0x40 /* Operation Select: Write Transfer Mode */
+#define OPSEL_RT 0x80 /* Operation Select: Read Transfer Mode */
+
+#define MODSEL_CH0 0x00 /* DMA Channel Select:channel 0 */
+#define MODSEL_CH1 0x01 /* DMA Channel Select:channel 1 */
+#define MODSEL_CH2 0x02 /* DMA Channel Select:channel 2 */
+#define MODSEL_CH30 0x03 /* DMA Channel Select:channel 3 */
+
+/* Slave DMA Clear Byte Pointer Register Masks */
+
+#define SLAVE_CBP 0xFF /* Slave DMA Clear Byte Pointer */
+
+/* Slave DMA Controller Reset Register Masks */
+
+#define SLAVE_RST 0xFF /* Slave DMA Controller Reset */
+
+/* Slave DMA Controller Temporary Register Masks */
+
+#define SLAVE_TMP 0xFF /* Slave DMa Controller Temporary Register */
+
+/* Slave DMA Mask Reset Register Masks */
+
+#define SLAVE_MSK_RST 0xFF /* Slave DMA Reset Mask */
+
+/* Slave DMA General Mask Register Bit Definitions */
+
+#define CH3_DIS 0x08 /* DMA Channel 3 Mask */
+#define CH2_DIS 0x04 /* DMA Channel 2 Mask */
+#define CH1_DIS 0x02 /* DMA Channel 1 Mask */
+#define CH0_DIS 0x01 /* DMA Channel 0 Mask */
+
+/* General 0 Register Masks */
+
+#define PORT80 0xFF /* General Purpose R/W Register */
+
+/* Slave DMA Channel 2 Page Register Masks */
+
+#define DMA2MAR 0xFF /* DMA Channel 2 Memory Address Bits [23-16] */
+
+/* Slave DMA Channel 3 Page Register Masks */
+
+#define DMA3MAR 0xFF /* DMA Channel 3 Memory Address Bits [23-16] */
+
+/* Slave DMA Channel 1 Page Register Masks */
+
+#define DMA1MAR 0xFF /* DMA Channel 1 Memory Address Bits [23-16] */
+
+/* General 1 Register Masks */
+
+#define PORT84 0xFF /* General Purpose R/W Register */
+
+/* General 2 Register Masks */
+
+#define PORT85 0xFF /* General Purpose R/W Register */
+
+/* General 3 Register Masks */
+
+#define PORT86 0xFF /* General Purpose R/W Register */
+
+/* Slave DMA Channel 0 Page Register Masks */
+
+#define DMA0MAR 0xFF /* DMA Channel 0 Memory Address Bits [23-16] */
+
+/* General 4 Register Masks */
+
+#define PORT88 0xFF /* General Purpose R/W Register */
+
+
+
+/* Master DMA Channel 6 Page Register Masks */
+
+#define DMA6MAR_H 0xFE /* DMA Channel 6 Memory Address Bits [23-17] */
+
+/* Master DMA Channel 7 Page Register Masks */
+
+#define DMA7MAR_H 0xFE /* DMA Channel 7 Memory Address Bits [23-17] */
+
+/* Master DMA Channel 5 Page Register Masks */
+
+#define DMA5MAR_H 0xFE /* DMA Channel 5 Memory Address Bits [23-17] */
+
+/* General 5 Register Masks */
+
+#define PORT8C 0xFF /* General Purpose R/W Register */
+
+/* General 6 Register Masks */
+
+#define PORT8D 0xFF /* General Purpose R/W Register */
+
+/* General 7 Register Masks */
+
+#define PORT8E 0xFF /* General Purpose R/W Register */
+
+/* General 8 Register Masks */
+
+#define PORT8F 0xFF /* General Purpose R/W Register */
+
+/* Master DMA Channel 4 Memory Address Register Masks */
+
+#define DMA4MAR 0xFF /* DMA Channel 4 Memory Address */
+
+/* Master DMA Channel 4 Transfer Count Register Masks */
+
+#define DMA4TC 0xFF /* DMA Channel 4 Transfer Count */
+
+/* Master DMA Channel 5 Memory Address Register Masks */
+
+#define DMA5MAR 0xFF /* DMA Channel 5 Memory Address */
+
+/* Master DMA Channel 5 Transfer Count Register Masks */
+
+#define DMA5TC 0xFF /* DMA Channel 5 Transfer Count */
+
+/* Master DMA Channel 6 Memory Address Register Masks */
+
+#define DMA6MAR 0xFF /* DMA Channel 6 Memory Address */
+
+/* Master DMA Channel 6 Transfer Count Register Masks */
+
+#define DMA6TC 0xFF /* DMA Channel 6 Transfer Count */
+
+/* Master DMA Channel 7 Memory Address Register Masks */
+
+#define DMA7MAR 0xFF /* DMA Channel 7 Memory Address */
+
+/* Master DMA Channel 7 Transfer Count Register Masks */
+
+#define DMA7TC 0xFF /* DMA Channel 7 Transfer Count */
+
+/* Master DMA Channel 4-7 Status Register Bit Definitions */
+
+#define DMAR7 0x80 /* Channel 7 DMA Request */
+#define DMAR6 0x40 /* Channel 6 DMA Request */
+#define DMAR5 0x20 /* Channel 5 DMA Request */
+#define DMAR4 0x10 /* Channel 4 DMA Request */
+
+#define TC7 0x08 /* Channel 7 Terminal Count */
+#define TC6 0x04 /* Channel 6 Terminal Count */
+#define TC5 0x02 /* Channel 5 Terminal Count */
+#define TC4 0x01 /* Channel 4 Terminal Count */
+
+/* Master DMA Channel 4-7 Control Bit Definitions already defined previously */
+
+ /* REQDMA bit already definined */
+
+#define REQSEL1_CH4 0x00 /* DMA Channel Select: Channel 4 */
+#define REQSEL1_CH5 0x01 /* DMA Channel Select: Channel 5 */
+#define REQSEL1_CH6 0x02 /* DMA Channel Select: Channel 6 */
+#define REQSEL1_CH7 0x03 /* DMA Channel Select: Channel 7 */
+
+/* Master DMA Channel 4-7 Mask Register Definitions */
+
+ /* CHMASK bit already correctly defined previously */
+
+#define MSKSEL_CH4 0x00 /* DMA Channel Mask Select: channel 4 */
+#define MSKSEL_CH5 0x01 /* DMA Channel Mask Select: channel 5 */
+#define MSKSEL_CH6 0x02 /* DMA Channel Mask Select: channel 6 */
+#define MSKSEL_CH7 0x03 /* DMA Channel Mask Select: channel 7 */
+
+/* Master DMA Channel 4-7 Mode Register Bit Definitions */
+
+ /* TRNMOD bits already defined */
+ /* ADDDEC bit already defined */
+ /* AINIT bit already defined */
+ /* OPSEL bits already defined */
+
+#define MODSEL_CH4 0x00 /* DMA Channel Select: Channel 4 */
+#define MODSEL_CH5 0x01 /* DMA Channel Select: Channel 5 */
+#define MODSEL_CH6 0x02 /* DMA Channel Select: Channel 6 */
+#define MODSEL_CH7 0x03 /* DMA Channel Select: Channel 7 */
+
+/* Master DEMA Clear Byte Pointer Register Masks */
+
+#define MASTR_CBP 0xFF /* Master DMA Clear Byte Pointer */
+
+/* Master DMA Controller Reset Register Masks */
+
+#define MASTR_RST 0xFF /* Master DMA Controller Reset */
+
+/* Master DMA Controller Temporary Register Masks */
+
+#define MASTR_TMP 0xFF /* Master DMA Controller Temporary Register */
+
+/* Master DMA Mask Reset Register Masks */
+
+#define MASTR_MSK_RST 0xFF /* Master DMA Reset Mask */
+
+/* Master DMA General Mask Register Bit Definitions */
+
+#define CH7_DIS 0x08 /* DMA Channel 7 Mask */
+#define CH6_DIS 0x04 /* DMA Channel 6 Mask */
+#define CH5_DIS 0x02 /* DMA Channel 5 Mask */
+#define CH4_DIS 0x01 /* DMA Channel 4 Mask */
+
+/****************************
+* Real-Time Clock Registers *
+****************************/
+
+/* Real-Time Clock Direct-Mapped Registers */
+
+#define RTCIDX 0x0070 /* RTC/CMOS RAM Index Register */
+#define RTCDATA 0x0071 /* RTC/CMOS RAM Data Port */
+
+/* Real-Time Clock Indexed Registers */
+
+#define RTCCURSEC 0x00 /* RTC Current Second */
+#define RTCALMSEC 0x01 /* RTC Alarm Second */
+#define RTCCURMIN 0x02 /* RTC Current Minute */
+#define RTCALMMIN 0x03 /* RTC Alarm Minute */
+#define RTCCURHR 0x04 /* RTC Current Hour */
+#define RTCALMHR 0x05 /* RTC Alarm Hour */
+#define RTCCURDOW 0x06 /* RTC Current Day of Week */
+#define RTCCURDOM 0x07 /* RTC Current Day of Month */
+#define RTCDURMON 0x08 /* RTC Current Month */
+#define RTCCURYR 0x09 /* RTC Current Year */
+#define RTCCTLA 0x0a /* RTC Control A */
+#define RTCCTLB 0x0b /* RTC Control A */
+#define RTCCTLC 0x0c /* RTC Control A */
+#define RTCCTLD 0x0d /* RTC Control A */
+
+/*
+Real-Time Clock Register Bit Definitions
+*/
+
+/* RTC/CMOS RAM Index Register Mask */
+
+#define CMOSIDX 0x7E /* RTC/CMOS RAM Index */
+
+/* RTC/CMOS RAM Data Port Register Mask */
+
+#define CMOSDATA 0xFF /* RTC/CMOS Data Port */
+
+/* RTC Current Second Register Mask */
+
+#define SECOND 0xFF /* RTC Current Second */
+
+/* RTC Alarm Second Register Mask */
+
+#define ALM_SECOND 0xFF /* RTC Alarm Second */
+
+/* RTC Current Minute Register Mask */
+
+#define MINUTE 0xFF /* RTC Current Minute */
+
+/* RTC Alarm Minute Register Mask */
+
+#define ALM_MINUTE 0xFF /* RTC Alarm Minute */
+
+/* RTC Current Hour Register Bit Definitions */
+
+#define AM_PM 0x80 /* RTC AM/PM Indicator */
+
+/* RTC Current Hour Register Mask */
+
+#define HOUR 0x7F /* RTC Current Hour */
+
+/* RTC Alarm Hour Register Bit Definitions */
+
+#define ALARM_AM_PM 0x80 /* RTC Alarm AM/PM Indicator */
+
+/* RTC Alarm Hour Register Mask */
+
+#define ALM_HOUR 0x7F /* RTC Alarm Hour */
+
+/* RTC Current Day of the Week Register Bit Definitions */
+
+#define SUNDAY 0x01 /* Sunday day of week */
+#define MONDAY 0x02 /* Monday day of week */
+#define TUESDAY 0x03 /* Tuesday day of week */
+#define WEDNESDAY 0x04 /* Wednesday day of week */
+#define THURDAY 0x05 /* Thursday day of week */
+#define FRIDAY 0x06 /* Friday!!!! day of week */
+#define SATURDAY 0x07 /* Saturday day of week */
+
+/* RTC Current Day of the Week Register Mask */
+
+#define DAY_OF_WEEK 0xFF /* RTC Current Day of the Week */
+
+/* RTC Current Day of the Month Register Mask */
+
+#define DAY_OF_MTH 0xFF /* RTC Current Day of the Month */
+
+/* RTC Current Month Register Bit Definitions */
+
+#define JANUARY 0x01 /* month of January */
+#define FEBRUARY 0x02 /* month of February */
+#define MARCH 0x03 /* month of March */
+#define APRIL 0x04 /* month of April */
+#define MAY 0x05 /* month of May */
+#define JUNE 0x06 /* month of June */
+#define JULY 0x07 /* month of July */
+#define AUGUST 0x08 /* month of August */
+#define SEPTEMBER 0x09 /* month of September */
+#define OCTOBER 0x0a /* month of October */
+#define NOVEMBER 0x0b /* month of November */
+#define DECEMBER 0x0c /* month of December */
+
+/* RTC Current Month Register Mask */
+
+#define MONTH 0xFF /* RTC Current Month */
+
+/* RTC Current Year Register Mask */
+
+#define YEAR 0xFF /* RTC Current Year */
+
+/* RTC Control A Register Bit Definitions */
+
+#define UIP 0x80 /* Update in Progress */
+
+#define OSC_CTL_ENB 0x20 /* Enable RTC to be updated once per sec (normal) */
+#define OSC_CTL_HOLD 0x60 /* Hold RTC in reset state */
+
+#define RATE_SEL_PID 0x00 /* Rate Selection: Periodic Interrupt Disabled */
+#define RATE_SEL_3_906m 0x01 /* Rate Selection: 3.906 milliseconds */
+#define RATE_SEL_7_812m 0x02 /* Rate Selection: 7.812 milliseconds */
+#define RATE_SEL_122_070u 0x03 /* Rate Selection: 122.070 microseconds */
+#define RATE_SEL_244_141u 0x04 /* Rate Selection: 244.141 microseconds */
+#define RATE_SEL_488_281u 0x05 /* Rate Selection: 488.281 microseconds */
+#define RATE_SEL_976_563u 0x06 /* Rate Selection: 976.563 microseconds */
+
+#define RATE_SEL_1_953m 0x09 /* Rate Selection: 1.953 milliseconds */
+#define RATE_SEL_15_625m 0x0a /* Rate Selection: 15.625 milliseconds */
+#define RATE_SEL_31_250m 0x0b /* Rate Selection: 31.250 milliseconds */
+#define RATE_SEL_62_500m 0x0c /* Rate Selection: 62.500 milliseconds */
+#define RATE_SEL_125_000m 0x0d /* Rate Selection: 125.000 milliseconds */
+#define RATE_SEL_250_000m 0x0e /* Rate Selection: 250.000 milliseconds */
+#define RATE_SEL_500_000m 0x0f /* Rate Selection: 500.000 milliseconds */
+
+/* RTC Control B Register Bit Definitions */
+
+#define SET 0x80 /* Set Bit */
+#define PER_INT_ENB 0x40 /* Periodic Interrupt Enable */
+#define ALM_INT_ENB 0x20 /* Alarm Interrupt Enable */
+#define UPD_INT_ENB 0x10 /* Update-Ended Interrupt Enable */
+#define DATE_MODE 0x04 /* Date Mode */
+#define HOUR_MODE_SEL 0x02 /* 12/24-Hour Mode Select Bit */
+#define DS_ENB 0x01 /* Daylight Savings Enable */
+
+/* RTC Status C Register Bit Definitions */
+
+#define INT_FLG 0x80 /* Interrupt Request Flag */
+#define PER_INT_FLG 0x40 /* Periodic Interrupt Flag */
+#define ALM_INT_FLG 0x20 /* Alarm Interrupt Flag */
+#define UPD_INT_FLG 0x10 /* Update-Ended Interrupt Flag */
+
+
+/* RTC Status D Register Bit Definitions */
+
+#define RTC_VRT 0x80 /* Valid Ram and Time */
+
+/* General-Purpose CMOS RAM Mask */
+
+#define RTC_CMOS_REG_X 0xFF /* CMOS RAM Location */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif /* _ASM_IC_SC520_DEFS_H_ */
diff --git a/include/configs/eNET.h b/include/configs/eNET.h
new file mode 100644
index 0000000..4f206df
--- /dev/null
+++ b/include/configs/eNET.h
@@ -0,0 +1,215 @@
+/*
+ * (C) Copyright 2002
+ * Daniel Engström, Omicron Ceti AB, daniel(a)omicron.se.
+ *
+ * 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
+ */
+
+/*
+ * board/config.h - configuration options, board specific
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+/*
+ * High Level Configuration Options
+ * (easy to change)
+ */
+#define DEBUG_PARSER
+
+#define CONFIG_X86 1 /* This is a X86 CPU */
+#define CONFIG_SC520 1 /* Include support for AMD SC520 */
+#define CONFIG_SC520_SSI
+
+/*
+ * No video hardware on the eNET
+ */
+#undef CONFIG_VIDEO /* No Video Hardware */
+#undef CONFIG_CFB_CONSOLE
+
+#define CFG_SDRAM_DRCTMCTL 0x18
+
+#undef CFG_SDRAM_PRECHARGE_DELAY /* CFG_SDRAM_DRCTMCTL Overrides */
+#undef CFG_SDRAM_REFRESH_RATE /* CFG_SDRAM_DRCTMCTL Overrides */
+#undef CFG_SDRAM_RAS_CAS_DELAY /* CFG_SDRAM_DRCTMCTL Overrides */
+#undef CFG_SDRAM_CAS_LATENCY_2T /* CFG_SDRAM_DRCTMCTL Overrides */
+#undef CFG_SDRAM_CAS_LATENCY_3T /* CFG_SDRAM_DRCTMCTL Overrides */
+
+#define CFG_SC520_HIGH_SPEED 0 /* 100 or 133MHz */
+#define CFG_RESET_GENERIC /* use tripple-fault to reset cpu */
+#undef CFG_RESET_SC520 /* use SC520 MMCR's to reset cpu */
+#define CFG_TIMER_SC520 /* use SC520 swtimers */
+#undef CFG_TIMER_GENERIC /* use the i8254 PIT timers */
+#undef CFG_TIMER_TSC /* use the Pentium TSC timers */
+#define CFG_USE_SIO_UART 0 /* prefer the uarts on the SIO to those
+ * in the SC520 on the CDP */
+
+#define CFG_STACK_SIZE 0x8000 /* Size of bootloader stack */
+#define CFG_RELOC_ADDR 0x03fd0000 /* Address of relocated code */
+
+#define CONFIG_SHOW_BOOT_PROGRESS 1
+#define CONFIG_LAST_STAGE_INIT 1
+
+/*
+ * Size of malloc() pool
+ */
+#define CONFIG_MALLOC_SIZE (CFG_ENV_SIZE + 128*1024)
+
+#define CONFIG_BAUDRATE 9600
+
+/*
+ * Command line configuration.
+ */
+#include <config_cmd_default.h>
+
+
+#define CONFIG_CMD_AUTOSCRIPT /* Autoscript Support */
+#define CONFIG_CMD_BDI /* bdinfo */
+#define CONFIG_CMD_BOOTD /* bootd */
+#define CONFIG_CMD_CONSOLE /* coninfo */
+#define CONFIG_CMD_ECHO /* echo arguments */
+#define CONFIG_CMD_ENV /* saveenv */
+#undef CONFIG_CMD_FLASH /* flinfo, erase, protect */
+#define CONFIG_CMD_FPGA /* FPGA configuration Support */
+#define CONFIG_CMD_IMI /* iminfo */
+#define CONFIG_CMD_IMLS /* List all found images */
+#define CONFIG_CMD_ITEST /* Integer (and string) test */
+#define CONFIG_CMD_LOADB /* loadb */
+#define CONFIG_CMD_LOADS /* loads */
+#define CONFIG_CMD_MEMORY /* md mm nm mw cp cmp crc base loop mtest */
+#define CONFIG_CMD_MISC /* Misc functions like sleep etc*/
+#undef CONFIG_CMD_NET /* bootp, tftpboot, rarpboot */
+#undef CONFIG_CMD_NFS /* NFS support */
+#define CONFIG_CMD_RUN /* run command in env variable */
+#define CONFIG_CMD_SETGETDCR /* DCR support on 4xx */
+#define CONFIG_CMD_XIMG /* Load part of Multi Image */
+
+
+
+
+
+#define CONFIG_BOOTDELAY 15
+#define CONFIG_BOOTARGS "root=/dev/mtdblock0 console=ttyS0,9600"
+/* #define CONFIG_BOOTCOMMAND "bootm 38000000" */
+
+#if defined(CONFIG_CMD_KGDB)
+#define CONFIG_KGDB_BAUDRATE 115200 /* speed to run kgdb serial port */
+#define CONFIG_KGDB_SER_INDEX 2 /* which serial port to use */
+#endif
+
+/*
+ * Miscellaneous configurable options
+ */
+#define CFG_LONGHELP /* undef to save memory */
+#define CFG_PROMPT "boot > " /* Monitor Command Prompt */
+#define CFG_CBSIZE 256 /* Console I/O Buffer Size */
+#define CFG_PBSIZE (CFG_CBSIZE+sizeof(CFG_PROMPT)+16) /* Print Buffer Size */
+#define CFG_MAXARGS 16 /* max number of command args */
+#define CFG_BARGSIZE CFG_CBSIZE /* Boot Argument Buffer Size */
+
+#define CFG_MEMTEST_START 0x00100000 /* memtest works on */
+#define CFG_MEMTEST_END 0x01000000 /* 1 ... 16 MB in DRAM */
+
+#undef CFG_CLKS_IN_HZ /* everything, incl board info, in Hz */
+
+#define CFG_LOAD_ADDR 0x100000 /* default load address */
+
+#define CFG_HZ 1024 /* incrementer freq: 1kHz */
+
+ /* valid baudrates */
+#define CFG_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 }
+
+/*-----------------------------------------------------------------------
+ * Physical Memory Map
+ */
+#define CONFIG_NR_DRAM_BANKS 4 /* we have 4 banks of DRAM */
+
+/*-----------------------------------------------------------------------
+ * FLASH and environment organization
+ */
+#define CFG_MAX_FLASH_BANKS 3 /* max number of memory banks */
+#define CFG_MAX_FLASH_SECT 64 /* max number of sectors on one chip */
+
+/* timeout values are in ticks */
+#define CFG_FLASH_ERASE_TOUT (2*CFG_HZ) /* Timeout for Flash Erase */
+#define CFG_FLASH_WRITE_TOUT (2*CFG_HZ) /* Timeout for Flash Write */
+
+/* allow to overwrite serial and ethaddr */
+#define CONFIG_ENV_OVERWRITE
+
+/* Environment in NVRAM */
+#define CONFIG_ENV_IS_IN_NVRAM
+#define CONFIG_ENV_ADDR 0x19000000
+#define CONFIG_ENV_SIZE 0x1000
+/************************************************************
+ * RTC
+ ***********************************************************/
+#define CONFIG_RTC_MC146818
+
+/*
+ * Enable hardware watchdog.
+ *
+ * WARNING: If CONFIG_HW_WATCHDOG is not defined, the watchdog jumper on the
+ * bottom (processor) board MUST be removed!
+ */
+#undef CONFIG_WATCHDOG
+#define CONFIG_HW_WATCHDOG
+
+/*
+ * PCI stuff
+ */
+#undef CONFIG_PCI /* include pci support */
+#undef CONFIG_PCI_PNP /* pci plug-and-play */
+#undef CONFIG_PCI_SCAN_SHOW
+
+#undef CFG_FIRST_PCI_IRQ
+#undef CFG_SECOND_PCI_IRQ
+#undef CFG_THIRD_PCI_IRQ
+#undef CFG_FORTH_PCI_IRQ
+/*
+ * #undef CFG_FIRST_PCI_IRQ 10
+ * #undef CFG_SECOND_PCI_IRQ 9
+ * #undef CFG_THIRD_PCI_IRQ 11
+ * #undef CFG_FORTH_PCI_IRQ 15
+ */
+/*
+ * Hardware watchdog stuff
+ */
+#define CFG_WATCHDOG_PIO_BIT 0x8000
+#define CFG_WATCHDIG_PIO_DATA SC520_PIODATA15_0
+#define CFG_WATCHDIG_PIO_CLR SC520_PIOCLR15_0
+#define CFG_WATCHDIG_PIO_SET SC520_PIOSET15_0
+
+/*
+ * FPGA stuff
+ */
+#define CFG_FPGA_PROGRAM_PIO_BIT 0x2000
+#define CFG_FPGA_INIT_PIO_BIT 0x4000
+#define CFG_FPGA_DONE_PIO_BIT 0x8000
+#define CFG_FPGA_PIO_DATA SC520_PIODATA31_16
+#define CFG_FPGA_PIO_DIRECTION SC520_PIODIR31_16
+#define CFG_FPGA_PIO_CLR SC520_PIOCLR31_16
+#define CFG_FPGA_PIO_SET SC520_PIOSET31_16
+#define CFG_FPGA_PROGRAM_BIT_DROP_TIME 1 /* milliseconds */
+#define CFG_FPGA_MAX_INIT_TIME 10 /* milliseconds */
+#define CFG_FPGA_MAX_FINALISE_TIME 10 /* milliseconds */
+#define CFG_FPGA_SSI_DATA_RATE 8333 /* kHz (33.3333MHz xtal) */
+
+#endif /* __CONFIG_H */
2
1

28 Oct '08
Split to meet mailing list size limit
Initial addition of eNET files - builds clean but will not run until
additional i386 code changes are made
Signed-off-by: Graeme Russ <graeme.russ(a)gmail.com>
--
diff --git a/MAKEALL b/MAKEALL
index 9ccb9ac..6f65870 100755
--- a/MAKEALL
+++ b/MAKEALL
@@ -645,6 +645,7 @@ LIST_I486=" \
sc520_cdp \
sc520_spunk \
sc520_spunk_rel \
+ sc520_eNET \
"
LIST_x86=" \
diff --git a/Makefile b/Makefile
index 7c13ce8..cdfca1c 100644
--- a/Makefile
+++ b/Makefile
@@ -2843,6 +2843,12 @@ sc520_spunk_config : unconfig
sc520_spunk_rel_config : unconfig
@$(MKCONFIG) $(@:_config=) i386 i386 sc520_spunk
+#########################################################################
+## Serck eNET
+#########################################################################
+eNET_config : unconfig
+ @$(MKCONFIG) $(@:_config=) i386 i386 eNET
+
#========================================================================
# MIPS
#========================================================================
diff --git a/board/eNET/Makefile b/board/eNET/Makefile
new file mode 100644
index 0000000..a124b33
--- /dev/null
+++ b/board/eNET/Makefile
@@ -0,0 +1,57 @@
+#
+# (C) Copyright 2008
+# Graeme Russ, graeme.russ(a)gmail.com.
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd(a)denx.de.
+#
+# (C) Copyright 2002
+# Daniel Engström, Omicron Ceti AB, daniel(a)omicron.se.
+#
+# 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 $(TOPDIR)/config.mk
+
+LIB = $(obj)lib$(BOARD).a
+
+COBJS := eNET.o flash.o fpga.o
+SOBJS := eNET_start16.o eNET_start.o
+
+SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+SOBJS := $(addprefix $(obj),$(SOBJS))
+
+$(LIB): $(obj).depend $(OBJS) $(SOBJS)
+ $(AR) $(ARFLAGS) $@ $(OBJS) $(SOBJS)
+
+clean:
+ rm -f $(SOBJS) $(OBJS)
+
+distclean: clean
+ rm -f $(LIB) core *.bak $(obj).depend
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/board/eNET/config.mk b/board/eNET/config.mk
new file mode 100644
index 0000000..6797f8a
--- /dev/null
+++ b/board/eNET/config.mk
@@ -0,0 +1,25 @@
+#
+# (C) Copyright 2002
+# Daniel Engström, Omicron Ceti AB, daniel(a)omicron.se.
+#
+# 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
+#
+
+
+TEXT_BASE = 0x38040000
diff --git a/board/eNET/eNET.c b/board/eNET/eNET.c
new file mode 100644
index 0000000..1b4af58
--- /dev/null
+++ b/board/eNET/eNET.c
@@ -0,0 +1,640 @@
+/*
+ *
+ * (C) Copyright 2002
+ * Daniel Engström, Omicron Ceti AB <daniel(a)omicron.se>.
+ *
+ * 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 <pci.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/ic/sc520.h>
+#include <spi.h>
+
+#ifdef CONFIG_HW_WATCHDOG
+#include <watchdog.h>
+#endif
+
+#include "hardware.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#undef SC520_CDP_DEBUG
+
+#ifdef SC520_CDP_DEBUG
+#define PRINTF(fmt,args...) printf (fmt ,##args)
+#else
+#define PRINTF(fmt,args...)
+#endif
+
+
+extern int do_autoscript (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_bdinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_iminfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+/* extern int do_imls (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); */
+extern int do_coninfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_itest (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_load_serial (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_load_serial_bin (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_md (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_mm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_nm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_mw (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_cp (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_cmp (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_crc (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_base (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_loop (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_mem_mtest (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_sleep (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_run (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_imgextract (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_version (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_echo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern int do_help (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+
+
+void hw_watchdog_reset(void)
+{
+ u16 wd_state;
+
+ wd_state = readw(CFG_WATCHDIG_PIO_DATA);
+
+ if (wd_state & CFG_WATCHDOG_PIO_BIT) {
+ /* Watchdog output high - lower it*/
+ writew(CFG_WATCHDOG_PIO_BIT, CFG_WATCHDIG_PIO_CLR);
+ }
+ else {
+ /* Watchdog output low - raise it*/
+ writew(CFG_WATCHDOG_PIO_BIT, CFG_WATCHDIG_PIO_SET);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+void init_sc520_enet (void)
+{
+
+ /* Set the UARTxCTL register at it's slower,
+ * baud clock giving us a 1.8432 MHz reference
+ */
+ write_mmcr_byte(SC520_UART1CTL, 7);
+
+
+ /* enable PCI bus arbitrer */
+/* mov ebx, SYSARBCTL ; SC520 control bits for the CPU bus arbiter and the PCI bus arbiter. */
+/* mov byte ptr [ebx], 06h ; */
+ write_mmcr_byte(SC520_SYSARBCTL,0x06); /* enable concurrent mode */
+
+
+ /* ?? mov ebx, SYSARBMENB ; SC520 System Arbiter Master Enable */
+/* ?? mov word ptr [ebx], 0003h ; */
+ write_mmcr_word(SC520_SYSARBMENB, 0x0003); /* enable external grants */
+
+
+ if (CFG_SC520_HIGH_SPEED) {
+ write_mmcr_byte(SC520_CPUCTL, 0x2); /* set it to 133 MHz and write back */
+ gd->cpu_clk = 133000000;
+/* printf("## CPU Speed set to 133MHz\n"); */
+ } else {
+ write_mmcr_byte(SC520_CPUCTL, 1); /* set CPU to 100 MHz and write back cache */
+/* printf("## CPU Speed set to 100MHz\n"); */
+ gd->cpu_clk = 100000000;
+ }
+
+
+ /* wait at least one millisecond */
+ asm("movl $0x2000,%%ecx\n"
+ "wait_loop: pushl %%ecx\n"
+ "popl %%ecx\n"
+ "loop wait_loop\n": : : "ecx");
+
+ /* turn on the SDRAM write buffer */
+ write_mmcr_byte(SC520_DBCTL, 0x11);
+
+ /* turn on the cache and disable write through */
+ asm("movl %%cr0, %%eax\n"
+ "andl $0x9fffffff, %%eax\n"
+ "movl %%eax, %%cr0\n" : : : "eax");
+}
+
+/*
+ * Theory:
+ * We first set up all IRQs to be non-pci, edge triggered,
+ * when we later enumerate the pci bus and pci_sc520_fixup_irq() gets
+ * called we reallocate irqs to the pci bus with sc520_pci_set_irq()
+ * as needed. Whe choose the irqs to gram from a configurable list
+ * inside pci_sc520_fixup_irq() (If this list contains stupid irq's
+ * such as 0 thngas will not work)
+ */
+
+static void irq_init(void)
+{
+#if 0
+ /* disable global interrupt mode */
+ write_mmcr_byte(SC520_PICICR, 0x40);
+
+ /* set all irqs to edge */
+ write_mmcr_byte(SC520_MPICMODE, 0x00);
+ write_mmcr_byte(SC520_SL1PICMODE, 0x00);
+ write_mmcr_byte(SC520_SL2PICMODE, 0x00);
+
+ /* active low polarity on PIC interrupt pins,
+ * active high polarity on all other irq pins */
+/* write_mmcr_word(SC520_INTPINPOL, 0x0000); */
+
+ /* set irq number mapping */
+ write_mmcr_byte(SC520_GPTMR1MAP, SC520_IRQ_DISABLED); /* disable GP timer 1 INT */
+ write_mmcr_byte(SC520_GPTMR2MAP, SC520_IRQ_DISABLED); /* disable GP timer 2 INT */
+ write_mmcr_byte(SC520_PIT0MAP, SC520_IRQ0); /* Set PIT timer 0 INT to IRQ0 */
+ write_mmcr_byte(SC520_PIT1MAP, SC520_IRQ_DISABLED); /* disable PIT timer 1 INT */
+ write_mmcr_byte(SC520_PIT2MAP, SC520_IRQ_DISABLED); /* disable PIT timer 2 INT */
+ write_mmcr_byte(SC520_PCIINTCMAP, SC520_IRQ_DISABLED); /* disable PCI INT C */
+ write_mmcr_byte(SC520_PCIINTDMAP, SC520_IRQ_DISABLED); /* disable PCI INT D */
+ write_mmcr_byte(SC520_DMABCINTMAP, SC520_IRQ_DISABLED); /* disable DMA INT */
+ write_mmcr_byte(SC520_SSIMAP, SC520_IRQ_DISABLED); /* disable Synchronius serial INT */
+ write_mmcr_byte(SC520_WDTMAP, SC520_IRQ_DISABLED); /* disable Watchdog INT */
+ write_mmcr_byte(SC520_RTCMAP, SC520_IRQ8); /* Set RTC int to 8 */
+ write_mmcr_byte(SC520_WPVMAP, SC520_IRQ_DISABLED); /* disable write protect INT */
+ write_mmcr_byte(SC520_ICEMAP, SC520_IRQ1); /* Set ICE Debug Serielport INT to IRQ1 */
+ write_mmcr_byte(SC520_FERRMAP,SC520_IRQ13); /* Set FP error INT to IRQ13 */
+#endif
+
+/* mov ebx, UART1MAP ; UART1MAP interrupt map */
+/* mov byte ptr [ebx], 02h ; connect to Master PIC IR1 */
+/* mov ebx, GPTMR0MAP ; GP Timer 0 Interrupt Mapping */
+/* mov byte ptr [ebx], 01h ; connect to Master PIC IR0 */
+/* mov ebx, PCIINTAMAP ; PCI Interrupt A Mapping Register (INTA_INT) */
+/* mov byte ptr [ebx], 03h ; connect to Slave 1 PIC IR0 */
+/* mov ebx, PCIINTBMAP ; PCI Interrupt B Mapping Register (INTB_INT) */
+/* mov byte ptr [ebx], 04h ; connect to Slave 1 PIC IR1 */
+/* mov ebx, GP0IMAP ; GPIRQ0 interrupt map (UART_A_INT) */
+/* mov byte ptr [ebx], 07h ; connect to Slave 1 PIC IR4 */
+/* mov ebx, GP1IMAP ; GPIRQ1 interrupt map (UART_B_INT) */
+/* mov byte ptr [ebx], 08h ; connect to Slave 1 PIC IR5 */
+/* mov ebx, GP2IMAP ; GPIRQ2 interrupt map (UART_C_INT) */
+/* mov byte ptr [ebx], 09h ; connect to Slave 1 PIC IR6 */
+/* mov ebx, GP3IMAP ; GPIRQ3 interrupt map (UART_D_INT) */
+/* mov byte ptr [ebx], 0ah ; connect to Slave 1 PIC IR7 */
+/* mov ebx, GP4IMAP ; GPIRQ4 interrupt map (DPRAM1_INT) */
+/* mov byte ptr [ebx], 0bh ; connect to Master PIC IR3 */
+/* mov ebx, GP6IMAP ; GPIRQ6 interrupt map (IRIG_INT) */
+/* mov byte ptr [ebx], 0ch ; connect to Master PIC IR4 */
+/* mov ebx, GP7IMAP ; GPIRQ7 interrupt map (EXP1_INT) */
+/* mov byte ptr [ebx], 0eh ; connect to Slave 2 PIC IR1 */
+/* mov ebx, GP8IMAP ; GPIRQ8 interrupt map (EXP2_INT) */
+/* mov byte ptr [ebx], 0fh ; connect to Slave 2 PIC IR2 */
+/* mov ebx, GP9IMAP ; GPIRQ9 interrupt map (EXP3_INT) */
+/* mov byte ptr [ebx], 010h ; connect to Slave 2 PIC IR3 */
+/* mov ebx, GP10IMAP ; GPIRQ10 interrupt map (EXP4_INT or I2C_INT) */
+/* mov byte ptr [ebx], 011h ; connect to Slave 2 PIC IR4 */
+/* mov ebx, BOOTCSCTL ; BOOTCS Control Register */
+/* mov word ptr [ebx], 0033h ; 3 wait states */
+/* mov ebx, ROMCS1CTL ; ROMCS1 Control Register */
+/* mov word ptr [ebx], 0615h */
+/* mov ebx, ROMCS2CTL ; ROMCS2 Control Register */
+/* mov word ptr [ebx], 0615h */
+/* mov ebx, ADDDECCTL ; SC520 Address Decode Control Register */
+/* mov byte ptr [ebx], 02h ; Enable RTC & UART1, Disable UART2 */
+
+ write_mmcr_word(SC520_INTPINPOL, 0x0410);
+ write_mmcr_byte(SC520_UART1MAP, 0x02);
+ write_mmcr_byte(SC520_GPTMR0MAP, 0x01);
+ write_mmcr_byte(SC520_PCIINTAMAP, 0x03);
+ write_mmcr_byte(SC520_PCIINTBMAP, 0x04);
+ write_mmcr_byte(SC520_GP0IMAP, 0x07);
+ write_mmcr_byte(SC520_GP1IMAP, 0x08);
+ write_mmcr_byte(SC520_GP2IMAP, 0x09);
+ write_mmcr_byte(SC520_GP3IMAP, 0x0a);
+ write_mmcr_byte(SC520_GP4IMAP, 0x0b);
+ write_mmcr_byte(SC520_GP6IMAP, 0x0c);
+ write_mmcr_byte(SC520_GP7IMAP, 0x0e);
+ write_mmcr_byte(SC520_GP8IMAP, 0x0f);
+ write_mmcr_byte(SC520_GP9IMAP, 0x10);
+ write_mmcr_byte(SC520_GP10IMAP, 0x11);
+ write_mmcr_word(SC520_BOOTCSCTL, 0x0033);
+ write_mmcr_word(SC520_ROMCS1CTL, 0x0615);
+ write_mmcr_word(SC520_ROMCS2CTL, 0x0615);
+ write_mmcr_byte(SC520_ADDDECCTL, 0x02);
+
+/* write_mmcr_byte(SC520_GP5IMAP, SC520_IRQ5); */ /* Set GPIRQ5 (ISA IRQ5) to IRQ5 */
+/* write_mmcr_byte(SC520_GP0IMAP, SC520_IRQ11); */ /* Set GPIRQ0 (ISA IRQ11) to IRQ10 */
+/* write_mmcr_byte(SC520_UART2MAP, SC520_IRQ_DISABLED); */ /* disable internal UART2 INT */
+/* write_mmcr_word(SC520_PCIHOSTMAP, 0x11f); */ /* Map PCI hostbridge INT to NMI */
+/* write_mmcr_word(SC520_ECCMAP, 0x100); */ /* Map SDRAM ECC failure INT to NMI */
+
+}
+
+#if 0
+/* PCI stuff */
+static void pci_sc520_cdp_fixup_irq(struct pci_controller *hose, pci_dev_t dev)
+{
+ /* a configurable lists of irqs to steal
+ * when we need one (a board with more pci interrupt pins
+ * would use a larger table */
+ static int irq_list[] = {
+ CFG_FIRST_PCI_IRQ,
+ CFG_SECOND_PCI_IRQ,
+ CFG_THIRD_PCI_IRQ,
+ CFG_FORTH_PCI_IRQ
+ };
+ static int next_irq_index=0;
+
+ unsigned char tmp_pin;
+ int pin;
+
+ pci_hose_read_config_byte(hose, dev, PCI_INTERRUPT_PIN, &tmp_pin);
+ pin = (int)tmp_pin;
+
+ pin-=1; /* pci config space use 1-based numbering */
+ if (-1 == pin) {
+ return; /* device use no irq */
+ }
+
+
+ /* map device number + pin to a pin on the sc520 */
+ switch (PCI_DEV(dev)) {
+ case 20:
+ pin+=SC520_PCI_INTA;
+ break;
+
+ case 19:
+ pin+=SC520_PCI_INTB;
+ break;
+
+ case 18:
+ pin+=SC520_PCI_INTC;
+ break;
+
+ case 17:
+ pin+=SC520_PCI_INTD;
+ break;
+
+ default:
+ return;
+ }
+
+ pin&=3; /* wrap around */
+
+ if (sc520_pci_ints[pin] == -1) {
+ /* re-route one interrupt for us */
+ if (next_irq_index > 3) {
+ return;
+ }
+ if (pci_sc520_set_irq(pin, irq_list[next_irq_index])) {
+ return;
+ }
+ next_irq_index++;
+ }
+
+
+ if (-1 != sc520_pci_ints[pin]) {
+ pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE,
+ sc520_pci_ints[pin]);
+ }
+ PRINTF("fixup_irq: device %d pin %c irq %d\n",
+ PCI_DEV(dev), 'A' + pin, sc520_pci_ints[pin]);
+}
+
+static struct pci_controller sc520_cdp_hose = {
+ fixup_irq: pci_sc520_cdp_fixup_irq,
+};
+
+void pci_init_board(void)
+{
+ pci_sc520_init(&sc520_cdp_hose);
+}
+#endif
+
+#if 0
+/* set up the ISA bus timing and system address mappings */
+static void bus_init(void)
+{
+
+ /* set up the GP IO pins */
+ write_mmcr_word(SC520_PIOPFS31_16, 0xf7ff); /* set the GPIO pin function 31-16 reg */
+ write_mmcr_word(SC520_PIOPFS15_0, 0xffff); /* set the GPIO pin function 15-0 reg */
+ write_mmcr_byte(SC520_CSPFS, 0xf8); /* set the CS pin function reg */
+ write_mmcr_byte(SC520_CLKSEL, 0x70);
+
+
+ write_mmcr_byte(SC520_GPCSRT, 1); /* set the GP CS offset */
+ write_mmcr_byte(SC520_GPCSPW, 3); /* set the GP CS pulse width */
+ write_mmcr_byte(SC520_GPCSOFF, 1); /* set the GP CS offset */
+ write_mmcr_byte(SC520_GPRDW, 3); /* set the RD pulse width */
+ write_mmcr_byte(SC520_GPRDOFF, 1); /* set the GP RD offset */
+ write_mmcr_byte(SC520_GPWRW, 3); /* set the GP WR pulse width */
+ write_mmcr_byte(SC520_GPWROFF, 1); /* set the GP WR offset */
+
+ write_mmcr_word(SC520_BOOTCSCTL, 0x1823); /* set up timing of BOOTCS */
+ write_mmcr_word(SC520_ROMCS1CTL, 0x1823); /* set up timing of ROMCS1 */
+ write_mmcr_word(SC520_ROMCS2CTL, 0x1823); /* set up timing of ROMCS2 */
+
+ /* adjust the memory map:
+ * by default the first 256MB (0x00000000 - 0x0fffffff) is mapped to SDRAM
+ * and 256MB to 1G-128k (0x1000000 - 0x37ffffff) is mapped to PCI mmio
+ * we need to map 1G-128k - 1G (0x38000000 - 0x3fffffff) to CS1 */
+
+ /* SRAM = GPCS3 128k @ d0000-effff*/
+ write_mmcr_long(SC520_PAR2, 0x4e00400d);
+
+ /* IDE0 = GPCS6 1f0-1f7 */
+ write_mmcr_long(SC520_PAR3, 0x380801f0);
+
+ /* IDE1 = GPCS7 3f6 */
+ write_mmcr_long(SC520_PAR4, 0x3c0003f6);
+ /* bootcs */
+ write_mmcr_long(SC520_PAR12, 0x8bffe800);
+ /* romcs2 */
+ write_mmcr_long(SC520_PAR13, 0xcbfff000);
+ /* romcs1 */
+ write_mmcr_long(SC520_PAR14, 0xabfff800);
+ /* 680 LEDS */
+ write_mmcr_long(SC520_PAR15, 0x30000640);
+
+ write_mmcr_byte(SC520_ADDDECCTL, 0);
+
+ asm ("wbinvd\n"); /* Flush cache, req. after setting the unchached attribute ona PAR */
+}
+#endif
+
+#if 0
+/*
+ * This function should map a chunk of size bytes
+ * of the system address space to the ISA bus
+ *
+ * The function will return the memory address
+ * as seen by the host (which may very will be the
+ * same as the bus address)
+ */
+u32 isa_map_rom(u32 bus_addr, int size)
+{
+ u32 par;
+
+ PRINTF("isa_map_rom asked to map %d bytes at %x\n",
+ size, bus_addr);
+
+ par = size;
+ if (par < 0x80000) {
+ par = 0x80000;
+ }
+ par >>= 12;
+ par--;
+ par&=0x7f;
+ par <<= 18;
+ par |= (bus_addr>>12);
+ par |= 0x50000000;
+
+ PRINTF ("setting PAR11 to %x\n", par);
+
+ /* Map rom 0x10000 with PAR1 */
+ write_mmcr_long(SC520_PAR11, par);
+
+ return bus_addr;
+}
+
+/*
+ * this function removed any mapping created
+ * with pci_get_rom_window()
+ */
+void isa_unmap_rom(u32 addr)
+{
+ PRINTF("isa_unmap_rom asked to unmap %x", addr);
+ if ((addr>>12) == (read_mmcr_long(SC520_PAR11)&0x3ffff)) {
+ write_mmcr_long(SC520_PAR11, 0);
+ PRINTF(" done\n");
+ return;
+ }
+ PRINTF(" not ours\n");
+}
+#endif
+
+#ifdef CONFIG_PCI
+#define PCI_ROM_TEMP_SPACE 0x10000
+/*
+ * This function should map a chunk of size bytes
+ * of the system address space to the PCI bus,
+ * suitable to map PCI ROMS (bus address < 16M)
+ * the function will return the host memory address
+ * which should be converted into a bus address
+ * before used to configure the PCI rom address
+ * decoder
+ */
+u32 pci_get_rom_window(struct pci_controller *hose, int size)
+{
+ u32 par;
+
+ par = size;
+ if (par < 0x80000) {
+ par = 0x80000;
+ }
+ par >>= 16;
+ par--;
+ par&=0x7ff;
+ par <<= 14;
+ par |= (PCI_ROM_TEMP_SPACE>>16);
+ par |= 0x72000000;
+
+ PRINTF ("setting PAR1 to %x\n", par);
+
+ /* Map rom 0x10000 with PAR1 */
+ write_mmcr_long(SC520_PAR1, par);
+
+ return PCI_ROM_TEMP_SPACE;
+}
+
+/*
+ * this function removed any mapping created
+ * with pci_get_rom_window()
+ */
+void pci_remove_rom_window(struct pci_controller *hose, u32 addr)
+{
+ PRINTF("pci_remove_rom_window: %x", addr);
+ if (addr == PCI_ROM_TEMP_SPACE) {
+ write_mmcr_long(SC520_PAR1, 0);
+ PRINTF(" done\n");
+ return;
+ }
+ PRINTF(" not ours\n");
+
+}
+
+/*
+ * This function is called in order to provide acces to the
+ * legacy video I/O ports on the PCI bus.
+ * After this function accesses to I/O ports 0x3b0-0x3bb and
+ * 0x3c0-0x3df shuld result in transactions on the PCI bus.
+ *
+ */
+int pci_enable_legacy_video_ports(struct pci_controller *hose)
+{
+ /* Map video memory to 0xa0000*/
+ write_mmcr_long(SC520_PAR0, 0x7200400a);
+
+ /* forward all I/O accesses to PCI */
+ write_mmcr_byte(SC520_ADDDECCTL,
+ read_mmcr_byte(SC520_ADDDECCTL) | IO_HOLE_DEST_PCI);
+
+
+ /* so we map away all io ports to pci (only way to access pci io
+ * below 0x400. But then we have to map back the portions that we dont
+ * use so that the generate cycles on the GPIO bus where the sio and
+ * ISA slots are connected, this requre the use of several PAR registers
+ */
+
+ /* bring 0x100 - 0x1ef back to ISA using PAR5 */
+ write_mmcr_long(SC520_PAR5, 0x30ef0100);
+
+ /* IDE use 1f0-1f7 */
+
+ /* bring 0x1f8 - 0x2f7 back to ISA using PAR6 */
+ write_mmcr_long(SC520_PAR6, 0x30ff01f8);
+
+ /* com2 use 2f8-2ff */
+
+ /* bring 0x300 - 0x3af back to ISA using PAR7 */
+ write_mmcr_long(SC520_PAR7, 0x30af0300);
+
+ /* vga use 3b0-3bb */
+
+ /* bring 0x3bc - 0x3bf back to ISA using PAR8 */
+ write_mmcr_long(SC520_PAR8, 0x300303bc);
+
+ /* vga use 3c0-3df */
+
+ /* bring 0x3e0 - 0x3f5 back to ISA using PAR9 */
+ write_mmcr_long(SC520_PAR9, 0x301503e0);
+
+ /* ide use 3f6 */
+
+ /* bring 0x3f7 back to ISA using PAR10 */
+ write_mmcr_long(SC520_PAR10, 0x300003f7);
+
+ /* com1 use 3f8-3ff */
+
+ return 0;
+}
+#endif
+
+/*
+ * Miscelaneous platform dependent initializations
+ */
+
+int board_init(void)
+{
+/* init_sc520(); */
+
+ init_sc520_enet();
+
+ /* bus_init(); */
+
+ irq_init();
+
+ /* max drive current on SDRAM */
+/* write_mmcr_word(SC520_DSCTL, 0x0100); */
+
+ /* enter debug mode after next reset (only if jumper is also set) */
+/* write_mmcr_byte(SC520_RESCFG, 0x00); */
+
+ /* Crystal is 33.000MHz */
+ gd->bus_clk = 33000000;
+
+ return 0;
+}
+
+int dram_init(void)
+{
+ init_sc520_dram();
+ return 0;
+}
+
+void show_boot_progress(int val)
+{
+ uchar led_mask;
+
+ led_mask = 0x00;
+
+ if (val < 0)
+ led_mask |= LED_ERR_BITMASK;
+
+ led_mask |= (uchar)(val & 0x001f);
+ outb(led_mask, LED_LATCH_ADDRESS);
+}
+
+
+int last_stage_init(void)
+{
+ int minor;
+ int major;
+
+ major = minor = 0;
+
+ printf("Serck Controls eNET\n");
+ printf("last_stage_init() at %08lx\n", (ulong)last_stage_init);
+
+ printf("autoscript => do_autoscript() @ 0x%08lx\n", (ulong)do_autoscript);
+ printf("bdinfo => do_bdinfo() @ 0x%08lx\n", (ulong)do_bdinfo);
+ printf("go => do_go() @ 0x%08lx\n", (ulong)do_go);
+ printf("reset => do_reset() @ 0x%08lx\n", (ulong)do_reset);
+ printf("bootm => do_bootm() @ 0x%08lx\n", (ulong)do_bootm);
+ printf("boot => do_bootd() @ 0x%08lx\n", (ulong)do_bootd);
+ printf("bootd => do_bootd() @ 0x%08lx\n", (ulong)do_bootd);
+ printf("iminfo => do_iminfo() @ 0x%08lx\n", (ulong)do_iminfo);
+/* printf("imls => do_imls() @ 0x%08lx\n", (ulong)do_imls); */
+ printf("imls => do_imls() @ <undefined>\n");
+ printf("coninfo => do_coninfo() @ 0x%08lx\n", (ulong)do_coninfo);
+ printf("itest => do_itest() @ 0x%08lx\n", (ulong)do_itest);
+ printf("loads => do_load_serial() @ 0x%08lx\n", (ulong)do_load_serial);
+ printf("loadb => do_load_serial_bin() @ 0x%08lx\n", (ulong)do_load_serial_bin);
+ printf("loady => do_load_serial_bin() @ 0x%08lx\n", (ulong)do_load_serial_bin);
+ printf("md => do_mem_md() @ 0x%08lx\n", (ulong)do_mem_md);
+ printf("mm => do_mem_mm() @ 0x%08lx\n", (ulong)do_mem_mm);
+ printf("nm => do_mem_nm() @ 0x%08lx\n", (ulong)do_mem_nm);
+ printf("mw => do_mem_mw() @ 0x%08lx\n", (ulong)do_mem_mw);
+ printf("cp => do_mem_cp() @ 0x%08lx\n", (ulong)do_mem_cp);
+ printf("cmp => do_mem_cmp() @ 0x%08lx\n", (ulong)do_mem_cmp);
+ printf("crc32 => do_mem_crc() @ 0x%08lx\n", (ulong)do_mem_crc);
+ printf("base => do_mem_base() @ 0x%08lx\n", (ulong)do_mem_base);
+ printf("loop => do_mem_loop() @ 0x%08lx\n", (ulong)do_mem_loop);
+ printf("mtest => do_mem_mtest() @ 0x%08lx\n", (ulong)do_mem_mtest);
+ printf("sleep => do_sleep() @ 0x%08lx\n", (ulong)do_sleep);
+ printf("printenv => do_printenv() @ 0x%08lx\n", (ulong)do_printenv);
+ printf("setenv => do_setenv() @ 0x%08lx\n", (ulong)do_setenv);
+ printf("saveenv => do_saveenv() @ 0x%08lx\n", (ulong)do_saveenv);
+ printf("run => do_run() @ 0x%08lx\n", (ulong)do_run);
+ printf("imxtract => do_imgextract() @ 0x%08lx\n", (ulong)do_imgextract);
+ printf("version => do_version() @ 0x%08lx\n", (ulong)do_version);
+ printf("echo => do_echo() @ 0x%08lx\n", (ulong)do_echo);
+ printf("help => do_help() @ 0x%08lx\n", (ulong)do_help);
+ printf("? => do_help() @ 0x%08lx\n", (ulong)do_help);
+
+
+ return 0;
+}
+
+
diff --git a/board/eNET/eNET_start.S b/board/eNET/eNET_start.S
new file mode 100644
index 0000000..124660c
--- /dev/null
+++ b/board/eNET/eNET_start.S
@@ -0,0 +1,224 @@
+/*
+ * (C) Copyright 2002
+ * Daniel Engström, Omicron Ceti AB <daniel(a)omicron.se>.
+ *
+ * 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 <asm/ic/sc520_defs.h>
+
+#include "hardware_defs.h"
+
+sc520_cdp_registers:
+/*
+ * This is the MMCR configuration array - only change the values here if
+ * you are absolutely sure you know what you are doing
+ */
+/* size offset value */
+.word 0x0001 ; .word 0x0040 ; .long 0x00000000 /* SDRAM buffer control */
+.word 0x0001 ; .word 0x0c08 ; .long 0x00000001 /* GP Chip Select Recovery Time */
+.word 0x0001 ; .word 0x0c09 ; .long 0x00000007 /* GP Chip Select Pulse Width */
+.word 0x0001 ; .word 0x0c0a ; .long 0x00000000 /* GP Chip Select Offset */
+.word 0x0001 ; .word 0x0c0b ; .long 0x00000005 /* GP Read pulse width */
+.word 0x0001 ; .word 0x0c0c ; .long 0x00000001 /* GP Read offset */
+.word 0x0001 ; .word 0x0c0d ; .long 0x00000005 /* GP Write pulse width */
+.word 0x0001 ; .word 0x0c0e ; .long 0x00000001 /* GP Write offset */
+.word 0x0002 ; .word 0x0c30 ; .long 0x00000630 /* PIO15_PIO0 Data */
+.word 0x0002 ; .word 0x0c32 ; .long 0x00002000 /* PIO31_PIO16 Data */
+.word 0x0002 ; .word 0x0c2c ; .long 0x00002000 /* GPIO directionreg */
+.word 0x0002 ; .word 0x0c2a ; .long 0x000087b5 /* GPIO directionreg */
+.word 0x0002 ; .word 0x0c22 ; .long 0x00000dfe /* GPIO pin function 31-16 reg */
+.word 0x0002 ; .word 0x0c20 ; .long 0x0000200a /* GPIO pin function 15-0 reg */
+.word 0x0001 ; .word 0x0c24 ; .long 0x000000F8 /* Chip Select Pin Function Select */
+.word 0x0004 ; .word 0x0090 ; .long 0x200713f8 /* PAR2 - Uart A (GPCS0, 0x013f8, 8 Bytes) */
+.word 0x0004 ; .word 0x0094 ; .long 0x2c0712f8 /* PAR3 - Uart B (GPCS3, 0x012f8, 8 Bytes) */
+.word 0x0004 ; .word 0x0098 ; .long 0x300711f8 /* PAR4 - Uart C (GPCS4, 0x011f8, 8 Bytes) */
+.word 0x0004 ; .word 0x009c ; .long 0x340710f8 /* PAR5 - Uart D (GPCS5, 0x010f8, 8 Bytes) */
+.word 0x0004 ; .word 0x00a0 ; .long 0xe3ffc000 /* PAR6 - SDRAM (0x00000000, 128MB) */
+.word 0x0004 ; .word 0x00a4 ; .long 0xaa3fd000 /* PAR7 - StrataFlash (ROMCS1, 0x10000000, 16MB) */
+.word 0x0004 ; .word 0x00a8 ; .long 0xca3fd100 /* PAR8 - StrataFlash (ROMCS2, 0x11000000, 16MB) */
+.word 0x0004 ; .word 0x00ac ; .long 0x4203d900 /* PAR9 - SRAM (GPCS0, 0x19000000, 1MB) */
+.word 0x0004 ; .word 0x00b0 ; .long 0x4e03d910 /* PAR10 -SRAM (GPCS3, 0x19100000, 1MB) */
+.word 0x0004 ; .word 0x00b4 ; .long 0x50018100 /* PAR11 -DP-RAM (GPCS4, 0x18100000, 4kB) */
+.word 0x0004 ; .word 0x00b8 ; .long 0x54020000 /* PAR12 -CFLASH1 (0x200000000, 4kB) */
+.word 0x0004 ; .word 0x00bc ; .long 0x5c020001 /* PAR13 -CFLASH2 (0x200010000, 4kB) */
+.word 0x0004 ; .word 0x00c0 ; .long 0x8bfff800 /* PAR14 - BOOTCS at 0x18000000 */
+.word 0x0004 ; .word 0x00c4 ; .long 0x38201000 /* PAR15 - LEDs etc (GPCS6, 0x1000, 20 Bytes */
+.word 0x0002 ; .word 0x0cb0 ; .long 0x00003333 /* Activate watchdog status register step 1 */
+.word 0x0002 ; .word 0x0cb0 ; .long 0x0000cccc /* Activate watchdog status register step 2 */
+.word 0x0002 ; .word 0x0cb0 ; .long 0x00000000 /* Disable Watchdog */
+.word 0x0000 ; .word 0x0000 ; .long 0x00000000 /* EOT */
+
+/* board early intialization */
+.globl early_board_init
+early_board_init:
+ movl $sc520_cdp_registers,%esi
+init_loop:
+ movl $0xfffef000,%edi /* MMCR base to edi */
+ movw (%esi), %bx /* load sizer to bx */
+ cmpw $0, %bx /* if sie is 0 we're done */
+ je done
+ xorl %edx,%edx
+ movw 2(%esi), %dx /* load MMCR offset to dx */
+ addl %edx, %edi /* add offset to base in edi */
+ movl 4(%esi), %eax /* load value in eax */
+ cmpw $1, %bx
+ je byte /* byte op? */
+ cmpw $2, %bx
+ je word /* word op? */
+ movl %eax, (%edi) /* must be long, then */
+ jmp next
+byte: movb %al,(%edi)
+ jmp next
+word: movw %ax,(%edi)
+next: addl $8, %esi /* advance esi */
+ jmp init_loop
+done:
+ /* The LEDs are now available */
+ movw $LED_LATCH_ADDRESS, %dx
+ movb $0x0f, %al
+ outb %al, %dx
+
+ /* Initialize 8259 master */
+# mov al, 78h ; Use int vectors 78h to 7fh.
+# mov dx, MPICICW2
+# out dx, al
+# jmp $+2
+# mov al, 24h ; IR2 and IR5 has a slave PIC.
+# out dx, al ; MPICICW3
+# jmp $+2
+# mov al, 01h ; Master, 8086 mode.
+# out dx, al ; MPICICW4
+# jmp $+2
+# mov al, 0ffh ; Mask interrupt of master PIC.
+# out dx, al ; MPICINTMSK
+# jmp $+2
+ movb $0x11, %al
+ movw $MPICICW1, %dx
+ outb %al, %dx
+ jmp .+2
+ movb $0x78, %al
+ movw $MPICICW2, %dx
+ outb %al, %dx
+ jmp .+2
+ movb $0x24, %al
+ out %al, %dx
+ jmp .+2
+ movb $0x01, %al
+ outb %al, %dx
+ jmp .+2
+ movb $0xff, %al
+ outb %al, %dx
+ jmp .+2
+
+ /* Initialize 8259 slave1 */
+# mov al, 11h ; Edge, slave 1, ICW4.
+# mov dx, S1PICICW1
+# out dx, al
+# jmp $+2
+# mov al, 70h ; Use int vectors 70h to 77h.
+# mov dx, S1PICICW2
+# out dx, al
+# jmp $+2
+# mov al, 02h ; Slave ID = 2.
+# out dx, al ; S1PICICW3
+# jmp $+2
+# mov al, 01h ; Enable device.
+# out dx, al ; S1PICICW4
+# jmp $+2
+# mov al, 0ffh ; Mask interrupt of slave PIC.
+# out dx, al ; S1PICINTMSK
+# jmp $+2
+ movb $0x11, %al
+ movw $S1PICICW1, %dx
+ outb %al, %dx
+ jmp .+2
+ movb $0x70, %al
+ movw $S1PICICW2, %dx
+ outb %al, %dx
+ jmp .+2
+ movb $0x02, %al
+ outb %al, %dx
+ jmp .+2
+ movb $0x01, %al
+ outb %al, %dx
+ jmp .+2
+ movb $0xff, %al
+ outb %al, %dx
+ jmp .+2
+
+ /* Initialize 8259 slave 2 */
+# mov al, 11h ; Edge, slave 2, ICW4.
+# mov dx, S2PICICW1
+# out dx, al
+# jmp $+2
+# mov al, 68h ; Use int vectors 68h to 6fh.
+# mov dx, S2PICICW2
+# out dx, al
+# jmp $+2
+# mov al, 05h ; Slave ID = 5.
+# out dx, al ; S2PICICW3
+# jmp $+2
+# mov al, 01h ; Enable Device.
+# out dx, al ; S2PICICW4
+# jmp $+2
+# mov al, 0ffh ; Mask interrupt of slave 2 PIC.
+# out dx, al ; S2PICINTMSK
+# jmp $+2
+ movb $0x11, %al
+ movw $S2PICICW1, %dx
+ outb %al, %dx
+ jmp .+2
+ movb $0x68, %al
+ movw $S2PICICW2, %dx
+ outb %al, %dx
+ jmp .+2
+ movb $0x05, %al
+ outb %al, %dx
+ jmp .+2
+ movb $0x01, %al
+ outb %al, %dx
+ jmp .+2
+ movb $0xff, %al
+ outb %al, %dx
+ jmp .+2
+
+
+ jmp *%ebp /* return to caller */
+
+.globl show_boot_progress_asm
+show_boot_progress_asm:
+
+ movb %al, %dl /* Create Working Copy */
+ andb $0x80, %dl /* Mask in only Error bit */
+ shrb $0x02, %dl /* Shift Error bit to Error LED */
+ andb $0x0f, %al /* Mask out 'Error' bit */
+ orb %dl, %al /* Mask in ERR LED */
+ movw $LED_LATCH_ADDRESS, %dx
+ outb %al, %dx
+
+ jmp *%ebp /* return to caller */
+
+
+.globl cpu_halt_asm
+cpu_halt_asm:
+ movb $0x0f, %al
+ movw $LED_LATCH_ADDRESS, %dx
+ outb %al, %dx
+ hlt
+ jmp cpu_halt_asm
diff --git a/board/eNET/eNET_start16.S b/board/eNET/eNET_start16.S
new file mode 100644
index 0000000..e0d2ebb
--- /dev/null
+++ b/board/eNET/eNET_start16.S
@@ -0,0 +1,91 @@
+/*
+ * (C) Copyright 2002
+ * Daniel Engström, Omicron Ceti AB <daniel(a)omicron.se>.
+ *
+ * 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
+ */
+
+/*
+ * 16bit initialization code.
+ * This code have to map the area of the boot flash
+ * that is used by U-boot to its final destination.
+ */
+
+#include "hardware.h"
+
+.text
+.section .start16, "ax"
+.code16
+.globl board_init16
+board_init16:
+ /* Alias MMCR to 0xdf000 */
+ movw $0xfffc, %dx
+ movl $0x800df0cb, %eax
+ outl %eax, %dx
+
+ /* Set ds to point to MMCR alias */
+ movw $0xdf00, %ax
+ movw %ax, %ds
+
+ /* Map the entire flash at 0x38000000
+ * (with BOOTCS and PAR14, use 0xabfff800 for ROMCS1) */
+ movl $0xc0, %edi
+ movl $0x8bfff800, %eax
+ movl %eax, (%di)
+
+ /* Disable SDRAM write buffer */
+ movw $0x40,%di
+ xorw %ax,%ax
+ movb %al, (%di)
+
+#; turn off the cache
+# mov eax, cr0
+# or eax,060000000h
+# mov cr0, eax
+# wbinvd ; flush the cache
+
+
+ /* Disabe MMCR alias */
+ movw $0xfffc, %dx
+ movl $0x000000cb, %eax
+ outl %eax, %dx
+
+ /* the return address is stored in bp */
+ jmp *%bp
+
+.section .bios, "ax"
+.code16
+.globl realmode_reset
+realmode_reset:
+ /* Alias MMCR to 0xdf000 */
+ movw $0xfffc, %dx
+ movl $0x800df0cb, %eax
+ outl %eax, %dx
+
+ /* Set ds to point to MMCR alias */
+ movw $0xdf00, %ax
+ movw %ax, %ds
+
+ /* issue software reset thorugh MMCR */
+ movl $0xd72, %edi
+ movb $0x01, %al
+ movb %al, (%di)
+
+1: hlt
+ jmp 1
diff --git a/board/eNET/flash.c b/board/eNET/flash.c
new file mode 100644
index 0000000..168477b
--- /dev/null
+++ b/board/eNET/flash.c
@@ -0,0 +1,641 @@
+/*
+ * (C) Copyright 2002, 2003
+ * Daniel Engström, Omicron Ceti AB, daniel(a)omicron.se
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu(a)sysgo.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 <asm/io.h>
+#include <pci.h>
+#include <asm/ic/sc520.h>
+
+#define PROBE_BUFFER_SIZE 1024
+static unsigned char buffer[PROBE_BUFFER_SIZE];
+
+#define SC520_MAX_FLASH_BANKS 3
+#define SC520_FLASH_BANK0_BASE 0x38000000 /* BOOTCS */
+#define SC520_FLASH_BANK1_BASE 0x10ffffff /* ROMCS0 */
+#define SC520_FLASH_BANK2_BASE 0x11ffffff /* ROMCS1 */
+#define SC520_FLASH_BANKSIZE 0x1000000
+
+#define AMD29LV016B_SIZE 0x80000
+#define AMD29LV016B_SECTORS 32
+
+flash_info_t flash_info[SC520_MAX_FLASH_BANKS];
+
+#define READY 1
+#define ERR 2
+#define TMO 4
+
+/*-----------------------------------------------------------------------
+ */
+
+
+static u32 _probe_flash(u32 addr, u32 bw, int il)
+{
+ u32 result=0;
+
+ /* First do an unlock cycle for the benefit of
+ * devices that need it */
+
+ switch (bw) {
+
+ case 1:
+ *(volatile u8*)(addr+0x5555) = 0xaa;
+ *(volatile u8*)(addr+0x2aaa) = 0x55;
+ *(volatile u8*)(addr+0x5555) = 0x90;
+
+ /* Read vendor */
+ result = *(volatile u8*)addr;
+ result <<= 16;
+
+ /* Read device */
+ result |= *(volatile u8*)(addr+2);
+
+ /* Return device to data mode */
+ *(volatile u8*)addr = 0xff;
+ *(volatile u8*)(addr+0x5555), 0xf0;
+ break;
+
+ case 2:
+ *(volatile u16*)(addr+0xaaaa) = 0xaaaa;
+ *(volatile u16*)(addr+0x5554) = 0x5555;
+
+ /* Issue identification command */
+ if (il == 2) {
+ *(volatile u16*)(addr+0xaaaa) = 0x9090;
+
+ /* Read vendor */
+ result = *(volatile u8*)addr;
+ result <<= 16;
+
+ /* Read device */
+ result |= *(volatile u8*)(addr+2);
+
+ /* Return device to data mode */
+ *(volatile u16*)addr = 0xffff;
+ *(volatile u16*)(addr+0xaaaa), 0xf0f0;
+
+ } else {
+ *(volatile u8*)(addr+0xaaaa) = 0x90;
+ /* Read vendor */
+ result = *(volatile u16*)addr;
+ result <<= 16;
+
+ /* Read device */
+ result |= *(volatile u16*)(addr+2);
+
+ /* Return device to data mode */
+ *(volatile u8*)addr = 0xff;
+ *(volatile u8*)(addr+0xaaaa), 0xf0;
+ }
+
+ break;
+
+ case 4:
+ *(volatile u32*)(addr+0x5554) = 0xaaaaaaaa;
+ *(volatile u32*)(addr+0xaaa8) = 0x55555555;
+
+ switch (il) {
+ case 1:
+ /* Issue identification command */
+ *(volatile u8*)(addr+0x5554) = 0x90;
+
+ /* Read vendor */
+ result = *(volatile u16*)addr;
+ result <<= 16;
+
+ /* Read device */
+ result |= *(volatile u16*)(addr+4);
+
+ /* Return device to data mode */
+ *(volatile u8*)addr = 0xff;
+ *(volatile u8*)(addr+0x5554), 0xf0;
+ break;
+
+ case 2:
+ /* Issue identification command */
+ *(volatile u32*)(addr + 0x5554) = 0x00900090;
+
+ /* Read vendor */
+ result = *(volatile u16*)addr;
+ result <<= 16;
+
+ /* Read device */
+ result |= *(volatile u16*)(addr+4);
+
+ /* Return device to data mode */
+ *(volatile u32*)addr = 0x00ff00ff;
+ *(volatile u32*)(addr+0x5554), 0x00f000f0;
+ break;
+
+ case 4:
+ /* Issue identification command */
+ *(volatile u32*)(addr+0x5554) = 0x90909090;
+
+ /* Read vendor */
+ result = *(volatile u8*)addr;
+ result <<= 16;
+
+ /* Read device */
+ result |= *(volatile u8*)(addr+4);
+
+ /* Return device to data mode */
+ *(volatile u32*)addr = 0xffffffff;
+ *(volatile u32*)(addr+0x5554), 0xf0f0f0f0;
+ break;
+ }
+ break;
+ }
+
+
+ return result;
+}
+
+extern int _probe_flash_end;
+asm ("_probe_flash_end:\n"
+ ".long 0\n");
+
+static int identify_flash(unsigned address, int width)
+{
+ int is;
+ int device;
+ int vendor;
+ int size;
+ unsigned res;
+
+ u32 (*_probe_flash_ptr)(u32 a, u32 bw, int il);
+
+ size = (unsigned)&_probe_flash_end - (unsigned)_probe_flash;
+
+ if (size > PROBE_BUFFER_SIZE) {
+ printf("_probe_flash() routine too large (%d) %p - %p\n",
+ size, &_probe_flash_end, _probe_flash);
+ return 0;
+ }
+
+ memcpy(buffer, _probe_flash, size);
+ _probe_flash_ptr = (void*)buffer;
+
+ is = disable_interrupts();
+ res = _probe_flash_ptr(address, width, 1);
+ if (is) {
+ enable_interrupts();
+ }
+
+
+ vendor = res >> 16;
+ device = res & 0xffff;
+
+
+ return res;
+}
+
+ulong flash_init(void)
+{
+ int i, j;
+ ulong size = 0;
+
+ printf("flash_init ()");
+
+ printf("Test %d Test %08x\n", 2, 1024);
+
+ for (i = 0; i < SC520_MAX_FLASH_BANKS; i++) {
+ unsigned id;
+ ulong flashbase = 0;
+ int sectsize = 0;
+
+ memset(flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
+ switch (i) {
+ case 0:
+ flashbase = SC520_FLASH_BANK0_BASE;
+ break;
+ case 1:
+ flashbase = SC520_FLASH_BANK1_BASE;
+ break;
+ case 2:
+ flashbase = SC520_FLASH_BANK2_BASE;
+ break;
+ default:
+ panic("configured too many flash banks!\n");
+ }
+
+ id = identify_flash(flashbase, 4);
+ switch (id & 0x00ff00ff) {
+ case 0x000100c8:
+ /* 29LV016B/29LV017B */
+ flash_info[i].flash_id =
+ (AMD_MANUFACT & FLASH_VENDMASK) |
+ (AMD_ID_LV016B & FLASH_TYPEMASK);
+
+ flash_info[i].size = AMD29LV016B_SIZE*4;
+ flash_info[i].sector_count = AMD29LV016B_SECTORS;
+ sectsize = (AMD29LV016B_SIZE*4)/AMD29LV016B_SECTORS;
+ printf("Bank %d: 4 x AMD 29LV017B\n", i);
+ break;
+
+
+ default:
+ printf("Bank %d have unknown flash %08x\n", i, id);
+ flash_info[i].flash_id = FLASH_UNKNOWN;
+ continue;
+ }
+
+ for (j = 0; j < flash_info[i].sector_count; j++) {
+ flash_info[i].start[j] = flashbase + j * sectsize;
+ }
+ size += flash_info[i].size;
+
+ flash_protect(FLAG_PROTECT_CLEAR,
+ flash_info[i].start[0],
+ flash_info[i].start[0] + flash_info[i].size - 1,
+ &flash_info[i]);
+ }
+
+ /*
+ * Protect monitor and environment sectors
+ */
+ flash_protect(FLAG_PROTECT_SET,
+ i386boot_start,
+ i386boot_end,
+ &flash_info[0]);
+#ifdef CFG_ENV_ADDR
+ flash_protect(FLAG_PROTECT_SET,
+ CFG_ENV_ADDR,
+ CFG_ENV_ADDR + CFG_ENV_SIZE - 1,
+ &flash_info[0]);
+#endif
+ return size;
+}
+
+/*-----------------------------------------------------------------------
+ */
+void flash_print_info(flash_info_t *info)
+{
+ int i;
+
+ switch (info->flash_id & FLASH_VENDMASK) {
+
+ case (AMD_MANUFACT & FLASH_VENDMASK):
+ printf("AMD: ");
+ switch (info->flash_id & FLASH_TYPEMASK) {
+ case (AMD_ID_LV016B & FLASH_TYPEMASK):
+ printf("4x AMD29LV017B (4x16Mbit)\n");
+ break;
+ default:
+ printf("Unknown Chip Type\n");
+ goto done;
+ break;
+ }
+
+ break;
+ default:
+ printf("Unknown Vendor ");
+ break;
+ }
+
+
+ printf(" Size: %ld MB in %d Sectors\n",
+ info->size >> 20, info->sector_count);
+
+ printf(" Sector Start Addresses:");
+ for (i = 0; i < info->sector_count; i++) {
+ if ((i % 5) == 0) {
+ printf ("\n ");
+ }
+ printf (" %08lX%s", info->start[i],
+ info->protect[i] ? " (RO)" : " ");
+ }
+ printf ("\n");
+
+done: ;
+}
+
+/*-----------------------------------------------------------------------
+ */
+
+/* this needs to be inlined, the SWTMRMMILLI register is reset by each read */
+#define __udelay(delay) \
+{ \
+ unsigned micro; \
+ unsigned milli=0; \
+ \
+ micro = *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI); \
+ \
+ for (;;) { \
+ \
+ milli += *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI); \
+ micro = *(volatile u16*)(0xfffef000+SC520_SWTMRMICRO); \
+ \
+ if ((delay) <= (micro + (milli * 1000))) { \
+ break; \
+ } \
+ } \
+} while (0)
+
+static u32 _amd_erase_flash(u32 addr, u32 sector)
+{
+ unsigned elapsed;
+
+ /* Issue erase */
+ *(volatile u32*)(addr + 0x5554) = 0xAAAAAAAA;
+ *(volatile u32*)(addr + 0xaaa8) = 0x55555555;
+ *(volatile u32*)(addr + 0x5554) = 0x80808080;
+ /* And one unlock */
+ *(volatile u32*)(addr + 0x5554) = 0xAAAAAAAA;
+ *(volatile u32*)(addr + 0xaaa8) = 0x55555555;
+ /* Sector erase command comes last */
+ *(volatile u32*)(addr + sector) = 0x30303030;
+
+ elapsed = *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI); /* dummy read */
+ elapsed = 0;
+ __udelay(50);
+ while (((*(volatile u32*)(addr + sector)) & 0x80808080) != 0x80808080) {
+
+ elapsed += *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI);
+ if (elapsed > ((CFG_FLASH_ERASE_TOUT/CFG_HZ) * 1000)) {
+ *(volatile u32*)(addr) = 0xf0f0f0f0;
+ return 1;
+ }
+ }
+
+ *(volatile u32*)(addr) = 0xf0f0f0f0;
+
+ return 0;
+}
+
+extern int _amd_erase_flash_end;
+asm ("_amd_erase_flash_end:\n"
+ ".long 0\n");
+
+int flash_erase(flash_info_t *info, int s_first, int s_last)
+{
+ u32 (*_erase_flash_ptr)(u32 a, u32 so);
+ int prot;
+ int sect;
+ unsigned size;
+
+ if ((s_first < 0) || (s_first > s_last)) {
+ if (info->flash_id == FLASH_UNKNOWN) {
+ printf("- missing\n");
+ } else {
+ printf("- no sectors to erase\n");
+ }
+ return 1;
+ }
+
+ if ((info->flash_id & FLASH_VENDMASK) == (AMD_MANUFACT & FLASH_VENDMASK)) {
+ size = (unsigned)&_amd_erase_flash_end - (unsigned)_amd_erase_flash;
+
+ if (size > PROBE_BUFFER_SIZE) {
+ printf("_amd_erase_flash() routine too large (%d) %p - %p\n",
+ size, &_amd_erase_flash_end, _amd_erase_flash);
+ return 0;
+ }
+
+ memcpy(buffer, _amd_erase_flash, size);
+ _erase_flash_ptr = (void*)buffer;
+
+ } else {
+ printf ("Can't erase unknown flash type - aborted\n");
+ return 1;
+ }
+
+ prot = 0;
+ for (sect=s_first; sect<=s_last; ++sect) {
+ if (info->protect[sect]) {
+ prot++;
+ }
+ }
+
+ if (prot) {
+ printf ("- Warning: %d protected sectors will not be erased!\n", prot);
+ } else {
+ printf ("\n");
+ }
+
+
+ /* Start erase on unprotected sectors */
+ for (sect = s_first; sect<=s_last; sect++) {
+
+ if (info->protect[sect] == 0) { /* not protected */
+ int res;
+ int flag;
+
+ /* Disable interrupts which might cause a timeout here */
+ flag = disable_interrupts();
+
+ res = _erase_flash_ptr(info->start[0], info->start[sect]-info->start[0]);
+
+ /* re-enable interrupts if necessary */
+ if (flag) {
+ enable_interrupts();
+ }
+
+
+ if (res) {
+ printf("Erase timed out, sector %d\n", sect);
+ return res;
+ }
+
+ putc('.');
+ }
+ }
+
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------
+ * Write a word to Flash, returns:
+ * 0 - OK
+ * 1 - write timeout
+ * 2 - Flash not erased
+ */
+static int _amd_write_word(unsigned start, unsigned dest, unsigned data)
+{
+ volatile u32 *addr2 = (u32*)start;
+ volatile u32 *dest2 = (u32*)dest;
+ volatile u32 *data2 = (u32*)&data;
+ unsigned elapsed;
+
+ /* Check if Flash is (sufficiently) erased */
+ if ((*((volatile u32*)dest) & (u32)data) != (u32)data) {
+ return 2;
+ }
+
+ addr2[0x5554] = 0xAAAAAAAA;
+ addr2[0xaaa8] = 0x55555555;
+ addr2[0x5554] = 0xA0A0A0A0;
+
+ dest2[0] = data;
+
+ elapsed = *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI); /* dummy read */
+ elapsed = 0;
+
+ /* data polling for D7 */
+ while ((dest2[0] & 0x80808080) != (data2[0] & 0x80808080)) {
+ elapsed += *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI);
+ if (elapsed > ((CFG_FLASH_WRITE_TOUT/CFG_HZ) * 1000)) {
+ addr2[0] = 0xf0f0f0f0;
+ return 1;
+ }
+ }
+
+
+ addr2[0] = 0xf0f0f0f0;
+
+ return 0;
+}
+
+extern int _amd_write_word_end;
+asm ("_amd_write_word_end:\n"
+ ".long 0\n");
+
+
+/*-----------------------------------------------------------------------
+ * Copy memory to flash, returns:
+ * 0 - OK
+ * 1 - write timeout
+ * 2 - Flash not erased
+ * 3 - Unsupported flash type
+ */
+
+int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt)
+{
+ ulong cp, wp, data;
+ int i, l, rc;
+ int flag;
+ u32 (*_write_word_ptr)(unsigned start, unsigned dest, unsigned data);
+ unsigned size;
+
+ if ((info->flash_id & FLASH_VENDMASK) == (AMD_MANUFACT & FLASH_VENDMASK)) {
+ size = (unsigned)&_amd_write_word_end - (unsigned)_amd_write_word;
+
+ if (size > PROBE_BUFFER_SIZE) {
+ printf("_amd_write_word() routine too large (%d) %p - %p\n",
+ size, &_amd_write_word_end, _amd_write_word);
+ return 0;
+ }
+
+ memcpy(buffer, _amd_write_word, size);
+ _write_word_ptr = (void*)buffer;
+
+ } else {
+ printf ("Can't program unknown flash type - aborted\n");
+ return 3;
+ }
+
+
+ wp = (addr & ~3); /* get lower word aligned address */
+
+
+ /*
+ * handle unaligned start bytes
+ */
+ if ((l = addr - wp) != 0) {
+ data = 0;
+ for (i=0, cp=wp; i<l; ++i, ++cp) {
+ data |= (*(uchar *)cp) << (8*i);
+ }
+ for (; i<4 && cnt>0; ++i) {
+ data |= *src++ << (8*i);
+ --cnt;
+ ++cp;
+ }
+ for (; cnt==0 && i<4; ++i, ++cp) {
+ data |= (*(uchar *)cp) << (8*i);
+ }
+
+ /* Disable interrupts which might cause a timeout here */
+ flag = disable_interrupts();
+
+ rc = _write_word_ptr(info->start[0], wp, data);
+
+ /* re-enable interrupts if necessary */
+ if (flag) {
+ enable_interrupts();
+ }
+ if (rc != 0) {
+ return rc;
+ }
+ wp += 4;
+ }
+
+ /*
+ * handle word aligned part
+ */
+ while (cnt >= 4) {
+ data = 0;
+
+ for (i=0; i<4; ++i) {
+ data |= *src++ << (8*i);
+ }
+
+ /* Disable interrupts which might cause a timeout here */
+ flag = disable_interrupts();
+
+ rc = _write_word_ptr(info->start[0], wp, data);
+
+ /* re-enable interrupts if necessary */
+ if (flag) {
+ enable_interrupts();
+ }
+ if (rc != 0) {
+ return rc;
+ }
+ wp += 4;
+ cnt -= 4;
+ }
+
+ if (cnt == 0) {
+ return 0;
+ }
+
+ /*
+ * handle unaligned tail bytes
+ */
+ data = 0;
+ for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) {
+ data |= *src++ << (8*i);
+ --cnt;
+ }
+
+ for (; i<4; ++i, ++cp) {
+ data |= (*(uchar *)cp) << (8*i);
+ }
+
+ /* Disable interrupts which might cause a timeout here */
+ flag = disable_interrupts();
+
+ rc = _write_word_ptr(info->start[0], wp, data);
+
+ /* re-enable interrupts if necessary */
+ if (flag) {
+ enable_interrupts();
+ }
+
+ return rc;
+
+}
2
1

28 Oct '08
Renamed show_boot_progress in assembler init phase to
show_boot_progress_asm to avoid link conflicts with C version
Signed-off-by: Graeme Russ <graeme.russ(a)gmail.com>
--
diff --git a/board/sc520_cdp/sc520_cdp_asm.S b/board/sc520_cdp/sc520_cdp_asm.S
index 6ac5a5d..3a8a03f 100644
--- a/board/sc520_cdp/sc520_cdp_asm.S
+++ b/board/sc520_cdp/sc520_cdp_asm.S
@@ -76,8 +76,8 @@ done: movb $0x88, %al
jmp *%ebp /* return to caller */
-.globl show_boot_progress
-show_boot_progress:
+.globl show_boot_progress_asm
+show_boot_progress_asm:
out %al, $0x80
xchg %al, %ah
movw $0x680, %dx
diff --git a/board/sc520_spunk/sc520_spunk_asm.S b/board/sc520_spunk/sc520_spunk_asm.S
index 3430b6a..eda7e91 100644
--- a/board/sc520_spunk/sc520_spunk_asm.S
+++ b/board/sc520_spunk/sc520_spunk_asm.S
@@ -73,8 +73,8 @@ done: movl $0xfffefc32,%edx
jmp *%ebp /* return to caller */
-.globl show_boot_progress
-show_boot_progress:
+.globl show_boot_progress_asm
+show_boot_progress_asm:
movl $0xfffefc32,%edx
xorw $0xffff, %ax
movw %ax,(%edx)
diff --git a/cpu/i386/start.S b/cpu/i386/start.S
index 264ac09..84888aa 100644
--- a/cpu/i386/start.S
+++ b/cpu/i386/start.S
@@ -55,7 +55,7 @@ early_board_init_ret:
/* so we try to indicate progress */
movw $0x01, %ax
movl $.progress0, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress0:
/* size memory */
@@ -74,7 +74,7 @@ mem_init_ret:
/* indicate (lack of) progress */
movw $0x81, %ax
movl $.progress0a, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress0a:
jmp die
mem_ok:
@@ -82,7 +82,7 @@ mem_ok:
/* indicate progress */
movw $0x02, %ax
movl $.progress1, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress1:
/* create a stack after the bss */
@@ -104,7 +104,7 @@ no_stack:
/* indicate (lack of) progress */
movw $0x82, %ax
movl $.progress1a, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress1a:
jmp die
@@ -113,7 +113,7 @@ stack_ok:
/* indicate progress */
movw $0x03, %ax
movl $.progress2, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress2:
/* copy data section to ram, size must be 4-byte aligned */
@@ -136,7 +136,7 @@ data_fail:
/* indicate (lack of) progress */
movw $0x83, %ax
movl $.progress2a, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress2a:
jmp die
@@ -145,7 +145,7 @@ data_ok:
/* indicate progress */
movw $0x04, %ax
movl $.progress3, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress3:
/* clear bss section in ram, size must be 4-byte aligned */
@@ -168,7 +168,7 @@ bss_fail:
/* indicate (lack of) progress */
movw $0x84, %ax
movl $.progress3a, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress3a:
jmp die
@@ -180,7 +180,7 @@ bss_ok:
/* indicate progress */
movw $0x05, %ax
movl $.progress4, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress4:
call start_i386boot /* Enter, U-boot! */
@@ -188,7 +188,7 @@ bss_ok:
/* indicate (lack of) progress */
movw $0x85, %ax
movl $.progress4a, %ebp
- jmp show_boot_progress
+ jmp show_boot_progress_asm
.progress4a:
die: hlt
2
1