diff options
Diffstat (limited to 'drivers/dma/fsldma.c')
-rw-r--r-- | drivers/dma/fsldma.c | 270 |
1 files changed, 88 insertions, 182 deletions
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index c0059ca58340..0b95dcce447e 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c | |||
@@ -370,7 +370,10 @@ static int fsl_dma_alloc_chan_resources(struct dma_chan *chan, | |||
370 | struct dma_client *client) | 370 | struct dma_client *client) |
371 | { | 371 | { |
372 | struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan); | 372 | struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan); |
373 | LIST_HEAD(tmp_list); | 373 | |
374 | /* Has this channel already been allocated? */ | ||
375 | if (fsl_chan->desc_pool) | ||
376 | return 1; | ||
374 | 377 | ||
375 | /* We need the descriptor to be aligned to 32bytes | 378 | /* We need the descriptor to be aligned to 32bytes |
376 | * for meeting FSL DMA specification requirement. | 379 | * for meeting FSL DMA specification requirement. |
@@ -410,6 +413,8 @@ static void fsl_dma_free_chan_resources(struct dma_chan *chan) | |||
410 | } | 413 | } |
411 | spin_unlock_irqrestore(&fsl_chan->desc_lock, flags); | 414 | spin_unlock_irqrestore(&fsl_chan->desc_lock, flags); |
412 | dma_pool_destroy(fsl_chan->desc_pool); | 415 | dma_pool_destroy(fsl_chan->desc_pool); |
416 | |||
417 | fsl_chan->desc_pool = NULL; | ||
413 | } | 418 | } |
414 | 419 | ||
415 | static struct dma_async_tx_descriptor * | 420 | static struct dma_async_tx_descriptor * |
@@ -786,159 +791,29 @@ static void dma_do_tasklet(unsigned long data) | |||
786 | fsl_chan_ld_cleanup(fsl_chan); | 791 | fsl_chan_ld_cleanup(fsl_chan); |
787 | } | 792 | } |
788 | 793 | ||
789 | static void fsl_dma_callback_test(void *param) | 794 | static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev, |
790 | { | 795 | struct device_node *node, u32 feature, const char *compatible) |
791 | struct fsl_dma_chan *fsl_chan = param; | ||
792 | if (fsl_chan) | ||
793 | dev_dbg(fsl_chan->dev, "selftest: callback is ok!\n"); | ||
794 | } | ||
795 | |||
796 | static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) | ||
797 | { | ||
798 | struct dma_chan *chan; | ||
799 | int err = 0; | ||
800 | dma_addr_t dma_dest, dma_src; | ||
801 | dma_cookie_t cookie; | ||
802 | u8 *src, *dest; | ||
803 | int i; | ||
804 | size_t test_size; | ||
805 | struct dma_async_tx_descriptor *tx1, *tx2, *tx3; | ||
806 | |||
807 | test_size = 4096; | ||
808 | |||
809 | src = kmalloc(test_size * 2, GFP_KERNEL); | ||
810 | if (!src) { | ||
811 | dev_err(fsl_chan->dev, | ||
812 | "selftest: Cannot alloc memory for test!\n"); | ||
813 | return -ENOMEM; | ||
814 | } | ||
815 | |||
816 | dest = src + test_size; | ||
817 | |||
818 | for (i = 0; i < test_size; i++) | ||
819 | src[i] = (u8) i; | ||
820 | |||
821 | chan = &fsl_chan->common; | ||
822 | |||
823 | if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) { | ||
824 | dev_err(fsl_chan->dev, | ||
825 | "selftest: Cannot alloc resources for DMA\n"); | ||
826 | err = -ENODEV; | ||
827 | goto out; | ||
828 | } | ||
829 | |||
830 | /* TX 1 */ | ||
831 | dma_src = dma_map_single(fsl_chan->dev, src, test_size / 2, | ||
832 | DMA_TO_DEVICE); | ||
833 | dma_dest = dma_map_single(fsl_chan->dev, dest, test_size / 2, | ||
834 | DMA_FROM_DEVICE); | ||
835 | tx1 = fsl_dma_prep_memcpy(chan, dma_dest, dma_src, test_size / 2, 0); | ||
836 | async_tx_ack(tx1); | ||
837 | |||
838 | cookie = fsl_dma_tx_submit(tx1); | ||
839 | fsl_dma_memcpy_issue_pending(chan); | ||
840 | msleep(2); | ||
841 | |||
842 | if (fsl_dma_is_complete(chan, cookie, NULL, NULL) != DMA_SUCCESS) { | ||
843 | dev_err(fsl_chan->dev, "selftest: Time out!\n"); | ||
844 | err = -ENODEV; | ||
845 | goto free_resources; | ||
846 | } | ||
847 | |||
848 | /* Test free and re-alloc channel resources */ | ||
849 | fsl_dma_free_chan_resources(chan); | ||
850 | |||
851 | if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) { | ||
852 | dev_err(fsl_chan->dev, | ||
853 | "selftest: Cannot alloc resources for DMA\n"); | ||
854 | err = -ENODEV; | ||
855 | goto free_resources; | ||
856 | } | ||
857 | |||
858 | /* Continue to test | ||
859 | * TX 2 | ||
860 | */ | ||
861 | dma_src = dma_map_single(fsl_chan->dev, src + test_size / 2, | ||
862 | test_size / 4, DMA_TO_DEVICE); | ||
863 | dma_dest = dma_map_single(fsl_chan->dev, dest + test_size / 2, | ||
864 | test_size / 4, DMA_FROM_DEVICE); | ||
865 | tx2 = fsl_dma_prep_memcpy(chan, dma_dest, dma_src, test_size / 4, 0); | ||
866 | async_tx_ack(tx2); | ||
867 | |||
868 | /* TX 3 */ | ||
869 | dma_src = dma_map_single(fsl_chan->dev, src + test_size * 3 / 4, | ||
870 | test_size / 4, DMA_TO_DEVICE); | ||
871 | dma_dest = dma_map_single(fsl_chan->dev, dest + test_size * 3 / 4, | ||
872 | test_size / 4, DMA_FROM_DEVICE); | ||
873 | tx3 = fsl_dma_prep_memcpy(chan, dma_dest, dma_src, test_size / 4, 0); | ||
874 | async_tx_ack(tx3); | ||
875 | |||
876 | /* Interrupt tx test */ | ||
877 | tx1 = fsl_dma_prep_interrupt(chan, 0); | ||
878 | async_tx_ack(tx1); | ||
879 | cookie = fsl_dma_tx_submit(tx1); | ||
880 | |||
881 | /* Test exchanging the prepared tx sort */ | ||
882 | cookie = fsl_dma_tx_submit(tx3); | ||
883 | cookie = fsl_dma_tx_submit(tx2); | ||
884 | |||
885 | if (dma_has_cap(DMA_INTERRUPT, ((struct fsl_dma_device *) | ||
886 | dev_get_drvdata(fsl_chan->dev->parent))->common.cap_mask)) { | ||
887 | tx3->callback = fsl_dma_callback_test; | ||
888 | tx3->callback_param = fsl_chan; | ||
889 | } | ||
890 | fsl_dma_memcpy_issue_pending(chan); | ||
891 | msleep(2); | ||
892 | |||
893 | if (fsl_dma_is_complete(chan, cookie, NULL, NULL) != DMA_SUCCESS) { | ||
894 | dev_err(fsl_chan->dev, "selftest: Time out!\n"); | ||
895 | err = -ENODEV; | ||
896 | goto free_resources; | ||
897 | } | ||
898 | |||
899 | err = memcmp(src, dest, test_size); | ||
900 | if (err) { | ||
901 | for (i = 0; (*(src + i) == *(dest + i)) && (i < test_size); | ||
902 | i++); | ||
903 | dev_err(fsl_chan->dev, "selftest: Test failed, data %d/%ld is " | ||
904 | "error! src 0x%x, dest 0x%x\n", | ||
905 | i, (long)test_size, *(src + i), *(dest + i)); | ||
906 | } | ||
907 | |||
908 | free_resources: | ||
909 | fsl_dma_free_chan_resources(chan); | ||
910 | out: | ||
911 | kfree(src); | ||
912 | return err; | ||
913 | } | ||
914 | |||
915 | static int __devinit of_fsl_dma_chan_probe(struct of_device *dev, | ||
916 | const struct of_device_id *match) | ||
917 | { | 796 | { |
918 | struct fsl_dma_device *fdev; | ||
919 | struct fsl_dma_chan *new_fsl_chan; | 797 | struct fsl_dma_chan *new_fsl_chan; |
920 | int err; | 798 | int err; |
921 | 799 | ||
922 | fdev = dev_get_drvdata(dev->dev.parent); | ||
923 | BUG_ON(!fdev); | ||
924 | |||
925 | /* alloc channel */ | 800 | /* alloc channel */ |
926 | new_fsl_chan = kzalloc(sizeof(struct fsl_dma_chan), GFP_KERNEL); | 801 | new_fsl_chan = kzalloc(sizeof(struct fsl_dma_chan), GFP_KERNEL); |
927 | if (!new_fsl_chan) { | 802 | if (!new_fsl_chan) { |
928 | dev_err(&dev->dev, "No free memory for allocating " | 803 | dev_err(fdev->dev, "No free memory for allocating " |
929 | "dma channels!\n"); | 804 | "dma channels!\n"); |
930 | return -ENOMEM; | 805 | return -ENOMEM; |
931 | } | 806 | } |
932 | 807 | ||
933 | /* get dma channel register base */ | 808 | /* get dma channel register base */ |
934 | err = of_address_to_resource(dev->node, 0, &new_fsl_chan->reg); | 809 | err = of_address_to_resource(node, 0, &new_fsl_chan->reg); |
935 | if (err) { | 810 | if (err) { |
936 | dev_err(&dev->dev, "Can't get %s property 'reg'\n", | 811 | dev_err(fdev->dev, "Can't get %s property 'reg'\n", |
937 | dev->node->full_name); | 812 | node->full_name); |
938 | goto err_no_reg; | 813 | goto err_no_reg; |
939 | } | 814 | } |
940 | 815 | ||
941 | new_fsl_chan->feature = *(u32 *)match->data; | 816 | new_fsl_chan->feature = feature; |
942 | 817 | ||
943 | if (!fdev->feature) | 818 | if (!fdev->feature) |
944 | fdev->feature = new_fsl_chan->feature; | 819 | fdev->feature = new_fsl_chan->feature; |
@@ -948,13 +823,13 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev, | |||
948 | */ | 823 | */ |
949 | WARN_ON(fdev->feature != new_fsl_chan->feature); | 824 | WARN_ON(fdev->feature != new_fsl_chan->feature); |
950 | 825 | ||
951 | new_fsl_chan->dev = &dev->dev; | 826 | new_fsl_chan->dev = &new_fsl_chan->common.dev; |
952 | new_fsl_chan->reg_base = ioremap(new_fsl_chan->reg.start, | 827 | new_fsl_chan->reg_base = ioremap(new_fsl_chan->reg.start, |
953 | new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1); | 828 | new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1); |
954 | 829 | ||
955 | new_fsl_chan->id = ((new_fsl_chan->reg.start - 0x100) & 0xfff) >> 7; | 830 | new_fsl_chan->id = ((new_fsl_chan->reg.start - 0x100) & 0xfff) >> 7; |
956 | if (new_fsl_chan->id > FSL_DMA_MAX_CHANS_PER_DEVICE) { | 831 | if (new_fsl_chan->id > FSL_DMA_MAX_CHANS_PER_DEVICE) { |
957 | dev_err(&dev->dev, "There is no %d channel!\n", | 832 | dev_err(fdev->dev, "There is no %d channel!\n", |
958 | new_fsl_chan->id); | 833 | new_fsl_chan->id); |
959 | err = -EINVAL; | 834 | err = -EINVAL; |
960 | goto err_no_chan; | 835 | goto err_no_chan; |
@@ -988,29 +863,23 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev, | |||
988 | &fdev->common.channels); | 863 | &fdev->common.channels); |
989 | fdev->common.chancnt++; | 864 | fdev->common.chancnt++; |
990 | 865 | ||
991 | new_fsl_chan->irq = irq_of_parse_and_map(dev->node, 0); | 866 | new_fsl_chan->irq = irq_of_parse_and_map(node, 0); |
992 | if (new_fsl_chan->irq != NO_IRQ) { | 867 | if (new_fsl_chan->irq != NO_IRQ) { |
993 | err = request_irq(new_fsl_chan->irq, | 868 | err = request_irq(new_fsl_chan->irq, |
994 | &fsl_dma_chan_do_interrupt, IRQF_SHARED, | 869 | &fsl_dma_chan_do_interrupt, IRQF_SHARED, |
995 | "fsldma-channel", new_fsl_chan); | 870 | "fsldma-channel", new_fsl_chan); |
996 | if (err) { | 871 | if (err) { |
997 | dev_err(&dev->dev, "DMA channel %s request_irq error " | 872 | dev_err(fdev->dev, "DMA channel %s request_irq error " |
998 | "with return %d\n", dev->node->full_name, err); | 873 | "with return %d\n", node->full_name, err); |
999 | goto err_no_irq; | 874 | goto err_no_irq; |
1000 | } | 875 | } |
1001 | } | 876 | } |
1002 | 877 | ||
1003 | err = fsl_dma_self_test(new_fsl_chan); | 878 | dev_info(fdev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id, |
1004 | if (err) | 879 | compatible, new_fsl_chan->irq); |
1005 | goto err_self_test; | ||
1006 | |||
1007 | dev_info(&dev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id, | ||
1008 | match->compatible, new_fsl_chan->irq); | ||
1009 | 880 | ||
1010 | return 0; | 881 | return 0; |
1011 | 882 | ||
1012 | err_self_test: | ||
1013 | free_irq(new_fsl_chan->irq, new_fsl_chan); | ||
1014 | err_no_irq: | 883 | err_no_irq: |
1015 | list_del(&new_fsl_chan->common.device_node); | 884 | list_del(&new_fsl_chan->common.device_node); |
1016 | err_no_chan: | 885 | err_no_chan: |
@@ -1020,38 +889,20 @@ err_no_reg: | |||
1020 | return err; | 889 | return err; |
1021 | } | 890 | } |
1022 | 891 | ||
1023 | const u32 mpc8540_dma_ip_feature = FSL_DMA_IP_85XX | FSL_DMA_BIG_ENDIAN; | 892 | static void fsl_dma_chan_remove(struct fsl_dma_chan *fchan) |
1024 | const u32 mpc8349_dma_ip_feature = FSL_DMA_IP_83XX | FSL_DMA_LITTLE_ENDIAN; | ||
1025 | |||
1026 | static struct of_device_id of_fsl_dma_chan_ids[] = { | ||
1027 | { | ||
1028 | .compatible = "fsl,eloplus-dma-channel", | ||
1029 | .data = (void *)&mpc8540_dma_ip_feature, | ||
1030 | }, | ||
1031 | { | ||
1032 | .compatible = "fsl,elo-dma-channel", | ||
1033 | .data = (void *)&mpc8349_dma_ip_feature, | ||
1034 | }, | ||
1035 | {} | ||
1036 | }; | ||
1037 | |||
1038 | static struct of_platform_driver of_fsl_dma_chan_driver = { | ||
1039 | .name = "of-fsl-dma-channel", | ||
1040 | .match_table = of_fsl_dma_chan_ids, | ||
1041 | .probe = of_fsl_dma_chan_probe, | ||
1042 | }; | ||
1043 | |||
1044 | static __init int of_fsl_dma_chan_init(void) | ||
1045 | { | 893 | { |
1046 | return of_register_platform_driver(&of_fsl_dma_chan_driver); | 894 | free_irq(fchan->irq, fchan); |
895 | list_del(&fchan->common.device_node); | ||
896 | iounmap(fchan->reg_base); | ||
897 | kfree(fchan); | ||
1047 | } | 898 | } |
1048 | 899 | ||
1049 | static int __devinit of_fsl_dma_probe(struct of_device *dev, | 900 | static int __devinit of_fsl_dma_probe(struct of_device *dev, |
1050 | const struct of_device_id *match) | 901 | const struct of_device_id *match) |
1051 | { | 902 | { |
1052 | int err; | 903 | int err; |
1053 | unsigned int irq; | ||
1054 | struct fsl_dma_device *fdev; | 904 | struct fsl_dma_device *fdev; |
905 | struct device_node *child; | ||
1055 | 906 | ||
1056 | fdev = kzalloc(sizeof(struct fsl_dma_device), GFP_KERNEL); | 907 | fdev = kzalloc(sizeof(struct fsl_dma_device), GFP_KERNEL); |
1057 | if (!fdev) { | 908 | if (!fdev) { |
@@ -1085,9 +936,9 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, | |||
1085 | fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending; | 936 | fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending; |
1086 | fdev->common.dev = &dev->dev; | 937 | fdev->common.dev = &dev->dev; |
1087 | 938 | ||
1088 | irq = irq_of_parse_and_map(dev->node, 0); | 939 | fdev->irq = irq_of_parse_and_map(dev->node, 0); |
1089 | if (irq != NO_IRQ) { | 940 | if (fdev->irq != NO_IRQ) { |
1090 | err = request_irq(irq, &fsl_dma_do_interrupt, IRQF_SHARED, | 941 | err = request_irq(fdev->irq, &fsl_dma_do_interrupt, IRQF_SHARED, |
1091 | "fsldma-device", fdev); | 942 | "fsldma-device", fdev); |
1092 | if (err) { | 943 | if (err) { |
1093 | dev_err(&dev->dev, "DMA device request_irq error " | 944 | dev_err(&dev->dev, "DMA device request_irq error " |
@@ -1097,7 +948,21 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, | |||
1097 | } | 948 | } |
1098 | 949 | ||
1099 | dev_set_drvdata(&(dev->dev), fdev); | 950 | dev_set_drvdata(&(dev->dev), fdev); |
1100 | of_platform_bus_probe(dev->node, of_fsl_dma_chan_ids, &dev->dev); | 951 | |
952 | /* We cannot use of_platform_bus_probe() because there is no | ||
953 | * of_platform_bus_remove. Instead, we manually instantiate every DMA | ||
954 | * channel object. | ||
955 | */ | ||
956 | for_each_child_of_node(dev->node, child) { | ||
957 | if (of_device_is_compatible(child, "fsl,eloplus-dma-channel")) | ||
958 | fsl_dma_chan_probe(fdev, child, | ||
959 | FSL_DMA_IP_85XX | FSL_DMA_BIG_ENDIAN, | ||
960 | "fsl,eloplus-dma-channel"); | ||
961 | if (of_device_is_compatible(child, "fsl,elo-dma-channel")) | ||
962 | fsl_dma_chan_probe(fdev, child, | ||
963 | FSL_DMA_IP_83XX | FSL_DMA_LITTLE_ENDIAN, | ||
964 | "fsl,elo-dma-channel"); | ||
965 | } | ||
1101 | 966 | ||
1102 | dma_async_device_register(&fdev->common); | 967 | dma_async_device_register(&fdev->common); |
1103 | return 0; | 968 | return 0; |
@@ -1109,6 +974,30 @@ err_no_reg: | |||
1109 | return err; | 974 | return err; |
1110 | } | 975 | } |
1111 | 976 | ||
977 | static int of_fsl_dma_remove(struct of_device *of_dev) | ||
978 | { | ||
979 | struct fsl_dma_device *fdev; | ||
980 | unsigned int i; | ||
981 | |||
982 | fdev = dev_get_drvdata(&of_dev->dev); | ||
983 | |||
984 | dma_async_device_unregister(&fdev->common); | ||
985 | |||
986 | for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) | ||
987 | if (fdev->chan[i]) | ||
988 | fsl_dma_chan_remove(fdev->chan[i]); | ||
989 | |||
990 | if (fdev->irq != NO_IRQ) | ||
991 | free_irq(fdev->irq, fdev); | ||
992 | |||
993 | iounmap(fdev->reg_base); | ||
994 | |||
995 | kfree(fdev); | ||
996 | dev_set_drvdata(&of_dev->dev, NULL); | ||
997 | |||
998 | return 0; | ||
999 | } | ||
1000 | |||
1112 | static struct of_device_id of_fsl_dma_ids[] = { | 1001 | static struct of_device_id of_fsl_dma_ids[] = { |
1113 | { .compatible = "fsl,eloplus-dma", }, | 1002 | { .compatible = "fsl,eloplus-dma", }, |
1114 | { .compatible = "fsl,elo-dma", }, | 1003 | { .compatible = "fsl,elo-dma", }, |
@@ -1116,15 +1005,32 @@ static struct of_device_id of_fsl_dma_ids[] = { | |||
1116 | }; | 1005 | }; |
1117 | 1006 | ||
1118 | static struct of_platform_driver of_fsl_dma_driver = { | 1007 | static struct of_platform_driver of_fsl_dma_driver = { |
1119 | .name = "of-fsl-dma", | 1008 | .name = "fsl-elo-dma", |
1120 | .match_table = of_fsl_dma_ids, | 1009 | .match_table = of_fsl_dma_ids, |
1121 | .probe = of_fsl_dma_probe, | 1010 | .probe = of_fsl_dma_probe, |
1011 | .remove = of_fsl_dma_remove, | ||
1122 | }; | 1012 | }; |
1123 | 1013 | ||
1124 | static __init int of_fsl_dma_init(void) | 1014 | static __init int of_fsl_dma_init(void) |
1125 | { | 1015 | { |
1126 | return of_register_platform_driver(&of_fsl_dma_driver); | 1016 | int ret; |
1017 | |||
1018 | pr_info("Freescale Elo / Elo Plus DMA driver\n"); | ||
1019 | |||
1020 | ret = of_register_platform_driver(&of_fsl_dma_driver); | ||
1021 | if (ret) | ||
1022 | pr_err("fsldma: failed to register platform driver\n"); | ||
1023 | |||
1024 | return ret; | ||
1025 | } | ||
1026 | |||
1027 | static void __exit of_fsl_dma_exit(void) | ||
1028 | { | ||
1029 | of_unregister_platform_driver(&of_fsl_dma_driver); | ||
1127 | } | 1030 | } |
1128 | 1031 | ||
1129 | subsys_initcall(of_fsl_dma_chan_init); | ||
1130 | subsys_initcall(of_fsl_dma_init); | 1032 | subsys_initcall(of_fsl_dma_init); |
1033 | module_exit(of_fsl_dma_exit); | ||
1034 | |||
1035 | MODULE_DESCRIPTION("Freescale Elo / Elo Plus DMA driver"); | ||
1036 | MODULE_LICENSE("GPL"); | ||