当前位置:首页 >> 信息与通信 >>

linux spi子系统驱动分析


linux spi 子系统驱动分析
2.6.18 内核下已经添加了完整的 spi 子系统了,参考 mtd 的分析,将从下到上层,再从上到下层的对其进行分析。 以下先从下到上的进行分析: driver/spi 下有两个底层相关的 spi 驱动程序: spi_s3c24xx.c 和 spi_s3c24xx_gpio.c 其中 spi_s3c24xx.c 是基于 s3c24x

x 下相应的 spi 接口的驱动程序,spi_s3c24xx_gpio.c 允许用户指定 3 个 gpio 口,分别充 当 spi_clk、spi_mosi 和 spi_miso 接口,模拟标准的 spi 总线。 s3c2410 自带了两个 spi 接口(spi0 和 spi1),在此我只研究基于 s3c2410 下 spi 接口的驱动程序 spi_s3c24xx.c。 首先从 spi 驱动的检测函数进行分析: static int s3c24xx_spi_probe(struct platform_device *pdev) { struct s3c24xx_spi *hw; struct spi_master *master; struct spi_board_info *bi; struct resource *res; int err = 0; int i; /* pi_alloc_master 函数申请了 struct spi_master+struct s3c24xx_spi 大小的数据, * spi_master_get_devdata 和 pi_master_get 分别取出 struct s3c24xx_spi 和 struct spi_master 结构指针 */ master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); if (master == NULL) { dev_err(&pdev->dev, "No memory for spi_master\n"); err = -ENOMEM; goto err_nomem; } /* 填充 struct spi_master 结构 */ hw = spi_master_get_devdata(master); memset(hw, 0, sizeof(struct s3c24xx_spi)); hw->master = spi_master_get(master); hw->pdata = pdev->dev.platform_data; hw->dev = &pdev->dev; if (hw->pdata == NULL) { dev_err(&pdev->dev, "No platform data supplied\n"); err = -ENOENT; goto err_no_pdata; } platform_set_drvdata(pdev, hw);//dev_set_drvdata(&pdev->dev, hw) init_completion(&hw->done); /* setup the state for the bitbang driver */ /* 填充 hw->bitbang 结构(hw->bitbang 结构充当一个中间层,相当与 input system 的 input_handle struct) */ hw->bitbang.master hw->bitbang.chipselect hw->bitbang.txrx_bufs = hw->master; = s3c24xx_spi_chipsel; = s3c24xx_spi_txrx;

hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;

-1-

hw->bitbang.master->setup = s3c24xx_spi_setup; dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); /* find and map our resources */ /* 申请 spi 所用到的资源:io、irq、时钟等 */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); err = -ENOENT; goto err_no_iores; } hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1, pdev->name); if (hw->ioarea == NULL) { dev_err(&pdev->dev, "Cannot reserve region\n"); err = -ENXIO; goto err_no_iores; } hw->regs = ioremap(res->start, (res->end - res->start)+1); if (hw->regs == NULL) { dev_err(&pdev->dev, "Cannot map IO\n"); err = -ENXIO; goto err_no_iomap; } hw->irq = platform_get_irq(pdev, 0); if (hw->irq < 0) { dev_err(&pdev->dev, "No IRQ specified\n"); err = -ENOENT; goto err_no_irq; } err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw); if (err) { dev_err(&pdev->dev, "Cannot claim IRQ\n"); goto err_no_irq; } hw->clk = clk_get(&pdev->dev, "spi"); if (IS_ERR(hw->clk)) { dev_err(&pdev->dev, "No clock for device\n"); err = PTR_ERR(hw->clk); goto err_no_clk; } /* for the moment, permanently enable the clock */ clk_enable(hw->clk); /* program defaults into the registers */ /* 初始化 spi 相关的寄存器 */ writeb(0xff, hw->regs + S3C2410_SPPRE); writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN); writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);

-2-

