|
|
|
@ -370,7 +370,10 @@ static int fsl_dma_alloc_chan_resources(struct dma_chan *chan,
|
|
|
|
|
struct dma_client *client)
|
|
|
|
|
{
|
|
|
|
|
struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan);
|
|
|
|
|
LIST_HEAD(tmp_list);
|
|
|
|
|
|
|
|
|
|
/* Has this channel already been allocated? */
|
|
|
|
|
if (fsl_chan->desc_pool)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
/* We need the descriptor to be aligned to 32bytes
|
|
|
|
|
* for meeting FSL DMA specification requirement.
|
|
|
|
@ -410,6 +413,8 @@ static void fsl_dma_free_chan_resources(struct dma_chan *chan)
|
|
|
|
|
}
|
|
|
|
|
spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
|
|
|
|
|
dma_pool_destroy(fsl_chan->desc_pool);
|
|
|
|
|
|
|
|
|
|
fsl_chan->desc_pool = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct dma_async_tx_descriptor *
|
|
|
|
@ -786,159 +791,29 @@ static void dma_do_tasklet(unsigned long data)
|
|
|
|
|
fsl_chan_ld_cleanup(fsl_chan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void fsl_dma_callback_test(void *param)
|
|
|
|
|
static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
|
|
|
|
|
struct device_node *node, u32 feature, const char *compatible)
|
|
|
|
|
{
|
|
|
|
|
struct fsl_dma_chan *fsl_chan = param;
|
|
|
|
|
if (fsl_chan)
|
|
|
|
|
dev_dbg(fsl_chan->dev, "selftest: callback is ok!\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan)
|
|
|
|
|
{
|
|
|
|
|
struct dma_chan *chan;
|
|
|
|
|
int err = 0;
|
|
|
|
|
dma_addr_t dma_dest, dma_src;
|
|
|
|
|
dma_cookie_t cookie;
|
|
|
|
|
u8 *src, *dest;
|
|
|
|
|
int i;
|
|
|
|
|
size_t test_size;
|
|
|
|
|
struct dma_async_tx_descriptor *tx1, *tx2, *tx3;
|
|
|
|
|
|
|
|
|
|
test_size = 4096;
|
|
|
|
|
|
|
|
|
|
src = kmalloc(test_size * 2, GFP_KERNEL);
|
|
|
|
|
if (!src) {
|
|
|
|
|
dev_err(fsl_chan->dev,
|
|
|
|
|
"selftest: Cannot alloc memory for test!\n");
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dest = src + test_size;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < test_size; i++)
|
|
|
|
|
src[i] = (u8) i;
|
|
|
|
|
|
|
|
|
|
chan = &fsl_chan->common;
|
|
|
|
|
|
|
|
|
|
if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) {
|
|
|
|
|
dev_err(fsl_chan->dev,
|
|
|
|
|
"selftest: Cannot alloc resources for DMA\n");
|
|
|
|
|
err = -ENODEV;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TX 1 */
|
|
|
|
|
dma_src = dma_map_single(fsl_chan->dev, src, test_size / 2,
|
|
|
|
|
DMA_TO_DEVICE);
|
|
|
|
|
dma_dest = dma_map_single(fsl_chan->dev, dest, test_size / 2,
|
|
|
|
|
DMA_FROM_DEVICE);
|
|
|
|
|
tx1 = fsl_dma_prep_memcpy(chan, dma_dest, dma_src, test_size / 2, 0);
|
|
|
|
|
async_tx_ack(tx1);
|
|
|
|
|
|
|
|
|
|
cookie = fsl_dma_tx_submit(tx1);
|
|
|
|
|
fsl_dma_memcpy_issue_pending(chan);
|
|
|
|
|
msleep(2);
|
|
|
|
|
|
|
|
|
|
if (fsl_dma_is_complete(chan, cookie, NULL, NULL) != DMA_SUCCESS) {
|
|
|
|
|
dev_err(fsl_chan->dev, "selftest: Time out!\n");
|
|
|
|
|
err = -ENODEV;
|
|
|
|
|
goto free_resources;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Test free and re-alloc channel resources */
|
|
|
|
|
fsl_dma_free_chan_resources(chan);
|
|
|
|
|
|
|
|
|
|
if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) {
|
|
|
|
|
dev_err(fsl_chan->dev,
|
|
|
|
|
"selftest: Cannot alloc resources for DMA\n");
|
|
|
|
|
err = -ENODEV;
|
|
|
|
|
goto free_resources;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Continue to test
|
|
|
|
|
* TX 2
|
|
|
|
|
*/
|
|
|
|
|
dma_src = dma_map_single(fsl_chan->dev, src + test_size / 2,
|
|
|
|
|
test_size / 4, DMA_TO_DEVICE);
|
|
|
|
|
dma_dest = dma_map_single(fsl_chan->dev, dest + test_size / 2,
|
|
|
|
|
test_size / 4, DMA_FROM_DEVICE);
|
|
|
|
|
tx2 = fsl_dma_prep_memcpy(chan, dma_dest, dma_src, test_size / 4, 0);
|
|
|
|
|
async_tx_ack(tx2);
|
|
|
|
|
|
|
|
|
|
/* TX 3 */
|
|
|
|
|
dma_src = dma_map_single(fsl_chan->dev, src + test_size * 3 / 4,
|
|
|
|
|
test_size / 4, DMA_TO_DEVICE);
|
|
|
|
|
dma_dest = dma_map_single(fsl_chan->dev, dest + test_size * 3 / 4,
|
|
|
|
|
test_size / 4, DMA_FROM_DEVICE);
|
|
|
|
|
tx3 = fsl_dma_prep_memcpy(chan, dma_dest, dma_src, test_size / 4, 0);
|
|
|
|
|
async_tx_ack(tx3);
|
|
|
|
|
|
|
|
|
|
/* Interrupt tx test */
|
|
|
|
|
tx1 = fsl_dma_prep_interrupt(chan, 0);
|
|
|
|
|
async_tx_ack(tx1);
|
|
|
|
|
cookie = fsl_dma_tx_submit(tx1);
|
|
|
|
|
|
|
|
|
|
/* Test exchanging the prepared tx sort */
|
|
|
|
|
cookie = fsl_dma_tx_submit(tx3);
|
|
|
|
|
cookie = fsl_dma_tx_submit(tx2);
|
|
|
|
|
|
|
|
|
|
if (dma_has_cap(DMA_INTERRUPT, ((struct fsl_dma_device *)
|
|
|
|
|
dev_get_drvdata(fsl_chan->dev->parent))->common.cap_mask)) {
|
|
|
|
|
tx3->callback = fsl_dma_callback_test;
|
|
|
|
|
tx3->callback_param = fsl_chan;
|
|
|
|
|
}
|
|
|
|
|
fsl_dma_memcpy_issue_pending(chan);
|
|
|
|
|
msleep(2);
|
|
|
|
|
|
|
|
|
|
if (fsl_dma_is_complete(chan, cookie, NULL, NULL) != DMA_SUCCESS) {
|
|
|
|
|
dev_err(fsl_chan->dev, "selftest: Time out!\n");
|
|
|
|
|
err = -ENODEV;
|
|
|
|
|
goto free_resources;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = memcmp(src, dest, test_size);
|
|
|
|
|
if (err) {
|
|
|
|
|
for (i = 0; (*(src + i) == *(dest + i)) && (i < test_size);
|
|
|
|
|
i++);
|
|
|
|
|
dev_err(fsl_chan->dev, "selftest: Test failed, data %d/%ld is "
|
|
|
|
|
"error! src 0x%x, dest 0x%x\n",
|
|
|
|
|
i, (long)test_size, *(src + i), *(dest + i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free_resources:
|
|
|
|
|
fsl_dma_free_chan_resources(chan);
|
|
|
|
|
out:
|
|
|
|
|
kfree(src);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __devinit of_fsl_dma_chan_probe(struct of_device *dev,
|
|
|
|
|
const struct of_device_id *match)
|
|
|
|
|
{
|
|
|
|
|
struct fsl_dma_device *fdev;
|
|
|
|
|
struct fsl_dma_chan *new_fsl_chan;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
fdev = dev_get_drvdata(dev->dev.parent);
|
|
|
|
|
BUG_ON(!fdev);
|
|
|
|
|
|
|
|
|
|
/* alloc channel */
|
|
|
|
|
new_fsl_chan = kzalloc(sizeof(struct fsl_dma_chan), GFP_KERNEL);
|
|
|
|
|
if (!new_fsl_chan) {
|
|
|
|
|
dev_err(&dev->dev, "No free memory for allocating "
|
|
|
|
|
dev_err(fdev->dev, "No free memory for allocating "
|
|
|
|
|
"dma channels!\n");
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get dma channel register base */
|
|
|
|
|
err = of_address_to_resource(dev->node, 0, &new_fsl_chan->reg);
|
|
|
|
|
err = of_address_to_resource(node, 0, &new_fsl_chan->reg);
|
|
|
|
|
if (err) {
|
|
|
|
|
dev_err(&dev->dev, "Can't get %s property 'reg'\n",
|
|
|
|
|
dev->node->full_name);
|
|
|
|
|
dev_err(fdev->dev, "Can't get %s property 'reg'\n",
|
|
|
|
|
node->full_name);
|
|
|
|
|
goto err_no_reg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_fsl_chan->feature = *(u32 *)match->data;
|
|
|
|
|
new_fsl_chan->feature = feature;
|
|
|
|
|
|
|
|
|
|
if (!fdev->feature)
|
|
|
|
|
fdev->feature = new_fsl_chan->feature;
|
|
|
|
@ -948,13 +823,13 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev,
|
|
|
|
|
*/
|
|
|
|
|
WARN_ON(fdev->feature != new_fsl_chan->feature);
|
|
|
|
|
|
|
|
|
|
new_fsl_chan->dev = &dev->dev;
|
|
|
|
|
new_fsl_chan->dev = &new_fsl_chan->common.dev;
|
|
|
|
|
new_fsl_chan->reg_base = ioremap(new_fsl_chan->reg.start,
|
|
|
|
|
new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1);
|
|
|
|
|
|
|
|
|
|
new_fsl_chan->id = ((new_fsl_chan->reg.start - 0x100) & 0xfff) >> 7;
|
|
|
|
|
if (new_fsl_chan->id > FSL_DMA_MAX_CHANS_PER_DEVICE) {
|
|
|
|
|
dev_err(&dev->dev, "There is no %d channel!\n",
|
|
|
|
|
dev_err(fdev->dev, "There is no %d channel!\n",
|
|
|
|
|
new_fsl_chan->id);
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto err_no_chan;
|
|
|
|
@ -988,29 +863,23 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev,
|
|
|
|
|
&fdev->common.channels);
|
|
|
|
|
fdev->common.chancnt++;
|
|
|
|
|
|
|
|
|
|
new_fsl_chan->irq = irq_of_parse_and_map(dev->node, 0);
|
|
|
|
|
new_fsl_chan->irq = irq_of_parse_and_map(node, 0);
|
|
|
|
|
if (new_fsl_chan->irq != NO_IRQ) {
|
|
|
|
|
err = request_irq(new_fsl_chan->irq,
|
|
|
|
|
&fsl_dma_chan_do_interrupt, IRQF_SHARED,
|
|
|
|
|
"fsldma-channel", new_fsl_chan);
|
|
|
|
|
if (err) {
|
|
|
|
|
dev_err(&dev->dev, "DMA channel %s request_irq error "
|
|
|
|
|
"with return %d\n", dev->node->full_name, err);
|
|
|
|
|
dev_err(fdev->dev, "DMA channel %s request_irq error "
|
|
|
|
|
"with return %d\n", node->full_name, err);
|
|
|
|
|
goto err_no_irq;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = fsl_dma_self_test(new_fsl_chan);
|
|
|
|
|
if (err)
|
|
|
|
|
goto err_self_test;
|
|
|
|
|
|
|
|
|
|
dev_info(&dev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id,
|
|
|
|
|
match->compatible, new_fsl_chan->irq);
|
|
|
|
|
dev_info(fdev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id,
|
|
|
|
|
compatible, new_fsl_chan->irq);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err_self_test:
|
|
|
|
|
free_irq(new_fsl_chan->irq, new_fsl_chan);
|
|
|
|
|
err_no_irq:
|
|
|
|
|
list_del(&new_fsl_chan->common.device_node);
|
|
|
|
|
err_no_chan:
|
|
|
|
@ -1020,38 +889,20 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev,
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const u32 mpc8540_dma_ip_feature = FSL_DMA_IP_85XX | FSL_DMA_BIG_ENDIAN;
|
|
|
|
|
const u32 mpc8349_dma_ip_feature = FSL_DMA_IP_83XX | FSL_DMA_LITTLE_ENDIAN;
|
|
|
|
|
|
|
|
|
|
static struct of_device_id of_fsl_dma_chan_ids[] = {
|
|
|
|
|
{
|
|
|
|
|
.compatible = "fsl,eloplus-dma-channel",
|
|
|
|
|
.data = (void *)&mpc8540_dma_ip_feature,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.compatible = "fsl,elo-dma-channel",
|
|
|
|
|
.data = (void *)&mpc8349_dma_ip_feature,
|
|
|
|
|
},
|
|
|
|
|
{}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct of_platform_driver of_fsl_dma_chan_driver = {
|
|
|
|
|
.name = "of-fsl-dma-channel",
|
|
|
|
|
.match_table = of_fsl_dma_chan_ids,
|
|
|
|
|
.probe = of_fsl_dma_chan_probe,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static __init int of_fsl_dma_chan_init(void)
|
|
|
|
|
static void fsl_dma_chan_remove(struct fsl_dma_chan *fchan)
|
|
|
|
|
{
|
|
|
|
|
return of_register_platform_driver(&of_fsl_dma_chan_driver);
|
|
|
|
|
free_irq(fchan->irq, fchan);
|
|
|
|
|
list_del(&fchan->common.device_node);
|
|
|
|
|
iounmap(fchan->reg_base);
|
|
|
|
|
kfree(fchan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __devinit of_fsl_dma_probe(struct of_device *dev,
|
|
|
|
|
const struct of_device_id *match)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
unsigned int irq;
|
|
|
|
|
struct fsl_dma_device *fdev;
|
|
|
|
|
struct device_node *child;
|
|
|
|
|
|
|
|
|
|
fdev = kzalloc(sizeof(struct fsl_dma_device), GFP_KERNEL);
|
|
|
|
|
if (!fdev) {
|
|
|
|
@ -1085,9 +936,9 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev,
|
|
|
|
|
fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
|
|
|
|
|
fdev->common.dev = &dev->dev;
|
|
|
|
|
|
|
|
|
|
irq = irq_of_parse_and_map(dev->node, 0);
|
|
|
|
|
if (irq != NO_IRQ) {
|
|
|
|
|
err = request_irq(irq, &fsl_dma_do_interrupt, IRQF_SHARED,
|
|
|
|
|
fdev->irq = irq_of_parse_and_map(dev->node, 0);
|
|
|
|
|
if (fdev->irq != NO_IRQ) {
|
|
|
|
|
err = request_irq(fdev->irq, &fsl_dma_do_interrupt, IRQF_SHARED,
|
|
|
|
|
"fsldma-device", fdev);
|
|
|
|
|
if (err) {
|
|
|
|
|
dev_err(&dev->dev, "DMA device request_irq error "
|
|
|
|
@ -1097,7 +948,21 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dev_set_drvdata(&(dev->dev), fdev);
|
|
|
|
|
of_platform_bus_probe(dev->node, of_fsl_dma_chan_ids, &dev->dev);
|
|
|
|
|
|
|
|
|
|
/* We cannot use of_platform_bus_probe() because there is no
|
|
|
|
|
* of_platform_bus_remove. Instead, we manually instantiate every DMA
|
|
|
|
|
* channel object.
|
|
|
|
|
*/
|
|
|
|
|
for_each_child_of_node(dev->node, child) {
|
|
|
|
|
if (of_device_is_compatible(child, "fsl,eloplus-dma-channel"))
|
|
|
|
|
fsl_dma_chan_probe(fdev, child,
|
|
|
|
|
FSL_DMA_IP_85XX | FSL_DMA_BIG_ENDIAN,
|
|
|
|
|
"fsl,eloplus-dma-channel");
|
|
|
|
|
if (of_device_is_compatible(child, "fsl,elo-dma-channel"))
|
|
|
|
|
fsl_dma_chan_probe(fdev, child,
|
|
|
|
|
FSL_DMA_IP_83XX | FSL_DMA_LITTLE_ENDIAN,
|
|
|
|
|
"fsl,elo-dma-channel");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dma_async_device_register(&fdev->common);
|
|
|
|
|
return 0;
|
|
|
|
@ -1109,6 +974,30 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev,
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int of_fsl_dma_remove(struct of_device *of_dev)
|
|
|
|
|
{
|
|
|
|
|
struct fsl_dma_device *fdev;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
fdev = dev_get_drvdata(&of_dev->dev);
|
|
|
|
|
|
|
|
|
|
dma_async_device_unregister(&fdev->common);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++)
|
|
|
|
|
if (fdev->chan[i])
|
|
|
|
|
fsl_dma_chan_remove(fdev->chan[i]);
|
|
|
|
|
|
|
|
|
|
if (fdev->irq != NO_IRQ)
|
|
|
|
|
free_irq(fdev->irq, fdev);
|
|
|
|
|
|
|
|
|
|
iounmap(fdev->reg_base);
|
|
|
|
|
|
|
|
|
|
kfree(fdev);
|
|
|
|
|
dev_set_drvdata(&of_dev->dev, NULL);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct of_device_id of_fsl_dma_ids[] = {
|
|
|
|
|
{ .compatible = "fsl,eloplus-dma", },
|
|
|
|
|
{ .compatible = "fsl,elo-dma", },
|
|
|
|
@ -1116,15 +1005,32 @@ static struct of_device_id of_fsl_dma_ids[] = {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct of_platform_driver of_fsl_dma_driver = {
|
|
|
|
|
.name = "of-fsl-dma",
|
|
|
|
|
.name = "fsl-elo-dma",
|
|
|
|
|
.match_table = of_fsl_dma_ids,
|
|
|
|
|
.probe = of_fsl_dma_probe,
|
|
|
|
|
.remove = of_fsl_dma_remove,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static __init int of_fsl_dma_init(void)
|
|
|
|
|
{
|
|
|
|
|
return of_register_platform_driver(&of_fsl_dma_driver);
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
pr_info("Freescale Elo / Elo Plus DMA driver\n");
|
|
|
|
|
|
|
|
|
|
ret = of_register_platform_driver(&of_fsl_dma_driver);
|
|
|
|
|
if (ret)
|
|
|
|
|
pr_err("fsldma: failed to register platform driver\n");
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __exit of_fsl_dma_exit(void)
|
|
|
|
|
{
|
|
|
|
|
of_unregister_platform_driver(&of_fsl_dma_driver);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subsys_initcall(of_fsl_dma_chan_init);
|
|
|
|
|
subsys_initcall(of_fsl_dma_init);
|
|
|
|
|
module_exit(of_fsl_dma_exit);
|
|
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("Freescale Elo / Elo Plus DMA driver");
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|