Merge branch 'spi-5.2' into spi-5.3
[linux-2.6-microblaze.git] / drivers / spi / spi.c
index 5e46540..232ed4b 100644 (file)
@@ -1090,6 +1090,60 @@ static int spi_transfer_wait(struct spi_controller *ctlr,
        return 0;
 }
 
+static void _spi_transfer_delay_ns(u32 ns)
+{
+       if (!ns)
+               return;
+       if (ns <= 1000) {
+               ndelay(ns);
+       } else {
+               u32 us = DIV_ROUND_UP(ns, 1000);
+
+               if (us <= 10)
+                       udelay(us);
+               else
+                       usleep_range(us, us + DIV_ROUND_UP(us, 10));
+       }
+}
+
+static void _spi_transfer_cs_change_delay(struct spi_message *msg,
+                                         struct spi_transfer *xfer)
+{
+       u32 delay = xfer->cs_change_delay;
+       u32 unit = xfer->cs_change_delay_unit;
+       u32 hz;
+
+       /* return early on "fast" mode - for everything but USECS */
+       if (!delay && unit != SPI_DELAY_UNIT_USECS)
+               return;
+
+       switch (unit) {
+       case SPI_DELAY_UNIT_USECS:
+               /* for compatibility use default of 10us */
+               if (!delay)
+                       delay = 10000;
+               else
+                       delay *= 1000;
+               break;
+       case SPI_DELAY_UNIT_NSECS: /* nothing to do here */
+               break;
+       case SPI_DELAY_UNIT_SCK:
+               /* if there is no effective speed know, then approximate
+                * by underestimating with half the requested hz
+                */
+               hz = xfer->effective_speed_hz ?: xfer->speed_hz / 2;
+               delay *= DIV_ROUND_UP(1000000000, hz);
+               break;
+       default:
+               dev_err_once(&msg->spi->dev,
+                            "Use of unsupported delay unit %i, using default of 10us\n",
+                            xfer->cs_change_delay_unit);
+               delay = 10000;
+       }
+       /* now sleep for the requested amount of time */
+       _spi_transfer_delay_ns(delay);
+}
+
 /*
  * spi_transfer_one_message - Default implementation of transfer_one_message()
  *
@@ -1148,14 +1202,8 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
                if (msg->status != -EINPROGRESS)
                        goto out;
 
-               if (xfer->delay_usecs) {
-                       u16 us = xfer->delay_usecs;
-
-                       if (us <= 10)
-                               udelay(us);
-                       else
-                               usleep_range(us, us + DIV_ROUND_UP(us, 10));
-               }
+               if (xfer->delay_usecs)
+                       _spi_transfer_delay_ns(xfer->delay_usecs * 1000);
 
                if (xfer->cs_change) {
                        if (list_is_last(&xfer->transfer_list,
@@ -1163,7 +1211,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
                                keep_cs = true;
                        } else {
                                spi_set_cs(msg->spi, false);
-                               udelay(10);
+                               _spi_transfer_cs_change_delay(msg, xfer);
                                spi_set_cs(msg->spi, true);
                        }
                }
@@ -3083,6 +3131,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
         */
        message->frame_length = 0;
        list_for_each_entry(xfer, &message->transfers, transfer_list) {
+               xfer->effective_speed_hz = 0;
                message->frame_length += xfer->len;
                if (!xfer->bits_per_word)
                        xfer->bits_per_word = spi->bits_per_word;
@@ -3762,4 +3811,3 @@ err0:
  * include needing to have boardinfo data structures be much more public.
  */
 postcore_initcall(spi_init);
-