Driver analysis of the lpc3250spi Controller

Source: Internet
Author: User

I am a beginner and only a memo.

Spi driver: SPI controller driver and SPI Device Driver

SPI controller driver: The following is the code in the arch-lpc32xx.c. The lpc3250 has two SSP controllers (which can be configured with two SPI controllers ). It registers two controllers as platform devices, but the two controllers use one driver. Only the IDs are different.

# If defined (config_spi_lpc32xx)

# Ifdefined (config_mach_lpc32xx_ssp0_enable)

Static struct resource ssp0_resources [] = {

[0] = {

. Start = ssp0_base,

. End = ssp0_base + sz_4k-1,

. Flags = ioresource_mem,

},

[1] = {

. Start = irq_ssp0,

. End = irq_ssp0,

. Flags = ioresource_irq,

},

 

};

Static struct platform_device ssp0_device = {

. Name = "spi_lpc32xx ",

. ID = 0,

. Dev = {

. Platform_data = & lpc32xx_spi1data,

},

. Num_resources = array_size (ssp0_resources ),

. Resource = ssp0_resources,

};

# Endif

# Ifdefined (config_mach_lpc32xx_ssp1_enable)

Static struct resource ssp1_resources [] = {

[0] = {

. Start = ssp1_base,

. End = ssp1_base + sz_4k-1,

. Flags = ioresource_mem,

},

[1] = {

. Start = irq_ssp1,

. End = irq_ssp1,

. Flags = ioresource_irq,

},

 

};

Static struct platform_device ssp1_device = {

. Name = "spi_lpc32xx ",

. ID = 1,

. Dev = {

. Platform_data = & lpc32xx_spi2data,

},

. Num_resources = array_size (ssp1_resources ),

. Resource = ssp1_resources,

};

# Endif

# Endif

With the above Code, the SPI controller registers the system with platform_device_register during system initialization.

The above code registers the device of the SPI controller. Register the driver of the SPI Controller

Static struct platform_driverlpc32xx_spi_driver = {

. Probe = maid,

. Remove = _ devexit_p (lpc32xx_spi_remove ),

. Driver = {

. Name = "spi_lpc32xx ",

. Owner = this_module,

},

};

In Linux, each type of driver will have a corresponding struct to describe. Here the spi_master is

It is used to describe the SPI host controller driver. Its main members are bus_num, Cs, SPI mode and clock settings.

Functions used for data transmission.

The API for allocating, registering, and deregistering the SPI host driver struct is provided by the SPI core layer:

Struct spi_master * spi_alloc_master (structdevice * Host, unsigned size );

Int spi_register_master (struct spi_master * master );

Void spi_unregister_master (structspi_master * master );

 

Use the platform bus API to register with Platform

The Controller devices and drivers on the bus are both common components. The specpacific part will be in the platform driver

After registration, the probe function is implemented based on the resource information of the Registered Common part. Called

The registration part of spi_master.

Spi_master Structure

1. structspi_master {

2. struct device dev;

3.

4. S16 bus_num; // bus number, starting from scratch

5.

6. num_chipselect; // number of supported parts. The number of parts selected from the device cannot be greater than this number.

7.

8./* setup mode and clock, etc (SPI driver may call timed times )*/

9. INT (* setup) (struct spi_device * SPI); // update the hardware configuration based on the SPI device.

10.

11. INT (* Transfer) (struct spi_device * SPI, struct spi_message * mesg); // Method for adding a message to a queue. This function cannot be sleep. It is responsible for arranging the transfer and calling the registered callback function complete ().

12.

13./* called on release () to free memory provided by spi_master */

14. Void (* cleanup) (struct spi_device * SPI); // The cleanup function is called in the spidev_release function, and spidev_release is registered as the SPI Dev release function.

15 .};

 

 

SPI Device Driver

The SPI device must be mounted to the SPI bus. This process is completed by Some APIs provided by spicore,

Spi_register_driver (), spi_register_device ().

Use spi_register_driver in the module init function to register the peripheral driver, register the char device, and export the SPI operation.

API.

