162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Broadcom specific AMBA
362306a36Sopenharmony_ci * ChipCommon serial flash interface
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "bcma_private.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/bcma/bcma.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic struct resource bcma_sflash_resource = {
1462306a36Sopenharmony_ci	.name	= "bcma_sflash",
1562306a36Sopenharmony_ci	.start	= BCMA_SOC_FLASH2,
1662306a36Sopenharmony_ci	.end	= 0,
1762306a36Sopenharmony_ci	.flags  = IORESOURCE_MEM | IORESOURCE_READONLY,
1862306a36Sopenharmony_ci};
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct platform_device bcma_sflash_dev = {
2162306a36Sopenharmony_ci	.name		= "bcma_sflash",
2262306a36Sopenharmony_ci	.resource	= &bcma_sflash_resource,
2362306a36Sopenharmony_ci	.num_resources	= 1,
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct bcma_sflash_tbl_e {
2762306a36Sopenharmony_ci	char *name;
2862306a36Sopenharmony_ci	u32 id;
2962306a36Sopenharmony_ci	u32 blocksize;
3062306a36Sopenharmony_ci	u16 numblocks;
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
3462306a36Sopenharmony_ci	{ "M25P20", 0x11, 0x10000, 4, },
3562306a36Sopenharmony_ci	{ "M25P40", 0x12, 0x10000, 8, },
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	{ "M25P16", 0x14, 0x10000, 32, },
3862306a36Sopenharmony_ci	{ "M25P32", 0x15, 0x10000, 64, },
3962306a36Sopenharmony_ci	{ "M25P64", 0x16, 0x10000, 128, },
4062306a36Sopenharmony_ci	{ "M25FL128", 0x17, 0x10000, 256, },
4162306a36Sopenharmony_ci	{ "MX25L25635F", 0x18, 0x10000, 512, },
4262306a36Sopenharmony_ci	{ NULL },
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
4662306a36Sopenharmony_ci	{ "SST25WF512", 1, 0x1000, 16, },
4762306a36Sopenharmony_ci	{ "SST25VF512", 0x48, 0x1000, 16, },
4862306a36Sopenharmony_ci	{ "SST25WF010", 2, 0x1000, 32, },
4962306a36Sopenharmony_ci	{ "SST25VF010", 0x49, 0x1000, 32, },
5062306a36Sopenharmony_ci	{ "SST25WF020", 3, 0x1000, 64, },
5162306a36Sopenharmony_ci	{ "SST25VF020", 0x43, 0x1000, 64, },
5262306a36Sopenharmony_ci	{ "SST25WF040", 4, 0x1000, 128, },
5362306a36Sopenharmony_ci	{ "SST25VF040", 0x44, 0x1000, 128, },
5462306a36Sopenharmony_ci	{ "SST25VF040B", 0x8d, 0x1000, 128, },
5562306a36Sopenharmony_ci	{ "SST25WF080", 5, 0x1000, 256, },
5662306a36Sopenharmony_ci	{ "SST25VF080B", 0x8e, 0x1000, 256, },
5762306a36Sopenharmony_ci	{ "SST25VF016", 0x41, 0x1000, 512, },
5862306a36Sopenharmony_ci	{ "SST25VF032", 0x4a, 0x1000, 1024, },
5962306a36Sopenharmony_ci	{ "SST25VF064", 0x4b, 0x1000, 2048, },
6062306a36Sopenharmony_ci	{ NULL },
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic const struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
6462306a36Sopenharmony_ci	{ "AT45DB011", 0xc, 256, 512, },
6562306a36Sopenharmony_ci	{ "AT45DB021", 0x14, 256, 1024, },
6662306a36Sopenharmony_ci	{ "AT45DB041", 0x1c, 256, 2048, },
6762306a36Sopenharmony_ci	{ "AT45DB081", 0x24, 256, 4096, },
6862306a36Sopenharmony_ci	{ "AT45DB161", 0x2c, 512, 4096, },
6962306a36Sopenharmony_ci	{ "AT45DB321", 0x34, 512, 8192, },
7062306a36Sopenharmony_ci	{ "AT45DB642", 0x3c, 1024, 8192, },
7162306a36Sopenharmony_ci	{ NULL },
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void bcma_sflash_cmd(struct bcma_drv_cc *cc, u32 opcode)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int i;
7762306a36Sopenharmony_ci	bcma_cc_write32(cc, BCMA_CC_FLASHCTL,
7862306a36Sopenharmony_ci			BCMA_CC_FLASHCTL_START | opcode);
7962306a36Sopenharmony_ci	for (i = 0; i < 1000; i++) {
8062306a36Sopenharmony_ci		if (!(bcma_cc_read32(cc, BCMA_CC_FLASHCTL) &
8162306a36Sopenharmony_ci		      BCMA_CC_FLASHCTL_BUSY))
8262306a36Sopenharmony_ci			return;
8362306a36Sopenharmony_ci		cpu_relax();
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	bcma_err(cc->core->bus, "SFLASH control command failed (timeout)!\n");
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Initialize serial flash access */
8962306a36Sopenharmony_ciint bcma_sflash_init(struct bcma_drv_cc *cc)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct bcma_bus *bus = cc->core->bus;
9262306a36Sopenharmony_ci	struct bcma_sflash *sflash = &cc->sflash;
9362306a36Sopenharmony_ci	const struct bcma_sflash_tbl_e *e;
9462306a36Sopenharmony_ci	u32 id, id2;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
9762306a36Sopenharmony_ci	case BCMA_CC_FLASHT_STSER:
9862306a36Sopenharmony_ci		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_DP);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 0);
10162306a36Sopenharmony_ci		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES);
10262306a36Sopenharmony_ci		id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 1);
10562306a36Sopenharmony_ci		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES);
10662306a36Sopenharmony_ci		id2 = bcma_cc_read32(cc, BCMA_CC_FLASHDATA);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		switch (id) {
10962306a36Sopenharmony_ci		case 0xbf:
11062306a36Sopenharmony_ci			for (e = bcma_sflash_sst_tbl; e->name; e++) {
11162306a36Sopenharmony_ci				if (e->id == id2)
11262306a36Sopenharmony_ci					break;
11362306a36Sopenharmony_ci			}
11462306a36Sopenharmony_ci			break;
11562306a36Sopenharmony_ci		case 0x13:
11662306a36Sopenharmony_ci			return -ENOTSUPP;
11762306a36Sopenharmony_ci		default:
11862306a36Sopenharmony_ci			for (e = bcma_sflash_st_tbl; e->name; e++) {
11962306a36Sopenharmony_ci				if (e->id == id)
12062306a36Sopenharmony_ci					break;
12162306a36Sopenharmony_ci			}
12262306a36Sopenharmony_ci			break;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci		if (!e->name) {
12562306a36Sopenharmony_ci			bcma_err(bus, "Unsupported ST serial flash (id: 0x%X, id2: 0x%X)\n", id, id2);
12662306a36Sopenharmony_ci			return -ENOTSUPP;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		break;
13062306a36Sopenharmony_ci	case BCMA_CC_FLASHT_ATSER:
13162306a36Sopenharmony_ci		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_STATUS);
13262306a36Sopenharmony_ci		id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA) & 0x3c;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		for (e = bcma_sflash_at_tbl; e->name; e++) {
13562306a36Sopenharmony_ci			if (e->id == id)
13662306a36Sopenharmony_ci				break;
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci		if (!e->name) {
13962306a36Sopenharmony_ci			bcma_err(bus, "Unsupported Atmel serial flash (id: 0x%X)\n", id);
14062306a36Sopenharmony_ci			return -ENOTSUPP;
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		break;
14462306a36Sopenharmony_ci	default:
14562306a36Sopenharmony_ci		bcma_err(bus, "Unsupported flash type\n");
14662306a36Sopenharmony_ci		return -ENOTSUPP;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	sflash->blocksize = e->blocksize;
15062306a36Sopenharmony_ci	sflash->numblocks = e->numblocks;
15162306a36Sopenharmony_ci	sflash->size = sflash->blocksize * sflash->numblocks;
15262306a36Sopenharmony_ci	sflash->present = true;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	bcma_info(bus, "Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n",
15562306a36Sopenharmony_ci		  e->name, sflash->size / 1024, sflash->blocksize,
15662306a36Sopenharmony_ci		  sflash->numblocks);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* Prepare platform device, but don't register it yet. It's too early,
15962306a36Sopenharmony_ci	 * malloc (required by device_private_init) is not available yet. */
16062306a36Sopenharmony_ci	bcma_sflash_dev.resource[0].end = bcma_sflash_dev.resource[0].start +
16162306a36Sopenharmony_ci					  sflash->size;
16262306a36Sopenharmony_ci	bcma_sflash_dev.dev.platform_data = sflash;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return 0;
16562306a36Sopenharmony_ci}
166