Merge remote-tracking branch 'spi/topic/s3c64xx' into spi-loop
authorMark Brown <broonie@linaro.org>
Fri, 11 Oct 2013 19:10:13 +0000 (20:10 +0100)
committerMark Brown <broonie@linaro.org>
Fri, 11 Oct 2013 19:10:13 +0000 (20:10 +0100)
1  2 
drivers/spi/spi-s3c64xx.c
drivers/spi/spi.c
include/linux/spi/spi.h

index 27b744be982e666369e1a67eaeb1e6e7726b8694,8e732a1b8a9b466c1af0320a17a62bb1f9d556b1..c7b36c06ef40fbb2cdd6ecc59bc351e624f47c9e
@@@ -205,7 -205,6 +205,6 @@@ struct s3c64xx_spi_driver_data 
  #endif
        struct s3c64xx_spi_port_config  *port_conf;
        unsigned int                    port_id;
-       unsigned long                   gpios[4];
        bool                            cs_gpio;
  };
  
@@@ -559,25 -558,18 +558,18 @@@ static void enable_datapath(struct s3c6
  static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd,
                                                struct spi_device *spi)
  {
-       struct s3c64xx_spi_csinfo *cs;
        if (sdd->tgl_spi != NULL) { /* If last device toggled after mssg */
                if (sdd->tgl_spi != spi) { /* if last mssg on diff device */
                        /* Deselect the last toggled device */
-                       cs = sdd->tgl_spi->controller_data;
-                       if (sdd->cs_gpio)
-                               gpio_set_value(cs->line,
+                       if (spi->cs_gpio >= 0)
+                               gpio_set_value(spi->cs_gpio,
                                        spi->mode & SPI_CS_HIGH ? 0 : 1);
                }
                sdd->tgl_spi = NULL;
        }
  
-       cs = spi->controller_data;
-       if (sdd->cs_gpio)
-               gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0);
-       /* Start the signals */
-       writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
+       if (spi->cs_gpio >= 0)
+               gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 1 : 0);
  }
  
  static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd,
@@@ -702,16 -694,11 +694,11 @@@ static int wait_for_xfer(struct s3c64xx
  static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
                                                struct spi_device *spi)
  {
-       struct s3c64xx_spi_csinfo *cs = spi->controller_data;
        if (sdd->tgl_spi == spi)
                sdd->tgl_spi = NULL;
  
-       if (sdd->cs_gpio)
-               gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1);
-       /* Quiese the signals */
-       writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
+       if (spi->cs_gpio >= 0)
+               gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1);
  }
  
  static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
@@@ -862,12 -849,16 +849,12 @@@ static void s3c64xx_spi_unmap_mssg(stru
        }
  }
  
 -static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
 -                                          struct spi_message *msg)
 +static int s3c64xx_spi_prepare_message(struct spi_master *master,
 +                                     struct spi_message *msg)
  {
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
        struct spi_device *spi = msg->spi;
        struct s3c64xx_spi_csinfo *cs = spi->controller_data;
 -      struct spi_transfer *xfer;
 -      int status = 0, cs_toggle = 0;
 -      u32 speed;
 -      u8 bpw;
  
        /* If Master's(controller) state differs from that needed by Slave */
        if (sdd->cur_speed != spi->max_speed_hz
        if (s3c64xx_spi_map_mssg(sdd, msg)) {
                dev_err(&spi->dev,
                        "Xfer: Unable to map message buffers!\n");
 -              status = -ENOMEM;
 -              goto out;
 +              return -ENOMEM;
        }
  
        /* Configure feedback delay */
        writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
  
 +      return 0;
 +}
 +
 +static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
 +                                          struct spi_message *msg)
 +{
 +      struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
 +      struct spi_device *spi = msg->spi;
 +      struct spi_transfer *xfer;
 +      int status = 0, cs_toggle = 0;
 +      u32 speed;
 +      u8 bpw;
 +
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
  
                unsigned long flags;
                        s3c64xx_spi_config(sdd);
                }
  
+               /* Slave Select */
+               enable_cs(sdd, spi);
                /* Polling method for xfers not bigger than FIFO capacity */
                use_dma = 0;
                if (!is_polling(sdd) &&
  
                enable_datapath(sdd, spi, xfer, use_dma);
  
-               /* Slave Select */
-               enable_cs(sdd, spi);
+               /* Start the signals */
+               writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
+               /* Start the signals */
+               writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
  
                spin_unlock_irqrestore(&sdd->lock, flags);
  
                        goto out;
                }
  
