Release 4.7 drivers/mtd/devices/bcm47xxsflash.c
  
  
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/mtd/mtd.h>
#include <linux/platform_device.h>
#include <linux/bcma/bcma.h>
#include "bcm47xxsflash.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Serial flash driver for BCMA bus");
static const char * const probes[] = { "bcm47xxpart", NULL };
/**************************************************
 * Various helpers
 **************************************************/
static void bcm47xxsflash_cmd(struct bcm47xxsflash *b47s, u32 opcode)
{
	int i;
	b47s->cc_write(b47s, BCMA_CC_FLASHCTL, BCMA_CC_FLASHCTL_START | opcode);
	for (i = 0; i < 1000; i++) {
		if (!(b47s->cc_read(b47s, BCMA_CC_FLASHCTL) &
		      BCMA_CC_FLASHCTL_BUSY))
			return;
		cpu_relax();
	}
	pr_err("Control command failed (timeout)!\n");
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 70 | 100.00% | 1 | 100.00% | 
 | Total | 70 | 100.00% | 1 | 100.00% | 
static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout)
{
	unsigned long deadline = jiffies + timeout;
	do {
		switch (b47s->type) {
		case BCM47XXSFLASH_TYPE_ST:
			bcm47xxsflash_cmd(b47s, OPCODE_ST_RDSR);
			if (!(b47s->cc_read(b47s, BCMA_CC_FLASHDATA) &
			      SR_ST_WIP))
				return 0;
			break;
		case BCM47XXSFLASH_TYPE_ATMEL:
			bcm47xxsflash_cmd(b47s, OPCODE_AT_STATUS);
			if (b47s->cc_read(b47s, BCMA_CC_FLASHDATA) &
			    SR_AT_READY)
				return 0;
			break;
		}
		cpu_relax();
		udelay(1);
	} while (!time_after_eq(jiffies, deadline));
	pr_err("Timeout waiting for flash to be ready!\n");
	return -EBUSY;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 118 | 100.00% | 1 | 100.00% | 
 | Total | 118 | 100.00% | 1 | 100.00% | 
/**************************************************
 * MTD ops
 **************************************************/
static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase)
{
	struct bcm47xxsflash *b47s = mtd->priv;
	int err;
	switch (b47s->type) {
	case BCM47XXSFLASH_TYPE_ST:
		bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN);
		b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr);
		/* Newer flashes have "sub-sectors" which can be erased
                 * independently with a new command: ST_SSE. The ST_SE command
                 * erases 64KB just as before.
                 */
		if (b47s->blocksize < (64 * 1024))
			bcm47xxsflash_cmd(b47s, OPCODE_ST_SSE);
		else
			bcm47xxsflash_cmd(b47s, OPCODE_ST_SE);
		break;
	case BCM47XXSFLASH_TYPE_ATMEL:
		b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr << 1);
		bcm47xxsflash_cmd(b47s, OPCODE_AT_PAGE_ERASE);
		break;
	}
	err = bcm47xxsflash_poll(b47s, HZ);
	if (err)
		erase->state = MTD_ERASE_FAILED;
	else
		erase->state = MTD_ERASE_DONE;
	if (erase->callback)
		erase->callback(erase);
	return err;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 156 | 100.00% | 1 | 100.00% | 
 | Total | 156 | 100.00% | 1 | 100.00% | 
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
			      size_t *retlen, u_char *buf)
{
	struct bcm47xxsflash *b47s = mtd->priv;
	/* Check address range */
	if ((from + len) > mtd->size)
		return -EINVAL;
	memcpy_fromio(buf, b47s->window + from, len);
	*retlen = len;
	return len;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 67 | 93.06% | 2 | 66.67% | 
| hauke mehrtens | hauke mehrtens | 5 | 6.94% | 1 | 33.33% | 
 | Total | 72 | 100.00% | 3 | 100.00% | 
static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len,
				  const u_char *buf)
{
	struct bcm47xxsflash *b47s = mtd->priv;
	int written = 0;
	/* Enable writes */
	bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN);
	/* Write first byte */
	b47s->cc_write(b47s, BCMA_CC_FLASHADDR, offset);
	b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++);
	/* Program page */
	if (b47s->bcma_cc->core->id.rev < 20) {
		bcm47xxsflash_cmd(b47s, OPCODE_ST_PP);
		return 1; /* 1B written */
	}
	/* Program page and set CSA (on newer chips we can continue writing) */
	bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | OPCODE_ST_PP);
	offset++;
	len--;
	written++;
	while (len > 0) {
		/* Page boundary, another function call is needed */
		if ((offset & 0xFF) == 0)
			break;
		bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | *buf++);
		offset++;
		len--;
		written++;
	}
	/* All done, drop CSA & poll */
	b47s->cc_write(b47s, BCMA_CC_FLASHCTL, 0);
	udelay(1);
	if (bcm47xxsflash_poll(b47s, HZ / 10))
		pr_err("Flash rejected dropping CSA\n");
	return written;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 192 | 100.00% | 1 | 100.00% | 
 | Total | 192 | 100.00% | 1 | 100.00% | 
