基於OMAPL138的linux平台8250快速串口實現--UART+EDMA__linux

來源:互聯網
上載者:User

  本文源碼基於dvsdk_omapl138-evm_04_03_00_06_setuplinux程式包裡的linux3.3.0版本的核心。實現EDMA支援UART功能主要修改dma和8250的源碼。
  首先為OMAPL138的arm和DSP分配dma通道資源,此處理器有三個UART,UART1佔用EDMA0cc0的channel8(RX)、channel9(TX)通道以及slot8(RX)和slot9(TX);
UART2佔用EDMA0cc0的channel12(RX)、channel13(TX)通道以及slot12(RX)和slot13(TX);UART2佔用EDMA0cc0的channel30(RX)、channel31(TX)通道以及slot30(RX)和slot31(TX)。我的板子其餘的EDMA資源全部分給DSP。因此可以設定dma的通道資源分派結構體為:
static const s16 da850_dma0_rsv_chans[][2] = {
/* (offset, number) */
       { 0, 32},//表示所有的endm0cc0通道都已經使用,其中通道8、9、12、13、30、31是後邊8250中通過直接擷取通道時使用。
{-1, -1}
};


static const s16 da850_dma0_rsv_slots[][2] = {
/* (offset, number) */
{0,   8},//表示slot0~slot7分配給DSP,如何?在下邊分析
{10,  2},//表示slot10、slot11分配給DSP
{14, 16},//表示slot14~slot30分配給DSP
{32, 96},//剩餘的slot全部分給DSP
{-1, -1}//此結構體表示slot8、9、12、13、30、31預留給arm
};


static const s16 da850_dma1_rsv_chans[][2] = {
/* (offset, number) */
{0 , 32},//endm1cc0的32個通道全留給DSP
{-1, -1}
};


static const s16 da850_dma1_rsv_slots[][2] = {
/* (offset, number) */
{0 ,128},//endm1cc0所有的slot全部留給DSP
{-1, -1}
};
現在分析如何?通道的分配:


static int __init edma_probe(struct platform_device *pdev)
{
struct edma_soc_info **info = pdev->dev.platform_data;
const s8 (*queue_priority_mapping)[2];
const s8 (*queue_tc_mapping)[2];
int i, j, off, ln, found = 0;
int status = -1;
const s16 (*rsv_chans)[2];
const s16 (*rsv_slots)[2];
int irq[EDMA_MAX_CC] = {0, 0};
int err_irq[EDMA_MAX_CC] = {0, 0};
struct resource *r[EDMA_MAX_CC] = {NULL};
resource_size_t len[EDMA_MAX_CC];
char res_name[10];
char irq_name[10];


if (!info)
return -ENODEV;
#if 0
    edma30_tc0cfg_reg = ioremap(EDMA30_TC0_CFG, 4);
    if (!edma30_tc0cfg_reg) {
status = -EBUSY;
goto fail2;
}


    edma_set_buswidth(1);
#endif
for (j = 0; j < EDMA_MAX_CC; j++) {
sprintf(res_name, "edma_cc%d", j);
r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM,
res_name);
if (!r[j] || !info[j]) {
if (found)
break;
else
return -ENODEV;
} else {
found = 1;
}


len[j] = resource_size(r[j]);


r[j] = request_mem_region(r[j]->start, len[j],
dev_name(&pdev->dev));
if (!r[j]) {
status = -EBUSY;
goto fail1;
}


edmacc_regs_base[j] = ioremap(r[j]->start, len[j]);
if (!edmacc_regs_base[j]) {
status = -EBUSY;
goto fail1;
}


edma_cc[j] = kzalloc(sizeof(struct edma), GFP_KERNEL);
if (!edma_cc[j]) {
status = -ENOMEM;
goto fail1;
}


edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel,
EDMA_MAX_DMACH);
edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot,
EDMA_MAX_PARAMENTRY);
edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc,
EDMA_MAX_CC);


edma_cc[j]->default_queue = info[j]->default_queue;


dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n",
edmacc_regs_base[j]);


for (i = 0; i < edma_cc[j]->num_slots; i++)
memcpy_toio(edmacc_regs_base[j] + PARM_OFFSET(i),
&dummy_paramset, PARM_SIZE);