+               flush_fifo(sdd);
                if (xfer->delay_usecs)
                        udelay(xfer->delay_usecs);
  
                }
  
                msg->actual_length += xfer->len;
-               flush_fifo(sdd);
        }
  
  out:
-       if (!cs_toggle || status)
+       if (!cs_toggle || status) {
+               /* Quiese the signals */
+               writel(S3C64XX_SPI_SLAVE_SIG_INACT,
+                      sdd->regs + S3C64XX_SPI_SLAVE_SEL);
                disable_cs(sdd, spi);
-       else
+       } else {
                sdd->tgl_spi = spi;
+       }
  
        s3c64xx_spi_unmap_mssg(sdd, msg);
  
        return 0;
  }
  
 +static int s3c64xx_spi_unprepare_message(struct spi_master *master,
 +                                          struct spi_message *msg)
 +{
 +      struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
 +
 +      s3c64xx_spi_unmap_mssg(sdd, msg);
 +
 +      return 0;
 +}
 +
  static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
                                struct spi_device *spi)
  {
@@@ -1089,6 -1068,8 +1086,8 @@@ static int s3c64xx_spi_setup(struct spi
                                        cs->line, err);
                                goto err_gpio_req;
                        }
+                       spi->cs_gpio = cs->line;
                }
  
                spi_set_ctldata(spi, cs);
        }
  
        pm_runtime_put(&sdd->pdev->dev);
+       writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
        disable_cs(sdd, spi);
        return 0;
  
  setup_exit:
        /* setup() returns with device de-selected */
+       writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
        disable_cs(sdd, spi);
  
        gpio_free(cs->line);
@@@ -1158,8 -1141,8 +1159,8 @@@ static void s3c64xx_spi_cleanup(struct 
        struct s3c64xx_spi_driver_data *sdd;
  
        sdd = spi_master_get_devdata(spi->master);
-       if (cs && sdd->cs_gpio) {
-               gpio_free(cs->line);
+       if (spi->cs_gpio) {
+               gpio_free(spi->cs_gpio);
                if (spi->dev.of_node)
                        kfree(cs);
        }
@@@ -1377,9 -1360,7 +1378,9 @@@ static int s3c64xx_spi_probe(struct pla
        master->setup = s3c64xx_spi_setup;
        master->cleanup = s3c64xx_spi_cleanup;
        master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
 +      master->prepare_message = s3c64xx_spi_prepare_message;
        master->transfer_one_message = s3c64xx_spi_transfer_one_message;
 +      master->unprepare_message = s3c64xx_spi_unprepare_message;
        master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
        master->num_chipselect = sci->num_cs;
        master->dma_alignment = 8;
               S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN,
               sdd->regs + S3C64XX_SPI_INT_EN);
  
-       if (spi_register_master(master)) {
-               dev_err(&pdev->dev, "cannot register SPI master\n");
-               ret = -EBUSY;
+       pm_runtime_enable(&pdev->dev);
+       ret = devm_spi_register_master(&pdev->dev, master);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "cannot register SPI master: %d\n", ret);
                goto err3;
        }
  
                                        mem_res,
                                        sdd->rx_dma.dmach, sdd->tx_dma.dmach);
  
-       pm_runtime_enable(&pdev->dev);
        return 0;
  
  err3:
@@@ -1481,16 -1462,12 +1482,12 @@@ static int s3c64xx_spi_remove(struct pl
  
        pm_runtime_disable(&pdev->dev);
  
-       spi_unregister_master(master);
        writel(0, sdd->regs + S3C64XX_SPI_INT_EN);
  
        clk_disable_unprepare(sdd->src_clk);
  
        clk_disable_unprepare(sdd->clk);
  
-       spi_master_put(master);
        return 0;
  }
  