static int bcm47xxsflash_write_at(struct mtd_info *mtd, u32 offset, size_t len,
				  const u_char *buf)
{
	struct bcm47xxsflash *b47s = mtd->priv;
	u32 mask = b47s->blocksize - 1;
	u32 page = (offset & ~mask) << 1;
	u32 byte = offset & mask;
	int written = 0;
	/* If we don't overwrite whole page, read it to the buffer first */
	if (byte || (len < b47s->blocksize)) {
		int err;
		b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page);
		bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_LOAD);
		/* 250 us for AT45DB321B */
		err = bcm47xxsflash_poll(b47s, HZ / 1000);
		if (err) {
			pr_err("Timeout reading page 0x%X info buffer\n", page);
			return err;
		}
	}
	/* Change buffer content with our data */
	while (len > 0) {
		/* Page boundary, another function call is needed */
		if (byte == b47s->blocksize)
			break;
		b47s->cc_write(b47s, BCMA_CC_FLASHADDR, byte++);
		b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++);
		bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_WRITE);
		len--;
		written++;
	}
	/* Program page with the buffer content */
	b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page);
	bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_PROGRAM);
	return written;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 207 | 100.00% | 1 | 100.00% | 
 | Total | 207 | 100.00% | 1 | 100.00% | 
static int bcm47xxsflash_write(struct mtd_info *mtd, loff_t to, size_t len,
			       size_t *retlen, const u_char *buf)
{
	struct bcm47xxsflash *b47s = mtd->priv;
	int written;
	/* Writing functions can return without writing all passed data, for
         * example when the hardware is too old or when we git page boundary.
         */
	while (len > 0) {
		switch (b47s->type) {
		case BCM47XXSFLASH_TYPE_ST:
			written = bcm47xxsflash_write_st(mtd, to, len, buf);
			break;
		case BCM47XXSFLASH_TYPE_ATMEL:
			written = bcm47xxsflash_write_at(mtd, to, len, buf);
			break;
		default:
			BUG_ON(1);
		}
		if (written < 0) {
			pr_err("Error writing at offset 0x%llX\n", to);
			return written;
		}
		to += (loff_t)written;
		len -= written;
		*retlen += written;
		buf += written;
	}
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 136 | 100.00% | 1 | 100.00% | 
 | Total | 136 | 100.00% | 1 | 100.00% | 
static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s,
				   struct device *dev)
{
	struct mtd_info *mtd = &b47s->mtd;
	mtd->priv = b47s;
	mtd->dev.parent = dev;
	mtd->name = "bcm47xxsflash";
	mtd->type = MTD_NORFLASH;
	mtd->flags = MTD_CAP_NORFLASH;
	mtd->size = b47s->size;
	mtd->erasesize = b47s->blocksize;
	mtd->writesize = 1;
	mtd->writebufsize = 1;
	mtd->_erase = bcm47xxsflash_erase;
	mtd->_read = bcm47xxsflash_read;
	mtd->_write = bcm47xxsflash_write;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 93 | 89.42% | 4 | 80.00% | 
| frans klaver | frans klaver | 11 | 10.58% | 1 | 20.00% | 
 | Total | 104 | 100.00% | 5 | 100.00% | 
/**************************************************
 * BCMA
 **************************************************/
static int bcm47xxsflash_bcma_cc_read(struct bcm47xxsflash *b47s, u16 offset)
{
	return bcma_cc_read32(b47s->bcma_cc, offset);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 24 | 100.00% | 1 | 100.00% | 
 | Total | 24 | 100.00% | 1 | 100.00% | 
static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset,
					u32 value)
{
	bcma_cc_write32(b47s->bcma_cc, offset, value);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 28 | 100.00% | 1 | 100.00% | 
 | Total | 28 | 100.00% | 1 | 100.00% | 
static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct bcma_sflash *sflash = dev_get_platdata(dev);
	struct bcm47xxsflash *b47s;
	struct resource *res;
	int err;
	b47s = devm_kzalloc(dev, sizeof(*b47s), GFP_KERNEL);
	if (!b47s)
		return -ENOMEM;
	sflash->priv = b47s;
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(dev, "invalid resource\n");
		return -EINVAL;
	}
	if (!devm_request_mem_region(dev, res->start, resource_size(res),
				     res->name)) {
		dev_err(dev, "can't request region for resource %pR\n", res);
		return -EBUSY;
	}
	b47s->window = ioremap_cache(res->start, resource_size(res));
	if (!b47s->window) {
		dev_err(dev, "ioremap failed for resource %pR\n", res);
		return -ENOMEM;
	}
	b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash);
	b47s->cc_read = bcm47xxsflash_bcma_cc_read;
	b47s->cc_write = bcm47xxsflash_bcma_cc_write;
	switch (b47s->bcma_cc->capabilities & BCMA_CC_CAP_FLASHT) {
	case BCMA_CC_FLASHT_STSER:
		b47s->type = BCM47XXSFLASH_TYPE_ST;
		break;
	case BCMA_CC_FLASHT_ATSER:
		b47s->type = BCM47XXSFLASH_TYPE_ATMEL;
		break;
	}
	b47s->blocksize = sflash->blocksize;
	b47s->numblocks = sflash->numblocks;
	b47s->size = sflash->size;
	bcm47xxsflash_fill_mtd(b47s, &pdev->dev);
	err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
	if (err) {
		pr_err("Failed to register MTD device: %d\n", err);
		iounmap(b47s->window);
		return err;
	}
	if (bcm47xxsflash_poll(b47s, HZ / 10))
		pr_warn("Serial flash busy\n");
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 191 | 58.05% | 7 | 70.00% | 
| brian norris | brian norris | 125 | 37.99% | 1 | 10.00% | 
| libo chen | libo chen | 8 | 2.43% | 1 | 10.00% | 
| frans klaver | frans klaver | 5 | 1.52% | 1 | 10.00% | 
 | Total | 329 | 100.00% | 10 | 100.00% | 
