blob: 2e5fa8c6104ad0bc96bec080088d333759c9fa10 [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
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070029
30struct eon_spi_flash_params {
Marc Jones79f60a52014-05-01 14:53:24 -060031 u16 id;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070032 u16 page_size;
33 u16 pages_per_sector;
34 u16 sectors_per_block;
35 u16 nr_sectors;
36 const char *name;
37};
38
39/* spi_flash needs to be first so upper layers can free() it */
40struct eon_spi_flash {
41 struct spi_flash flash;
42 const struct eon_spi_flash_params *params;
43};
44
45static inline struct eon_spi_flash *to_eon_spi_flash(struct spi_flash *flash)
46{
47 return container_of(flash, struct eon_spi_flash, flash);
48}
49
50static const struct eon_spi_flash_params eon_spi_flash_table[] = {
51 {
Marc Jones79f60a52014-05-01 14:53:24 -060052 .id = EON_ID_EN25Q128,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070053 .page_size = 256,
54 .pages_per_sector = 16,
55 .sectors_per_block = 16,
56 .nr_sectors = 4096,
57 .name = "EN25Q128",
58 },
Vladimir Serbinenko066dcec2014-01-05 06:46:19 +010059 {
Marc Jones79f60a52014-05-01 14:53:24 -060060 .id = EON_ID_EN25Q64,
Vladimir Serbinenko066dcec2014-01-05 06:46:19 +010061 .page_size = 256,
62 .pages_per_sector = 16,
63 .sectors_per_block = 16,
64 .nr_sectors = 2048,
65 .name = "EN25Q64",
66 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070067};
68
69static int eon_write(struct spi_flash *flash,
70 u32 offset, size_t len, const void *buf)
71{
72 struct eon_spi_flash *eon = to_eon_spi_flash(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070073 unsigned long byte_addr;
74 unsigned long page_size;
75 size_t chunk_len;
76 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -070077 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070078 u8 cmd[4];
79
Marc Jones79f60a52014-05-01 14:53:24 -060080 page_size = 1 << eon->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070081 byte_addr = offset % page_size;
82
Martin Roth3316cf22012-12-05 16:22:54 -070083 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070084
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070085 for (actual = 0; actual < len; actual += chunk_len) {
86 chunk_len = min(len - actual, page_size - byte_addr);
Marc Jones79f60a52014-05-01 14:53:24 -060087 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070088
Marc Jones79f60a52014-05-01 14:53:24 -060089 ret = spi_flash_cmd(flash->spi, CMD_EN25_WREN, NULL, 0);
90 if (ret < 0) {
91 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
92 goto out;
93 }
94
95 cmd[0] = CMD_EN25_PP;
96 cmd[1] = (offset >> 16) & 0xff;
97 cmd[2] = (offset >> 8) & 0xff;
98 cmd[3] = offset & 0xff;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070099
Stefan Reinauer5649b082012-05-23 11:03:29 -0700100#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700101 printk(BIOS_SPEW,
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700102 "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700103 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700104#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700105
Marc Jones79f60a52014-05-01 14:53:24 -0600106 ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd),
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700107 buf + actual, chunk_len);
108 if (ret < 0) {
109 printk(BIOS_WARNING, "SF: EON Page Program failed\n");
Marc Jones79f60a52014-05-01 14:53:24 -0600110 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700111 }
112
113 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
Marc Jones79f60a52014-05-01 14:53:24 -0600114 if (ret) {
115 printk(BIOS_WARNING, "SF: EON Page Program timeout\n");
116 goto out;
117 }
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700118
Marc Jones79f60a52014-05-01 14:53:24 -0600119 offset += chunk_len;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700120 byte_addr = 0;
121 }
122
Marc Jones747127d2012-12-03 22:16:29 -0700123#if CONFIG_DEBUG_SPI_FLASH
Marc Jones79f60a52014-05-01 14:53:24 -0600124 printk(BIOS_SPEW, "SF: EON: Successfully programmed %zu bytes @ %#x\n",
125 len, (unsigned int)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700126#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700127
Marc Jones79f60a52014-05-01 14:53:24 -0600128out:
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700129 return ret;
130}
131
132static int eon_erase(struct spi_flash *flash, u32 offset, size_t len)
133{
Marc Jones79f60a52014-05-01 14:53:24 -0600134 return spi_flash_cmd_erase(flash, CMD_EN25_SE, offset, len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700135}
136
137struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
138{
139 const struct eon_spi_flash_params *params;
140 struct eon_spi_flash *eon;
141 unsigned int i;
142
143 for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) {
144 params = &eon_spi_flash_table[i];
Marc Jones79f60a52014-05-01 14:53:24 -0600145 if (params->id == ((idcode[1] << 8) | idcode[2]))
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700146 break;
147 }
148
149 if (i == ARRAY_SIZE(eon_spi_flash_table)) {
Marc Jones79f60a52014-05-01 14:53:24 -0600150 printk(BIOS_WARNING, "SF: Unsupported EON ID %#02x%02x\n",
151 idcode[1], idcode[2]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700152 return NULL;
153 }
154
155 eon = malloc(sizeof(*eon));
156 if (!eon) {
157 printk(BIOS_WARNING, "SF: Failed to allocate memory\n");
158 return NULL;
159 }
160
161 eon->params = params;
162 eon->flash.spi = spi;
163 eon->flash.name = params->name;
164
165 eon->flash.write = eon_write;
166 eon->flash.erase = eon_erase;
167 eon->flash.read = spi_flash_cmd_read_fast;
Marc Jones79f60a52014-05-01 14:53:24 -0600168 eon->flash.sector_size = params->page_size * params->pages_per_sector;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700169 eon->flash.size = params->page_size * params->pages_per_sector
170 * params->nr_sectors;
171
172 return &eon->flash;
173}