@@@ -1548,9 -1525,17 +1545,17 @@@ static int s3c64xx_spi_runtime_resume(s
  {
        struct spi_master *master = dev_get_drvdata(dev);
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
+       int ret;
  
-       clk_prepare_enable(sdd->src_clk);
-       clk_prepare_enable(sdd->clk);
+       ret = clk_prepare_enable(sdd->src_clk);
+       if (ret != 0)
+               return ret;
+       ret = clk_prepare_enable(sdd->clk);
+       if (ret != 0) {
+               clk_disable_unprepare(sdd->src_clk);
+               return ret;
+       }
  
        return 0;
  }
@@@ -1636,6 -1621,18 +1641,18 @@@ static struct platform_device_id s3c64x
  };
  
  static const struct of_device_id s3c64xx_spi_dt_match[] = {
+       { .compatible = "samsung,s3c2443-spi",
+                       .data = (void *)&s3c2443_spi_port_config,
+       },
+       { .compatible = "samsung,s3c6410-spi",
+                       .data = (void *)&s3c6410_spi_port_config,
+       },
+       { .compatible = "samsung,s5pc100-spi",
+                       .data = (void *)&s5pc100_spi_port_config,
+       },
+       { .compatible = "samsung,s5pv210-spi",
+                       .data = (void *)&s5pv210_spi_port_config,
+       },
        { .compatible = "samsung,exynos4210-spi",
                        .data = (void *)&exynos4_spi_port_config,
        },
@@@ -1653,22 -1650,13 +1670,13 @@@ static struct platform_driver s3c64xx_s
                .pm = &s3c64xx_spi_pm,
                .of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
        },
+       .probe = s3c64xx_spi_probe,
        .remove = s3c64xx_spi_remove,
        .id_table = s3c64xx_spi_driver_ids,
  };
  MODULE_ALIAS("platform:s3c64xx-spi");
  
- static int __init s3c64xx_spi_init(void)
- {
-       return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
- }
- subsys_initcall(s3c64xx_spi_init);
- static void __exit s3c64xx_spi_exit(void)
- {
-       platform_driver_unregister(&s3c64xx_spi_driver);
- }
- module_exit(s3c64xx_spi_exit);
+ module_platform_driver(s3c64xx_spi_driver);
  
  MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
  MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