1. structspi_driver {

2. INT (* probe) (struct spi_device * SPI); // this method is called after the SPI matches successfully. Therefore, this method requires device and private data initialization.

3. INT (* remove) (struct spi_device * SPI); // unbind spi_device from spi_driver and release resources applied by probe.

4. Void (* shutdown) (struct spi_device * SPI); // close

5. INT (* suspend) (struct spi_device * SPI, pm_message_t mesg); // suspend

6. INT (* resume) (struct spi_device * SPI); // restore

7. struct device_driver driver;

8 .}

 

 

1. structspi_device {

2. struct device dev;

3. struct spi_master * master; // corresponding controller pointer

4. u32 max_speed_hz; // SPI communication clock

5. u8 chip_select; // chip number, used to differentiate devices on the same master controller.

6. u8 mode; // The definitions are as follows: Transmission Mode and chip polarity.

7. # definespi_cpha 0x01/* Clock Phase */

8. # definespi_cpol 0x02/* Clock polarity */

9. # definespi_mode_0 (0 | 0)/* (originalmicrowire )*/

10. # definespi_mode_1 (0 | spi_cpha)

11. # definespi_mode_2 (spi_cpol | 0)

12. # definespi_mode_3 (spi_cpol | spi_cpha)

13. # definespi_cs_high 0x04/* chipselect activehigh? */The selected potential is high.

14. # definespi_lsb_first 0x08/* per-wordbits-on-wire */output low bit first

15. # definespi_3wire 0x10/* Si/so signals shared */input/output shared interface. At this time, only half duplex is allowed.

16. # definespi_loop 0x20/* loopback mode */Write-back/echo mode

17. u8 bits_per_word; // Number of BITs for each character length.

18. Int IRQ; // used interrupt

19. Void * controller_state;

20. Void * controller_data;

21. Char modalias [32]; // name.

22 .};

23.

24 .~~~~~~~~~~~~~~~~~~~~~~~

25. This struct describes the device information.

26. structspi_board_info {

27. Char modalias [32]; // device name

28. Const void * platform_data; // Platform Data

29. Void * controller_data;

30. Int IRQ; // interrupt

31.

32./* slower Signaling on noisy or low voltage boards */

33. u32 max_speed_hz; // communication clock

34.

35. 2010bus_num; // bus number

36. chip_select; // part number

37.

38. u8 mode; // refer to the member in spi_device.

39 .};

Below are some spi-lpc32xx.c-driven analyses

Static int _ init lpc32xx_spi_probe (structplatform_device * pdev)