/* Mark all channels as unused */
memset(edma_cc[j]->edma_unused, 0xff, //此處先將dma的所有通道設為未使用
sizeof(edma_cc[j]->edma_unused));


if (info[j]->rsv) {


/* Clear the reserved channels in unused list */
rsv_chans = info[j]->rsv->rsv_chans;
if (rsv_chans) {
for (i = 0; rsv_chans[i][0] != -1; i++) {
off = rsv_chans[i][0];
ln = rsv_chans[i][1];
clear_bits(off, ln,
edma_cc[j]->edma_unused);        //此處將前邊結da850_dma0_rsv_chans、da850_dma1_rsv_chans構體中的通道標記為已經使用。
}
}


/* Set the reserved slots in inuse list */
rsv_slots = info[j]->rsv->rsv_slots;
if (rsv_slots) {
for (i = 0; rsv_slots[i][0] != -1; i++) {
off = rsv_slots[i][0];
ln = rsv_slots[i][1];
set_bits(off, ln,
edma_cc[j]->edma_inuse);          //此處將前邊結da850_dma0_rsv_slots、da850_dma1_rsv_slots構體中的slot標記為已經使用。
}
}
}


sprintf(irq_name, "edma%d", j);
irq[j] = platform_get_irq_byname(pdev, irq_name);
edma_cc[j]->irq_res_start = irq[j];
status = request_irq(irq[j], dma_irq_handler, 0, "edma",
&pdev->dev);
if (status < 0) {
dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n",
irq[j], status);
goto fail;
}


sprintf(irq_name, "edma%d_err", j);
err_irq[j] = platform_get_irq_byname(pdev, irq_name);
edma_cc[j]->irq_res_end = err_irq[j];
status = request_irq(err_irq[j], dma_ccerr_handler, 0,
"edma_error", &pdev->dev);
if (status < 0) {
dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n",
err_irq[j], status);
goto fail;
}


for (i = 0; i < edma_cc[j]->num_channels; i++)
map_dmach_queue(j, i, info[j]->default_queue);


queue_tc_mapping = info[j]->queue_tc_mapping;
queue_priority_mapping = info[j]->queue_priority_mapping;


/* Event queue to TC mapping */
for (i = 0; queue_tc_mapping[i][0] != -1; i++)
map_queue_tc(j, queue_tc_mapping[i][0],
queue_tc_mapping[i][1]);


/* Event queue priority mapping */
for (i = 0; queue_priority_mapping[i][0] != -1; i++)
assign_priority_to_queue(j,
queue_priority_mapping[i][0],
queue_priority_mapping[i][1]);


/* Map the channel to param entry if channel mapping logic
* exist
*/
if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST)
map_dmach_param(j);


for (i = 0; i < info[j]->n_region; i++) {
edma_write_array2(j, EDMA_DRAE, i, 0, 0x0);
edma_write_array2(j, EDMA_DRAE, i, 1, 0x0);
edma_write_array(j, EDMA_QRAE, i, 0x0);
}
arch_num_cc++;
}


if (tc_errs_handled) {
status = request_irq(IRQ_TCERRINT0, dma_tc0err_handler, 0,
"edma_tc0", &pdev->dev);
if (status < 0) {
dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n",
IRQ_TCERRINT0, status);
return status;
}
status = request_irq(IRQ_TCERRINT, dma_tc1err_handler, 0,
"edma_tc1", &pdev->dev);
if (status < 0) {
dev_dbg(&pdev->dev, "request_irq %d --> %d\n",
IRQ_TCERRINT, status);
return status;
}
}


return 0;


fail:
for (i = 0; i < EDMA_MAX_CC; i++) {
if (err_irq[i])
free_irq(err_irq[i], &pdev->dev);
if (irq[i])
free_irq(irq[i], &pdev->dev);
}
fail1:
for (i = 0; i < EDMA_MAX_CC; i++) {
if (r[i])
release_mem_region(r[i]->start, len[i]);
if (edmacc_regs_base[i])
iounmap(edmacc_regs_base[i]);
kfree(edma_cc[i]);
}
#if 0    
fail2:
    iounmap(edma30_tc0cfg_reg);
