对技术执着
分类: 嵌入式
2015-03-14 15:55:27
原文地址:Linux SPI框架(中) 作者:enzo26
水平有限,描述不当之处还请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7734150
上节介绍了SPI子系统中的一些重要数据结构和SPI子系统初始化的第一步,也就是注册SPI总线。这节介绍针对于s3c24xx平台的SPI子系统初始化,在看具体的代码之前,先上一张自己画的图,帮助理清初始化的主要步骤
显然,SPI是一种平台特定的资源,所以它是以platform平台设备的方式注册进 内核的,因此它的struct platform_device结构是已经静态定义好了的,现在只待它的struct platform_driver注册,然后和platform_device匹配。
初始化的入口:
static int __init s3c24xx_spi_init(void) { return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe); }
platform_driver_probe()会调用
platform_driver_register()来注册驱动,然后在注册的过程中寻求匹配的platform_device,一旦匹配成功,便会调
用probe函数,也就是s3c24xx_spi_probe(),在看这个函数之前,还得介绍几个相关的数据结构。
struct s3c2410_spi_info是一个板级结构,也是在移植时就定义好的,在初始化spi_master时用到,platform_device-->dev-->platform_data会指向这个结构。
struct s3c2410_spi_info { int pin_cs; /* simple gpio cs */ unsigned int num_cs; /* total chipselects */ int bus_num;/* bus number to use. */ void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable); void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); };
struct s3c24xx_spi用来具体描述s3c24xx平台上一个SPI控制器
struct s3c24xx_spi { /* bitbang has to be first */ struct spi_bitbang bitbang; struct completion done; void __iomem *regs; int irq; int len; int count; void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); /* data buffers */ const unsigned char *tx; unsigned char *rx; struct clk *clk; struct resource *ioarea; struct spi_master *master; struct spi_device *curdev; struct device *dev; struct s3c2410_spi_info *pdata; };
struct spi_bitbang用于控制实际的数据传输
struct spi_bitbang { struct workqueue_struct *workqueue; /*工作队列*/ struct work_struct work; spinlock_t lock; struct list_head queue; u8 busy; u8 use_dma; u8 flags; /* extra spi->mode support */ struct spi_master *master; /*bitbang所属的master*/ /*用于设置设备传输时的时钟,字长等*/ int (*setup_transfer)(struct spi_device *spi, struct spi_transfer *t); void (*chipselect)(struct spi_device *spi, int is_on); #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */ #define BITBANG_CS_INACTIVE 0 /*针对于平台的传输控制函数*/ int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t); /* txrx_word[SPI_MODE_*]() just looks like a shift register */ u32 (*txrx_word[4])(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits); };
下面来看s3c24xx_spi_probe()函数的实现
static int __init s3c24xx_spi_probe(struct platform_device *pdev) { struct s3c2410_spi_info *pdata; struct s3c24xx_spi *hw; struct spi_master *master; struct resource *res; int err = 0; /*创建spi_master,并将spi_master->private_data指向s3c24xx_spi*/ 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; } hw = spi_master_get_devdata(master);//获取s3c24xx_spi memset(hw, 0, sizeof(struct s3c24xx_spi)); hw->master = spi_master_get(master); hw->pdata = pdata = pdev->dev.platform_data; hw->dev = &pdev->dev; if (pdata == NULL) { dev_err(&pdev->dev, "No platform data supplied\n"); err = -ENOENT; goto err_no_pdata; } platform_set_drvdata(pdev, hw); init_completion(&hw->done); /* setup the master state. */ /*片选数和SPI主控制器编号是在platform_data中已经定义好了的*/ master->num_chipselect = hw->pdata->num_cs; master->bus_num = pdata->bus_num; /* setup the state for the bitbang driver */ /*设置bitbang的所属master和控制传输的相关函数*/ hw->bitbang.master = hw->master; hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; hw->bitbang.chipselect = s3c24xx_spi_chipsel; hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; hw->bitbang.master->setup = s3c24xx_spi_setup; dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); /* find and map our resources */ 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; } /*映射SPI控制寄存器*/ 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; } /* setup any gpio we can */ if (!pdata->set_cs) { if (pdata->pin_cs < 0) { dev_err(&pdev->dev, "No chipselect pin\n"); goto err_register; } err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev)); if (err) { dev_err(&pdev->dev, "Failed to get gpio for cs\n"); goto err_register; } hw->set_cs = s3c24xx_spi_gpiocs;//设定片选函数 gpio_direction_output(pdata->pin_cs, 1); } else hw->set_cs = pdata->set_cs; s3c24xx_spi_initialsetup(hw); /* register our spi controller */ /* 注册主机SPI控制器 */ err = spi_bitbang_start(&hw->bitbang); if (err) { dev_err(&pdev->dev, "Failed to register SPI master\n"); goto err_register; } return 0; err_register: if (hw->set_cs == s3c24xx_spi_gpiocs) gpio_free(pdata->pin_cs); clk_disable(hw->clk); clk_put(hw->clk); err_no_clk: free_irq(hw->irq, hw); err_no_irq: iounmap(hw->regs);
int spi_bitbang_start(struct spi_bitbang *bitbang) { int status; if (!bitbang->master || !bitbang->chipselect) return -EINVAL; /*初始化一个struct work,处理函数为bitbang_work*/ INIT_WORK(&bitbang->work, bitbang_work); spin_lock_init(&bitbang->lock); INIT_LIST_HEAD(&bitbang->queue); /*检测bitbang中的函数是否都定义了,如果没定义,则默认使用spi_bitbang_xxx*/ if (!bitbang->master->transfer) bitbang->master->transfer = spi_bitbang_transfer; 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; } } else if (!bitbang->master->setup) return -EINVAL; /* this task is the only thing to touch the SPI bits */ bitbang->busy = 0; /*创建bitbang的工作队列*/ bitbang->workqueue = create_singlethread_workqueue( dev_name(bitbang->master->dev.parent)); if (bitbang->workqueue == NULL) { status = -EBUSY; goto err1; } /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ /*注册spi_master*/ status = spi_register_master(bitbang->master); if (status < 0) goto err2; return status; err2: destroy_workqueue(bitbang->workqueue); err1: return status; }
下一个关键函数就是spi_register_master(),用于注册spi_master
int spi_register_master(struct spi_master *master) { static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); struct device *dev = master->dev.parent; int status = -ENODEV; int dynamic = 0; if (!dev) return -ENODEV; /* even if it's just one always-selected device, there must * be at least one chipselect */ if (master->num_chipselect == 0)//片选数不能为0 return -EINVAL; /* convention: dynamically assigned bus IDs count down from the max */ if (master->bus_num < 0) { /* FIXME switch to an IDR based scheme, something like * I2C now uses, so we can't run out of "dynamic" IDs */ 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. */ dev_set_name(&master->dev, "spi%u", master->bus_num); status = device_add(&master->dev);//添加spi_master设备 if (status < 0) goto done; dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); /* populate children from any spi device tables */ scan_boardinfo(master);//遍历板级信息,寻找可以挂接在该spi_master下的从设备 status = 0; done: return status; }
static void scan_boardinfo(struct spi_master *master) { struct boardinfo *bi; mutex_lock(&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; /* NOTE: this relies on spi_new_device to * issue diagnostics when given bogus inputs */ /*bus_num相等则创建新设备*/ (void) spi_new_device(master, chip); } } mutex_unlock(&board_lock); }
spi_board_info是板级信息,是在移植时就写好的,并且要将其注册
struct spi_board_info { char modalias[32]; /*名字*/ const void *platform_data; void *controller_data; int irq; /*中断号*/ u32 max_speed_hz; /*最高传输速率*/ u16 bus_num; /*所属的spi_master编号*/ u16 chip_select; /*片选号*/ u8 mode; /*传输模式*/ };
最后一步就是将相应的从设备注册进内核
struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip) { struct spi_device *proxy; int status; /* NOTE: caller did any chip->bus_num checks necessary. * * Also, unless we change the return value convention to use * error-or-pointer (not NULL-or-pointer), troubleshootability * suggests syslogged diagnostics are best here (ugh). */ /*创建SPI_device*/ proxy = spi_alloc_device(master); if (!proxy) return NULL; WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); /*初始化*/ proxy->chip_select = chip->chip_select; proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); proxy->dev.platform_data = (void *) chip->platform_data; proxy->controller_data = chip->controller_data; proxy->controller_state = NULL; /*将新设备添加进内核*/ status = spi_add_device(proxy); if (status < 0) { spi_dev_put(proxy); return NULL; } return proxy; }