diff --combined drivers/spi/spi.c
index 85c18d8a86b33616e20f0d6bf35010a20188352a,a586ceb111fcb1ef813b899ca1fad7845d053094..f37356c26b7dee118291b5fd08047d18c6841380
@@@ -39,9 -39,6 +39,9 @@@
  #include <linux/ioport.h>
  #include <linux/acpi.h>
  
 +#define CREATE_TRACE_POINTS
 +#include <trace/events/spi.h>
 +
  static void spidev_release(struct device *dev)
  {
        struct spi_device       *spi = to_spi_device(dev);
@@@ -526,95 -523,6 +526,95 @@@ int spi_register_board_info(struct spi_
  
  /*-------------------------------------------------------------------------*/
  
 +static void spi_set_cs(struct spi_device *spi, bool enable)
 +{
 +      if (spi->mode & SPI_CS_HIGH)
 +              enable = !enable;
 +
 +      if (spi->cs_gpio >= 0)
 +              gpio_set_value(spi->cs_gpio, !enable);
 +      else if (spi->master->set_cs)
 +              spi->master->set_cs(spi, !enable);
 +}
 +
 +/*
 + * spi_transfer_one_message - Default implementation of transfer_one_message()
 + *
 + * This is a standard implementation of transfer_one_message() for
 + * drivers which impelment a transfer_one() operation.  It provides
 + * standard handling of delays and chip select management.
 + */
 +static int spi_transfer_one_message(struct spi_master *master,
 +                                  struct spi_message *msg)
 +{
 +      struct spi_transfer *xfer;
 +      bool cur_cs = true;
 +      bool keep_cs = false;
 +      int ret = 0;
 +
 +      spi_set_cs(msg->spi, true);
 +
 +      list_for_each_entry(xfer, &msg->transfers, transfer_list) {
 +              trace_spi_transfer_start(msg, xfer);
 +
 +              INIT_COMPLETION(master->xfer_completion);
 +
 +              ret = master->transfer_one(master, msg->spi, xfer);
 +              if (ret < 0) {
 +                      dev_err(&msg->spi->dev,
 +                              "SPI transfer failed: %d\n", ret);
 +                      goto out;
 +              }
 +
 +              if (ret > 0)
 +                      wait_for_completion(&master->xfer_completion);
 +
 +              trace_spi_transfer_stop(msg, xfer);
 +
 +              if (msg->status != -EINPROGRESS)
 +                      goto out;
 +
 +              if (xfer->delay_usecs)
 +                      udelay(xfer->delay_usecs);
 +
 +              if (xfer->cs_change) {
 +                      if (list_is_last(&xfer->transfer_list,
 +                                       &msg->transfers)) {
 +                              keep_cs = true;
 +                      } else {
 +                              cur_cs = !cur_cs;
 +                              spi_set_cs(msg->spi, cur_cs);
 +                      }
 +              }
 +
 +              msg->actual_length += xfer->len;
 +      }
 +
 +out:
 +      if (ret != 0 || !keep_cs)
 +              spi_set_cs(msg->spi, false);
 +
 +      if (msg->status == -EINPROGRESS)
 +              msg->status = ret;
 +
 +      spi_finalize_current_message(master);
 +
 +      return ret;
 +}
 +
 +/**
 + * spi_finalize_current_transfer - report completion of a transfer
 + *
 + * Called by SPI drivers using the core transfer_one_message()
 + * implementation to notify it that the current interrupt driven
 + * transfer has finised and the next one may be scheduled.
 + */
 +void spi_finalize_current_transfer(struct spi_master *master)
 +{
 +      complete(&master->xfer_completion);
 +}
 +EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);
 +
  /**
   * spi_pump_messages - kthread work function which processes spi message queue
   * @work: pointer to kthread work struct contained in the master struct
@@@ -649,7 -557,6 +649,7 @@@ static void spi_pump_messages(struct kt
                        pm_runtime_mark_last_busy(master->dev.parent);
                        pm_runtime_put_autosuspend(master->dev.parent);
                }
 +              trace_spi_master_idle(master);
                return;
        }
  
                }
        }
  
 +      if (!was_busy)
 +              trace_spi_master_busy(master);
 +
        if (!was_busy && master->prepare_transfer_hardware) {
                ret = master->prepare_transfer_hardware(master);
                if (ret) {
                }
        }
  
 +      trace_spi_message_start(master->cur_msg);
 +
 +      if (master->prepare_message) {
 +              ret = master->prepare_message(master, master->cur_msg);
 +              if (ret) {
 +                      dev_err(&master->dev,
 +                              "failed to prepare message: %d\n", ret);
 +                      master->cur_msg->status = ret;
 +                      spi_finalize_current_message(master);
 +                      return;
 +              }
 +              master->cur_msg_prepared = true;
 +      }
 +
        ret = master->transfer_one_message(master, master->cur_msg);
        if (ret) {
                dev_err(&master->dev,
@@@ -788,7 -678,6 +788,7 @@@ void spi_finalize_current_message(struc
  {
        struct spi_message *mesg;
        unsigned long flags;
 +      int ret;
  
        spin_lock_irqsave(&master->queue_lock, flags);
        mesg = master->cur_msg;
        queue_kthread_work(&master->kworker, &master->pump_messages);
        spin_unlock_irqrestore(&master->queue_lock, flags);
  
 +      if (master->cur_msg_prepared && master->unprepare_message) {
 +              ret = master->unprepare_message(master, mesg);
 +              if (ret) {
 +                      dev_err(&master->dev,
 +                              "failed to unprepare message: %d\n", ret);
 +              }
 +      }
 +      master->cur_msg_prepared = false;
 +
        mesg->state = NULL;
        if (mesg->complete)
                mesg->complete(mesg->context);
 +
 +      trace_spi_message_done(mesg);
  }
  EXPORT_SYMBOL_GPL(spi_finalize_current_message);
  
@@@ -925,8 -803,6 +925,8 @@@ static int spi_master_initialize_queue(
  
        master->queued = true;
        master->transfer = spi_queued_transfer;
 +      if (!master->transfer_one_message)
 +              master->transfer_one_message = spi_transfer_one_message;
  
        /* Initialize and start queue */
        ret = spi_init_queue(master);
@@@ -1333,7 -1209,6 +1333,7 @@@ int spi_register_master(struct spi_mast
        spin_lock_init(&master->bus_lock_spinlock);
        mutex_init(&master->bus_lock_mutex);
        master->bus_lock_flag = 0;
 +      init_completion(&master->xfer_completion);
  
        /* register the device, then userspace will see it.
         * registration fails if the bus ID is in use.
@@@ -1370,6 -1245,41 +1370,41 @@@ done
  }
  EXPORT_SYMBOL_GPL(spi_register_master);
  
+ static void devm_spi_unregister(struct device *dev, void *res)
+ {
+       spi_unregister_master(*(struct spi_master **)res);
+ }
+ /**
+  * dev_spi_register_master - register managed SPI master controller
+  * @dev:    device managing SPI master
+  * @master: initialized master, originally from spi_alloc_master()
+  * Context: can sleep
+  *
+  * Register a SPI device as with spi_register_master() which will
+  * automatically be unregister
+  */
+ int devm_spi_register_master(struct device *dev, struct spi_master *master)
+ {
+       struct spi_master **ptr;
+       int ret;
+       ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+       ret = spi_register_master(master);
+       if (ret != 0) {
+               *ptr = master;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+       return ret;
+ }
+ EXPORT_SYMBOL_GPL(devm_spi_register_master);
  static int __unregister(struct device *dev, void *null)
  {
        spi_unregister_device(to_spi_device(dev));
@@@ -1546,10 -1456,6 +1581,10 @@@ static int __spi_async(struct spi_devic
        struct spi_master *master = spi->master;
        struct spi_transfer *xfer;
  
 +      message->spi = spi;
 +
 +      trace_spi_message_submit(message);
 +
        if (list_empty(&message->transfers))
                return -EINVAL;
        if (!message->complete)
                }
        }
  
 -      message->spi = spi;
        message->status = -EINPROGRESS;
        return master->transfer(spi, message);
  }
diff --combined include/linux/spi/spi.h
index da371ab5ebebb3fa5ac125c58d015959e5cbec50,4d634d66ba0bc9228df5ba720ca4a4dcda78c90a..1619ed48e75d19c2fa6dd01061af962a417d0754
@@@ -23,7 -23,6 +23,7 @@@
  #include <linux/mod_devicetable.h>
  #include <linux/slab.h>
  #include <linux/kthread.h>
 +#include <linux/completion.h>
  
  /*
   * INTERFACES between SPI master-side drivers and SPI infrastructure.
@@@ -151,7 -150,8 +151,7 @@@ static inline void *spi_get_drvdata(str
  }
  
  struct spi_message;
 -
 -
 +struct spi_transfer;
  
  /**
   * struct spi_driver - Host side "protocol" driver
@@@ -257,9 -257,6 +257,9 @@@ static inline void spi_unregister_drive
   * @queue_lock: spinlock to syncronise access to message queue
   * @queue: message queue
   * @cur_msg: the currently in-flight message
 + * @cur_msg_prepared: spi_prepare_message was called for the currently
 + *                    in-flight message
 + * @xfer_completion: used by core tranfer_one_message()
   * @busy: message pump is busy
   * @running: message pump is running
   * @rt: whether this queue is set to run as a realtime task
   * @unprepare_transfer_hardware: there are currently no more messages on the
   *    queue so the subsystem notifies the driver that it may relax the
   *    hardware by issuing this call
 + * @set_cs: assert or deassert chip select, true to assert.  May be called
 + *          from interrupt context.
 + * @prepare_message: set up the controller to transfer a single message,
 + *                   for example doing DMA mapping.  Called from threaded
 + *                   context.
 + * @transfer_one: transfer a single spi_transfer. When the
 + *              driver is finished with this transfer it must call
 + *              spi_finalize_current_transfer() so the subsystem can issue
 + *                the next transfer
 + * @unprepare_message: undo any work done by prepare_message().
   * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
   *    number. Any individual value may be -ENOENT for CS lines that
   *    are not GPIOs (driven by the SPI controller itself).
@@@ -401,25 -388,11 +401,25 @@@ struct spi_master 
        bool                            running;
        bool                            rt;
        bool                            auto_runtime_pm;
 +      bool                            cur_msg_prepared;
 +      struct completion               xfer_completion;
  
        int (*prepare_transfer_hardware)(struct spi_master *master);
        int (*transfer_one_message)(struct spi_master *master,
                                    struct spi_message *mesg);
        int (*unprepare_transfer_hardware)(struct spi_master *master);
 +      int (*prepare_message)(struct spi_master *master,
 +                             struct spi_message *message);
 +      int (*unprepare_message)(struct spi_master *master,
 +                               struct spi_message *message);
 +
 +      /*
 +       * These hooks are for drivers that use a generic implementation
 +       * of transfer_one_message() provied by the core.
 +       */
 +      void (*set_cs)(struct spi_device *spi, bool enable);
 +      int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
 +                          struct spi_transfer *transfer);
  
        /* gpio chip select */
        int                     *cs_gpios;
@@@ -455,13 -428,14 +455,15 @@@ extern int spi_master_resume(struct spi
  /* Calls the driver make to interact with the message queue */
  extern struct spi_message *spi_get_next_queued_message(struct spi_master *master);
  extern void spi_finalize_current_message(struct spi_master *master);
 +extern void spi_finalize_current_transfer(struct spi_master *master);
  
  /* the spi driver core manages memory for the spi_master classdev */
  extern struct spi_master *
  spi_alloc_master(struct device *host, unsigned size);
  
  extern int spi_register_master(struct spi_master *master);
+ extern int devm_spi_register_master(struct device *dev,
+                                   struct spi_master *master);
  extern void spi_unregister_master(struct spi_master *master);
  
  extern struct spi_master *spi_busnum_to_master(u16 busnum);