Classic analysis of mini2440 LED driver

Publisher:悠闲之旅Latest update time:2024-06-19 Source: elecfansKeywords:mini2440  led  driver Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

Linux kernel is 2.6.32.2

Source code analysis tool source insight

Preface: It is very simple to operate several GPIO ports in a bare metal machine, just configure the control registers and data registers, but it still takes some effort to implement the same function in a Linux system.

The following is the source code of the driver.

#include <……>
#define DEVICE_NAME "leds" //Device name

static unsigned long led_table [] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};

static unsigned int led_cfg_table [] = {
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};

static int sbc2440_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > 4) {
return -EINVAL;
}
s3c2410_gpio_setpin(led_table[ arg], !cmd);
return 0;
default:
return -EINVAL;
}
}

static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = sbc2440_leds_ioctl,
};

static struct miscdevice misc = { //Miscellaneous device structure
.minor = MISC_DYNAMIC_MINOR, //Minor device number
.name = DEVICE_NAME,
.fops = &dev_fops,
};

static int __init dev_init(void)
{
int ret;

int i;

for (i = 0; i < 4; i++) {
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i], 0);
}

ret = misc_register(&misc); //Miscellaneous device registration

printk (DEVICE_NAME"tinitializedn");

return ret;
}

static void __exit dev_exit(void)
{
misc_deregister(&misc); //Misc device deregistration
}

module_init(dev_init); //Module loading function

module_exit(dev_exit); //Module uninstall function
MODULE_LICENSE("GPL"); //Certificate
MODULE_AUTHOR("FriendlyARM Inc."); //Author

The above lists the source code of the MINI2440led driver with simple comments, and the header file has been omitted. For the above driver, we mainly analyze the overall framework of this driver and some important functions and macro definitions involved in it.


1. Misc equipment

Misc devices are translated into miscellaneous devices or mixed devices. Miscellaneous devices have been specifically introduced in one of my articles. Here, I will explain them in detail based on this driver. Linux device drivers are mainly divided into character devices, block devices, and network devices. However, the above drivers do not belong to the common forms of the above three categories. We call the above drivers miscellaneous devices. What are miscellaneous devices? Linux contains many types of devices, but no matter how they are classified, they can not completely summarize all devices. We classify those that do not belong to the above three forms into a category called miscellaneous devices. Miscellaneous devices share the same major device number (MISC_MAJOR, which is 10), but the minor device numbers are different. All miscellaneous devices form a linked list. When accessing the device, the kernel finds the corresponding device according to this device number, and then calls the various functions registered in its corresponding file_operations structure. For miscellaneous devices, the Linux kernel specifically provides such a structure miscdevice, which is very inclusive. The structure is as follows:

The following files are defined in /linux2.6.32.2/include/linux/Miscdivice.h

struct miscdevice {

int minor;

const char *name;

const struct file_operations *fops;

struct list_head list;

struct device *parent;

struct device *this_device;

const char *nodename;

mode_t mode;

};

The miscdevice registration and deregistration functions provided are as follows.

int misc_register(struct miscdevice * misc);

int misc_deregister(struct miscdevice *misc);

In fact, the essence of miscellaneous devices is still character devices, but the device driver has been encapsulated. The main body of miscellaneous devices is still the implementation of the file_operations structure, so it is not mysterious. But it should be noted that miscellaneous devices are often used in embedded systems. The basic form of their use is shown in this driver program and will not be described again.

2. List of GPIO ports corresponding to LED

static unsigned long led_table [] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};

The LED device driver mainly performs s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); (configuration pin function) on the above ports.

s3c2410_gpio_setpin(led_table[i], 0); (set pin level status)

Operation. Let's first clarify the definitions of these ports.

The following files are defined in /arch/arm/mach-s3c2410/include/mach/gpio-nrs.h

/* S3C2410 GPIO number definitions. */

#define S3C2410_GPA(_nr) (S3C2410_GPIO_A_START + (_nr))
#define S3C2410_GPB(_nr) (S3C2410_GPIO_B_START + (_nr))
#define S3C2410_GPC(_nr) (S3C2410_GPIO_C_START + (_nr))
#define S3C2410_GPD(_nr) (S3C2410_GPIO_D_START + (_nr ))
#define S3C2410_GPE(_nr) (S3C2410_GPIO_E_START + (_nr))
#define S3C2410_GPF(_nr) (S3C2410_GPIO_F_START + (_nr))
#define S3C2410_GPG(_nr) (S3C2410_GPIO_G_START + (_nr))
#define S3C2410_GPH(_nr) (S3C2410_GPIO_H_START + (_nr))

