DMA控制器驅動架構中的第二個函數:
s3c24xx_dma_order_set(&s3c2440_dma_order);
參數s3c2440_dma_order是一個全局變量,抽象的是下圖實體channel和邏輯channel及其互相關系:
static struct s3c24xx_dma_order __initdata s3c2440_dma_order = {
.channels = {
[DMACH_SDI] = {
.list = {
[0] = 3 | DMA_CH_VALID,
[1] = 2 | DMA_CH_VALID,
[2] = 1 | DMA_CH_VALID,
[3] = 0 | DMA_CH_VALID,
},
},
[DMACH_I2S_IN] = {
.list = {
[0] = 1 | DMA_CH_VALID,
[1] = 2 | DMA_CH_VALID,
},
},
......
},
};
對于SDIO/MMC/SD,其DMA源有4個(上圖紅框),位于實體channel的0~3通道;同樣,I2SSDI的DMA源有2個,位于通道0、1等等。
再來看s3c24xx_dma_order_set()函數:
int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord)
{
struct s3c24xx_dma_order *nord = dma_order;
if (nord == NULL)
nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL);
if (nord == NULL) {
printk(KERN_ERR "no memory to store dma channel order\n");
return -ENOMEM;
}
dma_order = nord;
memcpy(nord, ord, sizeof(struct s3c24xx_dma_order));
return 0;
}
還是很簡單:為全局變量dma_order配置設定空間并把s3c2440_dma_order的内容copy過去,何必多此一舉?
最後一個函數:
s3c24xx_dma_init_map(&s3c2440_dma_sel);
int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel)
{
struct s3c24xx_dma_map *nmap;
size_t map_sz = sizeof(*nmap) * sel->map_size;
int ptr;
nmap = kmalloc(map_sz, GFP_KERNEL);
if (nmap == NULL)
return -ENOMEM;
memcpy(nmap, sel->map, map_sz);
memcpy(&dma_sel, sel, sizeof(*sel));
dma_sel.map = nmap;
for (ptr = 0; ptr < sel->map_size; ptr++)
s3c24xx_dma_check_entry(nmap+ptr, ptr);
return 0;
}
和 s 3 c 2 4 xx_dma_order_set() 函數如出一轍,這裡使用s3c2440_dma_sel變量初始化dma_sel。
這樣,DMA Provider把基本的準備工作做好了,下面看Consumer如何使用它。
2、DMA的使用
在SDIO驅動的probe函數中:
static int __devinit s3cmci_probe(struct platform_device *pdev)
{
if (s3cmci_host_usedma(host)) {
host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
host);
if (host->dma < 0) {
dev_err(&pdev->dev, "cannot get DMA channel.\n");
if (!s3cmci_host_canpio()) {
ret = -EBUSY;
goto probe_free_gpio_wp;
} else {
dev_warn(&pdev->dev, "falling back to PIO.\n");
host->dodma = 0;
}
}
}
}
使用DMA傳輸資料,就從provider哪裡request:
static struct s3c2410_dma_client s3cmci_dma_client = {
.name = "s3c-mci",
};
s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client, host);
/*
* get control of an dma channel
*/
int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client, void *dev)
{
struct s3c2410_dma_chan *chan;
unsigned long flags;
int err;
pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n",
channel, client->name, dev);
local_irq_save(flags);
chan = s3c2410_dma_map_channel(channel);
if (chan == NULL) {
local_irq_restore(flags);
return -EBUSY;
}
dbg_showchan(chan);
chan->client = client;
chan->in_use = 1;
if (!chan->irq_claimed) {
pr_debug("dma%d: %s : requesting irq %d\n",
channel, __func__, chan->irq);
chan->irq_claimed = 1;
local_irq_restore(flags);
err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED,
client->name, (void *)chan);
local_irq_save(flags);
if (err) {
chan->in_use = 0;
chan->irq_claimed = 0;
local_irq_restore(flags);
printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n",
client->name, chan->irq, chan->number);
return err;
}
chan->irq_enabled = 1;
}
local_irq_restore(flags);
/* need to setup */
pr_debug("%s: channel initialised, %p\n", __func__, chan);
return chan->number | DMACH_LOW_LEVEL;
}
DMACH_SDI是邏輯channel,在使用之前肯定需要映射到實際的實體channel上去,這個功能由 s 3 c 2 4 1 0 _dma_map_channel()函數負責完成;映射成功後設定其in_use标志,在30行。之後申請DMA對應的中斷處理函數。
至此,DMA是申請下來了,下面該使用它了。用法就是圍繞“二、DMA介紹”中提出的5點資訊:
static struct mmc_host_ops s3cmci_ops = {
.request = s3cmci_request,
....
};
static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
s3cmci_send_request(mmc);
}
static void s3cmci_send_request(struct mmc_host *mmc)
{
if (s3cmci_host_usedma(host))
res = s3cmci_prepare_dma(host, cmd->data);
else
res = s3cmci_prepare_pio(host, cmd->data);
}
以上都是準備工作,幹活的是:
static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
{
int dma_len, i;
int rw = data->flags & MMC_DATA_WRITE;
BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (dma_len == 0)
return -ENOMEM;
host->dma_complete = 0;
host->dmatogo = dma_len;
for (i = 0; i < dma_len; i++) {
int res;
dbg(host, dbg_dma, "enqueue %i: %[email protected]%u\n", i,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));
res = s3c2410_dma_enqueue(host->dma, host,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));
if (res) {
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
return -EBUSY;
}
}
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
return 0;
}
s 3 cmci_prepare_dma( )函數 第8行,先确定資料傳輸方向:寫資料,方向是mem->dev;讀資料,反向是dev->mem,然後作為參數傳進s3cmci_dma_setup()函數:
static void s3cmci_dma_setup(struct s3cmci_host *host,
enum s3c2410_dmasrc source)
{
static enum s3c2410_dmasrc last_source = -1;
static int setup_ok;
if (last_source == source)
return;
last_source = source;
s3c2410_dma_devconfig(host->dma, source,
host->mem->start + host->sdidata);
if (!setup_ok) {
s3c2410_dma_config(host->dma, 4);
s3c2410_dma_set_buffdone_fn(host->dma,
s3cmci_dma_done_callback);
s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
setup_ok = 1;
}
}
s3c2410_dma_devconfig()函數配置DMA的源/目的寄存器:
/*
* configure the dma source/destination hardware type and address
* source: S3C2410_DMASRC_HW: source is hardware
* S3C2410_DMASRC_MEM: source is memory
* devaddr: physical address of the source
*/
int s3c2410_dma_devconfig(unsigned int channel,
enum s3c2410_dmasrc source,
unsigned long devaddr)
{
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
chan->source = source;
chan->dev_addr = devaddr;
switch (source) {
case S3C2410_DMASRC_HW:
/* source is hardware */
dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3);
dma_wrreg(chan, S3C2410_DMA_DISRC, devaddr);
dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));
chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
break;
case S3C2410_DMASRC_MEM:
/* source is memory */
dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0));
dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr);
dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);
chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);
break;
}
return 0;
}
s3cmci_dma_setup()函數用來配置DMA通道的屬性,第15行,setup_ok是一個static變量,意味着下次進入該函數其值為上次設定的值;首次進入自然為0,s3c2410_dma_config()函數配置DMA的控制寄存器(DMA CONTROL REGISTER),如傳輸資料個數(transfer count)、傳輸完成是否産生中斷等。 s3c2410_dma_set_buffdone_fn()函數設定 DMA通道對應的回調函數,這裡就是s3cmci_dma_done_callback()。s3c2410_dma_setflags()函數設定通道S3C2410_DMAF_AUTOSTART标志,意味着如果緩存取隊列不為空的情況下,DMA控制器将自動發起一次DMA傳輸。
s3cmci_prepare_dma()函數第9行,控制DMA的開啟、停止、暫停等:
int s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op op)
{
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
if (chan == NULL)
return -EINVAL;
switch (op) {
case S3C2410_DMAOP_START:
return s3c2410_dma_start(chan);
case S3C2410_DMAOP_STOP:
return s3c2410_dma_dostop(chan);
case S3C2410_DMAOP_PAUSE:
case S3C2410_DMAOP_RESUME:
return -ENOENT;
case S3C2410_DMAOP_FLUSH:
return s3c2410_dma_flush(chan);
case S3C2410_DMAOP_STARTED:
return s3c2410_dma_started(chan);
case S3C2410_DMAOP_TIMEOUT:
return 0;
}
return -ENOENT; /* unknown, don't bother */
}
20~35行把資料buf入隊列,然後37行發起DMA傳輸。如果設定了 S3C2410_DMAF_AUTOSTART标志,在DMA空閑狀态下傳輸自動進行。
經過以上設定,資料傳輸方向、傳輸使用的channel、傳輸的資料大小/位寬、傳輸控制、傳輸狀态、DMA傳輸情況的通知等都具備,DMA就開始歡快的工作了。