本文允许转载,但请注明出处:http://blog.csdn.net/u010944778/article/details/45271117
我原本只是想完成DM9000移植,而后想弄清楚其中base address的数据,继而再又了解了CPU与不同位宽外设间的连接。现在算是稍微了解了一些参数的由来吧。首先贴上看的s3c2440的datasheet以及dm9000的手册图:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
没注意看手册的可以回去自己看一下手册,通过上面的我们可以知道最基本的三点:(1)DM9000的I/Obase address 300H~370H;(2)我们接了片选,且可以通过CMD的状态来选择发送数据或地址;(3) DM9000的AEN与s3c2440的NGCS4相连。
下面我们来看看S3c2440的手册图:
/************************************************************************************************************************************************/
如图可知:网卡的内存区域在BANK4,也就是从地址0x20000000开始。另外DM9000可以直接与ISA总线相连,也可以与大多数CPU相连。在这里,我们当然是要让DM9000与s3c2440相连接了。DM9000对 外来说只有两个端口——地址口和数据口,地址口用于输入内部寄存器的地址,而数据口则完成对某一寄存器的读写。DM9000的CMD引脚用来区分这两个端 口,当CMD引脚为0时,DM9000的数据线上传输的是寄存器地址,当CMD引脚为1时,传输的是读写数据。我们把DM9000的A8和A9接为高电 平,把A4~A7接为低电平,并且把DM9000的AEN接到s3c2440的nGCS4引脚上,则DM9000的端口基址为0x20000300,如果 再把DM9000的CMD引脚接到s3c2440的ADDR2引脚上,则我们就可以定义DM9000的这两个端口地址,它们分别为:
#define DM_ADDR_PORT (*((volatile unsigned short *) 0x20000300))//地址口
#define DM_DATA_PORT (*((volatile unsigned short *) 0x20000304))//数据口
DM9000网卡通过看datasheet可知他IO基地址有8种基地址,它们分别是300H,310H,320H,330H,340H,350H,360H和370H。遵循datasheet的公式:IO base = (strap pin value of TXD[2:0]) * 10H + 300H
而有些应用场合会用到多片DM9000网卡,因此需要根据每个网卡的基地址来选择需要操作的网卡,SA4—9是s3c2440的地址总线,用来选择DM9000,datasheet说明当SA9和SA8引脚高电平,SA7和AEN处于低电平,SA6~4引脚状态与TXD[2:0](strap pins)引脚状态匹配时,该DM9000网卡就会被选中。
=================================================================================================
注意:我们所说的地址:0x20000000 和 0x20000004 是由ARM芯片的地址引脚决定的,注意,这个地址表示是用的16进制,那么,0x20000004的每一位的值都可以是0~f,它由ARM的4根地址线的电平高,比如最低的一位,就是由 ADDR3~ADDR0 这4个引脚的电平决定,如果 ADDR3~ADDR0 = 1111,则该位为f ,如果 LADDR3~LADDR0 = 0100,则该位为 4。因此,如果将 DM9000的CMD引脚接到s3c2440的ADDR2,由于CMD引脚的高低电平决定地址口和数据口,那么,ADDR2为0时,访问的就是地址口,所以地址口的起始地址为 ARRD2为0的情况,即0x20000000 ;ADDR2为1时,(LADDR3~LADDR0 = 0100)访问的就是数据口,所以数据口的地址即 0x20000004。
下面再上一张图:
可以看到,我们的S3C2440CPU其实可以接不同位宽的外设,CPU认为一个地址对应一个字节的。而我们的外设为16位的DM9000网卡,也就是会说对于网卡来说它认为一个地址线是对应两个字节,倘若外设为SDRAM32位则是4个字节,那么cpu与连接的外设如何通过那一根地址线互相协调获取字节数据呢?
最重要的一点就是:牛逼的内存控制器可以从所获的单元中自动选取所需要的地址。
所以当cpu要获得16位外设地址3的一个字节时,是先获取外设第1个地址单元(两个字节),然后从自动从这个单元的两个字节中选取具体所要的那个字节。而不论是获取地址2(0X0000,0010)或者是地址3(0X0000,0011)这两个字节都存放在同一个地址单元。那么cpu给内存控制器发送(0X0000,0010)或(0X0000,0011)获取指令后,内存控制器通过地址线ADDR1,也就是(0x0000,001x)来获取对应的地址单元后再选择即可。所以ADDR0线即第0地址位不论是1或者是0对于内存控制器来说都无所谓,他只要获取到地址单元,再负责把CPU老板告诉他的具体字节(即地址2或者地址3的那个字节)从地址单元中获取出来就完成任务。 同理;对于SDRAM来说,对外是32位的。对于内存控制器来说:0(0x0000,0000)~3(0x0000,0011)为一个单元,所以ADDR0和ADDR1都不用具体获取,CPU与SDRAM之间直接从ADDR2(0x0000,0x00)开始接,选择对应的地址单元后再获取具体的字节即可。
下面是简陋的图解:
具体的可以看韦东山老师的视频《怎么看原理图之内存类接口》,看完肯定会更理解些。
下面的修改驱动程序就很简单了。
--- mach-smdk2440_o.c 2015-04-24 11:39:20.492017397 +0800 +++ mach-smdk2440.c 2015-04-24 12:24:04.000000000 +0800 @@ -22,6 +22,7 @@ #include <linux/serial_core.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/dm9000.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> @@ -46,6 +47,69 @@ #include <plat/cpu.h> #include <plat/common-smdk.h> +#include <sound/s3c24xx_uda134x.h> +#include <mach/gpio-nrs.h> + +#define MACH_SMDK2440_DM9K_BASE (S3C2410_CS4 + 0x300) +/* audio */ + +static struct platform_device uda1340_codec = { + .name = "uda134x-codec", + .id = -1, +}; + + + +static struct s3c24xx_uda134x_platform_data smdk2440_audio_pins = { + .l3_clk = S3C2410_GPB(4), + .l3_mode = S3C2410_GPB(2), + .l3_data = S3C2410_GPB(3), + .model = UDA134X_UDA1341 +}; + +static struct platform_device smdk2440_audio = { + .name = "s3c24xx_uda134x", + //.id = 0, + .dev = { + .platform_data = &smdk2440_audio_pins, + }, +}; + + + +/*dm9000 */ +static struct resource smdk2440_dm9k_resource[] = { + [0] = { + .start = MACH_SMDK2440_DM9K_BASE, + .end = MACH_SMDK2440_DM9K_BASE + 3, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = MACH_SMDK2440_DM9K_BASE + 4, + .end = MACH_SMDK2440_DM9K_BASE + 7, + .flags = IORESOURCE_MEM + }, + [2] = { + .start = IRQ_EINT7, + .end = IRQ_EINT7, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, + } +}; + +static struct dm9000_plat_data smdk2440_dm9k_pdata = { + .flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM), +}; + +static struct platform_device smdk2440_device_eth = { + .name = "dm9000", + .id = -1, + .num_resources = ARRAY_SIZE(smdk2440_dm9k_resource), + .resource = smdk2440_dm9k_resource, + .dev = { + .platform_data = &smdk2440_dm9k_pdata, + }, +}; + static struct map_desc smdk2440_iodesc[] __initdata = { /* ISA IO Space map (memory space selected by A24) */ @@ -155,6 +219,11 @@ &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, + &smdk2440_device_eth, + &smdk2440_audio, + &uda1340_codec, + &samsung_asoc_dma, + &s3c_device_rtc, //额,RTC时钟使能真的只要添加一句话..加进去后修改内核配置。 }; static void __init smdk2440_map_io(void)
说到时钟,在Linux下的时间分为两种:系统时间与硬件时间(RTC)
粗略的说系统时间就是我们运行系统时,用date命令所能查看到的时间。
硬件时间:就是开发板上RTC芯片上面的时间,开发板上有个特定的电池为其供电,即使总电源断掉也不影响。
linux系统开机时,会从RTC中读取当前时间,作为系统时间,从此以后系统时间独立tick,即使你用date命令修改了系统时间,RTC时间也是不会收到影响的。如果此时关机,下次的系统时间还是不准确。要想将系统时间保存到硬件时间。我们可以通过date和hwclock配合来对时间随心的修改。
通过# hwclock --help 我们可以看到:
hwclock -r 显示硬件时钟
hwclock -s 用RTC时间替换掉当前的系统时间,也就是将RTC时间设置为当前系统的时间。
hwclock -w 将当前系统的时间设置为RTC时间。
Reference:http://blog.csdn.net/fulinus/article/details/41171879
还一段话的参考链接找不到了,如有要求请博主与我联系。