1. SPI Bus structure
SPI Serial Peripheral Interface, is a high-speed, full-duplex, synchronous communication bus. Adopt master-Slave mode architecture, support multiple slave, generally only support single master
There are 4 signal lines in the SPI interface, namely:
Device Selection Line (SS), clock Line (SCK), serial output data cable (MOSI), serial input data cable (miso).
2. Data transfer process
The master node reads data from the primary node at Simo from the Mosi line output data. Also, the MSB (highest bit) is output through the Smoi,
The master node reads the data from the node at Miso, and the entire process continues until all the data has been swapped out.
3. Bus timing
SPI bare Metal driver Design:
1. SPI Controller Workflow
There are no SPI peripherals on the Development Board, and there are links to other people who have tested the SPI bare Metal drive:
Http://blog.chinaunix.net/uid-24219701-id-3748675.html
http://blog.csdn.net/cp1300/article/details/8041760
http://blog.csdn.net/wanyeye/article/details/42494559
SPI Subsystem Architecture:
1. SPI Core
The link between the SPI controller driver and the device driver, which provides the SPI controller driver and device driver registration, logoff method and so on.
2. SPI Controller Driver
Implementation of the SPI controller driver
3. SPI device driver
Implementations of SPI slave devices, such as SPI Flash
First look at the source code in the SPI core driver:
Or the first part of the initialization module: You can see also the platform bus driver model! Jump directly to the probe function (the probe function in this file)
static int __init s3c24xx_spi_probe (struct platform_device *pdev)
There are more functions here.
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;master = Spi_alloc_master (&pdev->dev, sizeof (struct S3 C24XX_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); 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-& Gt;dev, "No platform Data supplied\n"); err =-enoent;goto err_no_pdata;} Platform_set_drvdata (Pdev, HW); init_completion (&hw->done);/* Initialise Fiq handler */s3c24xx_spi_initfiq (HW );/* Setup The Master state. *//* the Spi->mode bits understood by this driver: */master->mode_bits = Spi_cpol | Spi_cpha | Spi_cs_high;master->num_chipselect = Hw->pdata->num_cs;master->bus_num = Pdata->bus_num;/* setup the state for the Bitbang driver */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->master->setup = S3c24xx_spi_setup;hw->master->cleanup = S3c24xx_spi_cleanup;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, Resource_size (res), 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, Resource_size (res)), 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);//Interrupt related part 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);} Elsehw->set_cs = Pdata->set_cs;s3c24xx_spi_initialsetup (HW);//initialization of hardware-related parts/* Register our SPI Controller *// Register drive to SPI core 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); 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;}
Hardware Initialization section: (This is the same as the bare metal driver)
Of course, read-write and interrupt part is also the core of SPI, look at the source!
The following is a brief introduction to SPI slave device driver programming:
Kernel source file m25p80.c a kind of SPI interface Flash driver! (SPI peripheral, here is a brief look at the SPI peripheral driver)
First, let's look at the module initialization section above! Here, let's look at the M25P80 parameter type:
The M25p_probe function is called when the driver encounters the appropriate device.
/* * Board specific Setup should has ensured the SPI clock used here * matches what the READ command supports, at least u Ntil This driver * understands fast_read (for clocks over MHz). */static int __devinit m25p_probe (struct Spi_device *spi) {const struct SPI_DEVICE_ID*ID = spi_get_device_id (SPI); struct Flash_platform_data*data;struct m25p*flash;struct flash_info*info;unsignedi;struct mtd_partition*parts = NULL;intnr _parts = 0;/* Platform data helps sort out which chip type we had, as * well as what this board partitions it. If we don ' t have * a chip ID, try the JEDEC ID commands; They ' ll work for most * newer chips, even if we don ' t recognize the particular chip. */data = spi->dev.platform_data;if (Data && data->type) {const struct spi_device_id *plat_id;for (i = 0; I & Lt Array_size (M25p_ids)-1; i++) {plat_id = &m25p_ids[i];if (strcmp (Data->type, plat_id->name)) Continue;break;} if (I < array_size (m25p_ids)-1) id = Plat_id;elsedev_warn (&spi->deV, "Unrecognized ID%s\n", data->type);} info = (void *) Id->driver_data;if (info->jedec_id) {const struct spi_device_id *jid;jid = Jedec_probe (SPI); if (is_e RR (Jid)) {return ptr_err (Jid);} else if (Jid! = ID) {/* * JEDEC knows better, so overwrite platform ID. We * can ' t trust partitions any longer, but we'll let * MTD apply them anyway, since some partitions could be * marked read- Only, and we don ' t want to lose that * information, even if it's not 100% accurate. */dev_warn (&spi->dev, "found%s, expected%s\n", Jid->name, id->name); id = Jid;info = (void *) Jid->driver _data;}} Flash = kzalloc (sizeof *flash, gfp_kernel); if (!flash) Return-enomem;flash->command = Kmalloc (max_cmd_size + FAST_ Read_dummy_byte, Gfp_kernel); if (!flash->command) {kfree (flash); return-enomem;} Flash->spi = Spi;mutex_init (&flash->lock);d ev_set_drvdata (&spi->dev, Flash);/* * Atmel, SST and Intel /numonyx serial flash tend to power * up with the software protection bits set*/if (JEDEC_MFR (info->jedec_id) = = Cfi_mfr_atmel | | JEDEC_MFR (info->jedec_id) = = Cfi_mfr_intel | | JEDEC_MFR (info->jedec_id) = = cfi_mfr_sst) {write_enable (Flash); Write_sr (flash, 0);} if (data && data->name) flash->mtd.name = Data->name;elseflash->mtd.name = Dev_name (&spi-> dev); flash->mtd.type = Mtd_norflash;flash->mtd.writesize = 1;flash->mtd.flags = MTD_CAP_NORFLASH;flash-> Mtd.size = info->sector_size * Info->n_sectors;flash->mtd.erase = M25p80_erase;flash->mtd.read = m25p80_ read;/* SST Flash chips use AAI Word program */if (JEDEC_MFR (info->jedec_id) = = cfi_mfr_sst) Flash->mtd.write = Sst_w Rite;elseflash->mtd.write = m25p80_write;/* prefer "small sector" erase if possible */if (Info->flags & sect_4k) {Flash->erase_opcode = Opcode_be_4k;flash->mtd.erasesize = 4096;} else {Flash->erase_opcode = Opcode_se;flash->mtd.erasesize = info->sector_size;} if (Info->flags & m25p_no_erase) flash->mTd.flags |= mtd_no_erase;flash->mtd.dev.parent = &spi->dev;flash->page_size = Info->page_size;if ( Info->addr_width) Flash->addr_width = info->addr_width;else {/* Enable 4-byte addressing if the device exceeds 16 MiB */if (Flash->mtd.size > 0x1000000) {flash->addr_width = 4;set_4byte (Flash, info->jedec_id, 1);} Elseflash->addr_width = 3;} Dev_info (&spi->dev, "%s (%lld Kbytes) \ n", Id->name, (Long Long) flash->mtd.size >>);D Ebug (mtd_ Debug_level2, "MTD. Name =%s,. Size = 0x%llx (%lldmib)" ". Erasesize = 0x%.8x (%ukib). Numeraseregions =%d\n", FLASH->MT D.name, (Long Long) flash->mtd.size, (Long Long) (Flash->mtd.size >>), Flash->mtd.erasesize, flash- >mtd.erasesize/1024,flash->mtd.numeraseregions); if (flash->mtd.numeraseregions) for (i = 0; i < flash-> Mtd.numeraseregions; i++) DEBUG (Mtd_debug_level2, "mtd.eraseregions[%d] = {. offset = 0x%llx," ". Erasesize = 0x%.8x (%ukib)," ". Numblocks =%d}\ N ", I, (longLong) Flash->mtd.eraseregions[i].offset,flash->mtd.eraseregions[i].erasesize,flash->mtd.eraseregions[i ].erasesize/1024,flash->mtd.eraseregions[i].numblocks);/* partitions should match sector boundaries; And it may is good to * use readonly partitions for writeprotected sectors (BP2. BP0). */if (Mtd_has_cmdlinepart ()) {static const char *part_probes[]= {"Cmdlinepart", NULL,};nr_parts = Parse_mtd_partitions ( &flash->mtd,part_probes, &parts, 0);} if (nr_parts <= 0 && Data && data->parts) {parts = Data->parts;nr_parts = Data->nr_parts;} #ifdef config_mtd_of_partsif (nr_parts <= 0 && spi->dev.of_node) {nr_parts = Of_mtd_parse_partitions ( &spi->dev, Spi->dev.of_node, &parts);} #endifif (Nr_parts > 0) {for (i = 0; i < nr_parts; i++) {DEBUG (Mtd_debug_level2, "partitions[%d] =" "{. Name = %s,. offset = 0x%llx, "". Size = 0x%llx (%lldkib)}\n ", I, Parts[i].name, (Long Long) Parts[i].offset, (Long Long) parts[i].size, (Long Long) (Parts[i].size >> 10));} flash->partitioned = 1;} Return Mtd_device_register (&FLASH->MTD, parts, nr_parts) = = 1?-enodev:0; Registering a MTD device hard disk partition initialization part of a very important operation}
Here's a first focus on write, which is how the driver writes data to flash through the SPI bus
/* Write an address range to the Flash chip. Data must is written in * flash_pagesize chunks. The address range may be any size provided * it's within the physical boundaries. */static int m25p80_write (struct mtd_info *mtd, loff_t to, size_t len,size_t *retlen, const U_char *buf) {struct m25p *flas h = mtd_to_m25p (MTD); U32 page_offset, page_size;struct spi_transfer t[2];//This structure and the structure of the following line is very important to see the above diagram to see the struct SPI_ Message M;debug (Mtd_debug_level2, "%s:%s%s 0x%08x, Len%zd\n", Dev_name (&flash->spi->dev), __func__, "to", ( U32) to, len); *retlen = 0;/* Sanity checks */if (!len) return (0); if (to + len > flash->mtd.size) return-einval;spi_mes Sage_init (&m);//Initialize memset (t, 0, (sizeof t));//Array 0 t[0].tx_buf = flash->command;//command and data are actually data T[0].len = M25p_ CMDSZ (Flash); Spi_message_add_tail (&t[0], &m);//The linked list in the message is exactly the queue t[1].tx_buf = Buf;spi_message_add_tail ( &T[1], &m);//Hang into message mutex_lock (&flash->lock);/* Wait until finished previous write command. */if (Wait_till_ready (Flash)) {mutex_unlock (&flash->lock); return 1;} Write_enable (Flash);/* Set up the opcode in the write buffer. */flash->command[0] = Opcode_pp;m25p_addr2cmd (Flash, to, flash->command);p Age_offset = to & (Flash->page_ SIZE-1);/* do all the bytes fit onto one page? */if (Page_offset + len <= flash->page_size) {T[1].len = Len;spi_sync (Flash->spi, &m);//Submit a message to the Controller for processing The controller is sent to the SPI bus at the appropriate time *retlen = M.ACTUAL_LENGTH-M25P_CMDSZ (Flash);} else {u32 i;/* the size of data remaining on the first page */page_size = Flash->page_size-page_offset;t[1].len = Pag E_size;spi_sync (Flash->spi, &m); *retlen = M.actual_length-m25p_cmdsz (flash);/* Write everything in flash-> Page_size chunks */for (i = page_size; i < len; i + = page_size) {page_size = Len-i;if (Page_size > Flash->page_ Size) Page_size = flash->page_size;/* Write the next page to Flash */m25p_addr2cmd (flash, to + I, flash->command); t[1 ].tx_buf = buf + i;t[1].len = page_size;wait_till_ready (flash); write_enable (Flash); Spi_sync (Flash->spi, &m); *retlen + = M.actual_length -M25P_CMDSZ (Flash);}} Mutex_unlock (&flash->lock); return 0;}
Reading data is about the same as the above process! What is the link between the specific SPI and the device driver and the SPI controller?
The above one of the function call graph analysis of the comparison detailed!
Linux SPI Drive Design