{

Structspi_master * master;

Structlpc32xxspi * spidat;

Structresource * res;

Charclkname [16];

Intret, IRQ, I;

 

/* Get required resources */

Res = platform_get_resource (pdev, ioresource_mem, 0 );

IRQ = platform_get_irq (pdev, 0 );

// Obtain the corresponding memory and interrupted resources through the preceding statement.

If ((! Res) | (IRQ <0) | (IRQ> = nr_irqs ))

{

Return-ebusy;

}

 

Master = spi_alloc_master (& pdev-> Dev, sizeof (struct lpc32xxspi ));

// The second parameter specifies the space to be allocated for Dev. driver_data.

If (! Master)

{

Return-enodev;

}

Spidat = spi_master_get_devdata (master );

// Pass the spatial pointer of SPI-> master-> Dev-> driver_data to spidata

 

Platform_set_drvdata (pdev, Master );

// Assign pdev-> Dev-> driver_data to the master

// Pdev is the common part of the SPI controller and the master is the specific part of the SPI controller.

/* Save ID for this device */

Spidat-> id = pdev-> ID;

// ID indicates which controller to use

Spidat-> IRQ = IRQ;

Spin_lock_init (& spidat-> lock );

 

Init_work (& spidat-> Work, lpc32xx_work );

// Initialize the work queue

Init_list_head (& spidat-> Queue );

Init_waitqueue_head (& spidat-> waitq );

// Initialize the waiting queue

Spidat-> workqueue = create_singlethread_workqueue (master-> Dev. Parent-> bus_id );

// Create a single-threaded working queue

If (! Spidat-> workqueue)

{

Ret =-enomem;

Gotoerrout;

}

 

/* Generate clock name and get clock */

Snprintf (clkname, 10, "SPI % d_ck", spidat-> ID );

// Generate a clock name based on the Controller ID, such as spi0_ck

Spidat-> CLK = clk_get (& pdev-> Dev, clkname );

If (is_err (spidat-> CLK )){

Ret =-enodev;

Gotoerrout_qdel;

}

Clk_enable (spidat-> CLK );

 

/* Save Io Resources */

Spidat-> membase = ioremap (res-> Start, res-> end-res-> Start + 1 );

If (! Spidat-> membase)

{

Ret =-ebusy;

Gotoerrout2;

}

 

Ret = request_irq (spidat-> IRQ, lpc32xx_spi_irq,

Ir1__disabled, "spiirq", spidat );

If (RET)

{

Ret =-ebusy;

Gotoerrout3;

}

// Interrupt number spidat-> IRQ. this parameter is passed in by pdev. The function pointer to be executed is lpc32xx_spi_irq,

// Ir1__disabled, IRQ is blocked during the execution of the interrupt function

// The last is a void * pointer.

Disable_irq (spidat-> IRQ );

 

Master-> bus_num = spidat-> ID;

Master-> setup = maid;

Master-> transfer = lpc32xx_spi_transfer;

 

/* Is a board specific configuration available? */

Spidat-> psspcfg = (struct lpc32xx_spi_cfg *) pdev-> Dev. platform_data;

// The data in platform_data is the configuration data, but the configuration data is put in two places, one in // Dev. platform_data, and the other is the Explicit initialization of struct lpc32xx_spi_cfg in this file. // Psspcfg-> num_cs indicates the number of slave devices carried by the SPI controller.

If (spidat-> psspcfg = NULL)

{

Spidat-> psspcfg = & lpc32xx_stdspi_cfg;

}

If (spidat-> psspcfg-> num_cs <1)

{

Spidat-> psspcfg = & lpc32xx_stdspi_cfg;

}

 

Master-> num_chipselect = spidat-> psspcfg-> num_cs;

 

/* Initialize each chip select and set chip select low */

For (I = 0; I <spidat-> psspcfg-> num_cs; I ++)

{

If (spidat-> psspcfg-> spi_cs_setup! = NULL)

{

Spidat-> psspcfg-> spi_cs_setup (I );

// It is known through tracing that it calls static void smartarm3250_spi_cs_setup (int cs)

// This function only sets the SSEL pin (gpio_05) to a high level.

}

If (spidat-> psspcfg-> spi_cs_set! = NULL)

{

Spidat-> psspcfg-> spi_cs_set (I, 0 );

// It is known through tracing that it calls static int smartarm3250_spi_cs_set (int cs, int state)

// The function determines that the device does not exist when cs = 1. State and SSEL level synchronization.

}

}

 

/* Initial setup of SPI */

Lpc32xx_spi_prep (spidat );

 

/* Keep the SSP clock off until a transfer is saved med to save power */

Clk_disable (spidat-> CLK );

 

Ret = spi_register_master (master );

If (RET)

{

Gotoerrout4;

}

 

Return0;

 

Errout4:

Free_irq (spidat-> IRQ, pdev );

Errout3:

Iounmap (spidat-> membase );

Errout2:

Clk_disable (spidat-> CLK );

Clk_put (spidat-> CLK );

Errout_qdel:

Destroy_workqueue (spidat-> workqueue );

Errout:

Platform_set_drvdata (pdev, null );

Spi_master_put (master );

 

Returnret;

}

 

Static void lpc32xx_spi_prep (structlpc32xxspi * spidat)

{

U32tmp;

 

/* Clear and mask SSP interrupts */

_ Raw_writel (ssp_icr_roric | ssp_icr_rtic), ssp_rn (spidat-> membase ));

_ Raw_writel (0, ssp_imsc (spidat-> membase ));

 

/* Setup default SPI mode */

_ Raw_writel (ssp_cr0_dss (16) | ssp_cr0_frf_spi | ssp_cr0_cpol (0) |

Ssp_cr0_cpha (0) | ssp_cr0_scr (0), ssp_cr0 (spidat-> membase ));