/* add by lfc */ s3c2410_gpio_setpin(S3C2410_GPE13, 0); s3c2410_gpio_setpin(S3C2410_GPE12, 0); s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0); s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0); s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0); /* end add */ /* setup any gpio we can */ /* 片选 */ if (!hw->pdata->set_cs) { s3c2410_gpio_setpin(hw->pdata->pin_cs, 1); s3c2410_gpio_cfgpin(hw->pdata->pin_cs, S3C2410_GPIO_OUTPUT); } /* register our spi controller */ /* 最终通过调用 spi_register_master 来注册 spi 控制器(驱动) */ err = spi_bitbang_start(&hw->bitbang); if (err) { dev_err(&pdev->dev, "Failed to register SPI master\n"); goto err_register; } dev_dbg(hw->dev, "shutdown=%d\n", hw->bitbang.shutdown); /* register all the devices associated */ /* 注册所用使用本 spi 驱动的设备 */ bi = &hw->pdata->board_info[0]; for (i = 0; i < hw->pdata->board_size; i++, bi++) { dev_info(hw->dev, "registering %s\n", bi->modalias); bi->controller_data = hw; spi_new_device(master, bi); } return 0; err_register: clk_disable(hw->clk); clk_put(hw->clk); err_no_clk: free_irq(hw->irq, hw); err_no_irq: iounmap(hw->regs); err_no_iomap: release_resource(hw->ioarea); kfree(hw->ioarea); err_no_iores: err_no_pdata: spi_master_put(hw->master);; err_nomem: return err; } /*

-3-

* spi_alloc_master - allocate SPI master controller * @dev: the controller, possibly using the platform_bus * @size: how much driver-private data to preallocate; the pointer to this * * * * This call is used only by SPI master controller drivers, which are the * only ones directly touching chip registers. It's how they allocate * an spi_master structure, prior to calling spi_register_master(). * * This must be called from context that can sleep. It returns the SPI * master structure on success, else NULL. * * The caller is responsible for assigning the bus number and initializing * the master's methods before calling spi_register_master(); and (after errors * adding the device) calling spi_master_put() to prevent a memory leak. */ /*注释已经写得很清楚了,本函数旨在分配 spi_master struct *其中,device 为主控制设备,size 为需要预分配的设备私有数据大小 *该函数被 spi 主控制器驱动所调用,用于在调用 spi_register_master 注册主控制器前 *分配 spi_master struct,分配 bus number 和初始化主控制器的操作方法 *注意在分配 spi_master struct 的时候多分配了大小为 size 的设备私有数据 *并通过 spi_master_set_devdata 函数把其放到 class_data field 里,以后可以通过 spi_master_get_devdata 来访问 */ struct spi_master * __init_or_module spi_alloc_master(struct device *dev, unsigned size) { struct spi_master if (!dev) return NULL; master = kzalloc(size + sizeof *master, SLAB_KERNEL); if (!master) return NULL; class_device_initialize(&master->cdev); master->cdev.class = &spi_master_class; master->cdev.dev = get_device(dev); spi_master_set_devdata(master, &master[1]); return master; } /* * spi_bitbang_start - start up a polled/bitbanging SPI master driver * @bitbang: driver handle * * Caller should have zero-initialized all parts of the structure, and then * provided callbacks for chip selection and I/O loops. If the master has * a transfer method, its final step should call spi_bitbang_transfer; or, *master; memory is in the class_data field of the returned class_device, accessible with spi_master_get_devdata().

-4-

* that's the default if the transfer routine is not initialized. It should * also set up the bus number and number of chipselects. * * For i/o loops, provide callbacks either per-word (for bitbanging, or for * hardware that basically exposes a shift register) or per-spi_transfer * (which takes better advantage of hardware like fifos or DMA engines). * * Drivers using per-word I/O loops should use (or call) spi_bitbang_setup and * spi_bitbang_cleanup to handle those spi master methods. Those methods are * the defaults if the bitbang->txrx_bufs routine isn't initialized. * * This routine registers the spi_master, which will process requests in a * dedicated task, keeping IRQs unblocked most of the time. To stop * processing those requests, call spi_bitbang_stop(). */ int spi_bitbang_start(struct spi_bitbang *bitbang) { int status; return -EINVAL; /*bitbang_work * 初始化 a work,后面再 create_singlethread_workqueue, * 等 到 有 数 据 要 传 输 的 时 候 , 在 spi_bitbang_transfer 函 数 中 通 过 调 用 queue_work(bitbang->workqueue, &bitbang->work) * 把 work 扔进 workqueue 中调度运行 * 这是内核的一贯做法,在 mmc/sd 驱动中也是这样处理的^_^ */ INIT_WORK(&bitbang->work, bitbang_work, bitbang); /* 初始化自旋锁和链表头,以后用到 */ spin_lock_init(&bitbang->lock); spi_new_device INIT_LIST_HEAD(&bitbang->queue); if (!bitbang->master->transfer) bitbang->master->transfer = spi_bitbang_transfer;//spi 数据的传输就是通过调用这个方法来实现的 /* spi_s3c24xx.c 驱动中有相应的 txrx_bufs 处理方法,在 bitbang_work 中被调用 */ if (!bitbang->txrx_bufs) { bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; if (!bitbang->master->setup) { if (!bitbang->setup_transfer) bitbang->setup_transfer = spi_bitbang_setup_transfer; bitbang->master->setup = spi_bitbang_setup; bitbang->master->cleanup = spi_bitbang_cleanup; } /* spi_s3c24xx.c 驱动中有相应的 setup 处理方法,在稍后的 spi_new_device 中被调用 */ } else if (!bitbang->master->setup) return -EINVAL; if (!bitbang->master || !bitbang->chipselect)

-5-

/* this task is the only thing to touch the SPI bits */ bitbang->busy = 0; /* 创建工作者进程 */ bitbang->workqueue = create_singlethread_workqueue( bitbang->master->cdev.dev->bus_id); if (bitbang->workqueue == NULL) { status = -EBUSY; goto err1; } /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ status = spi_register_master(bitbang->master); if (status < 0) goto err2; return status; err2: destroy_workqueue(bitbang->workqueue); err1: return status; } /** * spi_register_master - register SPI master controller * @master: initialized master, originally from spi_alloc_master() * * SPI master controllers connect to their drivers using some non-SPI bus, * such as the platform bus. The final stage of probe() in that code * includes calling spi_register_master() to hook up to this SPI bus glue. * * SPI controllers use board specific (often SOC specific) bus numbers, * and board-specific addressing for SPI devices combines those numbers * with chip select numbers. Since SPI does not directly support dynamic * device identification, boards need configuration tables telling which * chip is at which address. * * This must be called from context that can sleep. It returns zero on * success, else a negative error code (dropping the master's refcount). * After a successful return, the caller is responsible for calling * spi_unregister_master(). */ int __init_or_module spi_register_master(struct spi_master *master) { static atomic_t struct device int dyn_bus_id = ATOMIC_INIT((1<<16) - 1); *dev = master->cdev.dev; status = -ENODEV;

-6-

int if (!dev)

dynamic = 0; return -ENODEV;

/* convention: dynamically assigned bus IDs count down from the max */ if (master->bus_num < 0) { master->bus_num = atomic_dec_return(&dyn_bus_id); dynamic = 1; } /* register the device, then userspace will see it. * registration fails if the bus ID is in use. */ snprintf(master->cdev.class_id, sizeof master->cdev.class_id, "spi%u", master->bus_num); status = class_device_add(&master->cdev);//注册设备 if (status < 0) goto done; dev_dbg(dev, "registered master %s%s\n", master->cdev.class_id, dynamic ? " (dynamic)" : ""); /* populate children from any spi device tables */ scan_boardinfo(master); status = 0; done: return status; } /* FIXME someone should add support for a __setup("spi", ...) that * creates board info from kernel command lines */ /* * scan board_list for spi_board_info which is registered by spi_register_board_info * 很可惜,s3c24xx 的 spi 驱动中不支持 spi_register_board_info 这种标准方式注册方式,而是直接调用 spi_new_device 内部函数 */ static void __init_or_module scan_boardinfo(struct spi_master *master) { struct boardinfo struct device down(&board_lock); list_for_each_entry(bi, &board_list, list) { struct spi_board_info unsigned n; *chip = bi->board_info; *bi; *dev = master->cdev.dev;

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

-7-

* 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); } /* * Board-specific early init code calls this (probably during arch_initcall) * with segments of the SPI device table. Any device nodes are created later, * after the relevant parent SPI controller (bus_num) is defined. We keep * this table of devices forever, so that reloading a controller driver will * not make Linux forget about these hard-wired devices. * * Other code can also call this, e.g. a particular add-on board might provide * SPI devices through its expansion connector, so code initializing that board * would naturally declare its SPI devices. * * The board info passed can safely be __initdata ... but be careful of * any embedded pointers (platform_data, etc), they're copied as-is. */ int __init spi_register_board_info(struct spi_board_info const *info, unsigned n) { struct boardinfo 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; } /* 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 *bi; bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);

-8-

* 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 struct device int *proxy;//这个结构很重要,以后就是通过这个结构来操作实际的 spi 设备的 *dev = master->cdev.dev; 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; } /* 初始化 spi_device 结构各成员 */ proxy->master = master; proxy->chip_select = chip->chip_select; proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; 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 */ /* 调用 master->setup(即 s3c24xx_spi_setup)函数初始化 spi 设备 */ 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) {

-9-

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; } static int s3c24xx_spi_setup(struct spi_device *spi) { int ret; /* 进行一些检查性操作 */ if (!spi->bits_per_word) spi->bits_per_word = 8; if ((spi->mode & SPI_LSB_FIRST) != 0) return -EINVAL; ret = s3c24xx_spi_setupxfer(spi, NULL); if (ret < 0) { dev_err(&spi->dev, "setupxfer returned %d\n", ret); return ret; } dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __FUNCTION__, spi->mode, spi->bits_per_word, spi->max_speed_hz); return 0; } static int s3c24xx_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) { struct s3c24xx_spi *hw = to_hw(spi); unsigned int bpw; unsigned int hz; unsigned int div; bpw = t ? t->bits_per_word : spi->bits_per_word; hz = t ? t->speed_hz : spi->max_speed_hz; if (bpw != 8) { dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); return -EINVAL; } div = clk_get_rate(hw->clk) / hz; /* is clk = pclk / (2 * (pre+1)), or is it * clk = (pclk * 2) / ( pre + 1) */

