This article is based on the mini2440 development board, and the Linux version number is: linux-2.6.32.2
1.IIC bus device hardware information
#define S3C2410_PA_IIC (0x54000000)
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC,
.end = S3C_PA_IIC + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC,
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
.id = 0,
#else
.id = -1,
#endif
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
.flags = 0,
.slave_addr = 0x10,
.frequency = 100*1000,
.sda_delay = 100,
};
Among them, the base address of the IIC register is 0x54000000.
The addresses of the registers of the s3c3440 chip are as follows:
2.IIC bus device registration
The IIC bus device is included in mini2440_devices, as shown in the following figure:
Register the mini2440_devices array containing USB, LCD, I2C, NAND and other devices as platform devices to the kernel
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))
3. Registration of IIC bus driver
The registration of the IIC bus driver calls the platform_driver_register function. The IIC bus driver registration means that the platform bus will automatically match the corresponding device. The matching rules are:
First match according to id_table
Then match according to the name of the device
This IICdriver can match two IIC devices, as shown below:
4. Matching of IIC bus driver and device
After the driver and device of the IIC bus are matched, the driver's probe function will be executed. The following actions are performed in the probe function:
Get the i2c clock and enable the clock
i2c->clk = clk_get(&pdev->dev, "i2c");
if (IS_ERR(i2c->clk)) {
dev_err(&pdev->dev, "cannot get clockn");
right = -ENOENT;
goto err_noclk;
}
clk_enable(i2c->clk);
Get the virtual address of the IIC register
i2c->regs = ioremap(res->start, resource_size(res));
Get IIC interrupt and apply IIC interrupt
i2c->irq = ret = platform_get_irq(pdev, 0);
if (ret <= 0) {
dev_err(&pdev->dev, "cannot find IRQn");
goto err_iomap;
}
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c);
IIC initialization, set clock, enable interrupt, enable ask, set register
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
{
unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
struct s3c2410_platform_i2c *pdata;
unsigned int freq;
/* get the plafrom data: Get platform device data
pdata = i2c->dev->platform_data;
/* inititalise the gpio */
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev));
/* write slave address */
writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
// IIC-bus acknowledge enable
//IC-Bus Tx/Rx interrupt enable
writel(iicon, i2c->regs + S3C2410_IICCON);
/* we need to work out the divisors for the clock... */
//Set the clock
if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
writel(0, i2c->regs + S3C2410_IICCON);
dev_err(i2c->dev, "cannot meet bus frequency requiredn");
return -SINGLE SELECT;
}
/* todo - check that the i2c lines aren't being dragged anywhere */
dev_info(i2c->dev, "bus frequency set to %d KHzn", freq);
dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lxn", iicon);
return 0;
}
Register IIC adapter. Before registering the adapter, you need to set the name, algorithm, parent node and other information of the adapter.
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = 50;
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.nr = pdata->bus_num;
ret = i2c_add_numbered_adapter(&i2c->adap);
5. Enable IIC bus response
Need to set bit 7 of the IICCON register to 1.
#define S3C2410_IICCON_ACKEN (1<<7)
static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)
{
unsigned long tmp;
tmp = readl(i2c->regs + S3C2410_IICCON);
writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);
}
6. Enable IIC bus interrupt
Need to set bit 5 of the IICCON register to 1.
#define S3C2410_IICCON_IRQEN (1<<5)
static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)
{
unsigned long tmp;
tmp = readl(i2c->regs + S3C2410_IICCON);
writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
}
7. Get the busy status of the IIC bus
Read bit 5 of the IICSTAT register, 1 indicates busy, 0 indicates idle
#define S3C2410_IICSTAT_BUSBUSY (1<<5)
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
return (iicstat & S3C2410_IICSTAT_BUSBUSY);
8. IIC hardware interrupt
Conditions for IIC interrupt generation
① A hardware interrupt is generated when one byte of data is sent or received.
②Send the slave device address and generate a hardware interrupt after successfully matching the slave device.
③ Bus arbitration failure generates an interrupt.
Under normal circumstances, the timing of IIC interruption
When sending data, the IICDS shift register sends a level signal to SDA one bit at a time, sending the high bit first. After 8 cycles, the data transmission is completed and an interrupt is generated after receiving the ASK signal.
When reading data, the IICDS shift register reads the level signal from the SDA line one bit at a time, reading the high bit first. After 8 cycles, data reception is completed and an interrupt is generated.
3. New IIC data comes during interrupt processing
After a hardware interrupt is generated, the interrupt pending flag will be set, SCL will be pulled low, and IIC transmission will be suspended to prevent a new IIC signal from coming during the interrupt processing.
After waiting for the interrupt function to complete, clear the interrupt pending flag, release SCL, and IIC continues to send and receive data.
Clear the interrupt pending flag and set bit 4 of the IICCON register to 0.
#define S3C2410_IICCON_IRQPEND (1<<4)
tmp = readl(i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
9. IIC bus start
Enable IIC response
s3c24xx_i2c_enable_ack(i2c);
2. Enable IIC data input and output
#define S3C2410_IICSTAT_TXRXEN (1<<4)
stat |= S3C2410_IICSTAT_TXRXEN;
3. Configure the host read/write mode. If reading, the lowest bit or upper bit of the device address is 1.
#define S3C2410_IICSTAT_MASTER_RX (2<<6)
#define S3C2410_IICSTAT_MASTER_TX (3<<6)
if (msg->flags & I2C_M_RD)
{
stat |= S3C2410_IICSTAT_MASTER_RX;
addr |= 1;
}
else
stat |= S3C2410_IICSTAT_MASTER_TX;
writel(stat, i2c->regs + S3C2410_IICSTAT);
4. Write the slave device address to the IICDS register
writeb(addr, i2c->regs + S3C2410_IICDS);
5. Start IIC transmission
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT);
10. IIC bus stops transmitting
1. Need to write 0 to bit 5 of the IICSTART register
unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);
/* stop the transfer */
iicstat &= ~S3C2410_IICSTAT_START;
writel(iicstat, i2c->regs + S3C2410_IICSTAT);
2. Disable IIC interrupt
s3c24xx_i2c_disable_irq(i2c);
11. IIC bus algo
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
s3c24xx_i2c_algorithm is assigned to adap.algo
i2c->adap.algo = &s3c24xx_i2c_algorithm;
This adap is added into the kernel, and the kernel then controls the data transmission of this IIC bus through this adap.
i2c_add_numbered_adapter(&i2c->adap);
12. IIC data transmission function s3c24xx_i2c_xfer
Configuring IIC pins
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev));
2. Call the s3c24xx_i2c_doxfer function to transfer data. Repeat the operation up to 2 times. Exit if successful and continue the operation if failed.
adap->retries = 2。
for (retry = 0; retry < adap->retries; retry++)
{
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
if (ret != -EAGAIN)
return right;
udelay(100);
}
13. IIC bus sends data
The IIC bus calls s3c24xx_i2c_xfer to send data, and the s3c24xx_i2c_xfer function calls s3c24xx_i2c_doxfer.
1. Wait until IIC is not busy.
ret = s3c24xx_i2c_set_master(i2c);
2. Set the IIC status flag
i2c->state = STATE_START;
3. Enable IIC interrupt
s3c24xx_i2c_enable_irq(i2c);
IIC bus starts transmission
s3c24xx_i2c_message_start(i2c, msgs);
5. Enter sleep mode and wait for IIC's waiting queue to wake up.
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
6. Wait for the interrupt to occur. The interrupt function is registered in s3c24xx_i2c_probe.
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);
7. Interrupt occurs, enter interrupt
8. Enter case STATE_START and determine whether the last received 1-bit data is ASK. If no ASK is received, exit IIC transmission (assuming that I2C_M_IGNORE_NAK mode is not set)
if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK))
{
/* ack was not received... */
dev_dbg(i2c->dev, "ack was not receivedn");
s3c24xx_i2c_stop(i2c, -ENXIO);
goto out_ack;
}
9. Determine whether to read or write IIC messages
if (i2c->msg->flags & I2C_M_RD)
i2c->state = STATE_READ;
else
i2c->state = STATE_WRITE;
Here is writing IIC, i2c->state = STATE_WRITE;
10. Determine whether this message is the last one. If so, stop IIC transmission. Otherwise, enter case STATE_WRITE. This is usually used when matching devices with i2c.
if (is_lastmsg(i2c) && i2c->msg->len == 0)
{
s3c24xx_i2c_stop(i2c, 0);
goto out_ack;
}
11. If the data of this msg has not been sent out, continue to send it, one byte at a time, which is to assign a value to the register S3C2410_IICDS.
Previous article:mini2440 linuxi2c driver
Next article:Analysis and study of mini2440 I2C driver (Part 2)
- Popular Resources
- Popular amplifiers
- Learn ARM development(14)
- Learn ARM development(15)
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Learn ARM development(14)
- Learn ARM development(15)
- Analysis of the application of several common contact parts in high-voltage connectors of new energy vehicles
- Wiring harness durability test and contact voltage drop test method
- From probes to power supplies, Tektronix is leading the way in comprehensive innovation in power electronics testing
- From probes to power supplies, Tektronix is leading the way in comprehensive innovation in power electronics testing
- Sn-doped CuO nanostructure-based ethanol gas sensor for real-time drunk driving detection in vehicles
- Design considerations for automotive battery wiring harness
- Do you know all the various motors commonly used in automotive electronics?
- What are the functions of the Internet of Vehicles? What are the uses and benefits of the Internet of Vehicles?
- EMI noise suppression in harsh environments
- Jump-start your new design with the TI motor control software development kit!
- PlanAhead14.7
- Is this a capacitor explosion or something else? It's out of warranty.
- Analog electronics elective test + DC and AC parameters
- Introduction to TI_DSP link command file (*.cmd)
- TI CC1310 sub1G SDK development unique identification number MAC address reading
- FPGA Implementation of ECT Image Reconstruction Algorithm
- Why does the AD21 shortcut key not work after modification?
- [ESP32-Audio-Kit Audio Development Board Review] Basic Use of the Development Board