]>
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_write(struct spi_flash *flash, |
60 | u32 offset, size_t len, const void *buf) | |
61 | { | |
62 | struct eon_spi_flash *eon = to_eon_spi_flash(flash); | |
63 | unsigned long page_addr; | |
64 | unsigned long byte_addr; | |
65 | unsigned long page_size; | |
66 | size_t chunk_len; | |
67 | size_t actual; | |
68 | int ret; | |
69 | u8 cmd[4]; | |
70 | ||
71 | page_size = eon->params->page_size; | |
72 | page_addr = offset / page_size; | |
73 | byte_addr = offset % page_size; | |
74 | ||
75 | ret = spi_claim_bus(flash->spi); | |
76 | if (ret) { | |
77 | debug("SF: Unable to claim SPI bus\n"); | |
78 | return ret; | |
79 | } | |
80 | ||
81 | ret = 0; | |
82 | for (actual = 0; actual < len; actual += chunk_len) { | |
83 | chunk_len = min(len - actual, page_size - byte_addr); | |
84 | ||
85 | cmd[0] = CMD_EN25Q128_PP; | |
86 | cmd[1] = page_addr >> 8; | |
87 | cmd[2] = page_addr; | |
88 | cmd[3] = byte_addr; | |
89 | ||
90 | debug | |
91 | ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", | |
92 | buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); | |
93 | ||
94 | ret = spi_flash_cmd(flash->spi, CMD_EN25Q128_WREN, NULL, 0); | |
95 | if (ret < 0) { | |
96 | debug("SF: Enabling Write failed\n"); | |
97 | break; | |
98 | } | |
99 | ||
100 | ret = spi_flash_cmd_write(flash->spi, cmd, 4, | |
101 | buf + actual, chunk_len); | |
102 | if (ret < 0) { | |
103 | debug("SF: EON Page Program failed\n"); | |
104 | break; | |
105 | } | |
106 | ||
6163045b MF |
107 | ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); |
108 | if (ret) | |
d1d90656 | 109 | break; |
d1d90656 CH |
110 | |
111 | page_addr++; | |
112 | byte_addr = 0; | |
113 | } | |
114 | ||
115 | debug("SF: EON: Successfully programmed %u bytes @ 0x%x\n", | |
116 | len, offset); | |
117 | ||
118 | spi_release_bus(flash->spi); | |
119 | return ret; | |
120 | } | |
121 | ||
f8f0757d | 122 | static int eon_erase(struct spi_flash *flash, u32 offset, size_t len) |
d1d90656 | 123 | { |
4e6a5158 | 124 | return spi_flash_cmd_erase(flash, CMD_EN25Q128_BE, offset, len); |
d1d90656 CH |
125 | } |
126 | ||
127 | struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode) | |
128 | { | |
129 | const struct eon_spi_flash_params *params; | |
130 | struct eon_spi_flash *eon; | |
131 | unsigned int i; | |
132 | ||
133 | for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) { | |
134 | params = &eon_spi_flash_table[i]; | |
135 | if (params->idcode1 == idcode[2]) | |
136 | break; | |
137 | } | |
138 | ||
139 | if (i == ARRAY_SIZE(eon_spi_flash_table)) { | |
140 | debug("SF: Unsupported EON ID %02x\n", idcode[1]); | |
141 | return NULL; | |
142 | } | |
143 | ||
144 | eon = malloc(sizeof(*eon)); | |
145 | if (!eon) { | |
146 | debug("SF: Failed to allocate memory\n"); | |
147 | return NULL; | |
148 | } | |
149 | ||
150 | eon->params = params; | |
151 | eon->flash.spi = spi; | |
152 | eon->flash.name = params->name; | |
153 | ||
154 | eon->flash.write = eon_write; | |
155 | eon->flash.erase = eon_erase; | |
a4c3b40b | 156 | eon->flash.read = spi_flash_cmd_read_fast; |
4e6a5158 RR |
157 | eon->flash.sector_size = params->page_size * params->pages_per_sector |
158 | * params->sectors_per_block; | |
d1d90656 CH |
159 | eon->flash.size = params->page_size * params->pages_per_sector |
160 | * params->nr_sectors; | |
161 | ||
d1d90656 CH |
162 | return &eon->flash; |
163 | } |