diff options
Diffstat (limited to 'drivers/mtd/onenand/onenand_base.c')
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 137 |
1 files changed, 118 insertions, 19 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 | } |