_ Raw_writel (ssp_cr1_ssp_enable, ssp_cr1 (spidat-> membase ));

_ Raw_writel (ssp_cpsr_cpdvsr (2), ssp_cpsr (spidat-> membase ));

 

// The default format is 16-bit. The first SPI transmission mode, host mode, SSP enabling, and the frequency is 2 points of pclk //

 

/* Flush FIFO */

While (_ raw_readl (ssp_sr (spidat-> membase) & ssp_sr_rne)

{

TMP = _ raw_readl (ssp_data (spidat-> membase ));

}

// The waiting for receiving FIFO is empty.

/* Controller stays disabled until a transfer occurs */

}

 

Static void lpc32xx_cs_set_state (structspi_device * SPI, unsigned int CS,

Unsignedint active, unsigned int delay_ns)

{

Structlpc32xxspi * spidat = spi_master_get_devdata (SPI-> master );

Intval = (SPI-> mode & spi_cs_high )? Active :! Active;

 

If (spidat-> psspcfg-> spi_cs_set! = NULL)

{

Spidat-> psspcfg-> spi_cs_set (CS, Val );

}

 

Ndelay (delay_ns );

}

 

// If a high-level valid state is selected, the active state and state are the same. If no value is selected, the State is the opposite.

Static int lpc32xx_spi_setup (structspi_device * SPI)

{

Structlpc32xxspi * spidat = spi_master_get_devdata (SPI-> master );

Unsignedlong flags;

Unsignedint bits = SPI-> bits_per_word;

U32tmp;

 

If (SPI-> chip_select> SPI-> master-> num_chipselect)

{

Dev_dbg (& SPI-> Dev,

"Setup: Invalid chipselect % u (% u defined) \ n ",

SPI-> chip_select, SPI-> master-> num_chipselect );

Return-einval;

}

 

If (BITS = 0)

{

Bits = 8;

}

If (BITS <4) | (BITS> 16 ))

{

Dev_dbg (& SPI-> Dev,

"Setup: Invalid bits_per_word % u (8 to 16) \ n", BITs );

Return-einval;

}

 

If (SPI-> mode &~ Modebits)

{

Dev_dbg (& SPI-> Dev, "setup: Unsupported mode bits % x \ n ",

SPI-> mode &~ Modebits );

Return-einval;

}

 

Spin_lock_irqsave (& spidat-> lock, flags );

Clk_enable (spidat-> CLK );

 

/* Setup Cr0 register */

TMP = ssp_cr0_frf_spi;

If (SPI-> mode & spi_cpol)

{

TMP | = ssp_cr0_cpol (1 );

}

If (SPI-> mode & spi_cpha)

{

TMP | = ssp_cr0_cpha (1 );

}

 

_ Raw_writel (TMP, ssp_cr0 (spidat-> membase ));

// Write the mode to the SPI Controller

Lpc32xx_update_spi_dwidth (spidat, BITs );

// Write the number of digits per frame

Lpc32xx_update_spi_clock (spidat, SPI-> max_speed_hz );

// Set the clock frequency

Lpc32xx_cs_set_state (SPI, SPI-> chip_select, 0, 0 );

// SPI-> chip_select indicates which slave is selected. Currently, only SPI-> chip_select = 0 is supported;

// The third parameter is associated with the CS display level, and the last parameter is latency.

Clk_disable (spidat-> CLK );

Spin_unlock_irqrestore (& spidat-> lock, flags );

 

# If defined (ssp_debug)

Dev_dbg (& SPI-> Dev, "SSP (% d) prog rate = % d /"

"Actualrate = % d, BITs = % d \ n", SPI-> chip_select,

SPI-> max_speed_hz, spidat-> current_speed_hz, spidat-> current_bits_wd );

# Endif

 

Return0;

}

 

Static irqreturn_t lpc32xx_spi_irq (int irq, void * dev_id)

{

Structlpc32xxspi * spidat = dev_id;

 

/* Disable interrupts for now, do not clear the interrupt States */

_ Raw_writel (0, ssp_imsc (spidat-> membase ));

// No interruption

Wake_up (& spidat-> waitq );

// Wake up the waiting queue during the interruption.

Returnirq_handled;

}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.