- 10 -

div = (div / 2) - 1;//求出预分频值 if (div < 0) div = 1; if (div > 255) div = 255; dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", div, hz); writeb(div, hw->regs + S3C2410_SPPRE);//设置预分频值 spin_lock(&hw->bitbang.lock); if (!hw->bitbang.busy) { hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);//修改时钟,先禁用 spi /* need to ndelay for 0.5 clocktick ? */ } spin_unlock(&hw->bitbang.lock); return 0; } static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) { struct s3c24xx_spi *hw = to_hw(spi); unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; unsigned int spcon; switch (value) { case BITBANG_CS_INACTIVE: /* 禁用 spi(禁用片选) */ if (hw->pdata->set_cs) hw->pdata->set_cs(hw->pdata, value, cspol); else s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol ^ 1); break; case BITBANG_CS_ACTIVE: /* * 启用 spi:根据需要设置寄存器并启用使能片选 * (如果 spi_board_info 中没有设置相应的 mode 选项的话, 那就只能使用默认值 SPPIN_DEFAULT 和 SPCON_DEFAULT 了) */ spcon = readb(hw->regs + S3C2410_SPCON); if (spi->mode & SPI_CPHA) spcon |= S3C2410_SPCON_CPHA_FMTB; else spcon &= ~S3C2410_SPCON_CPHA_FMTB; if (spi->mode & SPI_CPOL) spcon |= S3C2410_SPCON_CPOL_HIGH; else spcon &= ~S3C2410_SPCON_CPOL_HIGH; spcon |= S3C2410_SPCON_ENSCK; /* write new configration */ writeb(spcon, hw->regs + S3C2410_SPCON);

