15fe42d5bSRafał Miłecki #include <linux/kernel.h> 25fe42d5bSRafał Miłecki #include <linux/module.h> 35fe42d5bSRafał Miłecki #include <linux/slab.h> 4bddcb5e7SRafał Miłecki #include <linux/delay.h> 55fe42d5bSRafał Miłecki #include <linux/mtd/mtd.h> 65fe42d5bSRafał Miłecki #include <linux/platform_device.h> 75fe42d5bSRafał Miłecki #include <linux/bcma/bcma.h> 85fe42d5bSRafał Miłecki 9a2f74a7dSRafał Miłecki #include "bcm47xxsflash.h" 10a2f74a7dSRafał Miłecki 115fe42d5bSRafał Miłecki MODULE_LICENSE("GPL"); 125fe42d5bSRafał Miłecki MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); 135fe42d5bSRafał Miłecki 14afffeec9SArtem Bityutskiy static const char * const probes[] = { "bcm47xxpart", NULL }; 155fe42d5bSRafał Miłecki 16bddcb5e7SRafał Miłecki /************************************************** 17bddcb5e7SRafał Miłecki * Various helpers 18bddcb5e7SRafał Miłecki **************************************************/ 19bddcb5e7SRafał Miłecki 20bddcb5e7SRafał Miłecki static void bcm47xxsflash_cmd(struct bcm47xxsflash *b47s, u32 opcode) 21bddcb5e7SRafał Miłecki { 22bddcb5e7SRafał Miłecki int i; 23bddcb5e7SRafał Miłecki 24bddcb5e7SRafał Miłecki b47s->cc_write(b47s, BCMA_CC_FLASHCTL, BCMA_CC_FLASHCTL_START | opcode); 25bddcb5e7SRafał Miłecki for (i = 0; i < 1000; i++) { 26bddcb5e7SRafał Miłecki if (!(b47s->cc_read(b47s, BCMA_CC_FLASHCTL) & 27bddcb5e7SRafał Miłecki BCMA_CC_FLASHCTL_BUSY)) 28bddcb5e7SRafał Miłecki return; 29bddcb5e7SRafał Miłecki cpu_relax(); 30bddcb5e7SRafał Miłecki } 31bddcb5e7SRafał Miłecki pr_err("Control command failed (timeout)!\n"); 32bddcb5e7SRafał Miłecki } 33bddcb5e7SRafał Miłecki 34bddcb5e7SRafał Miłecki static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout) 35bddcb5e7SRafał Miłecki { 36bddcb5e7SRafał Miłecki unsigned long deadline = jiffies + timeout; 37bddcb5e7SRafał Miłecki 38bddcb5e7SRafał Miłecki do { 39bddcb5e7SRafał Miłecki switch (b47s->type) { 40bddcb5e7SRafał Miłecki case BCM47XXSFLASH_TYPE_ST: 41bddcb5e7SRafał Miłecki bcm47xxsflash_cmd(b47s, OPCODE_ST_RDSR); 42bddcb5e7SRafał Miłecki if (!(b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & 43bddcb5e7SRafał Miłecki SR_ST_WIP)) 44bddcb5e7SRafał Miłecki return 0; 45bddcb5e7SRafał Miłecki break; 46bddcb5e7SRafał Miłecki case BCM47XXSFLASH_TYPE_ATMEL: 47bddcb5e7SRafał Miłecki bcm47xxsflash_cmd(b47s, OPCODE_AT_STATUS); 48bddcb5e7SRafał Miłecki if (b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & 49bddcb5e7SRafał Miłecki SR_AT_READY) 50bddcb5e7SRafał Miłecki return 0; 51bddcb5e7SRafał Miłecki break; 52bddcb5e7SRafał Miłecki } 53bddcb5e7SRafał Miłecki 54bddcb5e7SRafał Miłecki cpu_relax(); 55bddcb5e7SRafał Miłecki udelay(1); 56bddcb5e7SRafał Miłecki } while (!time_after_eq(jiffies, deadline)); 57bddcb5e7SRafał Miłecki 58bddcb5e7SRafał Miłecki pr_err("Timeout waiting for flash to be ready!\n"); 59bddcb5e7SRafał Miłecki 60bddcb5e7SRafał Miłecki return -EBUSY; 61bddcb5e7SRafał Miłecki } 62bddcb5e7SRafał Miłecki 63bddcb5e7SRafał Miłecki /************************************************** 64bddcb5e7SRafał Miłecki * MTD ops 65bddcb5e7SRafał Miłecki **************************************************/ 66bddcb5e7SRafał Miłecki 67*c0fcbc56SRafał Miłecki static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase) 68*c0fcbc56SRafał Miłecki { 69*c0fcbc56SRafał Miłecki struct bcm47xxsflash *b47s = mtd->priv; 70*c0fcbc56SRafał Miłecki int err; 71*c0fcbc56SRafał Miłecki 72*c0fcbc56SRafał Miłecki switch (b47s->type) { 73*c0fcbc56SRafał Miłecki case BCM47XXSFLASH_TYPE_ST: 74*c0fcbc56SRafał Miłecki bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN); 75*c0fcbc56SRafał Miłecki b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr); 76*c0fcbc56SRafał Miłecki /* Newer flashes have "sub-sectors" which can be erased 77*c0fcbc56SRafał Miłecki * independently with a new command: ST_SSE. The ST_SE command 78*c0fcbc56SRafał Miłecki * erases 64KB just as before. 79*c0fcbc56SRafał Miłecki */ 80*c0fcbc56SRafał Miłecki if (b47s->blocksize < (64 * 1024)) 81*c0fcbc56SRafał Miłecki bcm47xxsflash_cmd(b47s, OPCODE_ST_SSE); 82*c0fcbc56SRafał Miłecki else 83*c0fcbc56SRafał Miłecki bcm47xxsflash_cmd(b47s, OPCODE_ST_SE); 84*c0fcbc56SRafał Miłecki break; 85*c0fcbc56SRafał Miłecki case BCM47XXSFLASH_TYPE_ATMEL: 86*c0fcbc56SRafał Miłecki b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr << 1); 87*c0fcbc56SRafał Miłecki bcm47xxsflash_cmd(b47s, OPCODE_AT_PAGE_ERASE); 88*c0fcbc56SRafał Miłecki break; 89*c0fcbc56SRafał Miłecki } 90*c0fcbc56SRafał Miłecki 91*c0fcbc56SRafał Miłecki err = bcm47xxsflash_poll(b47s, HZ); 92*c0fcbc56SRafał Miłecki if (err) 93*c0fcbc56SRafał Miłecki erase->state = MTD_ERASE_FAILED; 94*c0fcbc56SRafał Miłecki else 95*c0fcbc56SRafał Miłecki erase->state = MTD_ERASE_DONE; 96*c0fcbc56SRafał Miłecki 97*c0fcbc56SRafał Miłecki if (erase->callback) 98*c0fcbc56SRafał Miłecki erase->callback(erase); 99*c0fcbc56SRafał Miłecki 100*c0fcbc56SRafał Miłecki return err; 101*c0fcbc56SRafał Miłecki } 102*c0fcbc56SRafał Miłecki 1035fe42d5bSRafał Miłecki static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, 1045fe42d5bSRafał Miłecki size_t *retlen, u_char *buf) 1055fe42d5bSRafał Miłecki { 106a2f74a7dSRafał Miłecki struct bcm47xxsflash *b47s = mtd->priv; 1075fe42d5bSRafał Miłecki 1085fe42d5bSRafał Miłecki /* Check address range */ 1095fe42d5bSRafał Miłecki if ((from + len) > mtd->size) 1105fe42d5bSRafał Miłecki return -EINVAL; 1115fe42d5bSRafał Miłecki 112a2f74a7dSRafał Miłecki memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from), 1135fe42d5bSRafał Miłecki len); 11460aca067SHauke Mehrtens *retlen = len; 1155fe42d5bSRafał Miłecki 1165fe42d5bSRafał Miłecki return len; 1175fe42d5bSRafał Miłecki } 1185fe42d5bSRafał Miłecki 119a2f74a7dSRafał Miłecki static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) 1205fe42d5bSRafał Miłecki { 121a2f74a7dSRafał Miłecki struct mtd_info *mtd = &b47s->mtd; 122a2f74a7dSRafał Miłecki 123a2f74a7dSRafał Miłecki mtd->priv = b47s; 1245fe42d5bSRafał Miłecki mtd->name = "bcm47xxsflash"; 1255fe42d5bSRafał Miłecki mtd->owner = THIS_MODULE; 1265fe42d5bSRafał Miłecki mtd->type = MTD_ROM; 1275fe42d5bSRafał Miłecki 1285fe42d5bSRafał Miłecki /* TODO: implement writing support and verify/change following code */ 1295fe42d5bSRafał Miłecki mtd->flags = MTD_CAP_ROM; 130*c0fcbc56SRafał Miłecki mtd->size = b47s->size; 131*c0fcbc56SRafał Miłecki mtd->erasesize = b47s->blocksize; 1325fe42d5bSRafał Miłecki mtd->writebufsize = mtd->writesize = 1; 133*c0fcbc56SRafał Miłecki 134*c0fcbc56SRafał Miłecki mtd->_erase = bcm47xxsflash_erase; 135*c0fcbc56SRafał Miłecki mtd->_read = bcm47xxsflash_read; 1365fe42d5bSRafał Miłecki } 1375fe42d5bSRafał Miłecki 138f1a7c9d3SRafał Miłecki /************************************************** 139f1a7c9d3SRafał Miłecki * BCMA 140f1a7c9d3SRafał Miłecki **************************************************/ 141f1a7c9d3SRafał Miłecki 142265dfbd9SRafał Miłecki static int bcm47xxsflash_bcma_cc_read(struct bcm47xxsflash *b47s, u16 offset) 143265dfbd9SRafał Miłecki { 144265dfbd9SRafał Miłecki return bcma_cc_read32(b47s->bcma_cc, offset); 145265dfbd9SRafał Miłecki } 146265dfbd9SRafał Miłecki 147265dfbd9SRafał Miłecki static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset, 148265dfbd9SRafał Miłecki u32 value) 149265dfbd9SRafał Miłecki { 150265dfbd9SRafał Miłecki bcma_cc_write32(b47s->bcma_cc, offset, value); 151265dfbd9SRafał Miłecki } 152265dfbd9SRafał Miłecki 153f1a7c9d3SRafał Miłecki static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) 1545fe42d5bSRafał Miłecki { 1555fe42d5bSRafał Miłecki struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); 156a2f74a7dSRafał Miłecki struct bcm47xxsflash *b47s; 1575fe42d5bSRafał Miłecki int err; 1585fe42d5bSRafał Miłecki 159d2b1bd14SLibo Chen b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL); 160d2b1bd14SLibo Chen if (!b47s) 161d2b1bd14SLibo Chen return -ENOMEM; 162a2f74a7dSRafał Miłecki sflash->priv = b47s; 1635fe42d5bSRafał Miłecki 16441c81536SRafał Miłecki b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash); 165265dfbd9SRafał Miłecki b47s->cc_read = bcm47xxsflash_bcma_cc_read; 166265dfbd9SRafał Miłecki b47s->cc_write = bcm47xxsflash_bcma_cc_write; 16741c81536SRafał Miłecki 1681f816bc7SRafał Miłecki switch (b47s->bcma_cc->capabilities & BCMA_CC_CAP_FLASHT) { 1691f816bc7SRafał Miłecki case BCMA_CC_FLASHT_STSER: 1701f816bc7SRafał Miłecki b47s->type = BCM47XXSFLASH_TYPE_ST; 1711f816bc7SRafał Miłecki break; 1721f816bc7SRafał Miłecki case BCMA_CC_FLASHT_ATSER: 1731f816bc7SRafał Miłecki b47s->type = BCM47XXSFLASH_TYPE_ATMEL; 1741f816bc7SRafał Miłecki break; 1751f816bc7SRafał Miłecki } 1761f816bc7SRafał Miłecki 177a2f74a7dSRafał Miłecki b47s->window = sflash->window; 178a2f74a7dSRafał Miłecki b47s->blocksize = sflash->blocksize; 179a2f74a7dSRafał Miłecki b47s->numblocks = sflash->numblocks; 180a2f74a7dSRafał Miłecki b47s->size = sflash->size; 181a2f74a7dSRafał Miłecki bcm47xxsflash_fill_mtd(b47s); 182a2f74a7dSRafał Miłecki 183a2f74a7dSRafał Miłecki err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); 1845fe42d5bSRafał Miłecki if (err) { 1855fe42d5bSRafał Miłecki pr_err("Failed to register MTD device: %d\n", err); 186d2b1bd14SLibo Chen return err; 1875fe42d5bSRafał Miłecki } 1885fe42d5bSRafał Miłecki 189bddcb5e7SRafał Miłecki if (bcm47xxsflash_poll(b47s, HZ / 10)) 190bddcb5e7SRafał Miłecki pr_warn("Serial flash busy\n"); 191bddcb5e7SRafał Miłecki 1925fe42d5bSRafał Miłecki return 0; 1935fe42d5bSRafał Miłecki } 1945fe42d5bSRafał Miłecki 195f1a7c9d3SRafał Miłecki static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) 1965fe42d5bSRafał Miłecki { 1975fe42d5bSRafał Miłecki struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); 198a2f74a7dSRafał Miłecki struct bcm47xxsflash *b47s = sflash->priv; 1995fe42d5bSRafał Miłecki 200a2f74a7dSRafał Miłecki mtd_device_unregister(&b47s->mtd); 2015fe42d5bSRafał Miłecki 2025fe42d5bSRafał Miłecki return 0; 2035fe42d5bSRafał Miłecki } 2045fe42d5bSRafał Miłecki 2055fe42d5bSRafał Miłecki static struct platform_driver bcma_sflash_driver = { 206f1a7c9d3SRafał Miłecki .probe = bcm47xxsflash_bcma_probe, 207f1a7c9d3SRafał Miłecki .remove = bcm47xxsflash_bcma_remove, 2085fe42d5bSRafał Miłecki .driver = { 2095fe42d5bSRafał Miłecki .name = "bcma_sflash", 2105fe42d5bSRafał Miłecki .owner = THIS_MODULE, 2115fe42d5bSRafał Miłecki }, 2125fe42d5bSRafał Miłecki }; 2135fe42d5bSRafał Miłecki 214f1a7c9d3SRafał Miłecki /************************************************** 215f1a7c9d3SRafał Miłecki * Init 216f1a7c9d3SRafał Miłecki **************************************************/ 217f1a7c9d3SRafał Miłecki 2188268df26SLibo Chen module_platform_driver(bcma_sflash_driver); 219