OMAPL138 based Linux platform 8250 Fast serial port implementation--uart+edma__linux

Source: Internet
Author: User
Tags goto sprintf

This source is based on the linux3.3.0 version of the kernel in the Dvsdk_omapl138-evm_04_03_00_06_setuplinux package. Implementation of EDMA support UART features mainly modify DMA and 8250 of the source code.
First, the DMA channel resource is allocated for OMAPL138 arm and DSP, which has three uart,uart1 occupying edma0cc0 Channel8 (Rx), Channel9 (TX) channel and SLOT8 (RX) and Slot9 (TX);
The UART2 occupies the channel12 (Rx), CHANNEL13 (TX) channel of EDMA0CC0, Slot12 (Rx) and Slot13 (TX), UART2 occupies Edma0cc0 (RX), Channel31 (TX) channel and Slot30 (RX) and Slot31 (TX). The rest of my board EDMA resources are all assigned to DSP. The DMA channel resource allocation structure can therefore be set to:
static const S16 Da850_dma0_rsv_chans[][2] = {
/* (offset, number) * *
{0, 32},//indicates that all ENDM0CC0 channels are already in use, where channels 8, 9, 12, 13, 30, 31 are used when the channel is directly fetched from behind 8250.
{-1,-1}
};


static const S16 Da850_dma0_rsv_slots[][2] = {
/* (offset, number) * *
{0, 8},//represents SLOT0~SLOT7 assigned to DSP, how to implement the analysis at the bottom
{10, 2},//represents slot10, slot11 assigned to DSP
{14, 16},//indicates Slot14~slot30 assigned to DSP
{32, 96},//remaining slot all to DSP
{-1, -1}//this structure represents SLOT8, 9, 12, 13, 30, 31 reserved for arm
};


static const S16 Da850_dma1_rsv_chans[][2] = {
/* (offset, number) * *
{0, 32},//ENDM1CC0 32 channels are all left to DSP
{-1,-1}
};


static const S16 Da850_dma1_rsv_slots[][2] = {
/* (offset, number) * *
{0, 128},//endm1cc0 all slot left to DSP
{-1,-1}
};
Now analyze how to implement the allocation of channels:


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]-&GT;NUM_CC = min_t (unsigned, INFO[J]-&GT;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,//Here first DMA all channels are set to not be used
sizeof (edma_cc[j]->edma_unused));


if (INFO[J]-&GT;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); The channels in the front knot Da850_dma0_rsv_chans and Da850_dma1_rsv_chans are marked here as already in use.
}
}


/* 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); Here, the slot in the front knot da850_dma0_rsv_slots, the da850_dma1_rsv_slots structure are marked as already in use.
}
}
}


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


After this function completes the tag, the channel that has been marked as already used is no longer acquired when the DMA channel is acquired, but when the channel is acquired the fixed channel is not restricted by the top tag, which is analyzed as follows:
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 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) {//This is visible, only the channel value is less than zero to find the most anterior channel in the unused channel.
for (i = 0; i < arch_num_cc; i++) {
Channel = 0;
for (;;) {//secondary for Loop search channel
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 marked as already in use after it is found
Done = 1;
CTLR = i;
Break
}
channel++;
}
if (done)
Break
}
if (!done)
Return-enomem;
else if (channel >= edma_cc[ctlr]->num_channels) {
Return-einval;
Or else if (Test_and_set_bit (channel, Edma_cc[ctlr]->edma_inuse)) {/* here is the corresponding slot to use Detect and mark as already used, so da850_dma0_rsv_ Slots in the UART slot must not be allocated to the DSP, or after the failure to obtain channel/
Return-ebusy;
}


/* Ensure access through Shadow region 0 * *
Edma_or_array2 (CTLR, Edma_drae, 0, channel >> 5, BIT (Channel & 0x1f)); * * If the channel is greater than 0, you do not need to find, directly to this channel to set up, note that this channel is not marked as already used, this is the structure of the Da850_dma0_rsv_chans in the distribution of the DSP to the reason, in fact, in order to Edma_ The probe function to mark it as already in use. */


/* 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);
}
In order to support the 8250 use of EDMA, but also in the \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);
}
This function is used with the Edma_start interface because there are two kinds of EDMA events that are triggered by the OMAPL138 UART serial TX, one is the thre for the null trigger, and the other is the PWREMU_MGMT register that triggers when it can launch. Now the problem is 1, so that the launch is completed in the uboot phase; 2, the UART when the DMA launch did not copy the data to the thre, there will be no thre for the null trigger EDMA conditions. So add a function here, call the Edma_trig_manual function after edma_start the manual trigger.


At this time EDMA source code to support the implementation of the EDMA function 8250. Now it's a step-by-step implementation.
First, expand the Plat_serial8250_port structure, add EDMA support
struct SERIAL8250_DMA {
The int rx_dma_enable;//enables the DMA function to receive the flag, the second out in order to realize the unified UART receives and launches separately to add the DMA support, therefore separates
int tx_dma_enable;//enables the ability to emit DMA function flags
int rx_dma_channel;//Receive DMA channel number
int tx_dma_channel;//Transmit DMA channel number
The starting physical address of the 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;
};
Then the DMA support extension for the device resources:


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


To define the DMA channel number:
#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)
A 8250 dev modification is done here, and then the DRV is modified.
Extend the support of Uart_8250_port structure to DMA first
struct UART_8250_DMA {
int &

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.