|
@@ -370,7 +370,10 @@ static int fsl_dma_alloc_chan_resources(struct dma_chan *chan,
|
|
struct dma_client *client)
|
|
struct dma_client *client)
|
|
{
|
|
{
|
|
struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan);
|
|
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
|
|
/* We need the descriptor to be aligned to 32bytes
|
|
* for meeting FSL DMA specification requirement.
|
|
* 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);
|
|
spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
|
|
dma_pool_destroy(fsl_chan->desc_pool);
|
|
dma_pool_destroy(fsl_chan->desc_pool);
|
|
|
|
+
|
|
|
|
+ fsl_chan->desc_pool = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static struct dma_async_tx_descriptor *
|
|
static struct dma_async_tx_descriptor *
|
|
@@ -786,33 +791,29 @@ static void dma_do_tasklet(unsigned long data)
|
|
fsl_chan_ld_cleanup(fsl_chan);
|
|
fsl_chan_ld_cleanup(fsl_chan);
|
|
}
|
|
}
|
|
|
|
|
|
-static int __devinit of_fsl_dma_chan_probe(struct of_device *dev,
|
|
|
|
- const struct of_device_id *match)
|
|
|
|
|
|
+static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
|
|
|
|
+ struct device_node *node, u32 feature, const char *compatible)
|
|
{
|
|
{
|
|
- struct fsl_dma_device *fdev;
|
|
|
|
struct fsl_dma_chan *new_fsl_chan;
|
|
struct fsl_dma_chan *new_fsl_chan;
|
|
int err;
|
|
int err;
|
|
|
|
|
|
- fdev = dev_get_drvdata(dev->dev.parent);
|
|
|
|
- BUG_ON(!fdev);
|
|
|
|
-
|
|
|
|
/* alloc channel */
|
|
/* alloc channel */
|
|
new_fsl_chan = kzalloc(sizeof(struct fsl_dma_chan), GFP_KERNEL);
|
|
new_fsl_chan = kzalloc(sizeof(struct fsl_dma_chan), GFP_KERNEL);
|
|
if (!new_fsl_chan) {
|
|
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");
|
|
"dma channels!\n");
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
|
|
/* get dma channel register base */
|
|
/* 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) {
|
|
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;
|
|
goto err_no_reg;
|
|
}
|
|
}
|
|
|
|
|
|
- new_fsl_chan->feature = *(u32 *)match->data;
|
|
|
|
|
|
+ new_fsl_chan->feature = feature;
|
|
|
|
|
|
if (!fdev->feature)
|
|
if (!fdev->feature)
|
|
fdev->feature = new_fsl_chan->feature;
|
|
fdev->feature = new_fsl_chan->feature;
|
|
@@ -822,13 +823,13 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev,
|
|
*/
|
|
*/
|
|
WARN_ON(fdev->feature != new_fsl_chan->feature);
|
|
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_base = ioremap(new_fsl_chan->reg.start,
|
|
new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1);
|
|
new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1);
|
|
|
|
|
|
new_fsl_chan->id = ((new_fsl_chan->reg.start - 0x100) & 0xfff) >> 7;
|
|
new_fsl_chan->id = ((new_fsl_chan->reg.start - 0x100) & 0xfff) >> 7;
|
|
if (new_fsl_chan->id > FSL_DMA_MAX_CHANS_PER_DEVICE) {
|
|
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);
|
|
new_fsl_chan->id);
|
|
err = -EINVAL;
|
|
err = -EINVAL;
|
|
goto err_no_chan;
|
|
goto err_no_chan;
|
|
@@ -862,20 +863,20 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev,
|
|
&fdev->common.channels);
|
|
&fdev->common.channels);
|
|
fdev->common.chancnt++;
|
|
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) {
|
|
if (new_fsl_chan->irq != NO_IRQ) {
|
|
err = request_irq(new_fsl_chan->irq,
|
|
err = request_irq(new_fsl_chan->irq,
|
|
&fsl_dma_chan_do_interrupt, IRQF_SHARED,
|
|
&fsl_dma_chan_do_interrupt, IRQF_SHARED,
|
|
"fsldma-channel", new_fsl_chan);
|
|
"fsldma-channel", new_fsl_chan);
|
|
if (err) {
|
|
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;
|
|
goto err_no_irq;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- 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;
|
|
return 0;
|
|
|
|
|
|
@@ -888,38 +889,20 @@ err_no_reg:
|
|
return err;
|
|
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,
|
|
static int __devinit of_fsl_dma_probe(struct of_device *dev,
|
|
const struct of_device_id *match)
|
|
const struct of_device_id *match)
|
|
{
|
|
{
|
|
int err;
|
|
int err;
|
|
- unsigned int irq;
|
|
|
|
struct fsl_dma_device *fdev;
|
|
struct fsl_dma_device *fdev;
|
|
|
|
+ struct device_node *child;
|
|
|
|
|
|
fdev = kzalloc(sizeof(struct fsl_dma_device), GFP_KERNEL);
|
|
fdev = kzalloc(sizeof(struct fsl_dma_device), GFP_KERNEL);
|
|
if (!fdev) {
|
|
if (!fdev) {
|
|
@@ -953,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.device_issue_pending = fsl_dma_memcpy_issue_pending;
|
|
fdev->common.dev = &dev->dev;
|
|
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);
|
|
"fsldma-device", fdev);
|
|
if (err) {
|
|
if (err) {
|
|
dev_err(&dev->dev, "DMA device request_irq error "
|
|
dev_err(&dev->dev, "DMA device request_irq error "
|
|
@@ -965,7 +948,21 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev,
|
|
}
|
|
}
|
|
|
|
|
|
dev_set_drvdata(&(dev->dev), fdev);
|
|
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);
|
|
dma_async_device_register(&fdev->common);
|
|
return 0;
|
|
return 0;
|
|
@@ -977,6 +974,30 @@ err_no_reg:
|
|
return err;
|
|
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[] = {
|
|
static struct of_device_id of_fsl_dma_ids[] = {
|
|
{ .compatible = "fsl,eloplus-dma", },
|
|
{ .compatible = "fsl,eloplus-dma", },
|
|
{ .compatible = "fsl,elo-dma", },
|
|
{ .compatible = "fsl,elo-dma", },
|
|
@@ -984,15 +1005,32 @@ static struct of_device_id of_fsl_dma_ids[] = {
|
|
};
|
|
};
|
|
|
|
|
|
static struct of_platform_driver of_fsl_dma_driver = {
|
|
static struct of_platform_driver of_fsl_dma_driver = {
|
|
- .name = "of-fsl-dma",
|
|
|
|
|
|
+ .name = "fsl-elo-dma",
|
|
.match_table = of_fsl_dma_ids,
|
|
.match_table = of_fsl_dma_ids,
|
|
.probe = of_fsl_dma_probe,
|
|
.probe = of_fsl_dma_probe,
|
|
|
|
+ .remove = of_fsl_dma_remove,
|
|
};
|
|
};
|
|
|
|
|
|
static __init int of_fsl_dma_init(void)
|
|
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);
|
|
subsys_initcall(of_fsl_dma_init);
|
|
|
|
+module_exit(of_fsl_dma_exit);
|
|
|
|
+
|
|
|
|
+MODULE_DESCRIPTION("Freescale Elo / Elo Plus DMA driver");
|
|
|
|
+MODULE_LICENSE("GPL");
|