#endif    
return status;
}
liuning 20:29:57


此函數完成標記以後,在擷取dma通道時就不會再擷取已經標記為已經使用的通道,但是如果擷取通道時擷取固定通道,則不受上邊標記的限制,具體分析如下:
int edma_alloc_channel(int channel,
void (*callback)(unsigned channel, u16 ch_status, void *data),
void *data,
enum dma_event_q eventq_no)
{
unsigned i, done = 0, ctlr = 0;
int ret = 0;


if (!unused_chan_list_done) {
/*
* Scan all the platform devices to find out the EDMA channels
* used and clear them in the unused list, making the rest
* available for ARM usage.
*/
ret = bus_for_each_dev(&platform_bus_type, NULL, NULL,
prepare_unused_channel_list);
if (ret < 0)
return ret;


unused_chan_list_done = true;
}


if (channel >= 0) {
ctlr = EDMA_CTLR(channel);
channel = EDMA_CHAN_SLOT(channel);
}


if (channel < 0) { //由此處可見,只有channel值小於零時,才會在未使用的通道中找到最靠前的通道,
for (i = 0; i < arch_num_cc; i++) {
channel = 0;
for (;;) { //次for迴圈尋找通道
channel = find_next_bit(edma_cc[i]->edma_unused,
edma_cc[i]->num_channels,
channel);
if (channel == edma_cc[i]->num_channels)
break;
if (!test_and_set_bit(channel,
edma_cc[i]->edma_inuse)) {       //找到之後將其對應的slot標記為已經使用
done = 1;
ctlr = i;
break;
}
channel++;
}
if (done)
break;
}
if (!done)
return -ENOMEM;
} else if (channel >= edma_cc[ctlr]->num_channels) {
return -EINVAL;
} else if (test_and_set_bit(channel, edma_cc[ctlr]->edma_inuse)) {/*此處將其對應的slot做是否使用檢測並標記為已經使用,所以da850_dma0_rsv_slots中UART用的slot一定不能分配給DSP,不然後擷取通道失敗*/
return -EBUSY;
}


/* ensure access through shadow region 0 */
edma_or_array2(ctlr, EDMA_DRAE, 0, channel >> 5, BIT(channel & 0x1f)); /*如果通道是大於零的,則不用尋找,直接對此通道進行設定,注意,此處沒有把此通道標記為已經使用,這就是結構體da850_dma0_rsv_chans中將其分配給DSP的原因,其實就是為了在edma_probe函數中將其標記為已經使用。*/


/* ensure no events are pending */
edma_stop(EDMA_CTLR_CHAN(ctlr, channel));
memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel),
&dummy_paramset, PARM_SIZE);


if (callback)
setup_dma_interrupt(EDMA_CTLR_CHAN(ctlr, channel),
callback, data);


map_dmach_queue(ctlr, channel, eventq_no);


return EDMA_CTLR_CHAN(ctlr, channel);
}
為了支援8250使用EDMA,還要在\arch\arm\mach-davinci\Dma.c
void edma_trig_manual(unsigned channel)
{
unsigned ctlr;


ctlr = EDMA_CTLR(channel);
channel = EDMA_CHAN_SLOT(channel);
    int j = channel >> 5;
    unsigned int mask = BIT(channel & 0x1f);
    edma_shadow0_write_array(ctlr, SH_ESR, j, mask);
}
此函數配合edma_start介面使用,因為OMAPL138的UART串口TX觸發EDMA事件有兩種,一種是THRE為空白時觸發,一種是pwremu_mgmt寄存器使能發射的時候觸發。現在的問題是1、使能發射是在uboot階段就完成了;2、UART開啟dma發射時並沒有將資料先拷貝到THRE,也就不會有THRE為空白觸發EDMA的條件。所以此處添加一個函數,在edma_start後調用edma_trig_manual函數實現手動觸發。


此時EDMA的源碼才支援8250實現EDMA功能。現在一步一步實現。
一、擴充plat_serial8250_port結構體,添加EDMA的支援
struct serial8250_dma {
    int  rx_dma_enable;//使能接收dma功能標誌,次出為了實現統一個UART的接收和發射單獨添加dma支援,所以分開
    int  tx_dma_enable;//使能發射dma功能標誌
    int  rx_dma_channel;//接收dma通道號
    int  tx_dma_channel;//發射dma通道號
    unsigned long dma_phy_membase;//UART的起始物理地址。
 };
struct plat_serial8250_port {
unsigned long iobase; /* io base address */
void __iomem *membase; /* ioremap cookie or NULL */
resource_size_t mapbase; /* resource base */
unsigned int irq; /* interrupt number */
unsigned long irqflags; /* request_irq flags */
struct clk *clk;
unsigned int uartclk; /* UART clock rate */
void            *private_data;
unsigned char regshift; /* register shift */
unsigned char iotype; /* UPIO_* */
unsigned char hub6;
upf_t flags; /* UPF_* flags */
unsigned int type; /* If UPF_FIXED_TYPE */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
void (*set_termios)(struct uart_port *,
              struct ktermios *new,
              struct ktermios *old);
int (*handle_irq)(struct uart_port *);
void (*pm)(struct uart_port *, unsigned int state,
     unsigned old);
    struct serial8250_dma serial8250_dma;
};
然後對裝置資源進行dma支援擴充:


static struct plat_serial8250_port da8xx_serial_pdata[] = {
{
.mapbase = DA8XX_UART0_BASE,
.irq = IRQ_DA8XX_UARTINT0,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
UPF_IOREMAP | UPF_FIXED_TYPE,
.type = PORT_AR7,
.iotype = UPIO_MEM,
.regshift = 2,
.serial8250_dma.rx_dma_channel = DA8XX_DMA_UART0_RX,
.serial8250_dma.tx_dma_channel = DA8XX_DMA_UART0_TX,
.serial8250_dma.tx_dma_enable    = false,
.serial8250_dma.rx_dma_enable    = false,
.serial8250_dma.dma_phy_membase  = DA8XX_UART0_BASE,
},
{
.mapbase = DA8XX_UART1_BASE,
.irq = IRQ_DA8XX_UARTINT1,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
UPF_IOREMAP | UPF_FIXED_TYPE,
.type = PORT_8250,
.iotype = UPIO_MEM,
.regshift = 2,
.serial8250_dma.rx_dma_channel = DA8XX_DMA_UART1_RX,
.serial8250_dma.tx_dma_channel = DA8XX_DMA_UART1_TX,
.serial8250_dma.rx_dma_enable    = true,
.serial8250_dma.tx_dma_enable    = true,
.serial8250_dma.dma_phy_membase  = DA8XX_UART1_BASE,
},
{
.mapbase = DA8XX_UART2_BASE,
.irq = IRQ_DA8XX_UARTINT2,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
UPF_IOREMAP | UPF_FIXED_TYPE,
.type = PORT_AR7,
.iotype = UPIO_MEM,
.regshift = 2,
.serial8250_dma.rx_dma_channel = DA8XX_DMA_UART2_RX,
.serial8250_dma.tx_dma_channel = DA8XX_DMA_UART2_TX,
.serial8250_dma.rx_dma_enable    = false,
.serial8250_dma.tx_dma_enable    = false,
.serial8250_dma.dma_phy_membase  = DA8XX_UART2_BASE,
},
{
.flags = 0,
},
};
liuning 20:30:41


定義dma通道號:
#define DA8XX_DMA_UART0_RX EDMA_CTLR_CHAN(0, 8)
#define DA8XX_DMA_UART0_TX EDMA_CTLR_CHAN(0, 9)
#define DA8XX_DMA_UART1_RX EDMA_CTLR_CHAN(0, 12)
#define DA8XX_DMA_UART1_TX EDMA_CTLR_CHAN(0, 13)
#define DA8XX_DMA_UART2_RX EDMA_CTLR_CHAN(0, 30)
#define DA8XX_DMA_UART2_TX EDMA_CTLR_CHAN(0, 31)
此處就完成了8250的dev修改,然後對drv進行修改。
先擴充uart_8250_port 結構體對dma的支援
struct uart_8250_dma {
    int &

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.