⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 spi.c

📁 pxa3xx ssp driver for linux
💻 C
📖 第 1 页 / 共 2 页
字号:
 /*
  * spi.c - SPI init/core code
  *
  * Copyright (C) 2005 David Brownell
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  #include <linux/autoconf.h>
  #include <linux/kernel.h>
  #include <linux/device.h>
  #include <linux/init.h>
  #include <linux/cache.h>
  #include <linux/spi/spi.h>
  
  
  /* SPI bustype and spi_master class are registered after board init code
   * provides the SPI device tables, ensuring that both are present by the
   * time controller driver registration causes spi_devices to "enumerate".
   */
  static void spidev_release(struct device *dev)
  {
          const struct spi_device *spi = to_spi_device(dev);
  
          /* spi masters may cleanup for released devices */
          if (spi->master->cleanup)
                  spi->master->cleanup(spi);
  
          spi_master_put(spi->master);
          kfree(dev);
  }
  
  static ssize_t
  modalias_show(struct device *dev, struct device_attribute *a, char *buf)
  {
          const struct spi_device *spi = to_spi_device(dev);
  
          return snprintf(buf, BUS_ID_SIZE + 1, "%s\n", spi->modalias);
  }
  
  static struct device_attribute spi_dev_attrs[] = {
          __ATTR_RO(modalias),
          __ATTR_NULL,
  };
  
  /* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
   * and the sysfs version makes coldplug work too.
   */
  
  static int spi_match_device(struct device *dev, struct device_driver *drv)
  {
          const struct spi_device *spi = to_spi_device(dev);
  
          return strncmp(spi->modalias, drv->name, BUS_ID_SIZE) == 0;
  }
  
  static int spi_uevent(struct device *dev, char **envp, int num_envp,
                  char *buffer, int buffer_size)
  {
          const struct spi_device         *spi = to_spi_device(dev);
  
          envp[0] = buffer;
          snprintf(buffer, buffer_size, "MODALIAS=%s", spi->modalias);
          envp[1] = NULL;
          return 0;
  }
 
  #ifdef  CONFIG_PM
  
  /*
   * NOTE:  the suspend() method for an spi_master controller driver
   * should verify that all its child devices are marked as suspended;
   * suspend requests delivered through sysfs power/state files don't
   * enforce such constraints.
   */
  static int spi_suspend(struct device *dev, pm_message_t message)
  {
          int                     value;
          struct spi_driver       *drv = to_spi_driver(dev->driver);
  
          if (!drv || !drv->suspend)
                  return 0;
  
          /* suspend will stop irqs and dma; no more i/o */
          value = drv->suspend(to_spi_device(dev), message);
          if (value == 0)
                  dev->power.power_state = message;
         return value;
 }
 
 static int spi_resume(struct device *dev)
 {
         int                     value;
         struct spi_driver       *drv = to_spi_driver(dev->driver);
 
         if (!drv || !drv->resume)
                 return 0;
 
         /* resume may restart the i/o queue */
         value = drv->resume(to_spi_device(dev));
         if (value == 0)
                 dev->power.power_state = PMSG_ON;
         return value;
 }
 
 #else
 #define spi_suspend     NULL
 #define spi_resume      NULL
 #endif
 
 struct bus_type spi_bus_type = {
         .name           = "spi",
         .dev_attrs      = spi_dev_attrs,
         .match          = spi_match_device,
         .uevent         = spi_uevent,
         .suspend        = spi_suspend,
         .resume         = spi_resume,
 };
 EXPORT_SYMBOL_GPL(spi_bus_type);
 
 
 static int spi_drv_probe(struct device *dev)
 {
         const struct spi_driver         *sdrv = to_spi_driver(dev->driver);
 
         return sdrv->probe(to_spi_device(dev));
 }
 
 static int spi_drv_remove(struct device *dev)
 {
         const struct spi_driver         *sdrv = to_spi_driver(dev->driver);
 
         return sdrv->remove(to_spi_device(dev));
 }
 
 static void spi_drv_shutdown(struct device *dev)
 {
         const struct spi_driver         *sdrv = to_spi_driver(dev->driver);
 
         sdrv->shutdown(to_spi_device(dev));
 }
 
 int spi_register_driver(struct spi_driver *sdrv)
 {
         sdrv->driver.bus = &spi_bus_type;
         if (sdrv->probe)
                 sdrv->driver.probe = spi_drv_probe;
         if (sdrv->remove)
                 sdrv->driver.remove = spi_drv_remove;
         if (sdrv->shutdown)
                 sdrv->driver.shutdown = spi_drv_shutdown;
         return driver_register(&sdrv->driver);
 }
 EXPORT_SYMBOL_GPL(spi_register_driver);
 
 /*-------------------------------------------------------------------------*/
 
 /* SPI devices should normally not be created by SPI device drivers; that
  * would make them board-specific.  Similarly with SPI master drivers.
  * Device registration normally goes into like arch/.../mach.../board-YYY.c
  * with other readonly (flashable) information about mainboard devices.
  */
 
 struct boardinfo {
         struct list_head        list;
         unsigned                n_board_info;
         struct spi_board_info   board_info[0];
 };
 
 static LIST_HEAD(board_list);
 static DECLARE_MUTEX(board_lock);
 
 
 /* On typical mainboards, this is purely internal; and it's not needed
  * after board init creates the hard-wired devices.  Some development
  * platforms may not be able to use spi_register_board_info though, and
  * this is exported so that for example a USB or parport based adapter
  * driver could add devices (which it would learn about out-of-band).
  */
 struct spi_device *__init_or_module
 spi_new_device(struct spi_master *master, struct spi_board_info *chip)
 {
         struct spi_device       *proxy;
         struct device           *dev = master->cdev.dev;
         int                     status;
 
         /* NOTE:  caller did any chip->bus_num checks necessary */
 
         if (!spi_master_get(master))
                 return NULL;
 
         proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
         if (!proxy) {
                 dev_err(dev, "can't alloc dev for cs%d\n",
                         chip->chip_select);
                 goto fail;
         }
         proxy->master = master;
         proxy->chip_select = chip->chip_select;
         proxy->max_speed_hz = chip->max_speed_hz;
         proxy->irq = chip->irq;
         proxy->modalias = chip->modalias;
 
         snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
                         "%s.%u", master->cdev.class_id,
                         chip->chip_select);
         proxy->dev.parent = dev;
         proxy->dev.bus = &spi_bus_type;
         proxy->dev.platform_data = (void *) chip->platform_data;
         proxy->controller_data = chip->controller_data;
         proxy->controller_state = NULL;
         proxy->dev.release = spidev_release;
 
         /* drivers may modify this default i/o setup */
         status = master->setup(proxy);
         if (status < 0) {
                 dev_dbg(dev, "can't %s %s, status %d\n",
                                 "setup", proxy->dev.bus_id, status);
                 goto fail;
         }
 
         /* driver core catches callers that misbehave by defining
          * devices that already exist.
          */
         status = device_register(&proxy->dev);
         if (status < 0) {
                 dev_dbg(dev, "can't %s %s, status %d\n",
                                 "add", proxy->dev.bus_id, status);
                 goto fail;
         }
         dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id);
         return proxy;
 
 fail:
         spi_master_put(master);
         kfree(proxy);
         return NULL;
 }
 EXPORT_SYMBOL_GPL(spi_new_device);
 
 /*
254  * Board-specific early init code calls this (probably during arch_initcall)
255  * with segments of the SPI device table.  Any device nodes are created later,
256  * after the relevant parent SPI controller (bus_num) is defined.  We keep
257  * this table of devices forever, so that reloading a controller driver will
258  * not make Linux forget about these hard-wired devices.
259  *
260  * Other code can also call this, e.g. a particular add-on board might provide
261  * SPI devices through its expansion connector, so code initializing that board
262  * would naturally declare its SPI devices.
263  *
264  * The board info passed can safely be __initdata ... but be careful of
265  * any embedded pointers (platform_data, etc), they're copied as-is.
266  */
 int __init
 spi_register_board_info(struct spi_board_info const *info, unsigned n)
 {
         struct boardinfo        *bi;
 
         bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
         if (!bi)
                 return -ENOMEM;
         bi->n_board_info = n;
         memcpy(bi->board_info, info, n * sizeof *info);
 
         down(&board_lock);
         list_add_tail(&bi->list, &board_list);
         up(&board_lock);
         return 0;
 }
 EXPORT_SYMBOL_GPL(spi_register_board_info);
 
 /* FIXME someone should add support for a __setup("spi", ...) that
286  * creates board info from kernel command lines
287  */
 
 static void __init_or_module
 scan_boardinfo(struct spi_master *master)
 {
         struct boardinfo        *bi;
         struct device           *dev = master->cdev.dev;
 
         down(&board_lock);
         list_for_each_entry(bi, &board_list, list) {
                 struct spi_board_info   *chip = bi->board_info;
                 unsigned                n;
 
                 for (n = bi->n_board_info; n > 0; n--, chip++) {
                         if (chip->bus_num != master->bus_num)
                                 continue;
                         /* some controllers only have one chip, so they
                          * might not use chipselects.  otherwise, the
                          * chipselects are numbered 0..max.
                          */
                         if (chip->chip_select >= master->num_chipselect
                                         && master->num_chipselect) {
                                 dev_dbg(dev, "cs%d > max %d\n",
                                         chip->chip_select,
                                         master->num_chipselect);
                                 continue;
                         }
                         (void) spi_new_device(master, chip);
                 }
         }
         up(&board_lock);
 }
 
 /*-------------------------------------------------------------------------*/
 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -