diff options
Diffstat (limited to 'drivers/mtd/devices/st_spi_fsm.c')
-rw-r--r-- | drivers/mtd/devices/st_spi_fsm.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 000bc25edd19..7048ea75bf27 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c | |||
@@ -74,6 +74,10 @@ | |||
74 | #define SPI_CFG_CS_SETUPHOLD(x) (((x) & 0xff) << 16) | 74 | #define SPI_CFG_CS_SETUPHOLD(x) (((x) & 0xff) << 16) |
75 | #define SPI_CFG_DATA_HOLD(x) (((x) & 0xff) << 24) | 75 | #define SPI_CFG_DATA_HOLD(x) (((x) & 0xff) << 24) |
76 | 76 | ||
77 | #define SPI_CFG_DEFAULT_MIN_CS_HIGH SPI_CFG_MIN_CS_HIGH(0x0AA) | ||
78 | #define SPI_CFG_DEFAULT_CS_SETUPHOLD SPI_CFG_CS_SETUPHOLD(0xA0) | ||
79 | #define SPI_CFG_DEFAULT_DATA_HOLD SPI_CFG_DATA_HOLD(0x00) | ||
80 | |||
77 | /* | 81 | /* |
78 | * Register: SPI_FAST_SEQ_TRANSFER_SIZE | 82 | * Register: SPI_FAST_SEQ_TRANSFER_SIZE |
79 | */ | 83 | */ |
@@ -185,19 +189,136 @@ | |||
185 | #define STFSM_INST_WAIT STFSM_INSTR(STFSM_OPC_WAIT, 0) | 189 | #define STFSM_INST_WAIT STFSM_INSTR(STFSM_OPC_WAIT, 0) |
186 | #define STFSM_INST_STOP STFSM_INSTR(STFSM_OPC_STOP, 0) | 190 | #define STFSM_INST_STOP STFSM_INSTR(STFSM_OPC_STOP, 0) |
187 | 191 | ||
192 | #define STFSM_DEFAULT_EMI_FREQ 100000000UL /* 100 MHz */ | ||
193 | #define STFSM_DEFAULT_WR_TIME (STFSM_DEFAULT_EMI_FREQ * (15/1000)) /* 15ms */ | ||
194 | |||
195 | #define STFSM_FLASH_SAFE_FREQ 10000000UL /* 10 MHz */ | ||
196 | |||
188 | struct stfsm { | 197 | struct stfsm { |
189 | struct device *dev; | 198 | struct device *dev; |
190 | void __iomem *base; | 199 | void __iomem *base; |
191 | struct resource *region; | 200 | struct resource *region; |
192 | struct mtd_info mtd; | 201 | struct mtd_info mtd; |
193 | struct mutex lock; | 202 | struct mutex lock; |
203 | |||
204 | uint32_t fifo_dir_delay; | ||
194 | }; | 205 | }; |
195 | 206 | ||
207 | static inline uint32_t stfsm_fifo_available(struct stfsm *fsm) | ||
208 | { | ||
209 | return (readl(fsm->base + SPI_FAST_SEQ_STA) >> 5) & 0x7f; | ||
210 | } | ||
211 | |||
212 | static void stfsm_clear_fifo(struct stfsm *fsm) | ||
213 | { | ||
214 | uint32_t avail; | ||
215 | |||
216 | for (;;) { | ||
217 | avail = stfsm_fifo_available(fsm); | ||
218 | if (!avail) | ||
219 | break; | ||
220 | |||
221 | while (avail) { | ||
222 | readl(fsm->base + SPI_FAST_SEQ_DATA_REG); | ||
223 | avail--; | ||
224 | } | ||
225 | } | ||
226 | } | ||
227 | |||
228 | static int stfsm_set_mode(struct stfsm *fsm, uint32_t mode) | ||
229 | { | ||
230 | int ret, timeout = 10; | ||
231 | |||
232 | /* Wait for controller to accept mode change */ | ||
233 | while (--timeout) { | ||
234 | ret = readl(fsm->base + SPI_STA_MODE_CHANGE); | ||
235 | if (ret & 0x1) | ||
236 | break; | ||
237 | udelay(1); | ||
238 | } | ||
239 | |||
240 | if (!timeout) | ||
241 | return -EBUSY; | ||
242 | |||
243 | writel(mode, fsm->base + SPI_MODESELECT); | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static void stfsm_set_freq(struct stfsm *fsm, uint32_t spi_freq) | ||
249 | { | ||
250 | uint32_t emi_freq; | ||
251 | uint32_t clk_div; | ||
252 | |||
253 | /* TODO: Make this dynamic */ | ||
254 | emi_freq = STFSM_DEFAULT_EMI_FREQ; | ||
255 | |||
256 | /* | ||
257 | * Calculate clk_div - values between 2 and 128 | ||
258 | * Multiple of 2, rounded up | ||
259 | */ | ||
260 | clk_div = 2 * DIV_ROUND_UP(emi_freq, 2 * spi_freq); | ||
261 | if (clk_div < 2) | ||
262 | clk_div = 2; | ||
263 | else if (clk_div > 128) | ||
264 | clk_div = 128; | ||
265 | |||
266 | /* | ||
267 | * Determine a suitable delay for the IP to complete a change of | ||
268 | * direction of the FIFO. The required delay is related to the clock | ||
269 | * divider used. The following heuristics are based on empirical tests, | ||
270 | * using a 100MHz EMI clock. | ||
271 | */ | ||
272 | if (clk_div <= 4) | ||
273 | fsm->fifo_dir_delay = 0; | ||
274 | else if (clk_div <= 10) | ||
275 | fsm->fifo_dir_delay = 1; | ||
276 | else | ||
277 | fsm->fifo_dir_delay = DIV_ROUND_UP(clk_div, 10); | ||
278 | |||
279 | dev_dbg(fsm->dev, "emi_clk = %uHZ, spi_freq = %uHZ, clk_div = %u\n", | ||
280 | emi_freq, spi_freq, clk_div); | ||
281 | |||
282 | writel(clk_div, fsm->base + SPI_CLOCKDIV); | ||
283 | } | ||
284 | |||
285 | static int stfsm_init(struct stfsm *fsm) | ||
286 | { | ||
287 | int ret; | ||
288 | |||
289 | /* Perform a soft reset of the FSM controller */ | ||
290 | writel(SEQ_CFG_SWRESET, fsm->base + SPI_FAST_SEQ_CFG); | ||
291 | udelay(1); | ||
292 | writel(0, fsm->base + SPI_FAST_SEQ_CFG); | ||
293 | |||
294 | /* Set clock to 'safe' frequency initially */ | ||
295 | stfsm_set_freq(fsm, STFSM_FLASH_SAFE_FREQ); | ||
296 | |||
297 | /* Switch to FSM */ | ||
298 | ret = stfsm_set_mode(fsm, SPI_MODESELECT_FSM); | ||
299 | if (ret) | ||
300 | return ret; | ||
301 | |||
302 | /* Set timing parameters */ | ||
303 | writel(SPI_CFG_DEVICE_ST | | ||
304 | SPI_CFG_DEFAULT_MIN_CS_HIGH | | ||
305 | SPI_CFG_DEFAULT_CS_SETUPHOLD | | ||
306 | SPI_CFG_DEFAULT_DATA_HOLD, | ||
307 | fsm->base + SPI_CONFIGDATA); | ||
308 | writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG); | ||
309 | |||
310 | /* Clear FIFO, just in case */ | ||
311 | stfsm_clear_fifo(fsm); | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
196 | static int stfsm_probe(struct platform_device *pdev) | 316 | static int stfsm_probe(struct platform_device *pdev) |
197 | { | 317 | { |
198 | struct device_node *np = pdev->dev.of_node; | 318 | struct device_node *np = pdev->dev.of_node; |
199 | struct resource *res; | 319 | struct resource *res; |
200 | struct stfsm *fsm; | 320 | struct stfsm *fsm; |
321 | int ret; | ||
201 | 322 | ||
202 | if (!np) { | 323 | if (!np) { |
203 | dev_err(&pdev->dev, "No DT found\n"); | 324 | dev_err(&pdev->dev, "No DT found\n"); |
@@ -227,6 +348,12 @@ static int stfsm_probe(struct platform_device *pdev) | |||
227 | 348 | ||
228 | mutex_init(&fsm->lock); | 349 | mutex_init(&fsm->lock); |
229 | 350 | ||
351 | ret = stfsm_init(fsm); | ||
352 | if (ret) { | ||
353 | dev_err(&pdev->dev, "Failed to initialise FSM Controller\n"); | ||
354 | return ret; | ||
355 | } | ||
356 | |||
230 | fsm->mtd.dev.parent = &pdev->dev; | 357 | fsm->mtd.dev.parent = &pdev->dev; |
231 | fsm->mtd.type = MTD_NORFLASH; | 358 | fsm->mtd.type = MTD_NORFLASH; |
232 | fsm->mtd.writesize = 4; | 359 | fsm->mtd.writesize = 4; |