enum s3c_gpio_number {
S3C2410_GPIO_A_START = 0,
S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A),
S3C2410_GPIO_C_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_B),
S3C2410_GPIO_D_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_C),
S3C2410_GPIO_E_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_D),
S3C2410_GPIO_F_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_E),
S3C2410_GPIO_G_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_F),
S3C2410_GPIO_H_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_G),
};


#define S3C2410_GPIO_NEXT(__gpio)
((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)

CONFIG_S3C_GPIO_SPAC is a kernel configuration option, which can be found in .config. My configuration is:

CONFIG_S3C_GPIO_SPACE = 0

Therefore, taking S3C2410_GPB(5) as an example, its macro expansion is:

S3C2410_GPIO_NEXT(S3C2410_GPIO_A) +5 =>

(S3C2410_GPIO_A_START + S3C2410_GPIO_A_NR + CONFIG_S3C_GPIO_SPACE + 0) + 5 =>

Obviously, S3C2410_GPB(5) is the IO offset of the current GPB from the first address of GPA + the number of GPAs + the offset of GPB, that is,

0+32+5=37, same

S3C2410_GPB(0) is equivalent to 32

S3C2410_GPB(5) Equivalent to 37

S3C2410_GPB(6) Equivalent to 38

S3C2410_GPB(7) Equivalent to 39

S3C2410_GPB(8) is equivalent to 40

At this point we should understand that the function of this macro is to number the ports. For GPA, the port number range is 0~31, and the port number range of GPB is 32~63, and so on. Of course, not all numbers here are used. Because the number of ports in each group is different, 32 ports are defined for each group to ensure that each group has enough ports. After getting the port number, the result of dividing it by 32 can determine which group the port belongs to. For example, if the port number is 38, it is divided by 32 to get 1, and it knows that it belongs to the I/O port in GPB. This will be seen in further analysis later.


3. LED corresponding port status list analysis

static unsigned int led_cfg_table [] = {

S3C2410_GPIO_OUTPUT,

S3C2410_GPIO_OUTPUT,

S3C2410_GPIO_OUTPUT,

S3C2410_GPIO_OUTPUT,

};

S3C2410_GPIO_OUTPUT is defined in mach/regs-gpio.h

Here we mainly look at the last two digits, which indicate the status of the port.

00 represents input, 01 represents output, 10 represents function 2, and 1 represents function 3. Please note that GPA does not have an input function.

#define S3C2410_GPIO_LEAVE (0xFFFFFFFF)

#define S3C2410_GPIO_INPUT (0xFFFFFFF0) /* not available on A */

#define S3C2410_GPIO_OUTPUT (0xFFFFFFF1)

#define S3C2410_GPIO_IRQ (0xFFFFFFF2) /* not available for all */

#define S3C2410_GPIO_SFN2 (0xFFFFFFF2) /* bank A => addr/cs/nand */

#define S3C2410_GPIO_SFN3 (0xFFFFFFF3) /* not available on A */

4. s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]) analysis

The function source code is defined in linux/arch/arm/plat-s3c24xx/gpio.c

Function prototype:

void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)

{

void __iomem *base = S3C24XX_GPIO_BASE(pin);

unsigned long mask;

unsigned long con;

unsigned long flags;

if (pin < S3C2410_GPIO_BANKB) { //Judge whether the I/O port belongs to GPA,

mask = 1 << S3C2410_GPIO_OFFSET(pin);

} else {

mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;

}

switch (function) { //Perform corresponding operations according to the function of the pin to be set

case S3C2410_GPIO_LEAVE:

mask = 0;

function = 0;

break;

case S3C2410_GPIO_INPUT:

case S3C2410_GPIO_OUTPUT:

case S3C2410_GPIO_SFN2:

case S3C2410_GPIO_SFN3:

if (pin < S3C2410_GPIO_BANKB) {

function -= 1;

function &= 1;

function <<= S3C2410_GPIO_OFFSET(pin);

} else {

function &= 3;

function <<= S3C2410_GPIO_OFFSET(pin)*2;

}

}

/* modify the specified register wwith IRQs off */

local_irq_save(flags);

con = __raw_readl(base + 0x00);

con &= ~mask;

con |= function;

__raw_writel(con, base + 0x00);

local_irq_restore(flags);

}

Let's take a look at the main framework first. The main body uses switch(function) to find the corresponding function to be set and perform the corresponding operation. This is probably easy to understand. The following will explain in detail some of the difficult to understand parts.

For void __iomem *base = S3C24XX_GPIO_BASE(pin); first look at its implementation

The following content is defined in /linux-2.6.32.2/arch/arm/mach-s3c2410includemachRegs-gpio.h

[1] [2]
Keywords:mini2440  led  driver Reference address:Classic analysis of mini2440 LED driver

Previous article:Mini2440 Norflash driver transplantation process
Next article:How to re-burn supervivi on mini2440

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号