diff options
author | Wolfgang Denk <wd@denx.de> | 2009-06-14 22:05:42 +0200 |
---|---|---|
committer | Wolfgang Denk <wd@denx.de> | 2009-06-14 22:05:42 +0200 |
commit | 92afd368bba7d98b2b7bfb51082c3639bb2119b3 (patch) | |
tree | 74ffc8a3f4980f7c6bad6bf80bb41d3974eff685 /drivers | |
parent | 6b1f78ae6ad037382ad430b07064105c88f7ac02 (diff) | |
parent | 388517e4b745b00256c2fa201ce7bccb67b4f245 (diff) | |
download | u-boot-imx-92afd368bba7d98b2b7bfb51082c3639bb2119b3.zip u-boot-imx-92afd368bba7d98b2b7bfb51082c3639bb2119b3.tar.gz u-boot-imx-92afd368bba7d98b2b7bfb51082c3639bb2119b3.tar.bz2 |
Merge branch 'next' of ../master
Diffstat (limited to 'drivers')
34 files changed, 3227 insertions, 978 deletions
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index 1c5e314..e8273ee 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -221,7 +221,7 @@ U_BOOT_CMD( "pca953x invert pin 0|1\n" " - disable/enable polarity inversion for reads\n" "pca953x intput pin\n" - " - set pin as input and read value\n" + " - set pin as input and read value" ); #endif /* CONFIG_CMD_PCA953X */ diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index 185634d..59883a5 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -244,20 +244,6 @@ int i2c_set_bus_num(unsigned int bus) } #endif -/* TODO: add 100/400k switching */ -unsigned int i2c_get_bus_speed(void) -{ - return CONFIG_SYS_I2C_SPEED; -} - -int i2c_set_bus_speed(unsigned int speed) -{ - if (speed != CONFIG_SYS_I2C_SPEED) - return -1; - - return 0; -} - /*----------------------------------------------------------------------- * if ack == I2C_ACK, ACK the byte so can continue reading, else * send I2C_NOACK to end the read. diff --git a/drivers/i2c/tsi108_i2c.c b/drivers/i2c/tsi108_i2c.c index fda822c..defbba4 100644 --- a/drivers/i2c/tsi108_i2c.c +++ b/drivers/i2c/tsi108_i2c.c @@ -41,6 +41,15 @@ /* All functions assume that Tsi108 I2C block is the only master on the bus */ /* I2C read helper function */ +void i2c_init(int speed, int slaveaddr) +{ + /* + * The TSI108 has a fixed I2C clock rate and doesn't support slave + * operation. This function only exists as a stub to fit into the + * U-Boot I2C API. + */ +} + static int i2c_read_byte ( uint i2c_chan, /* I2C channel number: 0 - main, 1 - SDC SPD */ uchar chip_addr,/* I2C device address on the bus */ @@ -120,7 +129,7 @@ static int i2c_read_byte ( * chip_addr: I2C chip address, range 0..127 * (to read from SPD channel EEPROM use (0xD0 ... 0xD7) * NOTE: The bit 7 in the chip_addr serves as a channel select. - * This hack is for enabling "isdram" command on Tsi108 boards + * This hack is for enabling "i2c sdram" command on Tsi108 boards * without changes to common code. Used for I2C reads only. * byte_addr: Memory or register address within the chip * alen: Number of bytes to use for addr (typically 1, 2 for larger diff --git a/drivers/misc/ds4510.c b/drivers/misc/ds4510.c index 8b5fbbc..f2510a3 100644 --- a/drivers/misc/ds4510.c +++ b/drivers/misc/ds4510.c @@ -404,12 +404,14 @@ U_BOOT_CMD( "ds4510 pullup pin 0|1\n" " - disable/enable pullup on specified pin\n" "ds4510 nv 0|1\n" - " - make gpio and seeprom writes volatile/non-volatile\n" + " - make gpio and seeprom writes volatile/non-volatile" #ifdef CONFIG_CMD_DS4510_RST + "\n" "ds4510 rstdelay 0-3\n" - " - set reset output delay\n" + " - set reset output delay" #endif #ifdef CONFIG_CMD_DS4510_MEM + "\n" "ds4510 eeprom read addr off cnt\n" "ds4510 eeprom write addr off cnt\n" " - read/write 'cnt' bytes at EEPROM offset 'off'\n" @@ -418,7 +420,7 @@ U_BOOT_CMD( " - read/write 'cnt' bytes at SRAM-shadowed EEPROM offset 'off'\n" "ds4510 sram read addr off cnt\n" "ds4510 sram write addr off cnt\n" - " - read/write 'cnt' bytes at SRAM offset 'off'\n" + " - read/write 'cnt' bytes at SRAM offset 'off'" #endif ); #endif /* CONFIG_CMD_DS4510 */ diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index ed3f91e..754d648 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -25,7 +25,9 @@ include $(TOPDIR)/config.mk LIB := $(obj)libmtd.a -COBJS-$(CONFIG_MTD_PARTITIONS) += mtdcore.o mtdpart.o +COBJS-$(CONFIG_MTD_DEVICE) += mtdcore.o +COBJS-$(CONFIG_MTD_PARTITIONS) += mtdpart.o +COBJS-$(CONFIG_MTD_CONCAT) += mtdconcat.o COBJS-$(CONFIG_HAS_DATAFLASH) += at45.o COBJS-$(CONFIG_FLASH_CFI_DRIVER) += cfi_flash.o COBJS-$(CONFIG_FLASH_CFI_MTD) += cfi_mtd.o diff --git a/drivers/mtd/cfi_mtd.c b/drivers/mtd/cfi_mtd.c index 4a76917..c7e357b 100644 --- a/drivers/mtd/cfi_mtd.c +++ b/drivers/mtd/cfi_mtd.c @@ -25,14 +25,19 @@ #include <common.h> #include <flash.h> +#include <malloc.h> #include <asm/errno.h> #include <linux/mtd/mtd.h> +#include <linux/mtd/concat.h> extern flash_info_t flash_info[]; static struct mtd_info cfi_mtd_info[CONFIG_SYS_MAX_FLASH_BANKS]; static char cfi_mtd_names[CONFIG_SYS_MAX_FLASH_BANKS][16]; +#ifdef CONFIG_MTD_CONCAT +static char c_mtd_name[16]; +#endif static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) { @@ -118,7 +123,7 @@ static void cfi_mtd_sync(struct mtd_info *mtd) */ } -static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { flash_info_t *fi = mtd->priv; @@ -130,7 +135,7 @@ static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len) return 0; } -static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { flash_info_t *fi = mtd->priv; @@ -145,16 +150,68 @@ static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi) { int sect_size = 0; + int sect_size_old = 0; int sect; + int regions = 0; + int numblocks = 0; + ulong offset = 0; + ulong base_addr = fi->start[0]; /* - * Select the largest sector size as erasesize (e.g. for UBI) + * First detect the number of eraseregions so that we can allocate + * the array of eraseregions correctly */ for (sect = 0; sect < fi->sector_count; sect++) { + if (sect_size_old != flash_sector_size(fi, sect)) + regions++; + sect_size_old = flash_sector_size(fi, sect); + } + + mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) * regions); + + /* + * Now detect the largest sector and fill the eraseregions + */ + sect_size_old = 0; + regions = 0; + for (sect = 0; sect < fi->sector_count; sect++) { + if ((sect_size_old != flash_sector_size(fi, sect)) && + (sect_size_old != 0)) { + mtd->eraseregions[regions].offset = offset - base_addr; + mtd->eraseregions[regions].erasesize = sect_size_old; + mtd->eraseregions[regions].numblocks = numblocks; + + /* Now start counting the next eraseregions */ + numblocks = 0; + regions++; + } else { + numblocks++; + } + + if (sect_size_old != flash_sector_size(fi, sect)) + offset = fi->start[sect]; + + /* + * Select the largest sector size as erasesize (e.g. for UBI) + */ if (flash_sector_size(fi, sect) > sect_size) sect_size = flash_sector_size(fi, sect); + + sect_size_old = flash_sector_size(fi, sect); } + /* + * Set the last region + */ + mtd->eraseregions[regions].offset = offset - base_addr; + mtd->eraseregions[regions].erasesize = sect_size_old; + mtd->eraseregions[regions].numblocks = numblocks + 1; + + if (regions) + mtd->numeraseregions = regions + 1; + else + mtd->numeraseregions = 0; + mtd->erasesize = sect_size; return 0; @@ -165,6 +222,8 @@ int cfi_mtd_init(void) struct mtd_info *mtd; flash_info_t *fi; int error, i; + int devices_found = 0; + struct mtd_info *mtd_list[CONFIG_SYS_MAX_FLASH_BANKS]; for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { fi = &flash_info[i]; @@ -193,7 +252,25 @@ int cfi_mtd_init(void) if (add_mtd_device(mtd)) return -ENOMEM; + + mtd_list[devices_found++] = mtd; + } + +#ifdef CONFIG_MTD_CONCAT + if (devices_found > 1) { + /* + * We detected multiple devices. Concatenate them together. + */ + sprintf(c_mtd_name, "nor%d", devices_found); + mtd = mtd_concat_create(mtd_list, devices_found, c_mtd_name); + + if (mtd == NULL) + return -ENXIO; + + if (add_mtd_device(mtd)) + return -ENOMEM; } +#endif /* CONFIG_MTD_CONCAT */ return 0; } diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c new file mode 100644 index 0000000..fc22701 --- /dev/null +++ b/drivers/mtd/mtdconcat.c @@ -0,0 +1,807 @@ +/* + * MTD device concatenation layer + * + * (C) 2002 Robert Kaiser <rkaiser@sysgo.de> + * + * NAND support by Christian Gan <cgan@iders.ca> + * + * This code is GPL + */ + +#include <linux/mtd/mtd.h> +#include <linux/mtd/compat.h> +#include <linux/mtd/concat.h> +#include <ubi_uboot.h> + +/* + * Our storage structure: + * Subdev points to an array of pointers to struct mtd_info objects + * which is allocated along with this structure + * + */ +struct mtd_concat { + struct mtd_info mtd; + int num_subdev; + struct mtd_info **subdev; +}; + +/* + * how to calculate the size required for the above structure, + * including the pointer array subdev points to: + */ +#define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ + ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) + +/* + * Given a pointer to the MTD object in the mtd_concat structure, + * we can retrieve the pointer to that structure with this macro. + */ +#define CONCAT(x) ((struct mtd_concat *)(x)) + +/* + * MTD methods which look up the relevant subdevice, translate the + * effective address and pass through to the subdevice. + */ + +static int +concat_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int ret = 0, err; + int i; + + *retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (from >= subdev->size) { + /* Not destined for this subdev */ + size = 0; + from -= subdev->size; + continue; + } + if (from + len > subdev->size) + /* First part goes into this subdev */ + size = subdev->size - from; + else + /* Entire transaction goes into this subdev */ + size = len; + + err = subdev->read(subdev, from, size, &retsize, buf); + + /* Save information about bitflips! */ + if (unlikely(err)) { + if (err == -EBADMSG) { + mtd->ecc_stats.failed++; + ret = err; + } else if (err == -EUCLEAN) { + mtd->ecc_stats.corrected++; + /* Do not overwrite -EBADMSG !! */ + if (!ret) + ret = err; + } else + return err; + } + + *retlen += retsize; + len -= size; + if (len == 0) + return ret; + + buf += size; + from = 0; + } + return -EINVAL; +} + +static int +concat_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + *retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (to >= subdev->size) { + size = 0; + to -= subdev->size; + continue; + } + if (to + len > subdev->size) + size = subdev->size - to; + else + size = len; + + if (!(subdev->flags & MTD_WRITEABLE)) + err = -EROFS; + else + err = subdev->write(subdev, to, size, &retsize, buf); + + if (err) + break; + + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } + return err; +} + +static int +concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_oob_ops devops = *ops; + int i, err, ret = 0; + + ops->retlen = ops->oobretlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (from >= subdev->size) { + from -= subdev->size; + continue; + } + + /* partial read ? */ + if (from + devops.len > subdev->size) + devops.len = subdev->size - from; + + err = subdev->read_oob(subdev, from, &devops); + ops->retlen += devops.retlen; + ops->oobretlen += devops.oobretlen; + + /* Save information about bitflips! */ + if (unlikely(err)) { + if (err == -EBADMSG) { + mtd->ecc_stats.failed++; + ret = err; + } else if (err == -EUCLEAN) { + mtd->ecc_stats.corrected++; + /* Do not overwrite -EBADMSG !! */ + if (!ret) + ret = err; + } else + return err; + } + + if (devops.datbuf) { + devops.len = ops->len - ops->retlen; + if (!devops.len) + return ret; + devops.datbuf += devops.retlen; + } + if (devops.oobbuf) { + devops.ooblen = ops->ooblen - ops->oobretlen; + if (!devops.ooblen) + return ret; + devops.oobbuf += ops->oobretlen; + } + + from = 0; + } + return -EINVAL; +} + +static int +concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_oob_ops devops = *ops; + int i, err; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + ops->retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (to >= subdev->size) { + to -= subdev->size; + continue; + } + + /* partial write ? */ + if (to + devops.len > subdev->size) + devops.len = subdev->size - to; + + err = subdev->write_oob(subdev, to, &devops); + ops->retlen += devops.retlen; + if (err) + return err; + + if (devops.datbuf) { + devops.len = ops->len - ops->retlen; + if (!devops.len) + return 0; + devops.datbuf += devops.retlen; + } + if (devops.oobbuf) { + devops.ooblen = ops->ooblen - ops->oobretlen; + if (!devops.ooblen) + return 0; + devops.oobbuf += devops.oobretlen; + } + to = 0; + } + return -EINVAL; +} + +static void concat_erase_callback(struct erase_info *instr) +{ + /* Nothing to do here in U-Boot */ +} + +static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + int err; + wait_queue_head_t waitq; + DECLARE_WAITQUEUE(wait, current); + + /* + * This code was stol^H^H^H^Hinspired by mtdchar.c + */ + init_waitqueue_head(&waitq); + + erase->mtd = mtd; + erase->callback = concat_erase_callback; + erase->priv = (unsigned long) &waitq; + + /* + * FIXME: Allow INTERRUPTIBLE. Which means + * not having the wait_queue head on the stack. + */ + err = mtd->erase(mtd, erase); + if (!err) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&waitq, &wait); + if (erase->state != MTD_ERASE_DONE + && erase->state != MTD_ERASE_FAILED) + schedule(); + remove_wait_queue(&waitq, &wait); + set_current_state(TASK_RUNNING); + + err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0; + } + return err; +} + +static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_info *subdev; + int i, err; + uint64_t length, offset = 0; + struct erase_info *erase; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + if (instr->addr > concat->mtd.size) + return -EINVAL; + + if (instr->len + instr->addr > concat->mtd.size) + return -EINVAL; + + /* + * Check for proper erase block alignment of the to-be-erased area. + * It is easier to do this based on the super device's erase + * region info rather than looking at each particular sub-device + * in turn. + */ + if (!concat->mtd.numeraseregions) { + /* the easy case: device has uniform erase block size */ + if (instr->addr & (concat->mtd.erasesize - 1)) + return -EINVAL; + if (instr->len & (concat->mtd.erasesize - 1)) + return -EINVAL; + } else { + /* device has variable erase size */ + struct mtd_erase_region_info *erase_regions = + concat->mtd.eraseregions; + + /* + * Find the erase region where the to-be-erased area begins: + */ + for (i = 0; i < concat->mtd.numeraseregions && + instr->addr >= erase_regions[i].offset; i++) ; + --i; + + /* + * Now erase_regions[i] is the region in which the + * to-be-erased area begins. Verify that the starting + * offset is aligned to this region's erase size: + */ + if (instr->addr & (erase_regions[i].erasesize - 1)) + return -EINVAL; + + /* + * now find the erase region where the to-be-erased area ends: + */ + for (; i < concat->mtd.numeraseregions && + (instr->addr + instr->len) >= erase_regions[i].offset; + ++i) ; + --i; + /* + * check if the ending offset is aligned to this region's erase size + */ + if ((instr->addr + instr->len) & (erase_regions[i].erasesize - + 1)) + return -EINVAL; + } + + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + + /* make a local copy of instr to avoid modifying the caller's struct */ + erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); + + if (!erase) + return -ENOMEM; + + *erase = *instr; + length = instr->len; + + /* + * find the subdevice where the to-be-erased area begins, adjust + * starting offset to be relative to the subdevice start + */ + for (i = 0; i < concat->num_subdev; i++) { + subdev = concat->subdev[i]; + if (subdev->size <= erase->addr) { + erase->addr -= subdev->size; + offset += subdev->size; + } else { + break; + } + } + + /* must never happen since size limit has been verified above */ + BUG_ON(i >= concat->num_subdev); + + /* now do the erase: */ + err = 0; + for (; length > 0; i++) { + /* loop for all subdevices affected by this request */ + subdev = concat->subdev[i]; /* get current subdevice */ + + /* limit length to subdevice's size: */ + if (erase->addr + length > subdev->size) + erase->len = subdev->size - erase->addr; + else + erase->len = length; + + if (!(subdev->flags & MTD_WRITEABLE)) { + err = -EROFS; + break; + } + length -= erase->len; + if ((err = concat_dev_erase(subdev, erase))) { + /* sanity check: should never happen since + * block alignment has been checked above */ + BUG_ON(err == -EINVAL); + if (erase->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr = erase->fail_addr + offset; + break; + } + /* + * erase->addr specifies the offset of the area to be + * erased *within the current subdevice*. It can be + * non-zero only the first time through this loop, i.e. + * for the first subdevice where blocks need to be erased. + * All the following erases must begin at the start of the + * current subdevice, i.e. at offset zero. + */ + erase->addr = 0; + offset += subdev->size; + } + instr->state = erase->state; + kfree(erase); + if (err) + return err; + + if (instr->callback) + instr->callback(instr); + return 0; +} + +static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + uint64_t size; + + if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; + continue; + } + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = subdev->lock(subdev, ofs, size); + + if (err) + break; + + len -= size; + if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + + return err; +} + +static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = 0; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + uint64_t size; + + if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; + continue; + } + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = subdev->unlock(subdev, ofs, size); + + if (err) + break; + + len -= size; + if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + + return err; +} + +static void concat_sync(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + subdev->sync(subdev); + } +} + +static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, res = 0; + + if (!concat->subdev[0]->block_isbad) + return res; + + if (ofs > mtd->size) + return -EINVAL; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (ofs >= subdev->size) { + ofs -= subdev->size; + continue; + } + + res = subdev->block_isbad(subdev, ofs); + break; + } + + return res; +} + +static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; + + if (!concat->subdev[0]->block_markbad) + return 0; + + if (ofs > mtd->size) + return -EINVAL; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (ofs >= subdev->size) { + ofs -= subdev->size; + continue; + } + + err = subdev->block_markbad(subdev, ofs); + if (!err) + mtd->ecc_stats.badblocks++; + break; + } + + return err; +} + +/* + * This function constructs a virtual MTD device by concatenating + * num_devs MTD devices. A pointer to the new device object is + * stored to *new_dev upon success. This function does _not_ + * register any devices: this is the caller's responsibility. + */ +struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ + const char *name) +{ /* name for the new device */ + int i; + size_t size; + struct mtd_concat *concat; + uint32_t max_erasesize, curr_erasesize; + int num_erase_region; + + debug("Concatenating MTD devices:\n"); + for (i = 0; i < num_devs; i++) + debug("(%d): \"%s\"\n", i, subdev[i]->name); + debug("into device \"%s\"\n", name); + + /* allocate the device structure */ + size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); + concat = kzalloc(size, GFP_KERNEL); + if (!concat) { + printk + ("memory allocation error while creating concatenated device \"%s\"\n", + name); + return NULL; + } + concat->subdev = (struct mtd_info **) (concat + 1); + + /* + * Set up the new "super" device's MTD object structure, check for + * incompatibilites between the subdevices. + */ + concat->mtd.type = subdev[0]->type; + concat->mtd.flags = subdev[0]->flags; + concat->mtd.size = subdev[0]->size; + concat->mtd.erasesize = subdev[0]->erasesize; + concat->mtd.writesize = subdev[0]->writesize; + concat->mtd.subpage_sft = subdev[0]->subpage_sft; + concat->mtd.oobsize = subdev[0]->oobsize; + concat->mtd.oobavail = subdev[0]->oobavail; + if (subdev[0]->read_oob) + concat->mtd.read_oob = concat_read_oob; + if (subdev[0]->write_oob) + concat->mtd.write_oob = concat_write_oob; + if (subdev[0]->block_isbad) + concat->mtd.block_isbad = concat_block_isbad; + if (subdev[0]->block_markbad) + concat->mtd.block_markbad = concat_block_markbad; + + concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; + + concat->subdev[0] = subdev[0]; + + for (i = 1; i < num_devs; i++) { + if (concat->mtd.type != subdev[i]->type) { + kfree(concat); + printk("Incompatible device type on \"%s\"\n", + subdev[i]->name); + return NULL; + } + if (concat->mtd.flags != subdev[i]->flags) { + /* + * Expect all flags except MTD_WRITEABLE to be + * equal on all subdevices. + */ + if ((concat->mtd.flags ^ subdev[i]-> + flags) & ~MTD_WRITEABLE) { + kfree(concat); + printk("Incompatible device flags on \"%s\"\n", + subdev[i]->name); + return NULL; + } else + /* if writeable attribute differs, + make super device writeable */ + concat->mtd.flags |= + subdev[i]->flags & MTD_WRITEABLE; + } + + concat->mtd.size += subdev[i]->size; + concat->mtd.ecc_stats.badblocks += + subdev[i]->ecc_stats.badblocks; + if (concat->mtd.writesize != subdev[i]->writesize || + concat->mtd.subpage_sft != subdev[i]->subpage_sft || + concat->mtd.oobsize != subdev[i]->oobsize || + !concat->mtd.read_oob != !subdev[i]->read_oob || + !concat->mtd.write_oob != !subdev[i]->write_oob) { + kfree(concat); + printk("Incompatible OOB or ECC data on \"%s\"\n", + subdev[i]->name); + return NULL; + } + concat->subdev[i] = subdev[i]; + + } + + concat->mtd.ecclayout = subdev[0]->ecclayout; + + concat->num_subdev = num_devs; + concat->mtd.name = name; + + concat->mtd.erase = concat_erase; + concat->mtd.read = concat_read; + concat->mtd.write = concat_write; + concat->mtd.sync = concat_sync; + concat->mtd.lock = concat_lock; + concat->mtd.unlock = concat_unlock; + + /* + * Combine the erase block size info of the subdevices: + * + * first, walk the map of the new device and see how + * many changes in erase size we have + */ + max_erasesize = curr_erasesize = subdev[0]->erasesize; + num_erase_region = 1; + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* if it differs from the last subdevice's erase size, count it */ + ++num_erase_region; + curr_erasesize = subdev[i]->erasesize; + if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } else { + /* current subdevice has variable erase size */ + int j; + for (j = 0; j < subdev[i]->numeraseregions; j++) { + + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j].erasesize != + curr_erasesize) { + ++num_erase_region; + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; + if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + } + } + + if (num_erase_region == 1) { + /* + * All subdevices have the same uniform erase size. + * This is easy: + */ + concat->mtd.erasesize = curr_erasesize; + concat->mtd.numeraseregions = 0; + } else { + uint64_t tmp64; + + /* + * erase block size varies across the subdevices: allocate + * space to store the data describing the variable erase regions + */ + struct mtd_erase_region_info *erase_region_p; + uint64_t begin, position; + + concat->mtd.erasesize = max_erasesize; + concat->mtd.numeraseregions = num_erase_region; + concat->mtd.eraseregions = erase_region_p = + kmalloc(num_erase_region * + sizeof (struct mtd_erase_region_info), GFP_KERNEL); + if (!erase_region_p) { + kfree(concat); + printk + ("memory allocation error while creating erase region list" + " for device \"%s\"\n", name); + return NULL; + } + + /* + * walk the map of the new device once more and fill in + * in erase region info: + */ + curr_erasesize = subdev[0]->erasesize; + begin = position = 0; + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* + * fill in an mtd_erase_region_info structure for the area + * we have walked so far: + */ + erase_region_p->offset = begin; + erase_region_p->erasesize = + curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; + begin = position; + + curr_erasesize = subdev[i]->erasesize; + ++erase_region_p; + } + position += subdev[i]->size; + } else { + /* current subdevice has variable erase size */ + int j; + for (j = 0; j < subdev[i]->numeraseregions; j++) { + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j]. + erasesize != curr_erasesize) { + erase_region_p->offset = begin; + erase_region_p->erasesize = + curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; + begin = position; + + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; + ++erase_region_p; + } + position += + subdev[i]->eraseregions[j]. + numblocks * (uint64_t)curr_erasesize; + } + } + } + /* Now write the final entry */ + erase_region_p->offset = begin; + erase_region_p->erasesize = curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; + } + + return &concat->mtd; +} diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index f010f5e..e2e43ea 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -26,7 +26,7 @@ struct list_head mtd_partitions; struct mtd_part { struct mtd_info mtd; struct mtd_info *master; - u_int32_t offset; + uint64_t offset; int index; struct list_head list; int registered; @@ -44,50 +44,32 @@ struct mtd_part { * to the _real_ device. */ -static int part_read (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); + struct mtd_ecc_stats stats; int res; + stats = part->master->ecc_stats; + if (from >= mtd->size) len = 0; else if (from + len > mtd->size) len = mtd->size - from; - res = part->master->read (part->master, from + part->offset, + res = part->master->read(part->master, from + part->offset, len, retlen, buf); if (unlikely(res)) { if (res == -EUCLEAN) - mtd->ecc_stats.corrected++; + mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; if (res == -EBADMSG) - mtd->ecc_stats.failed++; + mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; } return res; } -#ifdef MTD_LINUX -static int part_point (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, void **virt, resource_size_t *phys) -{ - struct mtd_part *part = PART(mtd); - if (from >= mtd->size) - len = 0; - else if (from + len > mtd->size) - len = mtd->size - from; - return part->master->point (part->master, from + part->offset, - len, retlen, virt, phys); -} - -static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) -{ - struct mtd_part *part = PART(mtd); - - part->master->unpoint(part->master, from + part->offset, len); -} -#endif - static int part_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) + struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); int res; @@ -107,38 +89,38 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, return res; } -static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return part->master->read_user_prot_reg (part->master, from, + return part->master->read_user_prot_reg(part->master, from, len, retlen, buf); } -static int part_get_user_prot_info (struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int part_get_user_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) { struct mtd_part *part = PART(mtd); - return part->master->get_user_prot_info (part->master, buf, len); + return part->master->get_user_prot_info(part->master, buf, len); } -static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return part->master->read_fact_prot_reg (part->master, from, + return part->master->read_fact_prot_reg(part->master, from, len, retlen, buf); } -static int part_get_fact_prot_info (struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, + size_t len) { struct mtd_part *part = PART(mtd); - return part->master->get_fact_prot_info (part->master, buf, len); + return part->master->get_fact_prot_info(part->master, buf, len); } -static int part_write (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int part_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) @@ -147,13 +129,12 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, len = 0; else if (to + len > mtd->size) len = mtd->size - to; - return part->master->write (part->master, to + part->offset, + return part->master->write(part->master, to + part->offset, len, retlen, buf); } -#ifdef MTD_LINUX -static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) @@ -162,13 +143,12 @@ static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len, len = 0; else if (to + len > mtd->size) len = mtd->size - to; - return part->master->panic_write (part->master, to + part->offset, + return part->master->panic_write(part->master, to + part->offset, len, retlen, buf); } -#endif static int part_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) + struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); @@ -182,33 +162,22 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to, return part->master->write_oob(part->master, to + part->offset, ops); } -static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return part->master->write_user_prot_reg (part->master, from, + return part->master->write_user_prot_reg(part->master, from, len, retlen, buf); } -static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) -{ - struct mtd_part *part = PART(mtd); - return part->master->lock_user_prot_reg (part->master, from, len); -} - -#ifdef MTD_LINUX -static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen) +static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len) { struct mtd_part *part = PART(mtd); - if (!(mtd->flags & MTD_WRITEABLE)) - return -EROFS; - return part->master->writev (part->master, vecs, count, - to + part->offset, retlen); + return part->master->lock_user_prot_reg(part->master, from, len); } -#endif -static int part_erase (struct mtd_info *mtd, struct erase_info *instr) +static int part_erase(struct mtd_info *mtd, struct erase_info *instr) { struct mtd_part *part = PART(mtd); int ret; @@ -219,7 +188,7 @@ static int part_erase (struct mtd_info *mtd, struct erase_info *instr) instr->addr += part->offset; ret = part->master->erase(part->master, instr); if (ret) { - if (instr->fail_addr != 0xffffffff) + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) instr->fail_addr -= part->offset; instr->addr -= part->offset; } @@ -231,19 +200,15 @@ void mtd_erase_callback(struct erase_info *instr) if (instr->mtd->erase == part_erase) { struct mtd_part *part = PART(instr->mtd); - if (instr->fail_addr != 0xffffffff) + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) instr->fail_addr -= part->offset; instr->addr -= part->offset; } if (instr->callback) instr->callback(instr); } -#ifdef MTD_LINUX -EXPORT_SYMBOL_GPL(mtd_erase_callback); -#endif -#ifdef MTD_LINUX -static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_part *part = PART(mtd); if ((len + ofs) > mtd->size) @@ -251,14 +216,13 @@ static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) return part->master->lock(part->master, ofs + part->offset, len); } -static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_part *part = PART(mtd); if ((len + ofs) > mtd->size) return -EINVAL; return part->master->unlock(part->master, ofs + part->offset, len); } -#endif static void part_sync(struct mtd_info *mtd) { @@ -266,7 +230,6 @@ static void part_sync(struct mtd_info *mtd) part->master->sync(part->master); } -#ifdef MTD_LINUX static int part_suspend(struct mtd_info *mtd) { struct mtd_part *part = PART(mtd); @@ -278,9 +241,8 @@ static void part_resume(struct mtd_info *mtd) struct mtd_part *part = PART(mtd); part->master->resume(part->master); } -#endif -static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) +static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_part *part = PART(mtd); if (ofs >= mtd->size) @@ -289,7 +251,7 @@ static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) return part->master->block_isbad(part->master, ofs); } -static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) +static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_part *part = PART(mtd); int res; @@ -300,10 +262,8 @@ static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) return -EINVAL; ofs += part->offset; res = part->master->block_markbad(part->master, ofs); -#ifdef MTD_LINUX if (!res) mtd->ecc_stats.badblocks++; -#endif return res; } @@ -314,31 +274,193 @@ static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) int del_mtd_partitions(struct mtd_info *master) { - struct list_head *node; - struct mtd_part *slave; + struct mtd_part *slave, *next; - for (node = mtd_partitions.next; - node != &mtd_partitions; - node = node->next) { - slave = list_entry(node, struct mtd_part, list); + list_for_each_entry_safe(slave, next, &mtd_partitions, list) if (slave->master == master) { - struct list_head *prev = node->prev; - __list_del(prev, node->next); - if(slave->registered) + list_del(&slave->list); + if (slave->registered) del_mtd_device(&slave->mtd); kfree(slave); - node = prev; } - } return 0; } +static struct mtd_part *add_one_partition(struct mtd_info *master, + const struct mtd_partition *part, int partno, + uint64_t cur_offset) +{ + struct mtd_part *slave; + + /* allocate the partition structure */ + slave = kzalloc(sizeof(*slave), GFP_KERNEL); + if (!slave) { + printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", + master->name); + del_mtd_partitions(master); + return NULL; + } + list_add(&slave->list, &mtd_partitions); + + /* set up the MTD object for this partition */ + slave->mtd.type = master->type; + slave->mtd.flags = master->flags & ~part->mask_flags; + slave->mtd.size = part->size; + slave->mtd.writesize = master->writesize; + slave->mtd.oobsize = master->oobsize; + slave->mtd.oobavail = master->oobavail; + slave->mtd.subpage_sft = master->subpage_sft; + + slave->mtd.name = part->name; + slave->mtd.owner = master->owner; + + slave->mtd.read = part_read; + slave->mtd.write = part_write; + + if (master->panic_write) + slave->mtd.panic_write = part_panic_write; + + if (master->read_oob) + slave->mtd.read_oob = part_read_oob; + if (master->write_oob) + slave->mtd.write_oob = part_write_oob; + if (master->read_user_prot_reg) + slave->mtd.read_user_prot_reg = part_read_user_prot_reg; + if (master->read_fact_prot_reg) + slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; + if (master->write_user_prot_reg) + slave->mtd.write_user_prot_reg = part_write_user_prot_reg; + if (master->lock_user_prot_reg) + slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; + if (master->get_user_prot_info) + slave->mtd.get_user_prot_info = part_get_user_prot_info; + if (master->get_fact_prot_info) + slave->mtd.get_fact_prot_info = part_get_fact_prot_info; + if (master->sync) + slave->mtd.sync = part_sync; + if (!partno && master->suspend && master->resume) { + slave->mtd.suspend = part_suspend; + slave->mtd.resume = part_resume; + } + if (master->lock) + slave->mtd.lock = part_lock; + if (master->unlock) + slave->mtd.unlock = part_unlock; + if (master->block_isbad) + slave->mtd.block_isbad = part_block_isbad; + if (master->block_markbad) + slave->mtd.block_markbad = part_block_markbad; + slave->mtd.erase = part_erase; + slave->master = master; + slave->offset = part->offset; + slave->index = partno; + + if (slave->offset == MTDPART_OFS_APPEND) + slave->offset = cur_offset; + if (slave->offset == MTDPART_OFS_NXTBLK) { + slave->offset = cur_offset; + if (mtd_mod_by_eb(cur_offset, master) != 0) { + /* Round up to next erasesize */ + slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize; + printk(KERN_NOTICE "Moving partition %d: " + "0x%012llx -> 0x%012llx\n", partno, + (unsigned long long)cur_offset, (unsigned long long)slave->offset); + } + } + if (slave->mtd.size == MTDPART_SIZ_FULL) + slave->mtd.size = master->size - slave->offset; + + printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset, + (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); + + /* let's do some sanity checks */ + if (slave->offset >= master->size) { + /* let's register it anyway to preserve ordering */ + slave->offset = 0; + slave->mtd.size = 0; + printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", + part->name); + goto out_register; + } + if (slave->offset + slave->mtd.size > master->size) { + slave->mtd.size = master->size - slave->offset; + printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", + part->name, master->name, (unsigned long long)slave->mtd.size); + } + if (master->numeraseregions > 1) { + /* Deal with variable erase size stuff */ + int i, max = master->numeraseregions; + u64 end = slave->offset + slave->mtd.size; + struct mtd_erase_region_info *regions = master->eraseregions; + + /* Find the first erase regions which is part of this + * partition. */ + for (i = 0; i < max && regions[i].offset <= slave->offset; i++) + ; + /* The loop searched for the region _behind_ the first one */ + i--; + + /* Pick biggest erasesize */ + for (; i < max && regions[i].offset < end; i++) { + if (slave->mtd.erasesize < regions[i].erasesize) { + slave->mtd.erasesize = regions[i].erasesize; + } + } + BUG_ON(slave->mtd.erasesize == 0); + } else { + /* Single erase size */ + slave->mtd.erasesize = master->erasesize; + } + + if ((slave->mtd.flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->offset, &slave->mtd)) { + /* Doesn't start on a boundary of major erase size */ + /* FIXME: Let it be writable if it is on a boundary of + * _minor_ erase size though */ + slave->mtd.flags &= ~MTD_WRITEABLE; + printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", + part->name); + } + if ((slave->mtd.flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { + slave->mtd.flags &= ~MTD_WRITEABLE; + printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", + part->name); + } + + slave->mtd.ecclayout = master->ecclayout; + if (master->block_isbad) { + uint64_t offs = 0; + + while (offs < slave->mtd.size) { + if (master->block_isbad(master, + offs + slave->offset)) + slave->mtd.ecc_stats.badblocks++; + offs += slave->mtd.erasesize; + } + } + +out_register: + if (part->mtdp) { + /* store the object pointer (caller may or may not register it*/ + *part->mtdp = &slave->mtd; + slave->registered = 0; + } else { + /* register our partition */ + add_mtd_device(&slave->mtd); + slave->registered = 1; + } + return slave; +} + /* * This function, given a master MTD object and a partition table, creates * and registers slave MTD objects which are bound to the master according to * the partition definitions. - * (Q: should we register the master MTD object as well?) + * + * We don't register the master, or expect the caller to have done so, + * for reasons of data integrity. */ int add_mtd_partitions(struct mtd_info *master, @@ -346,7 +468,7 @@ int add_mtd_partitions(struct mtd_info *master, int nbparts) { struct mtd_part *slave; - u_int32_t cur_offset = 0; + uint64_t cur_offset = 0; int i; /* @@ -357,184 +479,14 @@ int add_mtd_partitions(struct mtd_info *master, if (mtd_partitions.next == NULL) INIT_LIST_HEAD(&mtd_partitions); - printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); for (i = 0; i < nbparts; i++) { - - /* allocate the partition structure */ - slave = kzalloc (sizeof(*slave), GFP_KERNEL); - if (!slave) { - printk ("memory allocation error while creating partitions for \"%s\"\n", - master->name); - del_mtd_partitions(master); + slave = add_one_partition(master, parts + i, i, cur_offset); + if (!slave) return -ENOMEM; - } - list_add(&slave->list, &mtd_partitions); - - /* set up the MTD object for this partition */ - slave->mtd.type = master->type; - slave->mtd.flags = master->flags & ~parts[i].mask_flags; - slave->mtd.size = parts[i].size; - slave->mtd.writesize = master->writesize; - slave->mtd.oobsize = master->oobsize; - slave->mtd.oobavail = master->oobavail; - slave->mtd.subpage_sft = master->subpage_sft; - - slave->mtd.name = parts[i].name; - slave->mtd.owner = master->owner; - - slave->mtd.read = part_read; - slave->mtd.write = part_write; - -#ifdef MTD_LINUX - if (master->panic_write) - slave->mtd.panic_write = part_panic_write; - - if(master->point && master->unpoint){ - slave->mtd.point = part_point; - slave->mtd.unpoint = part_unpoint; - } -#endif - - if (master->read_oob) - slave->mtd.read_oob = part_read_oob; - if (master->write_oob) - slave->mtd.write_oob = part_write_oob; - if(master->read_user_prot_reg) - slave->mtd.read_user_prot_reg = part_read_user_prot_reg; - if(master->read_fact_prot_reg) - slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; - if(master->write_user_prot_reg) - slave->mtd.write_user_prot_reg = part_write_user_prot_reg; - if(master->lock_user_prot_reg) - slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; - if(master->get_user_prot_info) - slave->mtd.get_user_prot_info = part_get_user_prot_info; - if(master->get_fact_prot_info) - slave->mtd.get_fact_prot_info = part_get_fact_prot_info; - if (master->sync) - slave->mtd.sync = part_sync; -#ifdef MTD_LINUX - if (!i && master->suspend && master->resume) { - slave->mtd.suspend = part_suspend; - slave->mtd.resume = part_resume; - } - if (master->writev) - slave->mtd.writev = part_writev; - if (master->lock) - slave->mtd.lock = part_lock; - if (master->unlock) - slave->mtd.unlock = part_unlock; -#endif - if (master->block_isbad) - slave->mtd.block_isbad = part_block_isbad; - if (master->block_markbad) - slave->mtd.block_markbad = part_block_markbad; - slave->mtd.erase = part_erase; - slave->master = master; - slave->offset = parts[i].offset; - slave->index = i; - - if (slave->offset == MTDPART_OFS_APPEND) - slave->offset = cur_offset; - if (slave->offset == MTDPART_OFS_NXTBLK) { - slave->offset = cur_offset; - if ((cur_offset % master->erasesize) != 0) { - /* Round up to next erasesize */ - slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; - printk(KERN_NOTICE "Moving partition %d: " - "0x%08x -> 0x%08x\n", i, - cur_offset, slave->offset); - } - } - if (slave->mtd.size == MTDPART_SIZ_FULL) - slave->mtd.size = master->size - slave->offset; cur_offset = slave->offset + slave->mtd.size; - - printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, - slave->offset + slave->mtd.size, slave->mtd.name); - - /* let's do some sanity checks */ - if (slave->offset >= master->size) { - /* let's register it anyway to preserve ordering */ - slave->offset = 0; - slave->mtd.size = 0; - printk ("mtd: partition \"%s\" is out of reach -- disabled\n", - parts[i].name); - } - if (slave->offset + slave->mtd.size > master->size) { - slave->mtd.size = master->size - slave->offset; - printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", - parts[i].name, master->name, slave->mtd.size); - } - if (master->numeraseregions>1) { - /* Deal with variable erase size stuff */ - int i; - struct mtd_erase_region_info *regions = master->eraseregions; - - /* Find the first erase regions which is part of this partition. */ - for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) - ; - - for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { - if (slave->mtd.erasesize < regions[i].erasesize) { - slave->mtd.erasesize = regions[i].erasesize; - } - } - } else { - /* Single erase size */ - slave->mtd.erasesize = master->erasesize; - } - - if ((slave->mtd.flags & MTD_WRITEABLE) && - (slave->offset % slave->mtd.erasesize)) { - /* Doesn't start on a boundary of major erase size */ - /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ - slave->mtd.flags &= ~MTD_WRITEABLE; - printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", - parts[i].name); - } - if ((slave->mtd.flags & MTD_WRITEABLE) && - (slave->mtd.size % slave->mtd.erasesize)) { - slave->mtd.flags &= ~MTD_WRITEABLE; - printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", - parts[i].name); - } - - slave->mtd.ecclayout = master->ecclayout; - if (master->block_isbad) { - uint32_t offs = 0; - - while(offs < slave->mtd.size) { - if (master->block_isbad(master, - offs + slave->offset)) - slave->mtd.ecc_stats.badblocks++; - offs += slave->mtd.erasesize; - } - } - -#ifdef MTD_LINUX - if (parts[i].mtdp) { - /* store the object pointer - * (caller may or may not register it */ - *parts[i].mtdp = &slave->mtd; - slave->registered = 0; - } else { - /* register our partition */ - add_mtd_device(&slave->mtd); - slave->registered = 1; - } -#else - /* register our partition */ - add_mtd_device(&slave->mtd); - slave->registered = 1; -#endif } return 0; } - -#ifdef MTD_LINUX -EXPORT_SYMBOL(add_mtd_partitions); -EXPORT_SYMBOL(del_mtd_partitions); -#endif diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 471cd6b..71dd5b9 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -40,6 +40,7 @@ COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o COBJS-$(CONFIG_NAND_DAVINCI) += davinci_nand.o COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +COBJS-$(CONFIG_NAND_MPC5121_NFC) += mpc5121_nfc.o COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.c COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c new file mode 100644 index 0000000..856cb36 --- /dev/null +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -0,0 +1,692 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. + * Copyright 2009 Semihalf. + * (C) Copyright 2009 Stefan Roese <sr@denx.de> + * + * Based on original driver from Freescale Semiconductor + * written by John Rigby <jrigby@freescale.com> on basis + * of drivers/mtd/nand/mxc_nand.c. Reworked and extended + * Piotr Ziecik <kosmo@semihalf.com>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <common.h> +#include <malloc.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/compat.h> + +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <nand.h> + +#define DRV_NAME "mpc5121_nfc" + +/* Timeouts */ +#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ +#define NFC_TIMEOUT 2000 /* 2000 us */ + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x200) + +/* Addresses for NFC SPARE BUFFER areas */ +#define NFC_SPARE_BUFFERS 8 +#define NFC_SPARE_LEN 0x40 +#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN)) + +/* MPC5121 NFC registers */ +#define NFC_BUF_ADDR 0x1E04 +#define NFC_FLASH_ADDR 0x1E06 +#define NFC_FLASH_CMD 0x1E08 +#define NFC_CONFIG 0x1E0A +#define NFC_ECC_STATUS1 0x1E0C +#define NFC_ECC_STATUS2 0x1E0E +#define NFC_SPAS 0x1E10 +#define NFC_WRPROT 0x1E12 +#define NFC_NF_WRPRST 0x1E18 +#define NFC_CONFIG1 0x1E1A +#define NFC_CONFIG2 0x1E1C +#define NFC_UNLOCKSTART_BLK0 0x1E20 +#define NFC_UNLOCKEND_BLK0 0x1E22 +#define NFC_UNLOCKSTART_BLK1 0x1E24 +#define NFC_UNLOCKEND_BLK1 0x1E26 +#define NFC_UNLOCKSTART_BLK2 0x1E28 +#define NFC_UNLOCKEND_BLK2 0x1E2A +#define NFC_UNLOCKSTART_BLK3 0x1E2C +#define NFC_UNLOCKEND_BLK3 0x1E2E + +/* Bit Definitions: NFC_BUF_ADDR */ +#define NFC_RBA_MASK (7 << 0) +#define NFC_ACTIVE_CS_SHIFT 5 +#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT) + +/* Bit Definitions: NFC_CONFIG */ +#define NFC_BLS_UNLOCKED (1 << 1) + +/* Bit Definitions: NFC_CONFIG1 */ +#define NFC_ECC_4BIT (1 << 0) +#define NFC_FULL_PAGE_DMA (1 << 1) +#define NFC_SPARE_ONLY (1 << 2) +#define NFC_ECC_ENABLE (1 << 3) +#define NFC_INT_MASK (1 << 4) +#define NFC_BIG_ENDIAN (1 << 5) +#define NFC_RESET (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_PPB_32 (0 << 9) +#define NFC_PPB_64 (1 << 9) +#define NFC_PPB_128 (2 << 9) +#define NFC_PPB_256 (3 << 9) +#define NFC_PPB_MASK (3 << 9) +#define NFC_FULL_PAGE_INT (1 << 11) + +/* Bit Definitions: NFC_CONFIG2 */ +#define NFC_COMMAND (1 << 0) +#define NFC_ADDRESS (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) +#define NFC_CMD_FAIL (1 << 15) +#define NFC_INT (1 << 15) + +/* Bit Definitions: NFC_WRPROT */ +#define NFC_WPC_LOCK_TIGHT (1 << 0) +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) + +struct mpc5121_nfc_prv { + struct mtd_info mtd; + struct nand_chip chip; + int irq; + void __iomem *regs; + struct clk *clk; + uint column; + int spareonly; + int chipsel; +}; + +int mpc5121_nfc_chip = 0; + +static void mpc5121_nfc_done(struct mtd_info *mtd); + +/* Read NFC register */ +static inline u16 nfc_read(struct mtd_info *mtd, uint reg) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + return in_be16(prv->regs + reg); +} + +/* Write NFC register */ +static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + out_be16(prv->regs + reg, val); +} + +/* Set bits in NFC register */ +static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); +} + +/* Clear bits in NFC register */ +static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); +} + +/* Invoke address cycle */ +static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr) +{ + nfc_write(mtd, NFC_FLASH_ADDR, addr); + nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS); + mpc5121_nfc_done(mtd); +} + +/* Invoke command cycle */ +static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd) +{ + nfc_write(mtd, NFC_FLASH_CMD, cmd); + nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND); + mpc5121_nfc_done(mtd); +} + +/* Send data from NFC buffers to NAND flash */ +static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_INPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive data from NAND flash */ +static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive ID from NAND flash */ +static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_ID); + mpc5121_nfc_done(mtd); +} + +/* Receive status from NAND flash */ +static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_STATUS); + mpc5121_nfc_done(mtd); +} + +static void mpc5121_nfc_done(struct mtd_info *mtd) +{ + int max_retries = NFC_TIMEOUT; + + while (1) { + max_retries--; + if (nfc_read(mtd, NFC_CONFIG2) & NFC_INT) + break; + udelay(1); + } + + if (max_retries <= 0) + printk(KERN_WARNING DRV_NAME + ": Timeout while waiting for completion.\n"); +} + +/* Do address cycle(s) */ +static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) +{ + struct nand_chip *chip = mtd->priv; + u32 pagemask = chip->pagemask; + + if (column != -1) { + mpc5121_nfc_send_addr(mtd, column); + if (mtd->writesize > 512) + mpc5121_nfc_send_addr(mtd, column >> 8); + } + + if (page != -1) { + do { + mpc5121_nfc_send_addr(mtd, page & 0xFF); + page >>= 8; + pagemask >>= 8; + } while (pagemask); + } +} + +/* Control chip select signals */ + +/* + * Selecting the active device: + * + * This is different than the linux version. Switching between chips + * is done via board_nand_select_device(). The Linux select_chip + * function used here in U-Boot has only 2 valid chip numbers: + * 0 select + * -1 deselect + */ + +/* + * Implement it as a weak default, so that boards with a specific + * chip-select routine can use their own function. + */ +void __mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + if (chip < 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_CE); + return; + } + + nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) & + NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_CONFIG1, NFC_CE); +} +void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) + __attribute__((weak, alias("__mpc5121_nfc_select_chip"))); + +void board_nand_select_device(struct nand_chip *nand, int chip) +{ + /* + * Only save this chip number in global variable here. This + * will be used later in mpc5121_nfc_select_chip(). + */ + mpc5121_nfc_chip = chip; +} + +/* Read NAND Ready/Busy signal */ +static int mpc5121_nfc_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles ready/busy signal internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +/* Write command to NAND flash */ +static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + prv->column = (column >= 0) ? column : 0; + prv->spareonly = 0; + + switch (command) { + case NAND_CMD_PAGEPROG: + mpc5121_nfc_send_prog_page(mtd); + break; + /* + * NFC does not support sub-page reads and writes, + * so emulate them using full page transfers. + */ + case NAND_CMD_READ0: + column = 0; + break; + + case NAND_CMD_READ1: + prv->column += 256; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_READOOB: + prv->spareonly = 1; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_SEQIN: + mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page); + column = 0; + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_READID: + case NAND_CMD_STATUS: + break; + + default: + return; + } + + mpc5121_nfc_send_cmd(mtd, command); + mpc5121_nfc_addr_cycle(mtd, column, page); + + switch (command) { + case NAND_CMD_READ0: + if (mtd->writesize > 512) + mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART); + mpc5121_nfc_send_read_page(mtd); + break; + + case NAND_CMD_READID: + mpc5121_nfc_send_read_id(mtd); + break; + + case NAND_CMD_STATUS: + mpc5121_nfc_send_read_status(mtd); + if (chip->options & NAND_BUSWIDTH_16) + prv->column = 1; + else + prv->column = 0; + break; + } +} + +/* Copy data from/to NFC spare buffers. */ +static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset, + u8 * buffer, uint size, int wr) +{ + struct nand_chip *nand = mtd->priv; + struct mpc5121_nfc_prv *prv = nand->priv; + uint o, s, sbsize, blksize; + + /* + * NAND spare area is available through NFC spare buffers. + * The NFC divides spare area into (page_size / 512) chunks. + * Each chunk is placed into separate spare memory area, using + * first (spare_size / num_of_chunks) bytes of the buffer. + * + * For NAND device in which the spare area is not divided fully + * by the number of chunks, number of used bytes in each spare + * buffer is rounded down to the nearest even number of bytes, + * and all remaining bytes are added to the last used spare area. + * + * For more information read section 26.6.10 of MPC5121e + * Microcontroller Reference Manual, Rev. 3. + */ + + /* Calculate number of valid bytes in each spare buffer */ + sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1; + + while (size) { + /* Calculate spare buffer number */ + s = offset / sbsize; + if (s > NFC_SPARE_BUFFERS - 1) + s = NFC_SPARE_BUFFERS - 1; + + /* + * Calculate offset to requested data block in selected spare + * buffer and its size. + */ + o = offset - (s * sbsize); + blksize = min(sbsize - o, size); + + if (wr) + memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, + buffer, blksize); + else + memcpy_fromio(buffer, + prv->regs + NFC_SPARE_AREA(s) + o, + blksize); + + buffer += blksize; + offset += blksize; + size -= blksize; + }; +} + +/* Copy data from/to NFC main and spare buffers */ +static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char * buf, int len, + int wr) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + uint c = prv->column; + uint l; + + /* Handle spare area access */ + if (prv->spareonly || c >= mtd->writesize) { + /* Calculate offset from beginning of spare area */ + if (c >= mtd->writesize) + c -= mtd->writesize; + + prv->column += len; + mpc5121_nfc_copy_spare(mtd, c, buf, len, wr); + return; + } + + /* + * Handle main area access - limit copy length to prevent + * crossing main/spare boundary. + */ + l = min((uint) len, mtd->writesize - c); + prv->column += l; + + if (wr) + memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); + else + memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); + + /* Handle crossing main/spare boundary */ + if (l != len) { + buf += l; + len -= l; + mpc5121_nfc_buf_copy(mtd, buf, len, wr); + } +} + +/* Read data from NFC buffers */ +static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, buf, len, 0); +} + +/* Write data to NFC buffers */ +static void mpc5121_nfc_write_buf(struct mtd_info *mtd, + const u_char * buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, (u_char *) buf, len, 1); +} + +/* Compare buffer with NAND flash */ +static int mpc5121_nfc_verify_buf(struct mtd_info *mtd, + const u_char * buf, int len) +{ + u_char tmp[256]; + uint bsize; + + while (len) { + bsize = min(len, 256); + mpc5121_nfc_read_buf(mtd, tmp, bsize); + + if (memcmp(buf, tmp, bsize)) + return 1; + + buf += bsize; + len -= bsize; + } + + return 0; +} + +/* Read byte from NFC buffers */ +static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) +{ + u8 tmp; + + mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp)); + + return tmp; +} + +/* Read word from NFC buffers */ +static u16 mpc5121_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + mpc5121_nfc_read_buf(mtd, (u_char *) & tmp, sizeof(tmp)); + + return tmp; +} + +/* + * Read NFC configuration from Reset Config Word + * + * NFC is configured during reset in basis of information stored + * in Reset Config Word. There is no other way to set NAND block + * size, spare size and bus width. + */ +static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + struct nand_chip *chip = mtd->priv; + uint rcw_pagesize = 0; + uint rcw_sparesize = 0; + uint rcw_width; + uint rcwh; + uint romloc, ps; + + rcwh = in_be32(&(im->reset.rcwh)); + + /* Bit 6: NFC bus width */ + rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1; + + /* Bit 7: NFC Page/Spare size */ + ps = (rcwh >> 7) & 0x1; + + /* Bits [22:21]: ROM Location */ + romloc = (rcwh >> 21) & 0x3; + + /* Decode RCW bits */ + switch ((ps << 2) | romloc) { + case 0x00: + case 0x01: + rcw_pagesize = 512; + rcw_sparesize = 16; + break; + case 0x02: + case 0x03: + rcw_pagesize = 4096; + rcw_sparesize = 128; + break; + case 0x04: + case 0x05: + rcw_pagesize = 2048; + rcw_sparesize = 64; + break; + case 0x06: + case 0x07: + rcw_pagesize = 4096; + rcw_sparesize = 218; + break; + } + + mtd->writesize = rcw_pagesize; + mtd->oobsize = rcw_sparesize; + if (rcw_width == 2) + chip->options |= NAND_BUSWIDTH_16; + + debug(KERN_NOTICE DRV_NAME ": Configured for " + "%u-bit NAND, page size %u with %u spare.\n", + rcw_width * 8, rcw_pagesize, rcw_sparesize); + return 0; +} + +int board_nand_init(struct nand_chip *chip) +{ + struct mpc5121_nfc_prv *prv; + struct mtd_info *mtd; + int resettime = 0; + int retval = 0; + int rev; + static int chip_nr = 0; + + /* + * Check SoC revision. This driver supports only NFC + * in MPC5121 revision 2. + */ + rev = (mfspr(SPRN_SVR) >> 4) & 0xF; + if (rev != 2) { + printk(KERN_ERR DRV_NAME + ": SoC revision %u is not supported!\n", rev); + return -ENXIO; + } + + prv = malloc(sizeof(*prv)); + if (!prv) { + printk(KERN_ERR DRV_NAME ": Memory exhausted!\n"); + return -ENOMEM; + } + + mtd = &nand_info[chip_nr++]; + mtd->priv = chip; + chip->priv = prv; + + /* Read NFC configuration from Reset Config Word */ + retval = mpc5121_nfc_read_hw_config(mtd); + if (retval) { + printk(KERN_ERR DRV_NAME ": Unable to read NFC config!\n"); + return retval; + } + + prv->regs = (void __iomem *)CONFIG_SYS_NAND_BASE; + chip->dev_ready = mpc5121_nfc_dev_ready; + chip->cmdfunc = mpc5121_nfc_command; + chip->read_byte = mpc5121_nfc_read_byte; + chip->read_word = mpc5121_nfc_read_word; + chip->read_buf = mpc5121_nfc_read_buf; + chip->write_buf = mpc5121_nfc_write_buf; + chip->verify_buf = mpc5121_nfc_verify_buf; + chip->select_chip = mpc5121_nfc_select_chip; + chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; + chip->ecc.mode = NAND_ECC_SOFT; + + /* Reset NAND Flash controller */ + nfc_set(mtd, NFC_CONFIG1, NFC_RESET); + while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) { + if (resettime++ >= NFC_RESET_TIMEOUT) { + printk(KERN_ERR DRV_NAME + ": Timeout while resetting NFC!\n"); + retval = -EINVAL; + goto error; + } + + udelay(1); + } + + /* Enable write to NFC memory */ + nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED); + + /* Enable write to all NAND pages */ + nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000); + nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF); + nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK); + + /* + * Setup NFC: + * - Big Endian transfers, + * - Interrupt after full page read/write. + */ + nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK | + NFC_FULL_PAGE_INT); + + /* Set spare area size */ + nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1); + + /* Detect NAND chips */ + if (nand_scan(mtd, 1)) { + printk(KERN_ERR DRV_NAME ": NAND Flash not found !\n"); + retval = -ENXIO; + goto error; + } + + /* Set erase block size */ + switch (mtd->erasesize / mtd->writesize) { + case 32: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32); + break; + + case 64: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64); + break; + + case 128: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128); + break; + + case 256: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256); + break; + + default: + printk(KERN_ERR DRV_NAME ": Unsupported NAND flash!\n"); + retval = -ENXIO; + goto error; + } + + return 0; +error: + return retval; +} diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c index d369115..9065fa9 100644 --- a/drivers/mtd/nand/nand.c +++ b/drivers/mtd/nand/nand.c @@ -57,7 +57,7 @@ static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand, else mtd->name += gd->reloc_off; -#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_DEVICE /* * Add MTD device so that we can reference it later * via the mtdcore infrastructure (e.g. ubi). diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 6ba52b3..88206d0 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -36,12 +36,15 @@ #include <malloc.h> #include <div64.h> - #include <asm/errno.h> #include <linux/mtd/mtd.h> #include <nand.h> #include <jffs2/jffs2.h> +#if !defined(CONFIG_SYS_64BIT_VSPRINTF) +#warning Please define CONFIG_SYS_64BIT_VSPRINTF for correct output! +#endif + typedef struct erase_info erase_info_t; typedef struct mtd_info mtd_info_t; @@ -127,7 +130,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) if (ret > 0) { if (!opts->quiet) printf("\rSkipping bad block at " - "0x%08x " + "0x%08llx " " \n", erase.addr); continue; @@ -181,11 +184,11 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) if (percent != percent_complete) { percent_complete = percent; - printf("\rErasing at 0x%x -- %3d%% complete.", + printf("\rErasing at 0x%llx -- %3d%% complete.", erase.addr, percent); if (opts->jffs2 && result == 0) - printf(" Cleanmarker written at 0x%x.", + printf(" Cleanmarker written at 0x%llx.", erase.addr); } } diff --git a/drivers/mtd/onenand/onenand_uboot.c b/drivers/mtd/onenand/onenand_uboot.c index a95b922..9823b5b 100644 --- a/drivers/mtd/onenand/onenand_uboot.c +++ b/drivers/mtd/onenand/onenand_uboot.c @@ -43,7 +43,7 @@ void onenand_init(void) puts("OneNAND: "); print_size(onenand_mtd.size, "\n"); -#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_DEVICE /* * Add MTD device so that we can reference it later * via the mtdcore infrastructure (e.g. ubi). diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index a71b16e..27dcbff 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -27,6 +27,7 @@ LIB := $(obj)libspi_flash.a COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o +COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c index c3b936f..3bc2dff 100644 --- a/drivers/mtd/spi/atmel.c +++ b/drivers/mtd/spi/atmel.c @@ -196,6 +196,75 @@ static int dataflash_read_fast_at45(struct spi_flash *flash, return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); } +/* + * TODO: the two write funcs (_p2/_at45) should get unified ... + */ +static int dataflash_write_p2(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_size; + u32 addr = offset; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * TODO: This function currently uses only page buffer #1. We can + * speed this up by using both buffers and loading one buffer while + * the other is being programmed into main memory. + */ + + page_size = (1 << asf->params->l2_page_size); + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - (addr % page_size)); + + /* Use the same address bits for both commands */ + cmd[0] = CMD_AT45_LOAD_BUF1; + cmd[1] = addr >> 16; + cmd[2] = addr >> 8; + cmd[3] = addr; + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Loading AT45 buffer failed\n"); + goto out; + } + + cmd[0] = CMD_AT45_PROG_BUF1; + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page programming failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page programming timed out\n"); + goto out; + } + + addr += chunk_len; + } + + debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + static int dataflash_write_at45(struct spi_flash *flash, u32 offset, size_t len, const void *buf) { @@ -209,6 +278,12 @@ static int dataflash_write_at45(struct spi_flash *flash, int ret; u8 cmd[4]; + /* + * TODO: This function currently uses only page buffer #1. We can + * speed this up by using both buffers and loading one buffer while + * the other is being programmed into main memory. + */ + page_shift = asf->params->l2_page_size; page_size = (1 << page_shift) + (1 << (page_shift - 5)); page_shift++; @@ -263,6 +338,68 @@ out: return ret; } +/* + * TODO: the two erase funcs (_p2/_at45) should get unified ... + */ +int dataflash_erase_p2(struct spi_flash *flash, u32 offset, size_t len) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_size; + + size_t actual; + int ret; + u8 cmd[4]; + + /* + * TODO: This function currently uses page erase only. We can + * probably speed things up by using block and/or sector erase + * when possible. + */ + + page_size = (1 << asf->params->l2_page_size); + + if (offset % page_size || len % page_size) { + debug("SF: Erase offset/length not multiple of page size\n"); + return -1; + } + + cmd[0] = CMD_AT45_ERASE_PAGE; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += page_size) { + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page erase failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page erase timed out\n"); + goto out; + } + + offset += page_size; + } + + debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len) { struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); @@ -382,6 +519,8 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) page_size += 1 << (params->l2_page_size - 5); } else { asf->flash.read = dataflash_read_fast_p2; + asf->flash.write = dataflash_write_p2; + asf->flash.erase = dataflash_erase_p2; } break; diff --git a/drivers/mtd/spi/macronix.c b/drivers/mtd/spi/macronix.c new file mode 100644 index 0000000..9464c84 --- /dev/null +++ b/drivers/mtd/spi/macronix.c @@ -0,0 +1,312 @@ +/* + * Copyright 2009(C) Marvell International Ltd. and its affiliates + * Prafulla Wadaskar <prafulla@marvell.com> + * + * Based on drivers/mtd/spi/stmicro.c + * + * Copyright 2008, Network Appliance Inc. + * Jason McMullan <mcmullan@netapp.com> + * + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* MX25xx-specific commands */ +#define CMD_MX25XX_WREN 0x06 /* Write Enable */ +#define CMD_MX25XX_WRDI 0x04 /* Write Disable */ +#define CMD_MX25XX_RDSR 0x05 /* Read Status Register */ +#define CMD_MX25XX_WRSR 0x01 /* Write Status Register */ +#define CMD_MX25XX_READ 0x03 /* Read Data Bytes */ +#define CMD_MX25XX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_MX25XX_PP 0x02 /* Page Program */ +#define CMD_MX25XX_SE 0x20 /* Sector Erase */ +#define CMD_MX25XX_BE 0xD8 /* Block Erase */ +#define CMD_MX25XX_CE 0xc7 /* Chip Erase */ +#define CMD_MX25XX_DP 0xb9 /* Deep Power-down */ +#define CMD_MX25XX_RES 0xab /* Release from DP, and Read Signature */ + +#define MXIC_ID_MX2516 0x15 +#define MXIC_ID_MX2520 0x12 +#define MXIC_ID_MX2532 0x16 +#define MXIC_ID_MX2540 0x13 +#define MXIC_ID_MX2564 0x17 +#define MXIC_ID_MX2580 0x14 +#define MXIC_ID_MX25128 0x18 + +#define MACRONIX_SR_WIP (1 << 0) /* Write-in-Progress */ + +struct macronix_spi_flash_params { + u8 idcode1; + u16 page_size; + u16 pages_per_sector; + u16 sectors_per_block; + u16 nr_blocks; + const char *name; +}; + +struct macronix_spi_flash { + struct spi_flash flash; + const struct macronix_spi_flash_params *params; +}; + +static inline struct macronix_spi_flash *to_macronix_spi_flash(struct spi_flash + *flash) +{ + return container_of(flash, struct macronix_spi_flash, flash); +} + +static const struct macronix_spi_flash_params macronix_spi_flash_table[] = { + { + .idcode1 = MXIC_ID_MX25128, + .page_size = 256, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 256, + .name = "MX25L12805D", + }, +}; + +static int macronix_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 status; + u8 cmd = CMD_MX25XX_RDSR; + + ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + timebase = get_timer(0); + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if ((status & MACRONIX_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & MACRONIX_SR_WIP) == 0) + return 0; + + /* Timed out */ + return -1; +} + +static int macronix_read_fast(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct macronix_spi_flash *mcx = to_macronix_spi_flash(flash); + unsigned long page_addr; + unsigned long page_size; + u8 cmd[5]; + + page_size = mcx->params->page_size; + page_addr = offset / page_size; + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = offset % page_size; + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int macronix_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct macronix_spi_flash *mcx = to_macronix_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_size = mcx->params->page_size; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + cmd[0] = CMD_MX25XX_PP; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = byte_addr; + + debug + ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", + buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_cmd(flash->spi, CMD_MX25XX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Macronix Page Program failed\n"); + break; + } + + ret = macronix_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: Macronix page programming timed out\n"); + break; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: Macronix: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + + spi_release_bus(flash->spi); + return ret; +} + +int macronix_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + struct macronix_spi_flash *mcx = to_macronix_spi_flash(flash); + unsigned long sector_size; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * This function currently uses sector erase only. + * probably speed things up by using bulk erase + * when possible. + */ + + sector_size = mcx->params->page_size * mcx->params->pages_per_sector + * mcx->params->sectors_per_block; + + if (offset % sector_size || len % sector_size) { + debug("SF: Erase offset/length not multiple of sector size\n"); + return -1; + } + + len /= sector_size; + cmd[0] = CMD_MX25XX_BE; + cmd[2] = 0x00; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual++) { + cmd[1] = (offset / sector_size) + actual; + + ret = spi_flash_cmd(flash->spi, CMD_MX25XX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: Macronix page erase failed\n"); + break; + } + + ret = macronix_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: Macronix page erase timed out\n"); + break; + } + } + + debug("SF: Macronix: Successfully erased %u bytes @ 0x%x\n", + len * sector_size, offset); + + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode) +{ + const struct macronix_spi_flash_params *params; + struct macronix_spi_flash *mcx; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(macronix_spi_flash_table); i++) { + params = ¯onix_spi_flash_table[i]; + if (params->idcode1 == idcode[2]) + break; + } + + if (i == ARRAY_SIZE(macronix_spi_flash_table)) { + debug("SF: Unsupported Macronix ID %02x\n", idcode[1]); + return NULL; + } + + mcx = malloc(sizeof(*mcx)); + if (!mcx) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + mcx->params = params; + mcx->flash.spi = spi; + mcx->flash.name = params->name; + + mcx->flash.write = macronix_write; + mcx->flash.erase = macronix_erase; + mcx->flash.read = macronix_read_fast; + mcx->flash.size = params->page_size * params->pages_per_sector + * params->sectors_per_block * params->nr_blocks; + + printf("SF: Detected %s with page size %u, total %u bytes\n", + params->name, params->page_size, mcx->flash.size); + + return &mcx->flash; +} diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 274895a..0c83231 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -134,6 +134,11 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, flash = spi_flash_probe_atmel(spi, idcode); break; #endif +#ifdef CONFIG_SPI_FLASH_MACRONIX + case 0xc2: + flash = spi_flash_probe_macronix(spi, idcode); + break; +#endif #ifdef CONFIG_SPI_FLASH_STMICRO case 0x20: flash = spi_flash_probe_stmicro(spi, idcode); diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index 5d1e395..0612383 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -46,5 +46,6 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, /* Manufacturer-specific probe functions */ struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode); diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index f4b01a9..4f50b2d 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -46,6 +46,10 @@ #include <ubi_uboot.h> #include "ubi.h" +#if (CONFIG_SYS_MALLOC_LEN < (512 << 10)) +#error Malloc area too small for UBI, increase CONFIG_SYS_MALLOC_LEN to >= 512k +#endif + /* Maximum length of the 'mtd=' parameter */ #define MTD_PARAM_LEN_MAX 64 diff --git a/drivers/net/mpc512x_fec.c b/drivers/net/mpc512x_fec.c index 7078c4e..fb2c19a 100644 --- a/drivers/net/mpc512x_fec.c +++ b/drivers/net/mpc512x_fec.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2003-2007 + * (C) Copyright 2003-2009 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * Derived from the MPC8xx FEC driver. @@ -7,11 +7,11 @@ */ #include <common.h> -#include <mpc512x.h> #include <malloc.h> #include <net.h> #include <netdev.h> #include <miiphy.h> +#include <asm/io.h> #include "mpc512x_fec.h" DECLARE_GLOBAL_DATA_PTR; @@ -26,11 +26,11 @@ DECLARE_GLOBAL_DATA_PTR; #endif #if (DEBUG & 0x40) -static uint32 local_crc32(char *string, unsigned int crc_value, int len); +static u32 local_crc32(char *string, unsigned int crc_value, int len); #endif -int fec512x_miiphy_read(char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal); -int fec512x_miiphy_write(char *devname, uint8 phyAddr, uint8 regAddr, uint16 data); +int fec512x_miiphy_read(char *devname, u8 phyAddr, u8 regAddr, u16 * retVal); +int fec512x_miiphy_write(char *devname, u8 phyAddr, u8 regAddr, u16 data); int mpc512x_fec_init_phy(struct eth_device *dev, bd_t * bis); static uchar rx_buff[FEC_BUFFER_SIZE]; @@ -40,9 +40,9 @@ static int rx_buff_idx = 0; #if (DEBUG & 0x2) static void mpc512x_fec_phydump (char *devname) { - uint16 phyStatus, i; - uint8 phyAddr = CONFIG_PHY_ADDR; - uint8 reg_mask[] = { + u16 phyStatus, i; + u8 phyAddr = CONFIG_PHY_ADDR; + u8 reg_mask[] = { /* regs to print: 0...8, 21,27,31 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, @@ -66,7 +66,8 @@ static int mpc512x_fec_bd_init (mpc512x_fec_priv *fec) * Receive BDs init */ for (ix = 0; ix < FEC_RBD_NUM; ix++) { - fec->bdBase->rbd[ix].dataPointer = (uint32)&fec->bdBase->recv_frames[ix]; + fec->bdBase->rbd[ix].dataPointer = + (u32)&fec->bdBase->recv_frames[ix]; fec->bdBase->rbd[ix].status = FEC_RBD_EMPTY; fec->bdBase->rbd[ix].dataLength = 0; } @@ -119,8 +120,9 @@ static void mpc512x_fec_rbd_clean (mpc512x_fec_priv *fec, volatile FEC_RBD * pRb /* * Now, we have an empty RxBD, notify FEC + * Set Descriptor polling active */ - fec->eth->r_des_active = 0x01000000; /* Descriptor polling active */ + out_be32(&fec->eth->r_des_active, 0x01000000); } /********************************************************************/ @@ -164,10 +166,10 @@ static void mpc512x_fec_tbd_scrub (mpc512x_fec_priv *fec) /********************************************************************/ static void mpc512x_fec_set_hwaddr (mpc512x_fec_priv *fec, char *mac) { - uint8 currByte; /* byte for which to compute the CRC */ + u8 currByte; /* byte for which to compute the CRC */ int byte; /* loop - counter */ int bit; /* loop - counter */ - uint32 crc = 0xffffffff; /* initial value */ + u32 crc = 0xffffffff; /* initial value */ /* * The algorithm used is the following: @@ -203,18 +205,20 @@ static void mpc512x_fec_set_hwaddr (mpc512x_fec_priv *fec, char *mac) * Set individual hash table register */ if (crc >= 32) { - fec->eth->iaddr1 = (1 << (crc - 32)); - fec->eth->iaddr2 = 0; + out_be32(&fec->eth->iaddr1, (1 << (crc - 32))); + out_be32(&fec->eth->iaddr2, 0); } else { - fec->eth->iaddr1 = 0; - fec->eth->iaddr2 = (1 << crc); + out_be32(&fec->eth->iaddr1, 0); + out_be32(&fec->eth->iaddr2, (1 << crc)); } /* * Set physical address */ - fec->eth->paddr1 = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; - fec->eth->paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; + out_be32(&fec->eth->paddr1, (mac[0] << 24) + (mac[1] << 16) + + (mac[2] << 8) + mac[3]); + out_be32(&fec->eth->paddr2, (mac[4] << 24) + (mac[5] << 16) + + 0x8808); } /********************************************************************/ @@ -227,45 +231,45 @@ static int mpc512x_fec_init (struct eth_device *dev, bd_t * bis) #endif /* Set interrupt mask register */ - fec->eth->imask = 0x00000000; + out_be32(&fec->eth->imask, 0x00000000); /* Clear FEC-Lite interrupt event register(IEVENT) */ - fec->eth->ievent = 0xffffffff; + out_be32(&fec->eth->ievent, 0xffffffff); /* Set transmit fifo watermark register(X_WMRK), default = 64 */ - fec->eth->x_wmrk = 0x0; + out_be32(&fec->eth->x_wmrk, 0x0); /* Set Opcode/Pause Duration Register */ - fec->eth->op_pause = 0x00010020; + out_be32(&fec->eth->op_pause, 0x00010020); /* Frame length=1522; MII mode */ - fec->eth->r_cntrl = (FEC_MAX_FRAME_LEN << 16) | 0x24; + out_be32(&fec->eth->r_cntrl, (FEC_MAX_FRAME_LEN << 16) | 0x24); /* Half-duplex, heartbeat disabled */ - fec->eth->x_cntrl = 0x00000000; + out_be32(&fec->eth->x_cntrl, 0x00000000); /* Enable MIB counters */ - fec->eth->mib_control = 0x0; + out_be32(&fec->eth->mib_control, 0x0); /* Setup recv fifo start and buff size */ - fec->eth->r_fstart = 0x500; - fec->eth->r_buff_size = FEC_BUFFER_SIZE; + out_be32(&fec->eth->r_fstart, 0x500); + out_be32(&fec->eth->r_buff_size, FEC_BUFFER_SIZE); /* Setup BD base addresses */ - fec->eth->r_des_start = (uint32)fec->bdBase->rbd; - fec->eth->x_des_start = (uint32)fec->bdBase->tbd; + out_be32(&fec->eth->r_des_start, (u32)fec->bdBase->rbd); + out_be32(&fec->eth->x_des_start, (u32)fec->bdBase->tbd); /* DMA Control */ - fec->eth->dma_control = 0xc0000000; + out_be32(&fec->eth->dma_control, 0xc0000000); /* Enable FEC */ - fec->eth->ecntrl |= 0x00000006; + setbits_be32(&fec->eth->ecntrl, 0x00000006); /* Initilize addresses and status words of BDs */ mpc512x_fec_bd_init (fec); /* Descriptor polling active */ - fec->eth->r_des_active = 0x01000000; + out_be32(&fec->eth->r_des_active, 0x01000000); #if (DEBUG & 0x1) printf("mpc512x_fec_init... Done \n"); @@ -277,9 +281,9 @@ static int mpc512x_fec_init (struct eth_device *dev, bd_t * bis) int mpc512x_fec_init_phy (struct eth_device *dev, bd_t * bis) { mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; - const uint8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + const u8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ int timeout = 1; - uint16 phyStatus; + u16 phyStatus; #if (DEBUG & 0x1) printf ("mpc512x_fec_init_phy... Begin\n"); @@ -288,19 +292,20 @@ int mpc512x_fec_init_phy (struct eth_device *dev, bd_t * bis) /* * Clear FEC-Lite interrupt event register(IEVENT) */ - fec->eth->ievent = 0xffffffff; + out_be32(&fec->eth->ievent, 0xffffffff); /* * Set interrupt mask register */ - fec->eth->imask = 0x00000000; + out_be32(&fec->eth->imask, 0x00000000); if (fec->xcv_type != SEVENWIRE) { /* * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock * and do not drop the Preamble. */ - fec->eth->mii_speed = (((gd->ips_clk / 1000000) / 5) + 1) << 1; + out_be32(&fec->eth->mii_speed, + (((gd->ips_clk / 1000000) / 5) + 1) << 1); /* * Reset PHY, then delay 300ns @@ -414,27 +419,28 @@ static void mpc512x_fec_halt (struct eth_device *dev) /* * mask FEC chip interrupts */ - fec->eth->imask = 0; + out_be32(&fec->eth->imask, 0); /* * issue graceful stop command to the FEC transmitter if necessary */ - fec->eth->x_cntrl |= 0x00000001; + setbits_be32(&fec->eth->x_cntrl, 0x00000001); /* * wait for graceful stop to register */ - while ((counter--) && (!(fec->eth->ievent & 0x10000000))) ; + while ((counter--) && (!(in_be32(&fec->eth->ievent) & 0x10000000))) + ; /* * Disable the Ethernet Controller */ - fec->eth->ecntrl &= 0xfffffffd; + clrbits_be32(&fec->eth->ecntrl, 0x00000002); /* * Issue a reset command to the FEC chip */ - fec->eth->ecntrl |= 0x1; + setbits_be32(&fec->eth->ecntrl, 0x1); /* * wait at least 16 clock cycles @@ -488,12 +494,12 @@ static int mpc512x_fec_send (struct eth_device *dev, volatile void *eth_data, */ pTbd = &fec->bdBase->tbd[fec->tbdIndex]; pTbd->dataLength = data_length; - pTbd->dataPointer = (uint32)eth_data; + pTbd->dataPointer = (u32)eth_data; pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; /* Activate transmit Buffer Descriptor polling */ - fec->eth->x_des_active = 0x01000000; /* Descriptor polling active */ + out_be32(&fec->eth->x_des_active, 0x01000000); #if (DEBUG & 0x8) printf ( "+" ); @@ -536,8 +542,8 @@ static int mpc512x_fec_recv (struct eth_device *dev) /* * Check if any critical events have happened */ - ievent = fec->eth->ievent; - fec->eth->ievent = ievent; + ievent = in_be32(&fec->eth->ievent); + out_be32(&fec->eth->ievent, ievent); if (ievent & 0x20060000) { /* BABT, Rx/Tx FIFO errors */ mpc512x_fec_halt (dev); @@ -546,13 +552,13 @@ static int mpc512x_fec_recv (struct eth_device *dev) } if (ievent & 0x80000000) { /* Heartbeat error */ - fec->eth->x_cntrl |= 0x00000001; + setbits_be32(&fec->eth->x_cntrl, 0x00000001); } if (ievent & 0x10000000) { /* Graceful stop complete */ - if (fec->eth->x_cntrl & 0x00000001) { + if (in_be32(&fec->eth->x_cntrl) & 0x00000001) { mpc512x_fec_halt (dev); - fec->eth->x_cntrl &= ~0x00000001; + clrbits_be32(&fec->eth->x_cntrl, 0x00000001);; mpc512x_fec_init (dev, NULL); } } @@ -574,7 +580,7 @@ static int mpc512x_fec_recv (struct eth_device *dev) printf ("recv data length 0x%08x data hdr: ", pRbd->dataLength); for (i = 0; i < 14; i++) - printf ("%x ", *((uint8*)pRbd->dataPointer + i)); + printf ("%x ", *((u8*)pRbd->dataPointer + i)); printf("\n"); } #endif @@ -598,13 +604,15 @@ static int mpc512x_fec_recv (struct eth_device *dev) } /* Try to fill Buffer Descriptors */ - fec->eth->r_des_active = 0x01000000; /* Descriptor polling active */ + out_be32(&fec->eth->r_des_active, 0x01000000); + return frame_length; } /********************************************************************/ int mpc512x_fec_initialize (bd_t * bis) { + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; mpc512x_fec_priv *fec; struct eth_device *dev; int i; @@ -615,7 +623,7 @@ int mpc512x_fec_initialize (bd_t * bis) dev = (struct eth_device *) malloc (sizeof(*dev)); memset (dev, 0, sizeof *dev); - fec->eth = (ethernet_regs *) MPC512X_FEC; + fec->eth = &im->fec; # ifndef CONFIG_FEC_10MBIT fec->xcv_type = MII100; @@ -623,7 +631,7 @@ int mpc512x_fec_initialize (bd_t * bis) fec->xcv_type = MII10; # endif dev->priv = (void *)fec; - dev->iobase = MPC512X_FEC; + dev->iobase = (int)&im->fec; dev->init = mpc512x_fec_init; dev->halt = mpc512x_fec_halt; dev->send = mpc512x_fec_send; @@ -638,25 +646,26 @@ int mpc512x_fec_initialize (bd_t * bis) #endif /* Clean up space FEC's MIB and FIFO RAM ...*/ - memset ((void *) MPC512X_FEC + 0x200, 0x00, 0x400); + memset ((void *)&im->fec.mib, 0x00, sizeof(im->fec.mib)); + memset ((void *)&im->fec.fifo, 0x00, sizeof(im->fec.fifo)); /* * Malloc space for BDs (must be quad word-aligned) * this pointer is lost, so cannot be freed */ bd = malloc (sizeof(mpc512x_buff_descs) + 0x1f); - fec->bdBase = (mpc512x_buff_descs*)((uint32)bd & 0xfffffff0); + fec->bdBase = (mpc512x_buff_descs*)((u32)bd & 0xfffffff0); memset ((void *) bd, 0x00, sizeof(mpc512x_buff_descs) + 0x1f); /* * Set interrupt mask register */ - fec->eth->imask = 0x00000000; + out_be32(&fec->eth->imask, 0x00000000); /* * Clear FEC-Lite interrupt event register(IEVENT) */ - fec->eth->ievent = 0xffffffff; + out_be32(&fec->eth->ievent, 0xffffffff); /* * Try to set the mac address now. The fec mac address is @@ -671,8 +680,8 @@ int mpc512x_fec_initialize (bd_t * bis) tmp = (*end) ? end+1 : end; } mpc512x_fec_set_hwaddr (fec, env_enetaddr); - fec->eth->gaddr1 = 0x00000000; - fec->eth->gaddr2 = 0x00000000; + out_be32(&fec->eth->gaddr1, 0x00000000); + out_be32(&fec->eth->gaddr2, 0x00000000); } mpc512x_fec_init_phy (dev, bis); @@ -682,11 +691,12 @@ int mpc512x_fec_initialize (bd_t * bis) /* MII-interface related functions */ /********************************************************************/ -int fec512x_miiphy_read (char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal) +int fec512x_miiphy_read (char *devname, u8 phyAddr, u8 regAddr, u16 * retVal) { - ethernet_regs *eth = (ethernet_regs *) MPC512X_FEC; - uint32 reg; /* convenient holder for the PHY register */ - uint32 phy; /* convenient holder for the PHY */ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile fec512x_t *eth = &im->fec; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ int timeout = 0xffff; /* @@ -696,12 +706,16 @@ int fec512x_miiphy_read (char *devname, uint8 phyAddr, uint8 regAddr, uint16 * r reg = regAddr << FEC_MII_DATA_RA_SHIFT; phy = phyAddr << FEC_MII_DATA_PA_SHIFT; - eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | phy | reg); + out_be32(ð->mii_data, FEC_MII_DATA_ST | + FEC_MII_DATA_OP_RD | + FEC_MII_DATA_TA | + phy | reg); /* * wait for the related interrupt */ - while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + while ((timeout--) && (!(in_be32(ð->ievent) & 0x00800000))) + ; if (timeout == 0) { #if (DEBUG & 0x2) @@ -713,34 +727,38 @@ int fec512x_miiphy_read (char *devname, uint8 phyAddr, uint8 regAddr, uint16 * r /* * clear mii interrupt bit */ - eth->ievent = 0x00800000; + out_be32(ð->ievent, 0x00800000); /* * it's now safe to read the PHY's register */ - *retVal = (uint16) eth->mii_data; + *retVal = (u16) in_be32(ð->mii_data); return 0; } /********************************************************************/ -int fec512x_miiphy_write (char *devname, uint8 phyAddr, uint8 regAddr, uint16 data) +int fec512x_miiphy_write (char *devname, u8 phyAddr, u8 regAddr, u16 data) { - ethernet_regs *eth = (ethernet_regs *) MPC512X_FEC; - uint32 reg; /* convenient holder for the PHY register */ - uint32 phy; /* convenient holder for the PHY */ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile fec512x_t *eth = &im->fec; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ int timeout = 0xffff; reg = regAddr << FEC_MII_DATA_RA_SHIFT; phy = phyAddr << FEC_MII_DATA_PA_SHIFT; - eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | - FEC_MII_DATA_TA | phy | reg | data); + out_be32(ð->mii_data, FEC_MII_DATA_ST | + FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | + phy | reg | data); /* * wait for the MII interrupt */ - while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + while ((timeout--) && (!(in_be32(ð->ievent) & 0x00800000))) + ; if (timeout == 0) { #if (DEBUG & 0x2) @@ -752,13 +770,13 @@ int fec512x_miiphy_write (char *devname, uint8 phyAddr, uint8 regAddr, uint16 da /* * clear MII interrupt bit */ - eth->ievent = 0x00800000; + out_be32(ð->ievent, 0x00800000); return 0; } #if (DEBUG & 0x40) -static uint32 local_crc32 (char *string, unsigned int crc_value, int len) +static u32 local_crc32 (char *string, unsigned int crc_value, int len) { int i; char c; diff --git a/drivers/net/mpc512x_fec.h b/drivers/net/mpc512x_fec.h index 9c38502..a083cca 100644 --- a/drivers/net/mpc512x_fec.h +++ b/drivers/net/mpc512x_fec.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2003 - 2007 + * (C) Copyright 2003 - 2009 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * Derived from the MPC8xx driver's header file. @@ -9,147 +9,18 @@ #define __MPC512X_FEC_H #include <common.h> -#include <mpc512x.h> - -typedef unsigned long uint32; -typedef unsigned short uint16; -typedef unsigned char uint8; - -typedef struct ethernet_register_set { - -/* [10:2]addr = 00 */ - -/* Control and status Registers (offset 000-1FF) */ - - volatile uint32 fec_id; /* MBAR_ETH + 0x000 */ - volatile uint32 ievent; /* MBAR_ETH + 0x004 */ - volatile uint32 imask; /* MBAR_ETH + 0x008 */ - - volatile uint32 RES0[1]; /* MBAR_ETH + 0x00C */ - volatile uint32 r_des_active; /* MBAR_ETH + 0x010 */ - volatile uint32 x_des_active; /* MBAR_ETH + 0x014 */ - - volatile uint32 RES1[3]; /* MBAR_ETH + 0x018-020 */ - volatile uint32 ecntrl; /* MBAR_ETH + 0x024 */ - - volatile uint32 RES2[6]; /* MBAR_ETH + 0x028-03C */ - volatile uint32 mii_data; /* MBAR_ETH + 0x040 */ - volatile uint32 mii_speed; /* MBAR_ETH + 0x044 */ - - volatile uint32 RES3[7]; /* MBAR_ETH + 0x048-060 */ - volatile uint32 mib_control; /* MBAR_ETH + 0x064 */ - - volatile uint32 RES4[7]; /* MBAR_ETH + 0x068-80 */ - volatile uint32 r_cntrl; /* MBAR_ETH + 0x084 */ - volatile uint32 r_hash; /* MBAR_ETH + 0x088 */ - - volatile uint32 RES5[14]; /* MBAR_ETH + 0x08c-0C0 */ - volatile uint32 x_cntrl; /* MBAR_ETH + 0x0C4 */ - - volatile uint32 RES6[7]; /* MBAR_ETH + 0x0C8-0E0 */ - volatile uint32 paddr1; /* MBAR_ETH + 0x0E4 */ - volatile uint32 paddr2; /* MBAR_ETH + 0x0E8 */ - volatile uint32 op_pause; /* MBAR_ETH + 0x0EC */ - - volatile uint32 RES7[10]; /* MBAR_ETH + 0x0F0-114 */ - volatile uint32 iaddr1; /* MBAR_ETH + 0x118 */ - volatile uint32 iaddr2; /* MBAR_ETH + 0x11C */ - volatile uint32 gaddr1; /* MBAR_ETH + 0x120 */ - volatile uint32 gaddr2; /* MBAR_ETH + 0x124 */ - - volatile uint32 RES8[6]; /* MBAR_ETH + 0x128-13C */ - volatile uint32 fifo_id; /* MBAR_ETH + 0x140 */ - volatile uint32 x_wmrk; /* MBAR_ETH + 0x144 */ - volatile uint32 RES9[1]; /* MBAR_ETH + 0x148 */ - volatile uint32 r_bound; /* MBAR_ETH + 0x14C */ - volatile uint32 r_fstart; /* MBAR_ETH + 0x150 */ - - volatile uint32 RES10[11]; /* MBAR_ETH + 0x154-17C */ - volatile uint32 r_des_start; /* MBAR_ETH + 0x180 */ - volatile uint32 x_des_start; /* MBAR_ETH + 0x184 */ - volatile uint32 r_buff_size; /* MBAR_ETH + 0x188 */ - volatile uint32 RES11[26]; /* MBAR_ETH + 0x18C-1F0 */ - volatile uint32 dma_control; /* MBAR_ETH + 0x1F4 */ - volatile uint32 RES12[2]; /* MBAR_ETH + 0x1F8-1FC */ - -/* MIB COUNTERS (Offset 200-2FF) */ - - volatile uint32 rmon_t_drop; /* MBAR_ETH + 0x200 */ - volatile uint32 rmon_t_packets; /* MBAR_ETH + 0x204 */ - volatile uint32 rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ - volatile uint32 rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ - volatile uint32 rmon_t_crc_align; /* MBAR_ETH + 0x210 */ - volatile uint32 rmon_t_undersize; /* MBAR_ETH + 0x214 */ - volatile uint32 rmon_t_oversize; /* MBAR_ETH + 0x218 */ - volatile uint32 rmon_t_frag; /* MBAR_ETH + 0x21C */ - volatile uint32 rmon_t_jab; /* MBAR_ETH + 0x220 */ - volatile uint32 rmon_t_col; /* MBAR_ETH + 0x224 */ - volatile uint32 rmon_t_p64; /* MBAR_ETH + 0x228 */ - volatile uint32 rmon_t_p65to127; /* MBAR_ETH + 0x22C */ - volatile uint32 rmon_t_p128to255; /* MBAR_ETH + 0x230 */ - volatile uint32 rmon_t_p256to511; /* MBAR_ETH + 0x234 */ - volatile uint32 rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ - volatile uint32 rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */ - volatile uint32 rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ - volatile uint32 rmon_t_octets; /* MBAR_ETH + 0x244 */ - volatile uint32 ieee_t_drop; /* MBAR_ETH + 0x248 */ - volatile uint32 ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ - volatile uint32 ieee_t_1col; /* MBAR_ETH + 0x250 */ - volatile uint32 ieee_t_mcol; /* MBAR_ETH + 0x254 */ - volatile uint32 ieee_t_def; /* MBAR_ETH + 0x258 */ - volatile uint32 ieee_t_lcol; /* MBAR_ETH + 0x25C */ - volatile uint32 ieee_t_excol; /* MBAR_ETH + 0x260 */ - volatile uint32 ieee_t_macerr; /* MBAR_ETH + 0x264 */ - volatile uint32 ieee_t_cserr; /* MBAR_ETH + 0x268 */ - volatile uint32 ieee_t_sqe; /* MBAR_ETH + 0x26C */ - volatile uint32 t_fdxfc; /* MBAR_ETH + 0x270 */ - volatile uint32 ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ - - volatile uint32 RES13[2]; /* MBAR_ETH + 0x278-27C */ - volatile uint32 rmon_r_drop; /* MBAR_ETH + 0x280 */ - volatile uint32 rmon_r_packets; /* MBAR_ETH + 0x284 */ - volatile uint32 rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ - volatile uint32 rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ - volatile uint32 rmon_r_crc_align; /* MBAR_ETH + 0x290 */ - volatile uint32 rmon_r_undersize; /* MBAR_ETH + 0x294 */ - volatile uint32 rmon_r_oversize; /* MBAR_ETH + 0x298 */ - volatile uint32 rmon_r_frag; /* MBAR_ETH + 0x29C */ - volatile uint32 rmon_r_jab; /* MBAR_ETH + 0x2A0 */ - - volatile uint32 rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ - - volatile uint32 rmon_r_p64; /* MBAR_ETH + 0x2A8 */ - volatile uint32 rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ - volatile uint32 rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ - volatile uint32 rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ - volatile uint32 rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ - volatile uint32 rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */ - volatile uint32 rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ - volatile uint32 rmon_r_octets; /* MBAR_ETH + 0x2C4 */ - volatile uint32 ieee_r_drop; /* MBAR_ETH + 0x2C8 */ - volatile uint32 ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ - volatile uint32 ieee_r_crc; /* MBAR_ETH + 0x2D0 */ - volatile uint32 ieee_r_align; /* MBAR_ETH + 0x2D4 */ - volatile uint32 r_macerr; /* MBAR_ETH + 0x2D8 */ - volatile uint32 r_fdxfc; /* MBAR_ETH + 0x2DC */ - volatile uint32 ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ - - volatile uint32 RES14[6]; /* MBAR_ETH + 0x2E4-2FC */ - - volatile uint32 RES15[64]; /* MBAR_ETH + 0x300-3FF */ -} ethernet_regs; /* Receive & Transmit Buffer Descriptor definitions */ typedef struct BufferDescriptor { - uint16 status; - uint16 dataLength; - uint32 dataPointer; + u16 status; + u16 dataLength; + u32 dataPointer; } FEC_RBD; typedef struct { - uint16 status; - uint16 dataLength; - uint32 dataPointer; + u16 status; + u16 dataLength; + u32 dataPointer; } FEC_TBD; /* private structure */ @@ -170,7 +41,7 @@ typedef enum { #define FEC_BUFFER_SIZE ((FEC_MAX_FRAME_LEN + 0x10) & (~0xf)) typedef struct { - uint8 frame[FEC_BUFFER_SIZE]; + u8 frame[FEC_BUFFER_SIZE]; } mpc512x_frame; typedef struct { @@ -180,13 +51,13 @@ typedef struct { } mpc512x_buff_descs; typedef struct { - ethernet_regs *eth; + volatile fec512x_t *eth; xceiver_type xcv_type; /* transceiver type */ mpc512x_buff_descs *bdBase; /* BD rings and recv buffer */ - uint16 rbdIndex; /* next receive BD to read */ - uint16 tbdIndex; /* next transmit BD to send */ - uint16 usedTbdIndex; /* next transmit BD to clean */ - uint16 cleanTbdNum; /* the number of available transmit BDs */ + u16 rbdIndex; /* next receive BD to read */ + u16 tbdIndex; /* next transmit BD to send */ + u16 usedTbdIndex; /* next transmit BD to clean */ + u16 cleanTbdNum; /* the number of available transmit BDs */ } mpc512x_fec_priv; /* RBD bits definitions */ diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index c20b981..35c82b9 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -403,7 +403,7 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev) PCI_DEV(dev)); break; #endif -#ifdef CONFIG_MPC834X +#ifdef CONFIG_MPC834x case PCI_CLASS_BRIDGE_OTHER: /* * The host/PCI bridge 1 seems broken in 8349 - it presents diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c index f114fe0..8882c4f 100644 --- a/drivers/qe/qe.c +++ b/drivers/qe/qe.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Freescale Semiconductor, Inc. + * Copyright (C) 2006-2009 Freescale Semiconductor, Inc. * * Dave Liu <daveliu@freescale.com> * based on source code of Shlomi Gridish @@ -108,14 +108,23 @@ static void qe_sdma_init(void) out_be32(&p->sdmr, QE_SDMR_GLB_1_MSK | (0x3 << QE_SDMR_CEN_SHIFT)); } -static u8 thread_snum[QE_NUM_OF_SNUM] = { +/* This table is a list of the serial numbers of the Threads, taken from the + * "SNUM Table" chart in the QE Reference Manual. The order is not important, + * we just need to know what the SNUMs are for the threads. + */ +static u8 thread_snum[] = { 0x04, 0x05, 0x0c, 0x0d, 0x14, 0x15, 0x1c, 0x1d, 0x24, 0x25, 0x2c, 0x2d, 0x34, 0x35, 0x88, 0x89, 0x98, 0x99, 0xa8, 0xa9, 0xb8, 0xb9, 0xc8, 0xc9, - 0xd8, 0xd9, 0xe8, 0xe9 + 0xd8, 0xd9, 0xe8, 0xe9, + 0x08, 0x09, 0x18, 0x19, + 0x28, 0x29, 0x38, 0x39, + 0x48, 0x49, 0x58, 0x59, + 0x68, 0x69, 0x78, 0x79, + 0x80, 0x81 }; static void qe_snums_init(void) @@ -258,9 +267,6 @@ int qe_set_mii_clk_src(int ucc_num) return 0; } -/* The maximum number of RISCs we support */ -#define MAX_QE_RISC 2 - /* Firmware information stored here for qe_get_firmware_info() */ static struct qe_firmware_info qe_firmware_info; @@ -473,5 +479,6 @@ U_BOOT_CMD( qe, 4, 0, qe_cmd, "QUICC Engine commands", "fw <addr> [<length>] - Upload firmware binary at address <addr> to " - "the QE,\n\twith optional length <length> verification.\n" - ); + "the QE,\n" + "\twith optional length <length> verification." +); diff --git a/drivers/qe/qe.h b/drivers/qe/qe.h index d78edba..faad43c 100644 --- a/drivers/qe/qe.h +++ b/drivers/qe/qe.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Freescale Semiconductor, Inc. + * Copyright (C) 2006-2009 Freescale Semiconductor, Inc. * * Dave Liu <daveliu@freescale.com> * based on source code of Shlomi Gridish @@ -25,7 +25,6 @@ #include "common.h" -#define QE_NUM_OF_SNUM 28 #define QE_NUM_OF_BRGS 16 #define UCC_MAX_NUM 8 @@ -46,11 +45,16 @@ typedef struct qe_snum { /* QE RISC allocation */ -typedef enum qe_risc_allocation { - QE_RISC_ALLOCATION_RISC1 = 1, /* RISC 1 */ - QE_RISC_ALLOCATION_RISC2 = 2, /* RISC 2 */ - QE_RISC_ALLOCATION_RISC1_AND_RISC2 = 3 /* RISC 1 or RISC 2 */ -} qe_risc_allocation_e; +#define QE_RISC_ALLOCATION_RISC1 0x1 /* RISC 1 */ +#define QE_RISC_ALLOCATION_RISC2 0x2 /* RISC 2 */ +#define QE_RISC_ALLOCATION_RISC3 0x4 /* RISC 3 */ +#define QE_RISC_ALLOCATION_RISC4 0x8 /* RISC 4 */ +#define QE_RISC_ALLOCATION_RISC1_AND_RISC2 (QE_RISC_ALLOCATION_RISC1 | \ + QE_RISC_ALLOCATION_RISC2) +#define QE_RISC_ALLOCATION_FOUR_RISCS (QE_RISC_ALLOCATION_RISC1 | \ + QE_RISC_ALLOCATION_RISC2 | \ + QE_RISC_ALLOCATION_RISC3 | \ + QE_RISC_ALLOCATION_RISC4) /* QE CECR commands for UCC fast. */ diff --git a/drivers/qe/uec.c b/drivers/qe/uec.c index bba3ef2..3686575 100644 --- a/drivers/qe/uec.c +++ b/drivers/qe/uec.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Freescale Semiconductor, Inc. + * Copyright (C) 2006-2009 Freescale Semiconductor, Inc. * * Dave Liu <daveliu@freescale.com> * @@ -31,146 +31,34 @@ #include "uec_phy.h" #include "miiphy.h" +static uec_info_t uec_info[] = { #ifdef CONFIG_UEC_ETH1 -static uec_info_t eth1_uec_info = { - .uf_info = { - .ucc_num = CONFIG_SYS_UEC1_UCC_NUM, - .rx_clock = CONFIG_SYS_UEC1_RX_CLK, - .tx_clock = CONFIG_SYS_UEC1_TX_CLK, - .eth_type = CONFIG_SYS_UEC1_ETH_TYPE, - }, -#if (CONFIG_SYS_UEC1_ETH_TYPE == FAST_ETH) - .num_threads_tx = UEC_NUM_OF_THREADS_1, - .num_threads_rx = UEC_NUM_OF_THREADS_1, -#else - .num_threads_tx = UEC_NUM_OF_THREADS_4, - .num_threads_rx = UEC_NUM_OF_THREADS_4, -#endif - .riscTx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .tx_bd_ring_len = 16, - .rx_bd_ring_len = 16, - .phy_address = CONFIG_SYS_UEC1_PHY_ADDR, - .enet_interface = CONFIG_SYS_UEC1_INTERFACE_MODE, -}; + STD_UEC_INFO(1), /* UEC1 */ #endif #ifdef CONFIG_UEC_ETH2 -static uec_info_t eth2_uec_info = { - .uf_info = { - .ucc_num = CONFIG_SYS_UEC2_UCC_NUM, - .rx_clock = CONFIG_SYS_UEC2_RX_CLK, - .tx_clock = CONFIG_SYS_UEC2_TX_CLK, - .eth_type = CONFIG_SYS_UEC2_ETH_TYPE, - }, -#if (CONFIG_SYS_UEC2_ETH_TYPE == FAST_ETH) - .num_threads_tx = UEC_NUM_OF_THREADS_1, - .num_threads_rx = UEC_NUM_OF_THREADS_1, -#else - .num_threads_tx = UEC_NUM_OF_THREADS_4, - .num_threads_rx = UEC_NUM_OF_THREADS_4, -#endif - .riscTx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .tx_bd_ring_len = 16, - .rx_bd_ring_len = 16, - .phy_address = CONFIG_SYS_UEC2_PHY_ADDR, - .enet_interface = CONFIG_SYS_UEC2_INTERFACE_MODE, -}; + STD_UEC_INFO(2), /* UEC2 */ #endif #ifdef CONFIG_UEC_ETH3 -static uec_info_t eth3_uec_info = { - .uf_info = { - .ucc_num = CONFIG_SYS_UEC3_UCC_NUM, - .rx_clock = CONFIG_SYS_UEC3_RX_CLK, - .tx_clock = CONFIG_SYS_UEC3_TX_CLK, - .eth_type = CONFIG_SYS_UEC3_ETH_TYPE, - }, -#if (CONFIG_SYS_UEC3_ETH_TYPE == FAST_ETH) - .num_threads_tx = UEC_NUM_OF_THREADS_1, - .num_threads_rx = UEC_NUM_OF_THREADS_1, -#else - .num_threads_tx = UEC_NUM_OF_THREADS_4, - .num_threads_rx = UEC_NUM_OF_THREADS_4, -#endif - .riscTx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .tx_bd_ring_len = 16, - .rx_bd_ring_len = 16, - .phy_address = CONFIG_SYS_UEC3_PHY_ADDR, - .enet_interface = CONFIG_SYS_UEC3_INTERFACE_MODE, -}; + STD_UEC_INFO(3), /* UEC3 */ #endif #ifdef CONFIG_UEC_ETH4 -static uec_info_t eth4_uec_info = { - .uf_info = { - .ucc_num = CONFIG_SYS_UEC4_UCC_NUM, - .rx_clock = CONFIG_SYS_UEC4_RX_CLK, - .tx_clock = CONFIG_SYS_UEC4_TX_CLK, - .eth_type = CONFIG_SYS_UEC4_ETH_TYPE, - }, -#if (CONFIG_SYS_UEC4_ETH_TYPE == FAST_ETH) - .num_threads_tx = UEC_NUM_OF_THREADS_1, - .num_threads_rx = UEC_NUM_OF_THREADS_1, -#else - .num_threads_tx = UEC_NUM_OF_THREADS_4, - .num_threads_rx = UEC_NUM_OF_THREADS_4, -#endif - .riscTx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .tx_bd_ring_len = 16, - .rx_bd_ring_len = 16, - .phy_address = CONFIG_SYS_UEC4_PHY_ADDR, - .enet_interface = CONFIG_SYS_UEC4_INTERFACE_MODE, -}; + STD_UEC_INFO(4), /* UEC4 */ #endif #ifdef CONFIG_UEC_ETH5 -static uec_info_t eth5_uec_info = { - .uf_info = { - .ucc_num = CONFIG_SYS_UEC5_UCC_NUM, - .rx_clock = CONFIG_SYS_UEC5_RX_CLK, - .tx_clock = CONFIG_SYS_UEC5_TX_CLK, - .eth_type = CONFIG_SYS_UEC5_ETH_TYPE, - }, -#if (CONFIG_SYS_UEC5_ETH_TYPE == FAST_ETH) - .num_threads_tx = UEC_NUM_OF_THREADS_1, - .num_threads_rx = UEC_NUM_OF_THREADS_1, -#else - .num_threads_tx = UEC_NUM_OF_THREADS_4, - .num_threads_rx = UEC_NUM_OF_THREADS_4, -#endif - .riscTx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .tx_bd_ring_len = 16, - .rx_bd_ring_len = 16, - .phy_address = CONFIG_SYS_UEC5_PHY_ADDR, - .enet_interface = CONFIG_SYS_UEC5_INTERFACE_MODE, -}; + STD_UEC_INFO(5), /* UEC5 */ #endif #ifdef CONFIG_UEC_ETH6 -static uec_info_t eth6_uec_info = { - .uf_info = { - .ucc_num = CONFIG_SYS_UEC6_UCC_NUM, - .rx_clock = CONFIG_SYS_UEC6_RX_CLK, - .tx_clock = CONFIG_SYS_UEC6_TX_CLK, - .eth_type = CONFIG_SYS_UEC6_ETH_TYPE, - }, -#if (CONFIG_SYS_UEC6_ETH_TYPE == FAST_ETH) - .num_threads_tx = UEC_NUM_OF_THREADS_1, - .num_threads_rx = UEC_NUM_OF_THREADS_1, -#else - .num_threads_tx = UEC_NUM_OF_THREADS_4, - .num_threads_rx = UEC_NUM_OF_THREADS_4, + STD_UEC_INFO(6), /* UEC6 */ #endif - .riscTx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, - .tx_bd_ring_len = 16, - .rx_bd_ring_len = 16, - .phy_address = CONFIG_SYS_UEC6_PHY_ADDR, - .enet_interface = CONFIG_SYS_UEC6_INTERFACE_MODE, -}; +#ifdef CONFIG_UEC_ETH7 + STD_UEC_INFO(7), /* UEC7 */ #endif +#ifdef CONFIG_UEC_ETH8 + STD_UEC_INFO(8), /* UEC8 */ +#endif +}; -#define MAXCONTROLLERS (6) +#define MAXCONTROLLERS (8) static struct eth_device *devlist[MAXCONTROLLERS]; @@ -491,6 +379,10 @@ static int uec_set_mac_if_mode(uec_private_t *uec, enet_interface_e if_mode) maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; upsmr |= (UPSMR_R10M | UPSMR_RMM); break; + case ENET_1000_SGMII: + maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE; + upsmr |= UPSMR_SGMM; + break; default: return -EINVAL; break; @@ -1020,7 +912,7 @@ static int uec_issue_init_enet_rxtx_cmd(uec_private_t *uec, /* Init Rx global parameter pointer */ p_init_enet_param->rgftgfrxglobal |= uec->rx_glbl_pram_offset | - (u32)uec_info->riscRx; + (u32)uec_info->risc_rx; /* Init Rx threads */ for (i = 0; i < (thread_rx + 1); i++) { @@ -1038,13 +930,13 @@ static int uec_issue_init_enet_rxtx_cmd(uec_private_t *uec, } entry_val = ((u32)snum << ENET_INIT_PARAM_SNUM_SHIFT) | - init_enet_offset | (u32)uec_info->riscRx; + init_enet_offset | (u32)uec_info->risc_rx; p_init_enet_param->rxthread[i] = entry_val; } /* Init Tx global parameter pointer */ p_init_enet_param->txglobal = uec->tx_glbl_pram_offset | - (u32)uec_info->riscTx; + (u32)uec_info->risc_tx; /* Init Tx threads */ for (i = 0; i < thread_tx; i++) { @@ -1057,7 +949,7 @@ static int uec_issue_init_enet_rxtx_cmd(uec_private_t *uec, UEC_THREAD_TX_PRAM_ALIGNMENT); entry_val = ((u32)snum << ENET_INIT_PARAM_SNUM_SHIFT) | - init_enet_offset | (u32)uec_info->riscTx; + init_enet_offset | (u32)uec_info->risc_tx; p_init_enet_param->txthread[i] = entry_val; } @@ -1190,6 +1082,18 @@ static int uec_startup(uec_private_t *uec) out_be32(&uec_regs->utbipar, utbipar); + /* Configure the TBI for SGMII operation */ + if (uec->uec_info->enet_interface == ENET_1000_SGMII) { + uec_write_phy_reg(uec->dev, uec_regs->utbipar, + ENET_TBI_MII_ANA, TBIANA_SETTINGS); + + uec_write_phy_reg(uec->dev, uec_regs->utbipar, + ENET_TBI_MII_TBICON, TBICON_CLK_SELECT); + + uec_write_phy_reg(uec->dev, uec_regs->utbipar, + ENET_TBI_MII_CR, TBICR_SETTINGS); + } + /* Allocate Tx BDs */ length = ((uec_info->tx_bd_ring_len * SIZEOFBD) / UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT) * @@ -1417,12 +1321,11 @@ static int uec_recv(struct eth_device* dev) return 1; } -int uec_initialize(int index) +int uec_initialize(bd_t *bis, uec_info_t *uec_info) { struct eth_device *dev; int i; uec_private_t *uec; - uec_info_t *uec_info; int err; dev = (struct eth_device *)malloc(sizeof(struct eth_device)); @@ -1437,42 +1340,18 @@ int uec_initialize(int index) } memset(uec, 0, sizeof(uec_private_t)); - /* Init UEC private struct, they come from board.h */ - uec_info = NULL; - if (index == 0) { -#ifdef CONFIG_UEC_ETH1 - uec_info = ð1_uec_info; -#endif - } else if (index == 1) { -#ifdef CONFIG_UEC_ETH2 - uec_info = ð2_uec_info; -#endif - } else if (index == 2) { -#ifdef CONFIG_UEC_ETH3 - uec_info = ð3_uec_info; -#endif - } else if (index == 3) { -#ifdef CONFIG_UEC_ETH4 - uec_info = ð4_uec_info; + /* Adjust uec_info */ +#if (MAX_QE_RISC == 4) + uec_info->risc_tx = QE_RISC_ALLOCATION_FOUR_RISCS; + uec_info->risc_rx = QE_RISC_ALLOCATION_FOUR_RISCS; #endif - } else if (index == 4) { -#ifdef CONFIG_UEC_ETH5 - uec_info = ð5_uec_info; -#endif - } else if (index == 5) { -#ifdef CONFIG_UEC_ETH6 - uec_info = ð6_uec_info; -#endif - } else { - printf("%s: index is illegal.\n", __FUNCTION__); - return -EINVAL; - } - devlist[index] = dev; + devlist[uec_info->uf_info.ucc_num] = dev; uec->uec_info = uec_info; + uec->dev = dev; - sprintf(dev->name, "FSL UEC%d", index); + sprintf(dev->name, "FSL UEC%d", uec_info->uf_info.ucc_num); dev->iobase = 0; dev->priv = (void *)uec; dev->init = uec_init; @@ -1499,3 +1378,20 @@ int uec_initialize(int index) return 1; } + +int uec_eth_init(bd_t *bis, uec_info_t *uecs, int num) +{ + int i; + + for (i = 0; i < num; i++) + uec_initialize(bis, &uecs[i]); + + return 0; +} + +int uec_standard_init(bd_t *bis) +{ + return uec_eth_init(bis, uec_info, ARRAY_SIZE(uec_info)); +} + + diff --git a/drivers/qe/uec.h b/drivers/qe/uec.h index 0b64499..1568310 100644 --- a/drivers/qe/uec.h +++ b/drivers/qe/uec.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Freescale Semiconductor, Inc. + * Copyright (C) 2006-2009 Freescale Semiconductor, Inc. * * Dave Liu <daveliu@freescale.com> * based on source code of Shlomi Gridish @@ -47,6 +47,7 @@ #define UPSMR_CAM 0x00000400 /* CAM Address Matching */ #define UPSMR_BRO 0x00000200 /* Broadcast Address */ #define UPSMR_RES1 0x00002000 /* Reserved feild - must be 1 */ +#define UPSMR_SGMM 0x00000020 /* SGMII mode */ #define UPSMR_INIT_VALUE (UPSMR_HSE | UPSMR_RES1) @@ -621,6 +622,31 @@ typedef enum enet_tbi_mii_reg { ENET_TBI_MII_TBICON = 0x11 } enet_tbi_mii_reg_e; +/* TBI MDIO register bit fields*/ +#define TBICON_CLK_SELECT 0x0020 +#define TBIANA_ASYMMETRIC_PAUSE 0x0100 +#define TBIANA_SYMMETRIC_PAUSE 0x0080 +#define TBIANA_HALF_DUPLEX 0x0040 +#define TBIANA_FULL_DUPLEX 0x0020 +#define TBICR_PHY_RESET 0x8000 +#define TBICR_ANEG_ENABLE 0x1000 +#define TBICR_RESTART_ANEG 0x0200 +#define TBICR_FULL_DUPLEX 0x0100 +#define TBICR_SPEED1_SET 0x0040 + +#define TBIANA_SETTINGS ( \ + TBIANA_ASYMMETRIC_PAUSE \ + | TBIANA_SYMMETRIC_PAUSE \ + | TBIANA_FULL_DUPLEX \ + ) + +#define TBICR_SETTINGS ( \ + TBICR_PHY_RESET \ + | TBICR_ANEG_ENABLE \ + | TBICR_FULL_DUPLEX \ + | TBICR_SPEED1_SET \ + ) + /* UEC number of threads */ typedef enum uec_num_of_threads { @@ -645,17 +671,36 @@ typedef enum enet_interface { ENET_1000_RGMII_ID, ENET_1000_RGMII_RXID, ENET_1000_TBI, - ENET_1000_RTBI + ENET_1000_RTBI, + ENET_1000_SGMII } enet_interface_e; /* UEC initialization info struct */ +#define STD_UEC_INFO(num) \ +{ \ + .uf_info = { \ + .ucc_num = CONFIG_SYS_UEC##num##_UCC_NUM,\ + .rx_clock = CONFIG_SYS_UEC##num##_RX_CLK, \ + .tx_clock = CONFIG_SYS_UEC##num##_TX_CLK, \ + .eth_type = CONFIG_SYS_UEC##num##_ETH_TYPE,\ + }, \ + .num_threads_tx = UEC_NUM_OF_THREADS_1, \ + .num_threads_rx = UEC_NUM_OF_THREADS_1, \ + .risc_tx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, \ + .risc_rx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, \ + .tx_bd_ring_len = 16, \ + .rx_bd_ring_len = 16, \ + .phy_address = CONFIG_SYS_UEC##num##_PHY_ADDR, \ + .enet_interface = CONFIG_SYS_UEC##num##_INTERFACE_MODE, \ +} + typedef struct uec_info { ucc_fast_info_t uf_info; uec_num_of_threads_e num_threads_tx; uec_num_of_threads_e num_threads_rx; - qe_risc_allocation_e riscTx; - qe_risc_allocation_e riscRx; + unsigned int risc_tx; + unsigned int risc_rx; u16 rx_bd_ring_len; u16 tx_bd_ring_len; u8 phy_address; @@ -716,4 +761,7 @@ typedef struct uec_private { int oldlink; } uec_private_t; +int uec_initialize(bd_t *bis, uec_info_t *uec_info); +int uec_eth_init(bd_t *bis, uec_info_t *uecs, int num); +int uec_standard_init(bd_t *bis); #endif /* __UEC_H__ */ diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 14c818d..ab5d565 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -39,11 +39,13 @@ COBJS-$(CONFIG_IMX_SERIAL) += serial_imx.o COBJS-$(CONFIG_IXP_SERIAL) += serial_ixp.o COBJS-$(CONFIG_KS8695_SERIAL) += serial_ks8695.o COBJS-$(CONFIG_LPC2292_SERIAL) += serial_lpc2292.o +COBJS-$(CONFIG_LH7A40X_SERIAL) += serial_lh7a40x.o COBJS-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o COBJS-$(CONFIG_MX31_UART) += serial_mx31.o COBJS-$(CONFIG_NETARM_SERIAL) += serial_netarm.o COBJS-$(CONFIG_PL010_SERIAL) += serial_pl01x.o COBJS-$(CONFIG_PL011_SERIAL) += serial_pl01x.o +COBJS-$(CONFIG_PXA_SERIAL) += serial_pxa.o COBJS-$(CONFIG_SA1100_SERIAL) += serial_sa1100.o COBJS-$(CONFIG_S3C24X0_SERIAL) += serial_s3c24x0.o COBJS-$(CONFIG_S3C44B0_SERIAL) += serial_s3c44b0.o diff --git a/drivers/serial/arm_dcc.c b/drivers/serial/arm_dcc.c index 5a7fb6b..dca73b9 100644 --- a/drivers/serial/arm_dcc.c +++ b/drivers/serial/arm_dcc.c @@ -29,69 +29,66 @@ #include <common.h> #include <devices.h> -#define DCC_ARM9_RBIT (1 << 0) -#define DCC_ARM9_WBIT (1 << 1) -#define DCC_ARM11_RBIT (1 << 30) -#define DCC_ARM11_WBIT (1 << 29) - -#define read_core_id(x) do { \ - __asm__ ("mrc p15, 0, %0, c0, c0, 0\n" : "=r" (x)); \ - x = (x >> 4) & 0xFFF; \ - } while (0); - +#if defined(CONFIG_CPU_V6) /* - * ARM9 + * ARMV6 */ -#define write_arm9_dcc(x) \ - __asm__ volatile ("mcr p14, 0, %0, c1, c0, 0\n" : : "r" (x)) - -#define read_arm9_dcc(x) \ - __asm__ volatile ("mrc p14, 0, %0, c1, c0, 0\n" : "=r" (x)) +#define DCC_RBIT (1 << 30) +#define DCC_WBIT (1 << 29) -#define status_arm9_dcc(x) \ - __asm__ volatile ("mrc p14, 0, %0, c0, c0, 0\n" : "=r" (x)) +#define write_dcc(x) \ + __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x)) -#define can_read_arm9_dcc(x) do { \ - status_arm9_dcc(x); \ - x &= DCC_ARM9_RBIT; \ - } while (0); +#define read_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x)) -#define can_write_arm9_dcc(x) do { \ - status_arm9_dcc(x); \ - x &= DCC_ARM9_WBIT; \ - x = (x == 0); \ - } while (0); +#define status_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x)) +#elif defined(CONFIG_CPU_XSCALE) /* - * ARM11 + * XSCALE */ -#define write_arm11_dcc(x) \ - __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x)) +#define DCC_RBIT (1 << 31) +#define DCC_WBIT (1 << 28) -#define read_arm11_dcc(x) \ - __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x)) +#define write_dcc(x) \ + __asm__ volatile ("mcr p14, 0, %0, c8, c0, 0\n" : : "r" (x)) -#define status_arm11_dcc(x) \ - __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x)) +#define read_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c9, c0, 0\n" : "=r" (x)) + +#define status_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c14, c0, 0\n" : "=r" (x)) + +#else +#define DCC_RBIT (1 << 0) +#define DCC_WBIT (1 << 1) + +#define write_dcc(x) \ + __asm__ volatile ("mcr p14, 0, %0, c1, c0, 0\n" : : "r" (x)) -#define can_read_arm11_dcc(x) do { \ - status_arm11_dcc(x); \ - x &= DCC_ARM11_RBIT; \ +#define read_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c1, c0, 0\n" : "=r" (x)) + +#define status_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c0, c0, 0\n" : "=r" (x)) + +#endif + +#define can_read_dcc(x) do { \ + status_dcc(x); \ + x &= DCC_RBIT; \ } while (0); -#define can_write_arm11_dcc(x) do { \ - status_arm11_dcc(x); \ - x &= DCC_ARM11_WBIT; \ - x = (x == 0); \ +#define can_write_dcc(x) do { \ + status_dcc(x); \ + x &= DCC_WBIT; \ + x = (x == 0); \ } while (0); #define TIMEOUT_COUNT 0x4000000 -static enum { - arm9_and_earlier, - arm11_and_later -} arm_type = arm9_and_earlier; - #ifndef CONFIG_ARM_DCC_MULTI #define arm_dcc_init serial_init void serial_setbrg(void) {} @@ -103,15 +100,6 @@ void serial_setbrg(void) {} int arm_dcc_init(void) { - register unsigned int id; - - read_core_id(id); - - if (id >= 0xb00) - arm_type = arm11_and_later; - else - arm_type = arm9_and_earlier; - return 0; } @@ -120,22 +108,10 @@ int arm_dcc_getc(void) int ch; register unsigned int reg; - switch (arm_type) { - case arm11_and_later: - do { - can_read_arm11_dcc(reg); - } while (!reg); - read_arm11_dcc(ch); - break; - - case arm9_and_earlier: - default: - do { - can_read_arm9_dcc(reg); - } while (!reg); - read_arm9_dcc(ch); - break; - } + do { + can_read_dcc(reg); + } while (!reg); + read_dcc(ch); return ch; } @@ -145,32 +121,15 @@ void arm_dcc_putc(char ch) register unsigned int reg; unsigned int timeout_count = TIMEOUT_COUNT; - switch (arm_type) { - case arm11_and_later: - while (--timeout_count) { - can_write_arm11_dcc(reg); - if (reg) - break; - } - if (timeout_count == 0) - return; - else - write_arm11_dcc(ch); - break; - - case arm9_and_earlier: - default: - while (--timeout_count) { - can_write_arm9_dcc(reg); - if (reg) - break; - } - if (timeout_count == 0) - return; - else - write_arm9_dcc(ch); - break; + while (--timeout_count) { + can_write_dcc(reg); + if (reg) + break; } + if (timeout_count == 0) + return; + else + write_dcc(ch); } void arm_dcc_puts(const char *s) @@ -183,15 +142,7 @@ int arm_dcc_tstc(void) { register unsigned int reg; - switch (arm_type) { - case arm11_and_later: - can_read_arm11_dcc(reg); - break; - case arm9_and_earlier: - default: - can_read_arm9_dcc(reg); - break; - } + can_read_dcc(reg); return reg; } @@ -214,13 +165,6 @@ int drv_arm_dcc_init(void) arm_dcc_dev.putc = arm_dcc_putc; /* 'putc' function */ arm_dcc_dev.puts = arm_dcc_puts; /* 'puts' function */ - rc = device_register(&arm_dcc_dev); - - if (rc == 0) { - arm_dcc_init(); - return 1; - } - - return 0; + return device_register(&arm_dcc_dev); } #endif diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 657c9da..2fcc8c3 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -17,7 +17,7 @@ void NS16550_init (NS16550_t com_port, int baud_divisor) { com_port->ier = 0x00; -#ifdef CONFIG_OMAP +#if defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2) com_port->mdr1 = 0x7; /* mode select reset TL16C750*/ #endif com_port->lcr = UART_LCR_BKSE | UART_LCRVAL; @@ -30,7 +30,7 @@ void NS16550_init (NS16550_t com_port, int baud_divisor) com_port->dll = baud_divisor & 0xff; com_port->dlm = (baud_divisor >> 8) & 0xff; com_port->lcr = UART_LCRVAL; -#if defined(CONFIG_OMAP) +#if defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2) #if defined(CONFIG_APTIX) com_port->mdr1 = 3; /* /13 mode so Aptix 6MHz can hit 115200 */ #else diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c new file mode 100644 index 0000000..4767489 --- /dev/null +++ b/drivers/serial/serial_lh7a40x.c @@ -0,0 +1,184 @@ +/* + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <common.h> +#include <lh7a40x.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CONSOLE_UART1) +# define UART_CONSOLE 1 +#elif defined(CONFIG_CONSOLE_UART2) +# define UART_CONSOLE 2 +#elif defined(CONFIG_CONSOLE_UART3) +# define UART_CONSOLE 3 +#else +# error "No console configured ... " +#endif + +void serial_setbrg (void) +{ + lh7a40x_uart_t* uart = LH7A40X_UART_PTR(UART_CONSOLE); + int i; + unsigned int reg = 0; + + /* + * userguide 15.1.2.4 + * + * BAUDDIV is (UART_REF_FREQ/(16 X BAUD))-1 + * + * UART_REF_FREQ = external system clock input / 2 (Hz) + * BAUD is desired baudrate (bits/s) + * + * NOTE: we add (divisor/2) to numerator to round for + * more precision + */ + reg = (((get_PLLCLK()/2) + ((16*gd->baudrate)/2)) / (16 * gd->baudrate)) - 1; + uart->brcon = reg; + + for (i = 0; i < 100; i++); +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + * + */ +int serial_init (void) +{ + lh7a40x_uart_t* uart = LH7A40X_UART_PTR(UART_CONSOLE); + + /* UART must be enabled before writing to any config registers */ + uart->con |= (UART_EN); + +#ifdef CONFIG_CONSOLE_UART1 + /* infrared disabled */ + uart->con |= UART_SIRD; +#endif + /* loopback disabled */ + uart->con &= ~(UART_LBE); + + /* modem lines and tx/rx polarities */ + uart->con &= ~(UART_MXP | UART_TXP | UART_RXP); + + /* FIFO enable, N81 */ + uart->fcon = (UART_WLEN_8 | UART_FEN | UART_STP2_1); + + /* set baudrate */ + serial_setbrg (); + + /* enable rx interrupt */ + uart->inten |= UART_RI; + + return (0); +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int serial_getc (void) +{ + lh7a40x_uart_t* uart = LH7A40X_UART_PTR(UART_CONSOLE); + + /* wait for character to arrive */ + while (uart->status & UART_RXFE); + + return(uart->data & 0xff); +} + +#ifdef CONFIG_HWFLOW +static int hwflow = 0; /* turned off by default */ +int hwflow_onoff(int on) +{ + switch(on) { + case 0: + default: + break; /* return current */ + case 1: + hwflow = 1; /* turn on */ + break; + case -1: + hwflow = 0; /* turn off */ + break; + } + return hwflow; +} +#endif + +#ifdef CONFIG_MODEM_SUPPORT +static int be_quiet = 0; +void disable_putc(void) +{ + be_quiet = 1; +} + +void enable_putc(void) +{ + be_quiet = 0; +} +#endif + + +/* + * Output a single byte to the serial port. + */ +void serial_putc (const char c) +{ + lh7a40x_uart_t* uart = LH7A40X_UART_PTR(UART_CONSOLE); + +#ifdef CONFIG_MODEM_SUPPORT + if (be_quiet) + return; +#endif + + /* wait for room in the tx FIFO */ + while (!(uart->status & UART_TXFE)); + +#ifdef CONFIG_HWFLOW + /* Wait for CTS up */ + while(hwflow && !(uart->status & UART_CTS)); +#endif + + uart->data = c; + + /* If \n, also do \r */ + if (c == '\n') + serial_putc ('\r'); +} + +/* + * Test whether a character is in the RX buffer + */ +int serial_tstc (void) +{ + lh7a40x_uart_t* uart = LH7A40X_UART_PTR(UART_CONSOLE); + + return(!(uart->status & UART_RXFE)); +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} diff --git a/drivers/serial/serial_pxa.c b/drivers/serial/serial_pxa.c new file mode 100644 index 0000000..9ba457e --- /dev/null +++ b/drivers/serial/serial_pxa.c @@ -0,0 +1,385 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, <wd@denx.de> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke <azu@sysgo.de> + * + * Copyright (C) 1999 2000 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * + * 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 <watchdog.h> +#include <serial.h> +#include <asm/arch/pxa-regs.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define FFUART_INDEX 0 +#define BTUART_INDEX 1 +#define STUART_INDEX 2 + +#ifndef CONFIG_SERIAL_MULTI +#if defined (CONFIG_FFUART) +#define UART_INDEX FFUART_INDEX +#elif defined (CONFIG_BTUART) +#define UART_INDEX BTUART_INDEX +#elif defined (CONFIG_STUART) +#define UART_INDEX STUART_INDEX +#else +#error "Bad: you didn't configure serial ..." +#endif +#endif + +void pxa_setbrg_dev (unsigned int uart_index) +{ + unsigned int quot = 0; + + if (gd->baudrate == 1200) + quot = 768; + else if (gd->baudrate == 9600) + quot = 96; + else if (gd->baudrate == 19200) + quot = 48; + else if (gd->baudrate == 38400) + quot = 24; + else if (gd->baudrate == 57600) + quot = 16; + else if (gd->baudrate == 115200) + quot = 8; + else + hang (); + + switch (uart_index) { + case FFUART_INDEX: +#ifdef CONFIG_CPU_MONAHANS + CKENA |= CKENA_22_FFUART; +#else + CKEN |= CKEN6_FFUART; +#endif /* CONFIG_CPU_MONAHANS */ + + FFIER = 0; /* Disable for now */ + FFFCR = 0; /* No fifos enabled */ + + /* set baud rate */ + FFLCR = LCR_WLS0 | LCR_WLS1 | LCR_DLAB; + FFDLL = quot & 0xff; + FFDLH = quot >> 8; + FFLCR = LCR_WLS0 | LCR_WLS1; + + FFIER = IER_UUE; /* Enable FFUART */ + break; + + case BTUART_INDEX: +#ifdef CONFIG_CPU_MONAHANS + CKENA |= CKENA_21_BTUART; +#else + CKEN |= CKEN7_BTUART; +#endif /* CONFIG_CPU_MONAHANS */ + + BTIER = 0; + BTFCR = 0; + + /* set baud rate */ + BTLCR = LCR_DLAB; + BTDLL = quot & 0xff; + BTDLH = quot >> 8; + BTLCR = LCR_WLS0 | LCR_WLS1; + + BTIER = IER_UUE; /* Enable BFUART */ + + break; + + case STUART_INDEX: +#ifdef CONFIG_CPU_MONAHANS + CKENA |= CKENA_23_STUART; +#else + CKEN |= CKEN5_STUART; +#endif /* CONFIG_CPU_MONAHANS */ + + STIER = 0; + STFCR = 0; + + /* set baud rate */ + STLCR = LCR_DLAB; + STDLL = quot & 0xff; + STDLH = quot >> 8; + STLCR = LCR_WLS0 | LCR_WLS1; + + STIER = IER_UUE; /* Enable STUART */ + break; + + default: + hang(); + } +} + + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + * + */ +int pxa_init_dev (unsigned int uart_index) +{ + pxa_setbrg_dev (uart_index); + + return (0); +} + + +/* + * Output a single byte to the serial port. + */ +void pxa_putc_dev (unsigned int uart_index,const char c) +{ + switch (uart_index) { + case FFUART_INDEX: + /* wait for room in the tx FIFO on FFUART */ + while ((FFLSR & LSR_TEMT) == 0) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ + FFTHR = c; + break; + + case BTUART_INDEX: + while ((BTLSR & LSR_TEMT ) == 0 ) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ + BTTHR = c; + break; + + case STUART_INDEX: + while ((STLSR & LSR_TEMT ) == 0 ) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ + STTHR = c; + break; + } + + /* If \n, also do \r */ + if (c == '\n') + pxa_putc_dev (uart_index,'\r'); +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int pxa_tstc_dev (unsigned int uart_index) +{ + switch (uart_index) { + case FFUART_INDEX: + return FFLSR & LSR_DR; + case BTUART_INDEX: + return BTLSR & LSR_DR; + case STUART_INDEX: + return STLSR & LSR_DR; + } + return -1; +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int pxa_getc_dev (unsigned int uart_index) +{ + switch (uart_index) { + case FFUART_INDEX: + while (!(FFLSR & LSR_DR)) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ + return (char) FFRBR & 0xff; + + case BTUART_INDEX: + while (!(BTLSR & LSR_DR)) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ + return (char) BTRBR & 0xff; + case STUART_INDEX: + while (!(STLSR & LSR_DR)) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ + return (char) STRBR & 0xff; + } + return -1; +} + +void +pxa_puts_dev (unsigned int uart_index,const char *s) +{ + while (*s) { + pxa_putc_dev (uart_index,*s++); + } +} + +#if defined (CONFIG_FFUART) +static int ffuart_init(void) +{ + return pxa_init_dev(FFUART_INDEX); +} + +static void ffuart_setbrg(void) +{ + return pxa_setbrg_dev(FFUART_INDEX); +} + +static void ffuart_putc(const char c) +{ + return pxa_putc_dev(FFUART_INDEX,c); +} + +static void ffuart_puts(const char *s) +{ + return pxa_puts_dev(FFUART_INDEX,s); +} + +static int ffuart_getc(void) +{ + return pxa_getc_dev(FFUART_INDEX); +} + +static int ffuart_tstc(void) +{ + return pxa_tstc_dev(FFUART_INDEX); +} + +struct serial_device serial_ffuart_device = +{ + "serial_ffuart", + "PXA", + ffuart_init, + ffuart_setbrg, + ffuart_getc, + ffuart_tstc, + ffuart_putc, + ffuart_puts, +}; +#endif + +#if defined (CONFIG_BTUART) +static int btuart_init(void) +{ + return pxa_init_dev(BTUART_INDEX); +} + +static void btuart_setbrg(void) +{ + return pxa_setbrg_dev(BTUART_INDEX); +} + +static void btuart_putc(const char c) +{ + return pxa_putc_dev(BTUART_INDEX,c); +} + +static void btuart_puts(const char *s) +{ + return pxa_puts_dev(BTUART_INDEX,s); +} + +static int btuart_getc(void) +{ + return pxa_getc_dev(BTUART_INDEX); +} + +static int btuart_tstc(void) +{ + return pxa_tstc_dev(BTUART_INDEX); +} + +struct serial_device serial_btuart_device = +{ + "serial_btuart", + "PXA", + btuart_init, + btuart_setbrg, + btuart_getc, + btuart_tstc, + btuart_putc, + btuart_puts, +}; +#endif + +#if defined (CONFIG_STUART) +static int stuart_init(void) +{ + return pxa_init_dev(STUART_INDEX); +} + +static void stuart_setbrg(void) +{ + return pxa_setbrg_dev(STUART_INDEX); +} + +static void stuart_putc(const char c) +{ + return pxa_putc_dev(STUART_INDEX,c); +} + +static void stuart_puts(const char *s) +{ + return pxa_puts_dev(STUART_INDEX,s); +} + +static int stuart_getc(void) +{ + return pxa_getc_dev(STUART_INDEX); +} + +static int stuart_tstc(void) +{ + return pxa_tstc_dev(STUART_INDEX); +} + +struct serial_device serial_stuart_device = +{ + "serial_stuart", + "PXA", + stuart_init, + stuart_setbrg, + stuart_getc, + stuart_tstc, + stuart_putc, + stuart_puts, +}; +#endif + + +#ifndef CONFIG_SERIAL_MULTI +inline int serial_init(void) { + return (pxa_init_dev(UART_INDEX)); +} +void serial_setbrg(void) { + pxa_setbrg_dev(UART_INDEX); +} +int serial_getc(void) { + return(pxa_getc_dev(UART_INDEX)); +} +int serial_tstc(void) { + return(pxa_tstc_dev(UART_INDEX)); +} +void serial_putc(const char c) { + pxa_putc_dev(UART_INDEX,c); +} +void serial_puts(const char *s) { + pxa_puts_dev(UART_INDEX,s); +} +#endif /* CONFIG_SERIAL_MULTI */ diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 86ee1d5..bf148c4 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -1,4 +1,6 @@ /* + * (C) Copyright 2009 Freescale Semiconductor, Inc. + * * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB * * Author: Tor Krill tor@excito.com @@ -22,12 +24,10 @@ #include <common.h> #include <pci.h> #include <usb.h> -#include <mpc83xx.h> #include <asm/io.h> -#include <asm/bitops.h> +#include <usb/ehci-fsl.h> #include "ehci.h" -#include "ehci-fsl.h" #include "ehci-core.h" /* @@ -38,54 +38,33 @@ */ int ehci_hcd_init(void) { - volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; - uint32_t addr, temp; + struct usb_ehci *ehci; - addr = (uint32_t)&(im->usb[0]); - hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); + ehci = (struct usb_ehci *)CONFIG_SYS_MPC8xxx_USB_ADDR; + hccr = (struct ehci_hccr *)((uint32_t)ehci->caplength); hcor = (struct ehci_hcor *)((uint32_t) hccr + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); - /* Configure clock */ - clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK, - MPC83XX_SCCR_USB_DRCM_11); - - /* Confgure interface. */ - temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); - out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp - | REFSEL_16MHZ | UTMI_PHY_EN); - - /* Wait for clock to stabilize */ - do { - temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); - udelay(1000); - } while (!(temp & PHY_CLK_VALID)); - /* Set to Host mode */ - temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); - out_le32((void *)(addr + FSL_SOC_USB_USBMODE), temp | CM_HOST); + setbits_le32((void *)ehci->usbmode, CM_HOST); - out_be32((void *)(addr + FSL_SOC_USB_SNOOP1), SNOOP_SIZE_2GB); - out_be32((void *)(addr + FSL_SOC_USB_SNOOP2), - 0x80000000 | SNOOP_SIZE_2GB); + out_be32((void *)ehci->snoop1, SNOOP_SIZE_2GB); + out_be32((void *)ehci->snoop2, 0x80000000 | SNOOP_SIZE_2GB); /* Init phy */ - /* TODO: handle different phys? */ - out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI); + if (!strcmp(getenv("usb_phy_type"), "utmi")) + out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI); + else + out_le32(&(hcor->or_portsc[0]), PORT_PTS_ULPI); /* Enable interface. */ - temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); - out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); - - out_be32((void *)(addr + FSL_SOC_USB_PRICTRL), 0x0000000c); - out_be32((void *)(addr + FSL_SOC_USB_AGECNTTHRSH), 0x00000040); - out_be32((void *)(addr + FSL_SOC_USB_SICTRL), 0x00000001); + setbits_be32((void *)ehci->control, USB_EN); - /* Enable interface. */ - temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); - out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); + out_be32((void *)ehci->prictrl, 0x0000000c); + out_be32((void *)ehci->age_cnt_limit, 0x00000040); + out_be32((void *)ehci->sictrl, 0x00000001); - temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); + in_le32((void *)ehci->usbmode); return 0; } diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h deleted file mode 100644 index c429af1..0000000 --- a/drivers/usb/host/ehci-fsl.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2005 freescale semiconductor - * Copyright (c) 2005 MontaVista Software - * Copyright (c) 2008 Excito Elektronik i Sk=E5ne AB - * - * 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 _EHCI_FSL_H -#define _EHCI_FSL_H - -/* Global offsets */ -#define FSL_SKIP_PCI 0x100 - -/* offsets for the non-ehci registers in the FSL SOC USB controller */ -#define FSL_SOC_USB_ULPIVP 0x170 -#define FSL_SOC_USB_PORTSC1 0x184 -#define PORT_PTS_MSK (3 << 30) -#define PORT_PTS_UTMI (0 << 30) -#define PORT_PTS_ULPI (2 << 30) -#define PORT_PTS_SERIAL (3 << 30) -#define PORT_PTS_PTW (1 << 28) - -/* USBMODE Register bits */ -#define CM_IDLE (0 << 0) -#define CM_RESERVED (1 << 0) -#define CM_DEVICE (2 << 0) -#define CM_HOST (3 << 0) -#define USBMODE_RESERVED_2 (0 << 2) -#define SLOM (1 << 3) -#define SDIS (1 << 4) - -/* CONTROL Register bits */ -#define ULPI_INT_EN (1 << 0) -#define WU_INT_EN (1 << 1) -#define USB_EN (1 << 2) -#define LSF_EN (1 << 3) -#define KEEP_OTG_ON (1 << 4) -#define OTG_PORT (1 << 5) -#define REFSEL_12MHZ (0 << 6) -#define REFSEL_16MHZ (1 << 6) -#define REFSEL_48MHZ (2 << 6) -#define PLL_RESET (1 << 8) -#define UTMI_PHY_EN (1 << 9) -#define PHY_CLK_SEL_UTMI (0 << 10) -#define PHY_CLK_SEL_ULPI (1 << 10) -#define CLKIN_SEL_USB_CLK (0 << 11) -#define CLKIN_SEL_USB_CLK2 (1 << 11) -#define CLKIN_SEL_SYS_CLK (2 << 11) -#define CLKIN_SEL_SYS_CLK2 (3 << 11) -#define RESERVED_18 (0 << 13) -#define RESERVED_17 (0 << 14) -#define RESERVED_16 (0 << 15) -#define WU_INT (1 << 16) -#define PHY_CLK_VALID (1 << 17) - -#define FSL_SOC_USB_PORTSC2 0x188 -#define FSL_SOC_USB_USBMODE 0x1a8 -#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ -#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ -#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ -#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ -#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ -#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ -#define SNOOP_SIZE_2GB 0x1e - -/* System Clock Control Register */ -#define MPC83XX_SCCR_USB_MASK 0x00f00000 -#define MPC83XX_SCCR_USB_DRCM_11 0x00300000 -#define MPC83XX_SCCR_USB_DRCM_01 0x00100000 -#define MPC83XX_SCCR_USB_DRCM_10 0x00200000 - -#endif /* _EHCI_FSL_H */ diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 5ee2314..bcafb27 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -1330,53 +1330,57 @@ static int video_init (void) /*****************************************************************************/ +/* + * Implement a weak default function for boards that optionally + * need to skip the video initialization. + */ +int __board_video_skip(void) +{ + /* As default, don't skip test */ + return 0; +} +int board_video_skip(void) __attribute__((weak, alias("__board_video_skip"))); + int drv_video_init (void) { int skip_dev_init; device_t console_dev; - skip_dev_init = 0; + /* Check if video initialization should be skipped */ + if (board_video_skip()) + return 0; /* Init video chip - returns with framebuffer cleared */ - if (video_init () == -1) - skip_dev_init = 1; + skip_dev_init = (video_init () == -1); -#ifdef CONFIG_VGA_AS_SINGLE_DEVICE - /* Devices VGA and Keyboard will be assigned seperately */ - /* Init vga device */ - if (!skip_dev_init) { - memset (&console_dev, 0, sizeof (console_dev)); - strcpy (console_dev.name, "vga"); - console_dev.ext = DEV_EXT_VIDEO; /* Video extensions */ - console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM; - console_dev.putc = video_putc; /* 'putc' function */ - console_dev.puts = video_puts; /* 'puts' function */ - console_dev.tstc = NULL; /* 'tstc' function */ - console_dev.getc = NULL; /* 'getc' function */ - - if (device_register (&console_dev) == 0) - return 1; - } -#else +#if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) PRINTD ("KBD: Keyboard init ...\n"); - if (VIDEO_KBD_INIT_FCT == -1) - skip_dev_init = 1; - - /* Init console device */ - if (!skip_dev_init) { - memset (&console_dev, 0, sizeof (console_dev)); - strcpy (console_dev.name, "vga"); - console_dev.ext = DEV_EXT_VIDEO; /* Video extensions */ - console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; - console_dev.putc = video_putc; /* 'putc' function */ - console_dev.puts = video_puts; /* 'puts' function */ - console_dev.tstc = VIDEO_TSTC_FCT; /* 'tstc' function */ - console_dev.getc = VIDEO_GETC_FCT; /* 'getc' function */ - - if (device_register (&console_dev) == 0) - return 1; - } + skip_dev_init |= (VIDEO_KBD_INIT_FCT == -1); +#endif + + if (skip_dev_init) + return 0; + + /* Init vga device */ + memset (&console_dev, 0, sizeof (console_dev)); + strcpy (console_dev.name, "vga"); + console_dev.ext = DEV_EXT_VIDEO; /* Video extensions */ + console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM; + console_dev.putc = video_putc; /* 'putc' function */ + console_dev.puts = video_puts; /* 'puts' function */ + console_dev.tstc = NULL; /* 'tstc' function */ + console_dev.getc = NULL; /* 'getc' function */ + +#if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) + /* Also init console device */ + console_dev.flags |= DEV_FLAGS_INPUT; + console_dev.tstc = VIDEO_TSTC_FCT; /* 'tstc' function */ + console_dev.getc = VIDEO_GETC_FCT; /* 'getc' function */ #endif /* CONFIG_VGA_AS_SINGLE_DEVICE */ - /* No console dev available */ - return 0; + + if (device_register (&console_dev) != 0) + return 0; + + /* Return success */ + return 1; } |