diff options
Diffstat (limited to 'drivers/spi/spi-uniphier.c')
-rw-r--r-- | drivers/spi/spi-uniphier.c | 89 |
1 files changed, 68 insertions, 21 deletions
diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 4e99a0f25c29..47cde1864630 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <linux/bitfield.h> | 7 | #include <linux/bitfield.h> |
8 | #include <linux/bitops.h> | 8 | #include <linux/bitops.h> |
9 | #include <linux/clk.h> | 9 | #include <linux/clk.h> |
10 | #include <linux/delay.h> | ||
10 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
11 | #include <linux/io.h> | 12 | #include <linux/io.h> |
12 | #include <linux/module.h> | 13 | #include <linux/module.h> |
@@ -16,6 +17,7 @@ | |||
16 | #include <asm/unaligned.h> | 17 | #include <asm/unaligned.h> |
17 | 18 | ||
18 | #define SSI_TIMEOUT_MS 2000 | 19 | #define SSI_TIMEOUT_MS 2000 |
20 | #define SSI_POLL_TIMEOUT_US 200 | ||
19 | #define SSI_MAX_CLK_DIVIDER 254 | 21 | #define SSI_MAX_CLK_DIVIDER 254 |
20 | #define SSI_MIN_CLK_DIVIDER 4 | 22 | #define SSI_MIN_CLK_DIVIDER 4 |
21 | 23 | ||
@@ -227,8 +229,7 @@ static void uniphier_spi_setup_transfer(struct spi_device *spi, | |||
227 | priv->speed_hz = t->speed_hz; | 229 | priv->speed_hz = t->speed_hz; |
228 | } | 230 | } |
229 | 231 | ||
230 | if (!priv->is_save_param) | 232 | priv->is_save_param = true; |
231 | priv->is_save_param = true; | ||
232 | 233 | ||
233 | /* reset FIFOs */ | 234 | /* reset FIFOs */ |
234 | val = SSI_FC_TXFFL | SSI_FC_RXFFL; | 235 | val = SSI_FC_TXFFL | SSI_FC_RXFFL; |
@@ -291,21 +292,23 @@ static void uniphier_spi_recv(struct uniphier_spi_priv *priv) | |||
291 | 292 | ||
292 | static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) | 293 | static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) |
293 | { | 294 | { |
294 | unsigned int tx_count; | 295 | unsigned int fifo_threshold, fill_bytes; |
295 | u32 val; | 296 | u32 val; |
296 | 297 | ||
297 | tx_count = DIV_ROUND_UP(priv->tx_bytes, | 298 | fifo_threshold = DIV_ROUND_UP(priv->rx_bytes, |
298 | bytes_per_word(priv->bits_per_word)); | 299 | bytes_per_word(priv->bits_per_word)); |
299 | tx_count = min(tx_count, SSI_FIFO_DEPTH); | 300 | fifo_threshold = min(fifo_threshold, SSI_FIFO_DEPTH); |
301 | |||
302 | fill_bytes = fifo_threshold - (priv->rx_bytes - priv->tx_bytes); | ||
300 | 303 | ||
301 | /* set fifo threshold */ | 304 | /* set fifo threshold */ |
302 | val = readl(priv->base + SSI_FC); | 305 | val = readl(priv->base + SSI_FC); |
303 | val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); | 306 | val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); |
304 | val |= FIELD_PREP(SSI_FC_TXFTH_MASK, tx_count); | 307 | val |= FIELD_PREP(SSI_FC_TXFTH_MASK, fifo_threshold); |
305 | val |= FIELD_PREP(SSI_FC_RXFTH_MASK, tx_count); | 308 | val |= FIELD_PREP(SSI_FC_RXFTH_MASK, fifo_threshold); |
306 | writel(val, priv->base + SSI_FC); | 309 | writel(val, priv->base + SSI_FC); |
307 | 310 | ||
308 | while (tx_count--) | 311 | while (fill_bytes--) |
309 | uniphier_spi_send(priv); | 312 | uniphier_spi_send(priv); |
310 | } | 313 | } |
311 | 314 | ||
@@ -324,20 +327,14 @@ static void uniphier_spi_set_cs(struct spi_device *spi, bool enable) | |||
324 | writel(val, priv->base + SSI_FPS); | 327 | writel(val, priv->base + SSI_FPS); |
325 | } | 328 | } |
326 | 329 | ||
327 | static int uniphier_spi_transfer_one(struct spi_master *master, | 330 | static int uniphier_spi_transfer_one_irq(struct spi_master *master, |
328 | struct spi_device *spi, | 331 | struct spi_device *spi, |
329 | struct spi_transfer *t) | 332 | struct spi_transfer *t) |
330 | { | 333 | { |
331 | struct uniphier_spi_priv *priv = spi_master_get_devdata(master); | 334 | struct uniphier_spi_priv *priv = spi_master_get_devdata(master); |
332 | struct device *dev = master->dev.parent; | 335 | struct device *dev = master->dev.parent; |
333 | unsigned long time_left; | 336 | unsigned long time_left; |
334 | 337 | ||
335 | /* Terminate and return success for 0 byte length transfer */ | ||
336 | if (!t->len) | ||
337 | return 0; | ||
338 | |||
339 | uniphier_spi_setup_transfer(spi, t); | ||
340 | |||
341 | reinit_completion(&priv->xfer_done); | 338 | reinit_completion(&priv->xfer_done); |
342 | 339 | ||
343 | uniphier_spi_fill_tx_fifo(priv); | 340 | uniphier_spi_fill_tx_fifo(priv); |
@@ -357,6 +354,59 @@ static int uniphier_spi_transfer_one(struct spi_master *master, | |||
357 | return priv->error; | 354 | return priv->error; |
358 | } | 355 | } |
359 | 356 | ||
357 | static int uniphier_spi_transfer_one_poll(struct spi_master *master, | ||
358 | struct spi_device *spi, | ||
359 | struct spi_transfer *t) | ||
360 | { | ||
361 | struct uniphier_spi_priv *priv = spi_master_get_devdata(master); | ||
362 | int loop = SSI_POLL_TIMEOUT_US * 10; | ||
363 | |||
364 | while (priv->tx_bytes) { | ||
365 | uniphier_spi_fill_tx_fifo(priv); | ||
366 | |||
367 | while ((priv->rx_bytes - priv->tx_bytes) > 0) { | ||
368 | while (!(readl(priv->base + SSI_SR) & SSI_SR_RNE) | ||
369 | && loop--) | ||
370 | ndelay(100); | ||
371 | |||
372 | if (loop == -1) | ||
373 | goto irq_transfer; | ||
374 | |||
375 | uniphier_spi_recv(priv); | ||
376 | } | ||
377 | } | ||
378 | |||
379 | return 0; | ||
380 | |||
381 | irq_transfer: | ||
382 | return uniphier_spi_transfer_one_irq(master, spi, t); | ||
383 | } | ||
384 | |||
385 | static int uniphier_spi_transfer_one(struct spi_master *master, | ||
386 | struct spi_device *spi, | ||
387 | struct spi_transfer *t) | ||
388 | { | ||
389 | struct uniphier_spi_priv *priv = spi_master_get_devdata(master); | ||
390 | unsigned long threshold; | ||
391 | |||
392 | /* Terminate and return success for 0 byte length transfer */ | ||
393 | if (!t->len) | ||
394 | return 0; | ||
395 | |||
396 | uniphier_spi_setup_transfer(spi, t); | ||
397 | |||
398 | /* | ||
399 | * If the transfer operation will take longer than | ||
400 | * SSI_POLL_TIMEOUT_US, it should use irq. | ||
401 | */ | ||
402 | threshold = DIV_ROUND_UP(SSI_POLL_TIMEOUT_US * priv->speed_hz, | ||
403 | USEC_PER_SEC * BITS_PER_BYTE); | ||
404 | if (t->len > threshold) | ||
405 | return uniphier_spi_transfer_one_irq(master, spi, t); | ||
406 | else | ||
407 | return uniphier_spi_transfer_one_poll(master, spi, t); | ||
408 | } | ||
409 | |||
360 | static int uniphier_spi_prepare_transfer_hardware(struct spi_master *master) | 410 | static int uniphier_spi_prepare_transfer_hardware(struct spi_master *master) |
361 | { | 411 | { |
362 | struct uniphier_spi_priv *priv = spi_master_get_devdata(master); | 412 | struct uniphier_spi_priv *priv = spi_master_get_devdata(master); |
@@ -420,7 +470,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) | |||
420 | { | 470 | { |
421 | struct uniphier_spi_priv *priv; | 471 | struct uniphier_spi_priv *priv; |
422 | struct spi_master *master; | 472 | struct spi_master *master; |
423 | struct resource *res; | ||
424 | unsigned long clk_rate; | 473 | unsigned long clk_rate; |
425 | int irq; | 474 | int irq; |
426 | int ret; | 475 | int ret; |
@@ -435,8 +484,7 @@ static int uniphier_spi_probe(struct platform_device *pdev) | |||
435 | priv->master = master; | 484 | priv->master = master; |
436 | priv->is_save_param = false; | 485 | priv->is_save_param = false; |
437 | 486 | ||
438 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 487 | priv->base = devm_platform_ioremap_resource(pdev, 0); |
439 | priv->base = devm_ioremap_resource(&pdev->dev, res); | ||
440 | if (IS_ERR(priv->base)) { | 488 | if (IS_ERR(priv->base)) { |
441 | ret = PTR_ERR(priv->base); | 489 | ret = PTR_ERR(priv->base); |
442 | goto out_master_put; | 490 | goto out_master_put; |
@@ -455,7 +503,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) | |||
455 | 503 | ||
456 | irq = platform_get_irq(pdev, 0); | 504 | irq = platform_get_irq(pdev, 0); |
457 | if (irq < 0) { | 505 | if (irq < 0) { |
458 | dev_err(&pdev->dev, "failed to get IRQ\n"); | ||
459 | ret = irq; | 506 | ret = irq; |
460 | goto out_disable_clk; | 507 | goto out_disable_clk; |
461 | } | 508 | } |