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

#define S3C24XX_GPIO_BASE(x) S3C2410_GPIO_BASE(x)

#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)

The following content is defined in /linux-2.6.32.2/arch/arm/plat-s3c24xx/include/plat/map.h

#define S3C24XX_VA_GPIO ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)

The following content is defined in /linux-2.6.32.2/arch/arm/mach-s3c2410/include/mach/map.h

#define S3C24XX_PA_GPIO S3C2410_PA_GPIO

#define S3C24XX_PA_UART S3C2410_PA_UART

The following content is defined in /linux-2.6.32.2/arch/arm/plat-s3c24xx/include/plat/map.h

#define S3C2410_PA_GPIO (0x56000000)

#define S3C2410_PA_UART (0x50000000)

The following content is defined in linux-2.6.32.2/arch/arm/plat-s3c24xx/include/plat/map.h

#define S3C24XX_VA_UART S3C_VA_UART

The following content is defined in linux-2.6.32.2/arch/arm/plat-s3c/include/plat/map.h

#define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */

The following content is defined in linux-2.6.32.2/arch/arm/plat-s3c/include/plat/Map-base.h

#ifndef __ASSEMBLY__

#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))

#else

#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))

#endif

#define S3C_ADDR_BASE (0xF4000000)

Here we have found all the macros that define S3C24XX_GPIO_BASE(x). From here we can see that the file definitions in Linux are scattered, which is also a headache for many beginners.

S3C24XX_VA_GPIO=((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)

=((0x56000000 - 0x50000000) + (0xF4000000 + 0x01000000))

= (0x06000000 + 0xF5000000)

= (0xFB000000)

#define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */

This sentence shows that the base address of the virtual address is offset by 0x01000000

Explain the following two:

#define S3C_ADDR_BASE (0xF4000000) The first virtual address of all registers

#S3C24XX_VA_GPIO GPIO virtual address first address

The value of S3C2410_GPB(5) is 37 according to the above calculation.

S3C24XX_GPIO_BASE(S3C2410_GPB(5))= S3C24XX_GPIO_BASE(37)

=((((37) & ~31) >> 1) + S3C24XX_VA_GPIO)

=((((37) & ~31) >> 1) + (0xFB000000))= 0xFB000010

So in the end *base = 0xFB000010, this is the virtual address of GPBCON, looking at its manual we know that the physical address of GPBCON is 0X56000010, the virtual address of GPACON is 0xFB000000, looking at its manual we know that the physical address of GPACON is 0X56000000, the following program accesses this virtual address to access the control register to configure the I/O port, at this time someone will ask, I access this virtual address, why can I access the physical address? This is the mapping problem of the virtual address and physical address of the MMU, the problem about this is more complicated, I found an article to introduce this virtual-real mapping relationship, see this article "Realization of Virtual-Real Mapping of Register Addresses under 2410" http://www.linuxidc.com/Linux/2013-01/77977.htm on this site , because the content is more, I will not introduce it here.


One more question

What does ((((pin) & ~31) >> 1) mean? This mainly depends on understanding. As mentioned above, each group of ports is defined as 32. ((pin) & ~31) is equivalent to clearing all the lower five bits to zero, and the range that the fifth bit can represent is exactly 32, which is a bit like matching the size of 32. If the obtained value is shifted right by 5 bits, the obtained value (set as ppvalue) can exactly represent which group of I/O ports it is. Why is it shifted right by 1 bit here? Let's see

The physical addresses of several GPXCON registers.

GPACON 0X56000000

GPBCON 0X56000010

GPCCON 0X56000020

GPDCON 0X56000030

The same goes for the rest. We can see the rule of this I/O port control register. If we shift ppvalue left by four bits and add the GPIO virtual base address, we can get the virtual address of the GPXCON control register. By the way, the mapping between the virtual and real addresses here only differs by an offset.

Analysis: if (pin < S3C2410_GPIO_BANKB)

The definition of S3C2410_GPIO_BANKB is as follows

#define S3C2410_GPIO_BANKA (32*0)

#define S3C2410_GPIO_BANKB (32*1)

#define S3C2410_GPIO_BANKC (32*2)

#define S3C2410_GPIO_BANKD (32*3)

#define S3C2410_GPIO_BANKE (32*4)

#define S3C2410_GPIO_BANKF (32*5)

#define S3C2410_GPIO_BANKG (32*6)

#define S3C2410_GPIO_BANKH (32*7)

Used to determine whether this I/O port is a GPA port. This is to distinguish GPA from other groups of ports, because the operation of the GPA control register is a little different from other ports. Also note that it has no input function. You can get a better understanding by looking at the datasheet.

Analysis: S3C2410_GPIO_OFFSET(pin)

#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31) //Use this macro to get the offset

if (pin < S3C2410_GPIO_BANKB) { //Judge whether the I/O port belongs to GPA, mask = 1 << S3C2410_GPIO_OFFSET(pin); //Set the mask code

} else {

mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; //Set the mask code

}

Analysis: local_irq_save(flags);

This is used in pairs with local_irq_restore(flags); that appears below to turn off and on interrupts, and store the interrupt flag in flags.

Analysis: __raw_readl(base + 0x00); __raw_writel(con, base + 0x00);

con = __raw_readl(base + 0x00); //Read control register data

con &= ~mask; //mask the corresponding bit

con |= function; //Set the bit to be set

__raw_writel(con, base + 0x00); //Write the changed data back to the control register

The above are two function macros, defined as follows

#define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v))
#define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
PS(ZXX): First check whether the pointer a is legal, and then write the value v to the space pointed to by a.

The three types correspond to char, short, and int respectively.
#define __raw_readb(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a))
#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
#define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a))
PS(ZXX): First check whether the pointer a is legal, and then read the value of the space pointed to by a.
The three types correspond to char, short, and int respectively .

5.Analysis s3c2410_gpio_setpin(led_table[i], 0);

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)

{

void __iomem *base = S3C24XX_GPIO_BASE(pin);

unsigned long offs = S3C2410_GPIO_OFFSET(pin);

unsigned long flags;

unsigned long dat;

local_irq_save(flags);

dat = __raw_readl(base + 0x04);

dat &= ~(1 << offs);

dat |= to << offs;

__raw_writel(dat, base + 0x04);

local_irq_restore(flags);

}

With the above analysis of s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]), the above codes are similar. Let's just talk about __raw_readl(base + 0x04);. This is to operate the data register. Looking at the datasheet, you will know that the address value of GPXDAT in each group is 4 greater than the address value of GPXCON.

Summary: This is the end of the LED driver analysis. Although the program is small, it involves a lot of things. It is mainly for beginners to feel confused. It is mainly helpful for beginners. There are several versions of this driver, and the kernel is constantly changing. I also posted another article about the LED driver, but the structure data involved is more complicated than this one. You can find it in my blog. If you understand it, you can still learn a lot.


[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号