static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
{
	struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
	struct bcm47xxsflash *b47s = sflash->priv;
	mtd_device_unregister(&b47s->mtd);
	iounmap(b47s->window);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 44 | 86.27% | 3 | 75.00% | 
| brian norris | brian norris | 7 | 13.73% | 1 | 25.00% | 
 | Total | 51 | 100.00% | 4 | 100.00% | 
static struct platform_driver bcma_sflash_driver = {
	.probe	= bcm47xxsflash_bcma_probe,
	.remove = bcm47xxsflash_bcma_remove,
	.driver = {
		.name = "bcma_sflash",
        },
};
/**************************************************
 * Init
 **************************************************/
module_platform_driver(bcma_sflash_driver);
Overall Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| rafal milecki | rafal milecki | 1403 | 89.14% | 9 | 56.25% | 
| brian norris | brian norris | 135 | 8.58% | 1 | 6.25% | 
| frans klaver | frans klaver | 16 | 1.02% | 1 | 6.25% | 
| libo chen | libo chen | 10 | 0.64% | 2 | 12.50% | 
| hauke mehrtens | hauke mehrtens | 9 | 0.57% | 2 | 12.50% | 
| artem bityutskiy | artem bityutskiy | 1 | 0.06% | 1 | 6.25% | 
 | Total | 1574 | 100.00% | 16 | 100.00% | 
  
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.