X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=drivers%2Fmtd%2Fspi%2Fatmel.c;h=6a92c4b774b41ff85dbb834ccc206e12b8a7017d;hp=fb7a4a939b062530e5c565af2b4bc6e7217ab9a1;hb=8dc16cf9dd6196d99969d12741df186a61a2f9a3;hpb=e44f3ea4e801d37ef293284cb57b9637382f211a diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c index fb7a4a939b..6a92c4b774 100644 --- a/drivers/mtd/spi/atmel.c +++ b/drivers/mtd/spi/atmel.c @@ -2,8 +2,9 @@ * Atmel SPI DataFlash support * * Copyright (C) 2008 Atmel Corporation + * Licensed under the GPL-2 or later. */ -#define DEBUG + #include #include #include @@ -39,9 +40,10 @@ struct atmel_spi_flash_params { const char *name; }; +/* spi_flash needs to be first so upper layers can free() it */ struct atmel_spi_flash { - const struct atmel_spi_flash_params *params; struct spi_flash flash; + const struct atmel_spi_flash_params *params; }; static inline struct atmel_spi_flash * @@ -51,6 +53,54 @@ to_atmel_spi_flash(struct spi_flash *flash) } static const struct atmel_spi_flash_params atmel_spi_flash_table[] = { + { + .idcode1 = 0x22, + .l2_page_size = 8, + .pages_per_block = 8, + .blocks_per_sector = 16, + .nr_sectors = 4, + .name = "AT45DB011D", + }, + { + .idcode1 = 0x23, + .l2_page_size = 8, + .pages_per_block = 8, + .blocks_per_sector = 16, + .nr_sectors = 8, + .name = "AT45DB021D", + }, + { + .idcode1 = 0x24, + .l2_page_size = 8, + .pages_per_block = 8, + .blocks_per_sector = 32, + .nr_sectors = 8, + .name = "AT45DB041D", + }, + { + .idcode1 = 0x25, + .l2_page_size = 8, + .pages_per_block = 8, + .blocks_per_sector = 32, + .nr_sectors = 16, + .name = "AT45DB081D", + }, + { + .idcode1 = 0x26, + .l2_page_size = 9, + .pages_per_block = 8, + .blocks_per_sector = 32, + .nr_sectors = 16, + .name = "AT45DB161D", + }, + { + .idcode1 = 0x27, + .l2_page_size = 9, + .pages_per_block = 8, + .blocks_per_sector = 64, + .nr_sectors = 64, + .name = "AT45DB321D", + }, { .idcode1 = 0x28, .l2_page_size = 10, @@ -59,6 +109,14 @@ static const struct atmel_spi_flash_params atmel_spi_flash_table[] = { .nr_sectors = 32, .name = "AT45DB642D", }, + { + .idcode1 = 0x47, + .l2_page_size = 8, + .pages_per_block = 16, + .blocks_per_sector = 16, + .nr_sectors = 64, + .name = "AT25DF321", + }, }; static int at45_wait_ready(struct spi_flash *flash, unsigned long timeout) @@ -120,31 +178,86 @@ static void at45_build_address(struct atmel_spi_flash *asf, u8 *cmd, u32 offset) cmd[2] = byte_addr; } -static int dataflash_read_fast_p2(struct spi_flash *flash, +static int dataflash_read_fast_at45(struct spi_flash *flash, u32 offset, size_t len, void *buf) { + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); u8 cmd[5]; cmd[0] = CMD_READ_ARRAY_FAST; - cmd[1] = offset >> 16; - cmd[2] = offset >> 8; - cmd[3] = offset; + at45_build_address(asf, cmd + 1, offset); cmd[4] = 0x00; return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); } -static int dataflash_read_fast_at45(struct spi_flash *flash, - u32 offset, size_t len, void *buf) +/* + * TODO: the two write funcs (_p2/_at45) should get unified ... + */ +static int dataflash_write_p2(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) { struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); - u8 cmd[5]; + unsigned long page_size; + u32 addr = offset; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; - cmd[0] = CMD_READ_ARRAY_FAST; - at45_build_address(asf, cmd + 1, offset); - cmd[4] = 0x00; + /* + * TODO: This function currently uses only page buffer #1. We can + * speed this up by using both buffers and loading one buffer while + * the other is being programmed into main memory. + */ - return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); + page_size = (1 << asf->params->l2_page_size); + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - (addr % page_size)); + + /* Use the same address bits for both commands */ + cmd[0] = CMD_AT45_LOAD_BUF1; + cmd[1] = addr >> 16; + cmd[2] = addr >> 8; + cmd[3] = addr; + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Loading AT45 buffer failed\n"); + goto out; + } + + cmd[0] = CMD_AT45_PROG_BUF1; + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page programming failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page programming timed out\n"); + goto out; + } + + addr += chunk_len; + } + + debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; } static int dataflash_write_at45(struct spi_flash *flash, @@ -160,6 +273,12 @@ static int dataflash_write_at45(struct spi_flash *flash, int ret; u8 cmd[4]; + /* + * TODO: This function currently uses only page buffer #1. We can + * speed this up by using both buffers and loading one buffer while + * the other is being programmed into main memory. + */ + page_shift = asf->params->l2_page_size; page_size = (1 << page_shift) + (1 << (page_shift - 5)); page_shift++; @@ -205,7 +324,7 @@ static int dataflash_write_at45(struct spi_flash *flash, byte_addr = 0; } - debug("SF: AT45: Successfully programmed %u bytes @ 0x%x\n", + debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n", len, offset); ret = 0; @@ -214,7 +333,69 @@ out: return ret; } -int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len) +/* + * TODO: the two erase funcs (_p2/_at45) should get unified ... + */ +static int dataflash_erase_p2(struct spi_flash *flash, u32 offset, size_t len) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_size; + + size_t actual; + int ret; + u8 cmd[4]; + + /* + * TODO: This function currently uses page erase only. We can + * probably speed things up by using block and/or sector erase + * when possible. + */ + + page_size = (1 << asf->params->l2_page_size); + + if (offset % page_size || len % page_size) { + debug("SF: Erase offset/length not multiple of page size\n"); + return -1; + } + + cmd[0] = CMD_AT45_ERASE_PAGE; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += page_size) { + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page erase failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page erase timed out\n"); + goto out; + } + + offset += page_size; + } + + debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +static int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len) { struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); unsigned long page_addr; @@ -268,7 +449,7 @@ int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len) page_addr++; } - debug("SF: AT45: Successfully erased %u bytes @ 0x%x\n", + debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n", len, offset); ret = 0; @@ -280,7 +461,7 @@ out: struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) { const struct atmel_spi_flash_params *params; - unsigned long page_size; + unsigned page_size; unsigned int family; struct atmel_spi_flash *asf; unsigned int i; @@ -299,15 +480,13 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) return NULL; } - asf = malloc(sizeof(struct atmel_spi_flash)); + asf = spi_flash_alloc(struct atmel_spi_flash, spi, params->name); if (!asf) { debug("SF: Failed to allocate memory\n"); return NULL; } asf->params = params; - asf->flash.spi = spi; - asf->flash.name = params->name; /* Assuming power-of-two page size initially. */ page_size = 1 << params->l2_page_size; @@ -332,14 +511,20 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) asf->flash.erase = dataflash_erase_at45; page_size += 1 << (params->l2_page_size - 5); } else { - asf->flash.read = dataflash_read_fast_p2; + asf->flash.write = dataflash_write_p2; + asf->flash.erase = dataflash_erase_p2; } + asf->flash.page_size = page_size; + asf->flash.sector_size = page_size; break; case DF_FAMILY_AT26F: case DF_FAMILY_AT26DF: - asf->flash.read = dataflash_read_fast_p2; + asf->flash.page_size = page_size; + asf->flash.sector_size = 4096; + /* clear SPRL# bit for locked flash */ + spi_flash_cmd_write_status(&asf->flash, 0); break; default: @@ -351,9 +536,6 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) * params->blocks_per_sector * params->nr_sectors; - debug("SF: Detected %s with page size %u, total %u bytes\n", - params->name, page_size, asf->flash.size); - return &asf->flash; err: