blob: eb71c6216e1d531041dab9f188e804b28b83b10c [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
Furquan Shaikhc28984d2016-11-20 21:04:00 -08009#include <console/console.h>
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070010#include <stdlib.h>
11#include <spi_flash.h>
Furquan Shaikhc28984d2016-11-20 21:04:00 -080012#include <spi-generic.h>
Furquan Shaikh810e2cd2016-12-05 20:32:24 -080013#include <string.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100014
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070015#include "spi_flash_internal.h"
16
Marc Jones79f60a52014-05-01 14:53:24 -060017/* EN25*-specific commands */
18#define CMD_EN25_WREN 0x06 /* Write Enable */
19#define CMD_EN25_WRDI 0x04 /* Write Disable */
20#define CMD_EN25_RDSR 0x05 /* Read Status Register */
21#define CMD_EN25_WRSR 0x01 /* Write Status Register */
22#define CMD_EN25_READ 0x03 /* Read Data Bytes */
23#define CMD_EN25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
24#define CMD_EN25_PP 0x02 /* Page Program */
25#define CMD_EN25_SE 0x20 /* Sector Erase */
26#define CMD_EN25_BE 0xd8 /* Block Erase */
27#define CMD_EN25_DP 0xb9 /* Deep Power-down */
28#define CMD_EN25_RES 0xab /* Release from DP, and Read Signature */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070029
Marc Jones79f60a52014-05-01 14:53:24 -060030#define EON_ID_EN25Q128 0x3018
31#define EON_ID_EN25Q64 0x3017
Marc Jones0658d232014-05-01 14:54:11 -060032#define EON_ID_EN25S64 0x3817
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070033
34struct eon_spi_flash_params {
Marc Jones79f60a52014-05-01 14:53:24 -060035 u16 id;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070036 u16 page_size;
37 u16 pages_per_sector;
38 u16 sectors_per_block;
39 u16 nr_sectors;
40 const char *name;
41};
42
43/* spi_flash needs to be first so upper layers can free() it */
44struct eon_spi_flash {
45 struct spi_flash flash;
46 const struct eon_spi_flash_params *params;
47};
48
Furquan Shaikhc28984d2016-11-20 21:04:00 -080049static inline
50struct eon_spi_flash *to_eon_spi_flash(const struct spi_flash *flash)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070051{
52 return container_of(flash, struct eon_spi_flash, flash);
53}
54
55static const struct eon_spi_flash_params eon_spi_flash_table[] = {
56 {
Marc Jones79f60a52014-05-01 14:53:24 -060057 .id = EON_ID_EN25Q128,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070058 .page_size = 256,
59 .pages_per_sector = 16,
60 .sectors_per_block = 16,
61 .nr_sectors = 4096,
62 .name = "EN25Q128",
63 },
Vladimir Serbinenko066dcec2014-01-05 06:46:19 +010064 {
Marc Jones79f60a52014-05-01 14:53:24 -060065 .id = EON_ID_EN25Q64,
Vladimir Serbinenko066dcec2014-01-05 06:46:19 +010066 .page_size = 256,
67 .pages_per_sector = 16,
68 .sectors_per_block = 16,
69 .nr_sectors = 2048,
70 .name = "EN25Q64",
71 },
Marc Jones0658d232014-05-01 14:54:11 -060072 {
73 .id = EON_ID_EN25S64,
74 .page_size = 256,
75 .pages_per_sector = 16,
76 .sectors_per_block = 16,
77 .nr_sectors = 2048,
78 .name = "EN25S64",
79 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070080};
81
Furquan Shaikhc28984d2016-11-20 21:04:00 -080082static int eon_write(const struct spi_flash *flash,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070083 u32 offset, size_t len, const void *buf)
84{
85 struct eon_spi_flash *eon = to_eon_spi_flash(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070086 unsigned long byte_addr;
87 unsigned long page_size;
88 size_t chunk_len;
89 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -070090 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070091 u8 cmd[4];
92
Marc Jones79f60a52014-05-01 14:53:24 -060093 page_size = 1 << eon->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070094
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070095 for (actual = 0; actual < len; actual += chunk_len) {
Aaron Durbin41f66902016-12-17 13:16:07 -060096 byte_addr = offset % page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070097 chunk_len = min(len - actual, page_size - byte_addr);
Marc Jones79f60a52014-05-01 14:53:24 -060098 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070099
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800100 ret = spi_flash_cmd(&flash->spi, CMD_EN25_WREN, NULL, 0);
Marc Jones79f60a52014-05-01 14:53:24 -0600101 if (ret < 0) {
102 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
103 goto out;
104 }
105
106 cmd[0] = CMD_EN25_PP;
107 cmd[1] = (offset >> 16) & 0xff;
108 cmd[2] = (offset >> 8) & 0xff;
109 cmd[3] = offset & 0xff;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700110
Stefan Reinauer5649b082012-05-23 11:03:29 -0700111#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700112 printk(BIOS_SPEW,
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700113 "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700114 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700115#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700116
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800117 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700118 buf + actual, chunk_len);
119 if (ret < 0) {
120 printk(BIOS_WARNING, "SF: EON Page Program failed\n");
Marc Jones79f60a52014-05-01 14:53:24 -0600121 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700122 }
123
124 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
Marc Jones79f60a52014-05-01 14:53:24 -0600125 if (ret) {
126 printk(BIOS_WARNING, "SF: EON Page Program timeout\n");
127 goto out;
128 }
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700129
Marc Jones79f60a52014-05-01 14:53:24 -0600130 offset += chunk_len;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700131 }
132
Marc Jones747127d2012-12-03 22:16:29 -0700133#if CONFIG_DEBUG_SPI_FLASH
Marc Jones79f60a52014-05-01 14:53:24 -0600134 printk(BIOS_SPEW, "SF: EON: Successfully programmed %zu bytes @ %#x\n",
135 len, (unsigned int)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700136#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700137
Marc Jones79f60a52014-05-01 14:53:24 -0600138out:
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700139 return ret;
140}
141
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700142struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
143{
144 const struct eon_spi_flash_params *params;
145 struct eon_spi_flash *eon;
146 unsigned int i;
147
148 for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) {
149 params = &eon_spi_flash_table[i];
Marc Jones79f60a52014-05-01 14:53:24 -0600150 if (params->id == ((idcode[1] << 8) | idcode[2]))
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700151 break;
152 }
153
154 if (i == ARRAY_SIZE(eon_spi_flash_table)) {
Marc Jones79f60a52014-05-01 14:53:24 -0600155 printk(BIOS_WARNING, "SF: Unsupported EON ID %#02x%02x\n",
156 idcode[1], idcode[2]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700157 return NULL;
158 }
159
160 eon = malloc(sizeof(*eon));
161 if (!eon) {
162 printk(BIOS_WARNING, "SF: Failed to allocate memory\n");
163 return NULL;
164 }
165
166 eon->params = params;
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800167 memcpy(&eon->flash.spi, spi, sizeof(*spi));
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700168 eon->flash.name = params->name;
169
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800170 eon->flash.internal_write = eon_write;
171 eon->flash.internal_erase = spi_flash_cmd_erase;
172 eon->flash.internal_status = spi_flash_cmd_status;
173 eon->flash.internal_read = spi_flash_cmd_read_fast;
Marc Jones79f60a52014-05-01 14:53:24 -0600174 eon->flash.sector_size = params->page_size * params->pages_per_sector;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700175 eon->flash.size = params->page_size * params->pages_per_sector
176 * params->nr_sectors;
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800177 eon->flash.erase_cmd = CMD_EN25_SE;
Duncan Lauriefb032392015-01-15 15:28:46 -0800178 eon->flash.status_cmd = CMD_EN25_RDSR;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700179
180 return &eon->flash;
181}