AM335x 是一款A8的CPU ,其片上的资源是有限的,有时候我们需要外扩的功能有很多,比如 16个串口,4个LAN,IRIG,IO设备等。但是CPU 直接出来的 UART 和 LAN口不够用呀,这时候我们可以直接通过 am335x GPMC 总线上来扩展,如果还是不够用呢,那就需要FPGA 来译码达到更多的设备支持。
AM335X 上面有7个片选,我的设计将CS0 分给了 16个串口,CS3分给了IRIG 和 Nodeid. GPMC 总线上是支持 8bit 和 16 bit 模式的,正常情况下,如果选用8 bit , 那8位数据线一定是接到低8位的,否则读写错误是会出错的,如果选用16bit,要用到16位的数据线,一次读写可以进行读写2个字节的数据,这样就很方便。不过有时间我们引脚资源会很紧张,可能16位数据线上会被其他引脚给复用到,这时候我们就需要考虑到底如何配置才能在总线数据位不足的情况,正常读写呢?
下面这张图基本就将整个逻辑设计都讲清楚了
我们可以在 board-generic.c 中进行初始化配置总线的时序,然后注册 UART 设备,最后会与 8250的串口驱动进行匹配。
//------------------------------------------------------------------------------
// += CS Setting
//------------------------------------------------------------------------------// CS0 for TL16C752C
#define TL16C752C_UARTCLK (14745600)// CS0 A16/A15/A12/A9 4 address line --> 2^4=16 devices
#define CONFIG_TL16C752C_0_BASE 0x01000000
#define CONFIG_TL16C752C_1_BASE 0x01000200
#define CONFIG_TL16C752C_2_BASE 0x01001000
#define CONFIG_TL16C752C_3_BASE 0x01001200
#define CONFIG_TL16C752C_4_BASE 0x01008000
#define CONFIG_TL16C752C_5_BASE 0x01008200
#define CONFIG_TL16C752C_6_BASE 0x01009000
#define CONFIG_TL16C752C_7_BASE 0x01009200#define CONFIG_TL16C752C_8_BASE 0x01010000
#define CONFIG_TL16C752C_9_BASE 0x01010200
#define CONFIG_TL16C752C_10_BASE 0x01011000
#define CONFIG_TL16C752C_11_BASE 0x01011200
#define CONFIG_TL16C752C_12_BASE 0x01018000
#define CONFIG_TL16C752C_13_BASE 0x01018200
#define CONFIG_TL16C752C_14_BASE 0x01019000
#define CONFIG_TL16C752C_15_BASE 0x01019200static struct plat_serial8250_port tl16c752c_platform_data[] = {{.mapbase = CONFIG_TL16C752C_0_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_1_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_2_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_3_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_4_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_5_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_6_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_7_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_8_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_9_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_10_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_11_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_12_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_13_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_14_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{.mapbase = CONFIG_TL16C752C_15_BASE,.irqflags = IRQF_TRIGGER_HIGH,.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_SHARE_IRQ,.iotype = UPIO_MEM,.regshift = 6,.uartclk = TL16C752C_UARTCLK},{}
};static struct platform_device tl16c752c_device = {.name = "serial8250",.id = PLAT8250_DEV_PLATFORM,.dev = {.platform_data = tl16c752c_platform_data,},
};#define TL16C752C_SHARE_IRQ_GPIO GPIO_TO_PIN(0, 2)static struct gpio tl16c752c_uart_gpios[] __initdata = {{ TL16C752C_SHARE_IRQ_GPIO, GPIOF_IN, "uart_irq" },
};static int __init tl16c752c_uarts_init(void)
{int ret;ret = gpio_request_array(tl16c752c_uart_gpios, ARRAY_SIZE(tl16c752c_uart_gpios));if (ret < 0) {printk(KERN_ERR "Failed to request GPIO for UART IRQ\n");return -1;}tl16c752c_platform_data[0].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[1].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[2].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[3].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[4].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[5].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[6].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[7].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[8].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[9].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[10].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[11].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[12].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[13].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[14].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);tl16c752c_platform_data[15].irq = gpio_to_irq(TL16C752C_SHARE_IRQ_GPIO);platform_device_register(&tl16c752c_device);return 0;
}// TL16C752C CS0 config
#define TL16C752C_GPMC_CONFIG1 0x00001000 //16 bit width
//#define TL16C752C_GPMC_CONFIG1 0x00000000 //8 bit width// Configure GPMC registers for TL16C752D UART
static void gpmc_tl16c752c_config(void)
{struct gpmc_timings t;u32 tl16c752c_gpmc_config[6] = {0,};memset(&t, 0, sizeof(t));/* Read timings */t.cs_on = 0;t.adv_on = t.cs_on;t.adv_rd_off = 1;t.oe_on = t.adv_rd_off + 1;t.oe_off = t.oe_on + 6;t.cs_rd_off = t.oe_off + 2;t.access = t.cs_rd_off;t.rd_cycle = t.access + 1;/* Write timings */t.adv_wr_off = 1;t.we_on = t.adv_wr_off + 1;t.we_off = t.we_on + 6;t.cs_wr_off = t.we_off + 1;t.wr_cycle = t.cs_wr_off;// GPMC_CONFIG1tl16c752c_gpmc_config[0] = TL16C752C_GPMC_CONFIG1;// GPMC_CONFIG2 CS timingtl16c752c_gpmc_config[1] = t.cs_on | (t.cs_rd_off << 8) | (t.cs_wr_off << 16);// GPMC_CONFIG3 nADV / ALE timingtl16c752c_gpmc_config[2] = t.adv_on | (t.adv_rd_off << 8) | (t.adv_wr_off << 16);// GPMC_CONFIG4 nWE / nOE timingtl16c752c_gpmc_config[3] = t.oe_on | (t.oe_off << 8) | (t.we_on << 16) | (t.we_off << 24);// GPMC_CONFIG5 RdAccessTime / CycleTime timingtl16c752c_gpmc_config[4] = t.rd_cycle | (t.wr_cycle << 8) | (t.access << 16) | (t.page_burst_access << 24);// GPMC_CONFIG6 WrAccessTime / WrDataOnADmuxBus timingtl16c752c_gpmc_config[5] = (( ((u32)1) << 0 ) // Bus turn around latency between successive accesses to the same CS (read to write) or to a different CS (read to read and read to write)| ( ((u32)1) << 6 ) // Add CYCLE2CYCLEDELAY between successive accesses to a different CS (any access type)| ( ((u32)1) << 7 ) // Add CYCLE2CYCLEDELAY between successive accesses to the same CS (any access type)| ( ((u32)4) << 8 ) // CYCLE2CYCLEDELAY Chip-select high pulse delay between successive accesses (0..15 clk)| ( ((u32)8) << 16 ) // WRDATAONADMUXBUS (1 clk after ADV off, at which address is latched)//| ( ((u32)1) << 24 ) // Write access time [clk 0..31] from start of cycle -> used as wait time in async mode);setup_gpmc_cs_config(tl16c752c_gpmc_config, 0, 0x01000000, GPMC_SIZE_16M);
}