diff options
author | Robert Jarzmik <robert.jarzmik@free.fr> | 2012-03-22 16:00:53 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2012-03-26 20:03:27 -0400 |
commit | 7b0e67f604e1829e5292e1ad7743eb18dc42ea7c (patch) | |
tree | 48cb065f253ed6552e8e5805bba7647eb25a8ad7 | |
parent | 1b15a5f93bbd9a6f5346cfa449720a7e32115f86 (diff) |
mtd: docg3 add protection against concurrency
As docg3 is intolerant against reentrancy, especially
because of its weird register access (ie. a register read is
performed by a first register write), each access to the
docg3 IO space must be locked.
Lock the IO space with a mutex, shared by all chips on the
same cascade, as they all share the same IO space.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r-- | drivers/mtd/devices/docg3.c | 50 | ||||
-rw-r--r-- | drivers/mtd/devices/docg3.h | 2 |
2 files changed, 41 insertions, 11 deletions
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 935d4c6e9321..8272c02668d6 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c | |||
@@ -875,6 +875,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, | |||
875 | ops->retlen = 0; | 875 | ops->retlen = 0; |
876 | ret = 0; | 876 | ret = 0; |
877 | skip = from % DOC_LAYOUT_PAGE_SIZE; | 877 | skip = from % DOC_LAYOUT_PAGE_SIZE; |
878 | mutex_lock(&docg3->cascade->lock); | ||
878 | while (!ret && (len > 0 || ooblen > 0)) { | 879 | while (!ret && (len > 0 || ooblen > 0)) { |
879 | calc_block_sector(from - skip, &block0, &block1, &page, &ofs, | 880 | calc_block_sector(from - skip, &block0, &block1, &page, &ofs, |
880 | docg3->reliable); | 881 | docg3->reliable); |
@@ -882,7 +883,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, | |||
882 | nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE); | 883 | nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE); |
883 | ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); | 884 | ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); |
884 | if (ret < 0) | 885 | if (ret < 0) |
885 | goto err; | 886 | goto out; |
886 | ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); | 887 | ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); |
887 | if (ret < 0) | 888 | if (ret < 0) |
888 | goto err_in_read; | 889 | goto err_in_read; |
@@ -950,11 +951,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, | |||
950 | skip = 0; | 951 | skip = 0; |
951 | } | 952 | } |
952 | 953 | ||
954 | out: | ||
955 | mutex_unlock(&docg3->cascade->lock); | ||
953 | return ret; | 956 | return ret; |
954 | err_in_read: | 957 | err_in_read: |
955 | doc_read_page_finish(docg3); | 958 | doc_read_page_finish(docg3); |
956 | err: | 959 | goto out; |
957 | return ret; | ||
958 | } | 960 | } |
959 | 961 | ||
960 | /** | 962 | /** |
@@ -1194,7 +1196,6 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info) | |||
1194 | int block0, block1, page, ret, ofs = 0; | 1196 | int block0, block1, page, ret, ofs = 0; |
1195 | 1197 | ||
1196 | doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len); | 1198 | doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len); |
1197 | doc_set_device_id(docg3, docg3->device_id); | ||
1198 | 1199 | ||
1199 | info->state = MTD_ERASE_PENDING; | 1200 | info->state = MTD_ERASE_PENDING; |
1200 | calc_block_sector(info->addr + info->len, &block0, &block1, &page, | 1201 | calc_block_sector(info->addr + info->len, &block0, &block1, &page, |
@@ -1206,6 +1207,8 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info) | |||
1206 | ret = 0; | 1207 | ret = 0; |
1207 | calc_block_sector(info->addr, &block0, &block1, &page, &ofs, | 1208 | calc_block_sector(info->addr, &block0, &block1, &page, &ofs, |
1208 | docg3->reliable); | 1209 | docg3->reliable); |
1210 | mutex_lock(&docg3->cascade->lock); | ||
1211 | doc_set_device_id(docg3, docg3->device_id); | ||
1209 | doc_set_reliable_mode(docg3); | 1212 | doc_set_reliable_mode(docg3); |
1210 | for (len = info->len; !ret && len > 0; len -= mtd->erasesize) { | 1213 | for (len = info->len; !ret && len > 0; len -= mtd->erasesize) { |
1211 | info->state = MTD_ERASING; | 1214 | info->state = MTD_ERASING; |
@@ -1213,6 +1216,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info) | |||
1213 | block0 += 2; | 1216 | block0 += 2; |
1214 | block1 += 2; | 1217 | block1 += 2; |
1215 | } | 1218 | } |
1219 | mutex_unlock(&docg3->cascade->lock); | ||
1216 | 1220 | ||
1217 | if (ret) | 1221 | if (ret) |
1218 | goto reset_err; | 1222 | goto reset_err; |
@@ -1399,7 +1403,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, | |||
1399 | struct mtd_oob_ops *ops) | 1403 | struct mtd_oob_ops *ops) |
1400 | { | 1404 | { |
1401 | struct docg3 *docg3 = mtd->priv; | 1405 | struct docg3 *docg3 = mtd->priv; |
1402 | int block0, block1, page, ret, pofs = 0, autoecc, oobdelta; | 1406 | int ret, autoecc, oobdelta; |
1403 | u8 *oobbuf = ops->oobbuf; | 1407 | u8 *oobbuf = ops->oobbuf; |
1404 | u8 *buf = ops->datbuf; | 1408 | u8 *buf = ops->datbuf; |
1405 | size_t len, ooblen; | 1409 | size_t len, ooblen; |
@@ -1451,6 +1455,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, | |||
1451 | if (autoecc < 0) | 1455 | if (autoecc < 0) |
1452 | return autoecc; | 1456 | return autoecc; |
1453 | 1457 | ||
1458 | mutex_lock(&docg3->cascade->lock); | ||
1454 | while (!ret && len > 0) { | 1459 | while (!ret && len > 0) { |
1455 | memset(oob, 0, sizeof(oob)); | 1460 | memset(oob, 0, sizeof(oob)); |
1456 | if (ofs == docg3->oob_write_ofs) | 1461 | if (ofs == docg3->oob_write_ofs) |
@@ -1471,8 +1476,9 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, | |||
1471 | } | 1476 | } |
1472 | ops->retlen += DOC_LAYOUT_PAGE_SIZE; | 1477 | ops->retlen += DOC_LAYOUT_PAGE_SIZE; |
1473 | } | 1478 | } |
1474 | err: | 1479 | |
1475 | doc_set_device_id(docg3, 0); | 1480 | doc_set_device_id(docg3, 0); |
1481 | mutex_unlock(&docg3->cascade->lock); | ||
1476 | return ret; | 1482 | return ret; |
1477 | } | 1483 | } |
1478 | 1484 | ||
@@ -1529,9 +1535,11 @@ static ssize_t dps0_is_key_locked(struct device *dev, | |||
1529 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); | 1535 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); |
1530 | int dps0; | 1536 | int dps0; |
1531 | 1537 | ||
1538 | mutex_lock(&docg3->cascade->lock); | ||
1532 | doc_set_device_id(docg3, docg3->device_id); | 1539 | doc_set_device_id(docg3, docg3->device_id); |
1533 | dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); | 1540 | dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); |
1534 | doc_set_device_id(docg3, 0); | 1541 | doc_set_device_id(docg3, 0); |
1542 | mutex_unlock(&docg3->cascade->lock); | ||
1535 | 1543 | ||
1536 | return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK)); | 1544 | return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK)); |
1537 | } | 1545 | } |
@@ -1542,9 +1550,11 @@ static ssize_t dps1_is_key_locked(struct device *dev, | |||
1542 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); | 1550 | struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); |
1543 | int dps1; | 1551 | int dps1; |
1544 | 1552 | ||
1553 | mutex_lock(&docg3->cascade->lock); | ||
1545 | doc_set_device_id(docg3, docg3->device_id); | 1554 | doc_set_device_id(docg3, docg3->device_id); |
1546 | dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); | 1555 | dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); |
1547 | doc_set_device_id(docg3, 0); | 1556 | doc_set_device_id(docg3, 0); |
1557 | mutex_unlock(&docg3->cascade->lock); | ||
1548 | 1558 | ||
1549 | return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK)); | 1559 | return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK)); |
1550 | } | 1560 | } |
@@ -1559,10 +1569,12 @@ static ssize_t dps0_insert_key(struct device *dev, | |||
1559 | if (count != DOC_LAYOUT_DPS_KEY_LENGTH) | 1569 | if (count != DOC_LAYOUT_DPS_KEY_LENGTH) |
1560 | return -EINVAL; | 1570 | return -EINVAL; |
1561 | 1571 | ||
1572 | mutex_lock(&docg3->cascade->lock); | ||
1562 | doc_set_device_id(docg3, docg3->device_id); | 1573 | doc_set_device_id(docg3, docg3->device_id); |
1563 | for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) | 1574 | for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) |
1564 | doc_writeb(docg3, buf[i], DOC_DPS0_KEY); | 1575 | doc_writeb(docg3, buf[i], DOC_DPS0_KEY); |
1565 | doc_set_device_id(docg3, 0); | 1576 | doc_set_device_id(docg3, 0); |
1577 | mutex_unlock(&docg3->cascade->lock); | ||
1566 | return count; | 1578 | return count; |
1567 | } | 1579 | } |
1568 | 1580 | ||
@@ -1576,10 +1588,12 @@ static ssize_t dps1_insert_key(struct device *dev, | |||
1576 | if (count != DOC_LAYOUT_DPS_KEY_LENGTH) | 1588 | if (count != DOC_LAYOUT_DPS_KEY_LENGTH) |
1577 | return -EINVAL; | 1589 | return -EINVAL; |
1578 | 1590 | ||
1591 | mutex_lock(&docg3->cascade->lock); | ||
1579 | doc_set_device_id(docg3, docg3->device_id); | 1592 | doc_set_device_id(docg3, docg3->device_id); |
1580 | for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) | 1593 | for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) |
1581 | doc_writeb(docg3, buf[i], DOC_DPS1_KEY); | 1594 | doc_writeb(docg3, buf[i], DOC_DPS1_KEY); |
1582 | doc_set_device_id(docg3, 0); | 1595 | doc_set_device_id(docg3, 0); |
1596 | mutex_unlock(&docg3->cascade->lock); | ||
1583 | return count; | 1597 | return count; |
1584 | } | 1598 | } |
1585 | 1599 | ||
@@ -1634,7 +1648,11 @@ static int dbg_flashctrl_show(struct seq_file *s, void *p) | |||
1634 | struct docg3 *docg3 = (struct docg3 *)s->private; | 1648 | struct docg3 *docg3 = (struct docg3 *)s->private; |
1635 | 1649 | ||
1636 | int pos = 0; | 1650 | int pos = 0; |
1637 | u8 fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); | 1651 | u8 fctrl; |
1652 | |||
1653 | mutex_lock(&docg3->cascade->lock); | ||
1654 | fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); | ||
1655 | mutex_unlock(&docg3->cascade->lock); | ||
1638 | 1656 | ||
1639 | pos += seq_printf(s, | 1657 | pos += seq_printf(s, |
1640 | "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n", | 1658 | "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n", |
@@ -1652,9 +1670,12 @@ static int dbg_asicmode_show(struct seq_file *s, void *p) | |||
1652 | { | 1670 | { |
1653 | struct docg3 *docg3 = (struct docg3 *)s->private; | 1671 | struct docg3 *docg3 = (struct docg3 *)s->private; |
1654 | 1672 | ||
1655 | int pos = 0; | 1673 | int pos = 0, pctrl, mode; |
1656 | int pctrl = doc_register_readb(docg3, DOC_ASICMODE); | 1674 | |
1657 | int mode = pctrl & 0x03; | 1675 | mutex_lock(&docg3->cascade->lock); |
1676 | pctrl = doc_register_readb(docg3, DOC_ASICMODE); | ||
1677 | mode = pctrl & 0x03; | ||
1678 | mutex_unlock(&docg3->cascade->lock); | ||
1658 | 1679 | ||
1659 | pos += seq_printf(s, | 1680 | pos += seq_printf(s, |
1660 | "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (", | 1681 | "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (", |
@@ -1686,7 +1707,11 @@ static int dbg_device_id_show(struct seq_file *s, void *p) | |||
1686 | { | 1707 | { |
1687 | struct docg3 *docg3 = (struct docg3 *)s->private; | 1708 | struct docg3 *docg3 = (struct docg3 *)s->private; |
1688 | int pos = 0; | 1709 | int pos = 0; |
1689 | int id = doc_register_readb(docg3, DOC_DEVICESELECT); | 1710 | int id; |
1711 | |||
1712 | mutex_lock(&docg3->cascade->lock); | ||
1713 | id = doc_register_readb(docg3, DOC_DEVICESELECT); | ||
1714 | mutex_unlock(&docg3->cascade->lock); | ||
1690 | 1715 | ||
1691 | pos += seq_printf(s, "DeviceId = %d\n", id); | 1716 | pos += seq_printf(s, "DeviceId = %d\n", id); |
1692 | return pos; | 1717 | return pos; |
@@ -1699,6 +1724,7 @@ static int dbg_protection_show(struct seq_file *s, void *p) | |||
1699 | int pos = 0; | 1724 | int pos = 0; |
1700 | int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high; | 1725 | int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high; |
1701 | 1726 | ||
1727 | mutex_lock(&docg3->cascade->lock); | ||
1702 | protect = doc_register_readb(docg3, DOC_PROTECTION); | 1728 | protect = doc_register_readb(docg3, DOC_PROTECTION); |
1703 | dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); | 1729 | dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); |
1704 | dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW); | 1730 | dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW); |
@@ -1706,6 +1732,7 @@ static int dbg_protection_show(struct seq_file *s, void *p) | |||
1706 | dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); | 1732 | dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); |
1707 | dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW); | 1733 | dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW); |
1708 | dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH); | 1734 | dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH); |
1735 | mutex_unlock(&docg3->cascade->lock); | ||
1709 | 1736 | ||
1710 | pos += seq_printf(s, "Protection = 0x%02x (", | 1737 | pos += seq_printf(s, "Protection = 0x%02x (", |
1711 | protect); | 1738 | protect); |
@@ -2022,6 +2049,7 @@ static int __init docg3_probe(struct platform_device *pdev) | |||
2022 | if (!cascade) | 2049 | if (!cascade) |
2023 | goto nomem1; | 2050 | goto nomem1; |
2024 | cascade->base = base; | 2051 | cascade->base = base; |
2052 | mutex_init(&cascade->lock); | ||
2025 | cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T, | 2053 | cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T, |
2026 | DOC_ECC_BCH_PRIMPOLY); | 2054 | DOC_ECC_BCH_PRIMPOLY); |
2027 | if (!cascade->bch) | 2055 | if (!cascade->bch) |
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 642e60667cfd..19fb93f96a3a 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h | |||
@@ -273,11 +273,13 @@ | |||
273 | * @floors: floors (ie. one physical docg3 chip is one floor) | 273 | * @floors: floors (ie. one physical docg3 chip is one floor) |
274 | * @base: IO space to access all chips in the cascade | 274 | * @base: IO space to access all chips in the cascade |
275 | * @bch: the BCH correcting control structure | 275 | * @bch: the BCH correcting control structure |
276 | * @lock: lock to protect docg3 IO space from concurrent accesses | ||
276 | */ | 277 | */ |
277 | struct docg3_cascade { | 278 | struct docg3_cascade { |
278 | struct mtd_info *floors[DOC_MAX_NBFLOORS]; | 279 | struct mtd_info *floors[DOC_MAX_NBFLOORS]; |
279 | void __iomem *base; | 280 | void __iomem *base; |
280 | struct bch_control *bch; | 281 | struct bch_control *bch; |
282 | struct mutex lock; | ||
281 | }; | 283 | }; |
282 | 284 | ||
283 | /** | 285 | /** |