blob: 923b0a56012edd2d98e42afb022df50892342974 [file] [log] [blame]
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07001/*
2 * (C) Copyright 2010, ucRobotics Inc.
Marc Jones79f60a52014-05-01 14:53:24 -06003 * Copyright (c) 2014, Sage Electronic Engineering, LLC
4 * .
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07005 * Author: Chong Huang <chuang@ucrobotics.com>
6 * Licensed under the GPL-2 or later.
7 */
8
9#include <stdlib.h>
10#include <spi_flash.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100011
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070012#include "spi_flash_internal.h"
13
Marc Jones79f60a52014-05-01 14:53:24 -060014/* EN25*-specific commands */
15#define CMD_EN25_WREN 0x06 /* Write Enable */
16#define CMD_EN25_WRDI 0x04 /* Write Disable */
17#define CMD_EN25_RDSR 0x05 /* Read Status Register */
18#define CMD_EN25_WRSR 0x01 /* Write Status Register */
19#define CMD_EN25_READ 0x03 /* Read Data Bytes */
20#define CMD_EN25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
21#define CMD_EN25_PP 0x02 /* Page Program */
22#define CMD_EN25_SE 0x20 /* Sector Erase */
23#define CMD_EN25_BE 0xd8 /* Block Erase */
24#define CMD_EN25_DP 0xb9 /* Deep Power-down */
25#define CMD_EN25_RES 0xab /* Release from DP, and Read Signature */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070026
Marc Jones79f60a52014-05-01 14:53:24 -060027#define EON_ID_EN25Q128 0x3018
28#define EON_ID_EN25Q64 0x3017
Marc Jones0658d232014-05-01 14:54:11 -060029#define EON_ID_EN25S64 0x3817
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070030
31struct eon_spi_flash_params {
Marc Jones79f60a52014-05-01 14:53:24 -060032 u16 id;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070033 u16 page_size;
34 u16 pages_per_sector;
35 u16 sectors_per_block;
36 u16 nr_sectors;
37 const char *name;
38};
39
40/* spi_flash needs to be first so upper layers can free() it */
41struct eon_spi_flash {
42 struct spi_flash flash;
43 const struct eon_spi_flash_params *params;
44};
45
46static inline struct eon_spi_flash *to_eon_spi_flash(struct spi_flash *flash)
47{
48 return container_of(flash, struct eon_spi_flash, flash);
49}
50
51static const struct eon_spi_flash_params eon_spi_flash_table[] = {
52 {
Marc Jones79f60a52014-05-01 14:53:24 -060053 .id = EON_ID_EN25Q128,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070054 .page_size = 256,
55 .pages_per_sector = 16,
56 .sectors_per_block = 16,
57 .nr_sectors = 4096,
58 .name = "EN25Q128",
59 },
Vladimir Serbinenko066dcec2014-01-05 06:46:19 +010060 {
Marc Jones79f60a52014-05-01 14:53:24 -060061 .id = EON_ID_EN25Q64,
Vladimir Serbinenko066dcec2014-01-05 06:46:19 +010062 .page_size = 256,
63 .pages_per_sector = 16,
64 .sectors_per_block = 16,
65 .nr_sectors = 2048,
66 .name = "EN25Q64",
67 },
Marc Jones0658d232014-05-01 14:54:11 -060068 {
69 .id = EON_ID_EN25S64,
70 .page_size = 256,
71 .pages_per_sector = 16,
72 .sectors_per_block = 16,
73 .nr_sectors = 2048,
74 .name = "EN25S64",
75 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070076};
77
78static int eon_write(struct spi_flash *flash,
79 u32 offset, size_t len, const void *buf)
80{
81 struct eon_spi_flash *eon = to_eon_spi_flash(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070082 unsigned long byte_addr;
83 unsigned long page_size;
84 size_t chunk_len;
85 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -070086 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070087 u8 cmd[4];
88
Marc Jones79f60a52014-05-01 14:53:24 -060089 page_size = 1 << eon->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070090 byte_addr = offset % page_size;
91
Martin Roth3316cf22012-12-05 16:22:54 -070092 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070093
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070094 for (actual = 0; actual < len; actual += chunk_len) {
95 chunk_len = min(len - actual, page_size - byte_addr);
Marc Jones79f60a52014-05-01 14:53:24 -060096 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070097
Marc Jones79f60a52014-05-01 14:53:24 -060098 ret = spi_flash_cmd(flash->spi, CMD_EN25_WREN, NULL, 0);
99 if (ret < 0) {
100 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
101 goto out;
102 }
103
104 cmd[0] = CMD_EN25_PP;
105 cmd[1] = (offset >> 16) & 0xff;
106 cmd[2] = (offset >> 8) & 0xff;
107 cmd[3] = offset & 0xff;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700108
Stefan Reinauer5649b082012-05-23 11:03:29 -0700109#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700110 printk(BIOS_SPEW,
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700111 "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700112 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700113#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700114
Marc Jones79f60a52014-05-01 14:53:24 -0600115 ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd),
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700116 buf + actual, chunk_len);
117 if (ret < 0) {
118 printk(BIOS_WARNING, "SF: EON Page Program failed\n");
Marc Jones79f60a52014-05-01 14:53:24 -0600119 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700120 }
121
122 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
Marc Jones79f60a52014-05-01 14:53:24 -0600123 if (ret) {
124 printk(BIOS_WARNING, "SF: EON Page Program timeout\n");
125 goto out;
126 }
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700127
Marc Jones79f60a52014-05-01 14:53:24 -0600128 offset += chunk_len;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700129 byte_addr = 0;
130 }
131
Marc Jones747127d2012-12-03 22:16:29 -0700132#if CONFIG_DEBUG_SPI_FLASH
Marc Jones79f60a52014-05-01 14:53:24 -0600133 printk(BIOS_SPEW, "SF: EON: Successfully programmed %zu bytes @ %#x\n",
134 len, (unsigned int)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700135#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700136
Marc Jones79f60a52014-05-01 14:53:24 -0600137out:
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700138 return ret;
139}
140
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700141struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
142{
143 const struct eon_spi_flash_params *params;
144 struct eon_spi_flash *eon;
145 unsigned int i;
146
147 for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) {
148 params = &eon_spi_flash_table[i];
Marc Jones79f60a52014-05-01 14:53:24 -0600149 if (params->id == ((idcode[1] << 8) | idcode[2]))
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700150 break;
151 }
152
153 if (i == ARRAY_SIZE(eon_spi_flash_table)) {
Marc Jones79f60a52014-05-01 14:53:24 -0600154 printk(BIOS_WARNING, "SF: Unsupported EON ID %#02x%02x\n",
155 idcode[1], idcode[2]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700156 return NULL;
157 }
158
159 eon = malloc(sizeof(*eon));
160 if (!eon) {
161 printk(BIOS_WARNING, "SF: Failed to allocate memory\n");
162 return NULL;
163 }
164
165 eon->params = params;
166 eon->flash.spi = spi;
167 eon->flash.name = params->name;
168
169 eon->flash.write = eon_write;
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800170 eon->flash.erase = spi_flash_cmd_erase;
Duncan Lauriefb032392015-01-15 15:28:46 -0800171 eon->flash.status = spi_flash_cmd_status;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700172 eon->flash.read = spi_flash_cmd_read_fast;
Marc Jones79f60a52014-05-01 14:53:24 -0600173 eon->flash.sector_size = params->page_size * params->pages_per_sector;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700174 eon->flash.size = params->page_size * params->pages_per_sector
175 * params->nr_sectors;
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800176 eon->flash.erase_cmd = CMD_EN25_SE;
Duncan Lauriefb032392015-01-15 15:28:46 -0800177 eon->flash.status_cmd = CMD_EN25_RDSR;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700178
179 return &eon->flash;
180}