]>
Commit | Line | Data |
---|---|---|
d1d90656 CH |
1 | /* |
2 | * (C) Copyright 2010, ucRobotics Inc. | |
3 | * Author: Chong Huang <chuang@ucrobotics.com> | |
4 | * Licensed under the GPL-2 or later. | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
8 | #include <malloc.h> | |
9 | #include <spi_flash.h> | |
10 | ||
11 | #include "spi_flash_internal.h" | |
12 | ||
13 | /* EN25Q128-specific commands */ | |
14 | #define CMD_EN25Q128_WREN 0x06 /* Write Enable */ | |
15 | #define CMD_EN25Q128_WRDI 0x04 /* Write Disable */ | |
16 | #define CMD_EN25Q128_RDSR 0x05 /* Read Status Register */ | |
17 | #define CMD_EN25Q128_WRSR 0x01 /* Write Status Register */ | |
18 | #define CMD_EN25Q128_READ 0x03 /* Read Data Bytes */ | |
19 | #define CMD_EN25Q128_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ | |
20 | #define CMD_EN25Q128_PP 0x02 /* Page Program */ | |
21 | #define CMD_EN25Q128_SE 0x20 /* Sector Erase */ | |
22 | #define CMD_EN25Q128_BE 0xd8 /* Block Erase */ | |
23 | #define CMD_EN25Q128_DP 0xb9 /* Deep Power-down */ | |
24 | #define CMD_EN25Q128_RES 0xab /* Release from DP, and Read Signature */ | |
25 | ||
26 | #define EON_ID_EN25Q128 0x18 | |
27 | ||
d1d90656 CH |
28 | struct eon_spi_flash_params { |
29 | u8 idcode1; | |
30 | u16 page_size; | |
31 | u16 pages_per_sector; | |
32 | u16 sectors_per_block; | |
33 | u16 nr_sectors; | |
34 | const char *name; | |
35 | }; | |
36 | ||
37 | /* spi_flash needs to be first so upper layers can free() it */ | |
38 | struct eon_spi_flash { | |
39 | struct spi_flash flash; | |
40 | const struct eon_spi_flash_params *params; | |
41 | }; | |
42 | ||
43 | static inline struct eon_spi_flash *to_eon_spi_flash(struct spi_flash *flash) | |
44 | { | |
45 | return container_of(flash, struct eon_spi_flash, flash); | |
46 | } | |
47 | ||
48 | static const struct eon_spi_flash_params eon_spi_flash_table[] = { | |
49 | { | |
50 | .idcode1 = EON_ID_EN25Q128, | |
51 | .page_size = 256, | |
52 | .pages_per_sector = 16, | |
53 | .sectors_per_block = 16, | |
54 | .nr_sectors = 4096, | |
55 | .name = "EN25Q128", | |
56 | }, | |
57 | }; | |
58 | ||
d1d90656 CH |
59 | static int eon_read_fast(struct spi_flash *flash, |
60 | u32 offset, size_t len, void *buf) | |
61 | { | |
62 | struct eon_spi_flash *eon = to_eon_spi_flash(flash); | |
63 | unsigned long page_addr; | |
64 | unsigned long page_size; | |
65 | u8 cmd[5]; | |
66 | ||
67 | page_size = eon->params->page_size; | |
68 | page_addr = offset / page_size; | |
69 | ||
70 | cmd[0] = CMD_READ_ARRAY_FAST; | |
71 | cmd[1] = page_addr >> 8; | |
72 | cmd[2] = page_addr; | |
73 | cmd[3] = offset % page_size; | |
74 | cmd[4] = 0x00; | |
75 | ||
76 | return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); | |
77 | } | |
78 | ||
79 | static int eon_write(struct spi_flash *flash, | |
80 | u32 offset, size_t len, const void *buf) | |
81 | { | |
82 | struct eon_spi_flash *eon = to_eon_spi_flash(flash); | |
83 | unsigned long page_addr; | |
84 | unsigned long byte_addr; | |
85 | unsigned long page_size; | |
86 | size_t chunk_len; | |
87 | size_t actual; | |
88 | int ret; | |
89 | u8 cmd[4]; | |
90 | ||
91 | page_size = eon->params->page_size; | |
92 | page_addr = offset / page_size; | |
93 | byte_addr = offset % page_size; | |
94 | ||
95 | ret = spi_claim_bus(flash->spi); | |
96 | if (ret) { | |
97 | debug("SF: Unable to claim SPI bus\n"); | |
98 | return ret; | |
99 | } | |
100 | ||
101 | ret = 0; | |
102 | for (actual = 0; actual < len; actual += chunk_len) { | |
103 | chunk_len = min(len - actual, page_size - byte_addr); | |
104 | ||
105 | cmd[0] = CMD_EN25Q128_PP; | |
106 | cmd[1] = page_addr >> 8; | |
107 | cmd[2] = page_addr; | |
108 | cmd[3] = byte_addr; | |
109 | ||
110 | debug | |
111 | ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", | |
112 | buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); | |
113 | ||
114 | ret = spi_flash_cmd(flash->spi, CMD_EN25Q128_WREN, NULL, 0); | |
115 | if (ret < 0) { | |
116 | debug("SF: Enabling Write failed\n"); | |
117 | break; | |
118 | } | |
119 | ||
120 | ret = spi_flash_cmd_write(flash->spi, cmd, 4, | |
121 | buf + actual, chunk_len); | |
122 | if (ret < 0) { | |
123 | debug("SF: EON Page Program failed\n"); | |
124 | break; | |
125 | } | |
126 | ||
6163045b MF |
127 | ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); |
128 | if (ret) | |
d1d90656 | 129 | break; |
d1d90656 CH |
130 | |
131 | page_addr++; | |
132 | byte_addr = 0; | |
133 | } | |
134 | ||
135 | debug("SF: EON: Successfully programmed %u bytes @ 0x%x\n", | |
136 | len, offset); | |
137 | ||
138 | spi_release_bus(flash->spi); | |
139 | return ret; | |
140 | } | |
141 | ||
142 | int eon_erase(struct spi_flash *flash, u32 offset, size_t len) | |
143 | { | |
d1d90656 | 144 | struct eon_spi_flash *eon = to_eon_spi_flash(flash); |
e7b44edd MF |
145 | return spi_flash_cmd_erase(flash, CMD_EN25Q128_BE, |
146 | eon->params->page_size * eon->params->pages_per_sector * | |
147 | eon->params->sectors_per_block; | |
148 | offset, len); | |
d1d90656 CH |
149 | } |
150 | ||
151 | struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode) | |
152 | { | |
153 | const struct eon_spi_flash_params *params; | |
154 | struct eon_spi_flash *eon; | |
155 | unsigned int i; | |
156 | ||
157 | for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) { | |
158 | params = &eon_spi_flash_table[i]; | |
159 | if (params->idcode1 == idcode[2]) | |
160 | break; | |
161 | } | |
162 | ||
163 | if (i == ARRAY_SIZE(eon_spi_flash_table)) { | |
164 | debug("SF: Unsupported EON ID %02x\n", idcode[1]); | |
165 | return NULL; | |
166 | } | |
167 | ||
168 | eon = malloc(sizeof(*eon)); | |
169 | if (!eon) { | |
170 | debug("SF: Failed to allocate memory\n"); | |
171 | return NULL; | |
172 | } | |
173 | ||
174 | eon->params = params; | |
175 | eon->flash.spi = spi; | |
176 | eon->flash.name = params->name; | |
177 | ||
178 | eon->flash.write = eon_write; | |
179 | eon->flash.erase = eon_erase; | |
180 | eon->flash.read = eon_read_fast; | |
181 | eon->flash.size = params->page_size * params->pages_per_sector | |
182 | * params->nr_sectors; | |
183 | ||
184 | debug("SF: Detected %s with page size %u, total %u bytes\n", | |
185 | params->name, params->page_size, eon->flash.size); | |
186 | ||
187 | return &eon->flash; | |
188 | } |