diff options
| -rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 137 | ||||
| -rw-r--r-- | include/linux/mtd/onenand.h | 6 | ||||
| -rw-r--r-- | include/linux/mtd/onenand_regs.h | 4 |
3 files changed, 125 insertions, 22 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 09aefe2164aa..8ed68b28afe3 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * linux/drivers/mtd/onenand/onenand_base.c | 2 | * linux/drivers/mtd/onenand/onenand_base.c |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2005 Samsung Electronics | 4 | * Copyright (C) 2005-2006 Samsung Electronics |
| 5 | * Kyungmin Park <kyungmin.park@samsung.com> | 5 | * Kyungmin Park <kyungmin.park@samsung.com> |
| 6 | * | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
| @@ -199,6 +199,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
| 199 | case ONENAND_CMD_UNLOCK: | 199 | case ONENAND_CMD_UNLOCK: |
| 200 | case ONENAND_CMD_LOCK: | 200 | case ONENAND_CMD_LOCK: |
| 201 | case ONENAND_CMD_LOCK_TIGHT: | 201 | case ONENAND_CMD_LOCK_TIGHT: |
| 202 | case ONENAND_CMD_UNLOCK_ALL: | ||
| 202 | block = -1; | 203 | block = -1; |
| 203 | page = -1; | 204 | page = -1; |
| 204 | break; | 205 | break; |
| @@ -1211,11 +1212,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 1211 | end = len >> this->erase_shift; | 1212 | end = len >> this->erase_shift; |
| 1212 | 1213 | ||
| 1213 | /* Continuous lock scheme */ | 1214 | /* Continuous lock scheme */ |
| 1214 | if (this->options & ONENAND_CONT_LOCK) { | 1215 | if (this->options & ONENAND_HAS_CONT_LOCK) { |
| 1215 | /* Set start block address */ | 1216 | /* Set start block address */ |
| 1216 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | 1217 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); |
| 1217 | /* Set end block address */ | 1218 | /* Set end block address */ |
| 1218 | this->write_word(end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); | 1219 | this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); |
| 1219 | /* Write unlock command */ | 1220 | /* Write unlock command */ |
| 1220 | this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); | 1221 | this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); |
| 1221 | 1222 | ||
| @@ -1236,7 +1237,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 1236 | } | 1237 | } |
| 1237 | 1238 | ||
| 1238 | /* Block lock scheme */ | 1239 | /* Block lock scheme */ |
| 1239 | for (block = start; block < end; block++) { | 1240 | for (block = start; block < start + end; block++) { |
| 1240 | /* Set block address */ | 1241 | /* Set block address */ |
| 1241 | value = onenand_block_address(this, block); | 1242 | value = onenand_block_address(this, block); |
| 1242 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); | 1243 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); |
| @@ -1265,6 +1266,79 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 1265 | return 0; | 1266 | return 0; |
| 1266 | } | 1267 | } |
| 1267 | 1268 | ||
| 1269 | /** | ||
| 1270 | * onenand_check_lock_status - [OneNAND Interface] Check lock status | ||
| 1271 | * @param this onenand chip data structure | ||
| 1272 | * | ||
| 1273 | * Check lock status | ||
| 1274 | */ | ||
| 1275 | static void onenand_check_lock_status(struct onenand_chip *this) | ||
| 1276 | { | ||
| 1277 | unsigned int value, block, status; | ||
| 1278 | unsigned int end; | ||
| 1279 | |||
| 1280 | end = this->chipsize >> this->erase_shift; | ||
| 1281 | for (block = 0; block < end; block++) { | ||
| 1282 | /* Set block address */ | ||
| 1283 | value = onenand_block_address(this, block); | ||
| 1284 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); | ||
| 1285 | /* Select DataRAM for DDP */ | ||
| 1286 | value = onenand_bufferram_address(this, block); | ||
| 1287 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | ||
| 1288 | /* Set start block address */ | ||
| 1289 | this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | ||
| 1290 | |||
| 1291 | /* Check lock status */ | ||
| 1292 | status = this->read_word(this->base + ONENAND_REG_WP_STATUS); | ||
| 1293 | if (!(status & ONENAND_WP_US)) | ||
| 1294 | printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); | ||
| 1295 | } | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | /** | ||
| 1299 | * onenand_unlock_all - [OneNAND Interface] unlock all blocks | ||
| 1300 | * @param mtd MTD device structure | ||
| 1301 | * | ||
| 1302 | * Unlock all blocks | ||
| 1303 | */ | ||
| 1304 | static int onenand_unlock_all(struct mtd_info *mtd) | ||
| 1305 | { | ||
| 1306 | struct onenand_chip *this = mtd->priv; | ||
| 1307 | |||
| 1308 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { | ||
| 1309 | /* Write unlock command */ | ||
| 1310 | this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); | ||
| 1311 | |||
| 1312 | /* There's no return value */ | ||
| 1313 | this->wait(mtd, FL_UNLOCKING); | ||
| 1314 | |||
| 1315 | /* Sanity check */ | ||
| 1316 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) | ||
| 1317 | & ONENAND_CTRL_ONGO) | ||
| 1318 | continue; | ||
| 1319 | |||
| 1320 | /* Workaround for all block unlock in DDP */ | ||
| 1321 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | ||
| 1322 | loff_t ofs; | ||
| 1323 | size_t len; | ||
| 1324 | |||
| 1325 | /* 1st block on another chip */ | ||
| 1326 | ofs = this->chipsize >> 1; | ||
| 1327 | len = 1 << this->erase_shift; | ||
| 1328 | |||
| 1329 | onenand_unlock(mtd, ofs, len); | ||
| 1330 | } | ||
| 1331 | |||
| 1332 | onenand_check_lock_status(this); | ||
| 1333 | |||
| 1334 | return 0; | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | mtd->unlock(mtd, 0x0, this->chipsize); | ||
| 1338 | |||
| 1339 | return 0; | ||
| 1340 | } | ||
| 1341 | |||
| 1268 | #ifdef CONFIG_MTD_ONENAND_OTP | 1342 | #ifdef CONFIG_MTD_ONENAND_OTP |
| 1269 | 1343 | ||
| 1270 | /* Interal OTP operation */ | 1344 | /* Interal OTP operation */ |
| @@ -1564,12 +1638,43 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | |||
| 1564 | #endif /* CONFIG_MTD_ONENAND_OTP */ | 1638 | #endif /* CONFIG_MTD_ONENAND_OTP */ |
| 1565 | 1639 | ||
| 1566 | /** | 1640 | /** |
| 1641 | * onenand_lock_scheme - Check and set OneNAND lock scheme | ||
| 1642 | * @param mtd MTD data structure | ||
| 1643 | * | ||
| 1644 | * Check and set OneNAND lock scheme | ||
| 1645 | */ | ||
| 1646 | static void onenand_lock_scheme(struct mtd_info *mtd) | ||
| 1647 | { | ||
| 1648 | struct onenand_chip *this = mtd->priv; | ||
| 1649 | unsigned int density, process; | ||
| 1650 | |||
| 1651 | /* Lock scheme depends on density and process */ | ||
| 1652 | density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT; | ||
| 1653 | process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; | ||
| 1654 | |||
| 1655 | /* Lock scheme */ | ||
| 1656 | if (density >= ONENAND_DEVICE_DENSITY_1Gb) { | ||
| 1657 | /* A-Die has all block unlock */ | ||
| 1658 | if (process) { | ||
| 1659 | printk(KERN_DEBUG "Chip support all block unlock\n"); | ||
| 1660 | this->options |= ONENAND_HAS_UNLOCK_ALL; | ||
| 1661 | } | ||
| 1662 | } else { | ||
| 1663 | /* Some OneNAND has continues lock scheme */ | ||
| 1664 | if (!process) { | ||
| 1665 | printk(KERN_DEBUG "Lock scheme is Continues Lock\n"); | ||
| 1666 | this->options |= ONENAND_HAS_CONT_LOCK; | ||
| 1667 | } | ||
| 1668 | } | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | /** | ||
| 1567 | * onenand_print_device_info - Print device ID | 1672 | * onenand_print_device_info - Print device ID |
| 1568 | * @param device device ID | 1673 | * @param device device ID |
| 1569 | * | 1674 | * |
| 1570 | * Print device ID | 1675 | * Print device ID |
| 1571 | */ | 1676 | */ |
| 1572 | static void onenand_print_device_info(int device) | 1677 | static void onenand_print_device_info(int device, int version) |
| 1573 | { | 1678 | { |
| 1574 | int vcc, demuxed, ddp, density; | 1679 | int vcc, demuxed, ddp, density; |
| 1575 | 1680 | ||
| @@ -1583,6 +1688,7 @@ static void onenand_print_device_info(int device) | |||
| 1583 | (16 << density), | 1688 | (16 << density), |
| 1584 | vcc ? "2.65/3.3" : "1.8", | 1689 | vcc ? "2.65/3.3" : "1.8", |
| 1585 | device); | 1690 | device); |
| 1691 | printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version); | ||
| 1586 | } | 1692 | } |
| 1587 | 1693 | ||
| 1588 | static const struct onenand_manufacturers onenand_manuf_ids[] = { | 1694 | static const struct onenand_manufacturers onenand_manuf_ids[] = { |
| @@ -1625,8 +1731,7 @@ static int onenand_check_maf(int manuf) | |||
| 1625 | static int onenand_probe(struct mtd_info *mtd) | 1731 | static int onenand_probe(struct mtd_info *mtd) |
| 1626 | { | 1732 | { |
| 1627 | struct onenand_chip *this = mtd->priv; | 1733 | struct onenand_chip *this = mtd->priv; |
| 1628 | int bram_maf_id, bram_dev_id, maf_id, dev_id; | 1734 | int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id; |
| 1629 | int version_id; | ||
| 1630 | int density; | 1735 | int density; |
| 1631 | int syscfg; | 1736 | int syscfg; |
| 1632 | 1737 | ||
| @@ -1657,14 +1762,16 @@ static int onenand_probe(struct mtd_info *mtd) | |||
| 1657 | /* Read manufacturer and device IDs from Register */ | 1762 | /* Read manufacturer and device IDs from Register */ |
| 1658 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); | 1763 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); |
| 1659 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); | 1764 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); |
| 1765 | ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID); | ||
| 1660 | 1766 | ||
| 1661 | /* Check OneNAND device */ | 1767 | /* Check OneNAND device */ |
| 1662 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) | 1768 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) |
| 1663 | return -ENXIO; | 1769 | return -ENXIO; |
| 1664 | 1770 | ||
| 1665 | /* Flash device information */ | 1771 | /* Flash device information */ |
| 1666 | onenand_print_device_info(dev_id); | 1772 | onenand_print_device_info(dev_id, ver_id); |
| 1667 | this->device_id = dev_id; | 1773 | this->device_id = dev_id; |
| 1774 | this->version_id = ver_id; | ||
| 1668 | 1775 | ||
| 1669 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; | 1776 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; |
| 1670 | this->chipsize = (16 << density) << 20; | 1777 | this->chipsize = (16 << density) << 20; |
| @@ -1687,16 +1794,8 @@ static int onenand_probe(struct mtd_info *mtd) | |||
| 1687 | 1794 | ||
| 1688 | mtd->size = this->chipsize; | 1795 | mtd->size = this->chipsize; |
| 1689 | 1796 | ||
| 1690 | /* Version ID */ | 1797 | /* Check OneNAND lock scheme */ |
| 1691 | version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); | 1798 | onenand_lock_scheme(mtd); |
| 1692 | printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id); | ||
| 1693 | |||
| 1694 | /* Lock scheme */ | ||
| 1695 | if (density <= ONENAND_DEVICE_DENSITY_512Mb && | ||
| 1696 | !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) { | ||
| 1697 | printk(KERN_INFO "Lock scheme is Continues Lock\n"); | ||
| 1698 | this->options |= ONENAND_CONT_LOCK; | ||
| 1699 | } | ||
| 1700 | 1799 | ||
| 1701 | return 0; | 1800 | return 0; |
| 1702 | } | 1801 | } |
| @@ -1832,7 +1931,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
| 1832 | mtd->owner = THIS_MODULE; | 1931 | mtd->owner = THIS_MODULE; |
| 1833 | 1932 | ||
| 1834 | /* Unlock whole block */ | 1933 | /* Unlock whole block */ |
| 1835 | mtd->unlock(mtd, 0x0, this->chipsize); | 1934 | onenand_unlock_all(mtd); |
| 1836 | 1935 | ||
| 1837 | return this->scan_bbt(mtd); | 1936 | return this->scan_bbt(mtd); |
| 1838 | } | 1937 | } |
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 1f4972155249..6f045b586e76 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * linux/include/linux/mtd/onenand.h | 2 | * linux/include/linux/mtd/onenand.h |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2005 Samsung Electronics | 4 | * Copyright (C) 2005-2006 Samsung Electronics |
| 5 | * Kyungmin Park <kyungmin.park@samsung.com> | 5 | * Kyungmin Park <kyungmin.park@samsung.com> |
| 6 | * | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
| @@ -96,6 +96,7 @@ struct onenand_chip { | |||
| 96 | void __iomem *base; | 96 | void __iomem *base; |
| 97 | unsigned int chipsize; | 97 | unsigned int chipsize; |
| 98 | unsigned int device_id; | 98 | unsigned int device_id; |
| 99 | unsigned int version_id; | ||
| 99 | unsigned int density_mask; | 100 | unsigned int density_mask; |
| 100 | unsigned int options; | 101 | unsigned int options; |
| 101 | 102 | ||
| @@ -149,7 +150,8 @@ struct onenand_chip { | |||
| 149 | /* | 150 | /* |
| 150 | * Options bits | 151 | * Options bits |
| 151 | */ | 152 | */ |
| 152 | #define ONENAND_CONT_LOCK (0x0001) | 153 | #define ONENAND_HAS_CONT_LOCK (0x0001) |
| 154 | #define ONENAND_HAS_UNLOCK_ALL (0x0002) | ||
| 153 | #define ONENAND_PAGEBUF_ALLOC (0x1000) | 155 | #define ONENAND_PAGEBUF_ALLOC (0x1000) |
| 154 | 156 | ||
| 155 | /* | 157 | /* |
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 4a72818d2545..9e409fe6ded6 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | * | 3 | * |
| 4 | * OneNAND Register header file | 4 | * OneNAND Register header file |
| 5 | * | 5 | * |
| 6 | * Copyright (C) 2005 Samsung Electronics | 6 | * Copyright (C) 2005-2006 Samsung Electronics |
| 7 | * | 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
| @@ -72,6 +72,7 @@ | |||
| 72 | #define ONENAND_DEVICE_VCC_MASK (0x3) | 72 | #define ONENAND_DEVICE_VCC_MASK (0x3) |
| 73 | 73 | ||
| 74 | #define ONENAND_DEVICE_DENSITY_512Mb (0x002) | 74 | #define ONENAND_DEVICE_DENSITY_512Mb (0x002) |
| 75 | #define ONENAND_DEVICE_DENSITY_1Gb (0x003) | ||
| 75 | 76 | ||
| 76 | /* | 77 | /* |
| 77 | * Version ID Register F002h (R) | 78 | * Version ID Register F002h (R) |
| @@ -110,6 +111,7 @@ | |||
| 110 | #define ONENAND_CMD_UNLOCK (0x23) | 111 | #define ONENAND_CMD_UNLOCK (0x23) |
| 111 | #define ONENAND_CMD_LOCK (0x2A) | 112 | #define ONENAND_CMD_LOCK (0x2A) |
| 112 | #define ONENAND_CMD_LOCK_TIGHT (0x2C) | 113 | #define ONENAND_CMD_LOCK_TIGHT (0x2C) |
| 114 | #define ONENAND_CMD_UNLOCK_ALL (0x27) | ||
| 113 | #define ONENAND_CMD_ERASE (0x94) | 115 | #define ONENAND_CMD_ERASE (0x94) |
| 114 | #define ONENAND_CMD_RESET (0xF0) | 116 | #define ONENAND_CMD_RESET (0xF0) |
| 115 | #define ONENAND_CMD_OTP_ACCESS (0x65) | 117 | #define ONENAND_CMD_OTP_ACCESS (0x65) |