- 11 -

if (hw->pdata->set_cs) hw->pdata->set_cs(hw->pdata, value, cspol); else s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol); break; } } 好了,至此 spi 主控制器(驱动)和板上 spi 设备注册完毕,以后要使用 spi 来传输数据的话,只要先获得 spi 设备结构,然后就可 以利用它来和 spi 驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)

- 12 -


相关文章:
Linux下spi驱动开发
Linuxspi驱动开发_计算机软件及应用_IT/计算机_专业资料。本文从硬件原理图讲解开始,详细讲解了linuxspi开发的过程。Linuxspi 驱动开发一、概述 基于子系统...
SPI驱动框架源码分析
SPI驱动框架源码分析_信息与通信_工程科技_专业资料。SPI通讯解释SPI...下面介绍 spi 子系统的数据结构: 在 Linux 中,使用 spi_master 结构来描述一...
关于Linux设备驱动中input子系统的介绍
关于Linux设备驱动中input子系统的介绍_计算机软件及应用_IT/计算机_专业资料。[导读] 对于输入类设备如键盘、 鼠标、 触摸屏之类的 Linux 驱动, 内核提供 input ...
Linux输入子系统及触摸屏驱动分析
Linux输入子系统及触摸屏驱动分析_IT/计算机_专业资料。在Linux内核中,input设备用input_dev结构体描述,使用input子系统实现输入设备驱动的时候,驱动的核心工作是向系...
Linux五大子系统
Linux 内核向用户提供了统一的系统调用接口,但是 在不同处理器上系统调用的方法...linux spi子系统驱动分析... 12页 1下载券 Linux下的五大查询指令 暂无评价 ...
Linux的Graphics子系统分析
Linux的Graphics子系统分析_电脑基础知识_IT/计算机_专业资料。linux的Graphics子...为了使用这些功能 Linux 需要为相应 GPU 提供驱动程序,如 ARM@Mali 系列的 GPU...
Linux内核SCSI子系统驱动架构
Linux内核SCSI子系统驱动架构_计算机软件及应用_IT/计算机_专业资料。Linux内核SCSI子系统驱动架构 SCSI 子系统驱动架构 2013-2-2 代码布局: 一、读写数据流: ...
linux的I2C子系统详解
linux的I2C子系统详解_计算机硬件及网络_IT/计算机_专业资料。Linux 中 I2C 驱动子系统详解 Author :Aston Mail 写在前面 首先,本教程不来自于权威书籍或文献,完全...
Linux内核驱动之gpio子系统(一)gpio的使用
Linux内核驱动之gpio子系统(一)gpio的使用_计算机软件及应用_IT/计算机_专业资料...2014教师资格材料分析辅... 2014小学教师资格考试《... 2014年幼儿园教师资格考...
linux驱动及g-sensor学习心得
driver code 的研究学习,我对 linux 设备驱动开发和...3. linux 的 input 子系统 Linux 系统提供了 ...general mode with SPI interface general mode with...
更多相关标签:
linux spi子系统 | linux led驱动子系统 | linux驱动子系统 | linux spi驱动 | linux spi驱动框架 | linux spi设备驱动 | linux spi flash驱动 | linux spi驱动详解 |