.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2015, Sony Mobile Communications AB. |
---|
3 | 4 | * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify |
---|
6 | | - * it under the terms of the GNU General Public License version 2 and |
---|
7 | | - * only version 2 as published by the Free Software Foundation. |
---|
8 | | - * |
---|
9 | | - * This program is distributed in the hope that it will be useful, |
---|
10 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
12 | | - * GNU General Public License for more details. |
---|
13 | 5 | */ |
---|
14 | 6 | |
---|
15 | 7 | #include <linux/hwspinlock.h> |
---|
.. | .. |
---|
18 | 10 | #include <linux/of.h> |
---|
19 | 11 | #include <linux/of_address.h> |
---|
20 | 12 | #include <linux/platform_device.h> |
---|
| 13 | +#include <linux/sizes.h> |
---|
21 | 14 | #include <linux/slab.h> |
---|
22 | 15 | #include <linux/soc/qcom/smem.h> |
---|
23 | 16 | |
---|
.. | .. |
---|
91 | 84 | #define SMEM_GLOBAL_HOST 0xfffe |
---|
92 | 85 | |
---|
93 | 86 | /* Max number of processors/hosts in a system */ |
---|
94 | | -#define SMEM_HOST_COUNT 10 |
---|
| 87 | +#define SMEM_HOST_COUNT 11 |
---|
95 | 88 | |
---|
96 | 89 | /** |
---|
97 | 90 | * struct smem_proc_comm - proc_comm communication struct (legacy) |
---|
.. | .. |
---|
275 | 268 | struct smem_partition_header *partitions[SMEM_HOST_COUNT]; |
---|
276 | 269 | size_t cacheline[SMEM_HOST_COUNT]; |
---|
277 | 270 | u32 item_count; |
---|
| 271 | + struct platform_device *socinfo; |
---|
278 | 272 | |
---|
279 | 273 | unsigned num_regions; |
---|
280 | | - struct smem_region regions[0]; |
---|
| 274 | + struct smem_region regions[]; |
---|
281 | 275 | }; |
---|
282 | 276 | |
---|
283 | 277 | static void * |
---|
.. | .. |
---|
489 | 483 | size_t *size) |
---|
490 | 484 | { |
---|
491 | 485 | struct smem_header *header; |
---|
492 | | - struct smem_region *area; |
---|
| 486 | + struct smem_region *region; |
---|
493 | 487 | struct smem_global_entry *entry; |
---|
494 | 488 | u32 aux_base; |
---|
495 | 489 | unsigned i; |
---|
.. | .. |
---|
502 | 496 | aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK; |
---|
503 | 497 | |
---|
504 | 498 | for (i = 0; i < smem->num_regions; i++) { |
---|
505 | | - area = &smem->regions[i]; |
---|
| 499 | + region = &smem->regions[i]; |
---|
506 | 500 | |
---|
507 | | - if (area->aux_base == aux_base || !aux_base) { |
---|
| 501 | + if (region->aux_base == aux_base || !aux_base) { |
---|
508 | 502 | if (size != NULL) |
---|
509 | 503 | *size = le32_to_cpu(entry->size); |
---|
510 | | - return area->virt_base + le32_to_cpu(entry->offset); |
---|
| 504 | + return region->virt_base + le32_to_cpu(entry->offset); |
---|
511 | 505 | } |
---|
512 | 506 | } |
---|
513 | 507 | |
---|
.. | .. |
---|
722 | 716 | return le16_to_cpu(info->num_items); |
---|
723 | 717 | } |
---|
724 | 718 | |
---|
| 719 | +/* |
---|
| 720 | + * Validate the partition header for a partition whose partition |
---|
| 721 | + * table entry is supplied. Returns a pointer to its header if |
---|
| 722 | + * valid, or a null pointer otherwise. |
---|
| 723 | + */ |
---|
| 724 | +static struct smem_partition_header * |
---|
| 725 | +qcom_smem_partition_header(struct qcom_smem *smem, |
---|
| 726 | + struct smem_ptable_entry *entry, u16 host0, u16 host1) |
---|
| 727 | +{ |
---|
| 728 | + struct smem_partition_header *header; |
---|
| 729 | + u32 size; |
---|
| 730 | + |
---|
| 731 | + header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); |
---|
| 732 | + |
---|
| 733 | + if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) { |
---|
| 734 | + dev_err(smem->dev, "bad partition magic %02x %02x %02x %02x\n", |
---|
| 735 | + header->magic[0], header->magic[1], |
---|
| 736 | + header->magic[2], header->magic[3]); |
---|
| 737 | + return NULL; |
---|
| 738 | + } |
---|
| 739 | + |
---|
| 740 | + if (host0 != le16_to_cpu(header->host0)) { |
---|
| 741 | + dev_err(smem->dev, "bad host0 (%hu != %hu)\n", |
---|
| 742 | + host0, le16_to_cpu(header->host0)); |
---|
| 743 | + return NULL; |
---|
| 744 | + } |
---|
| 745 | + if (host1 != le16_to_cpu(header->host1)) { |
---|
| 746 | + dev_err(smem->dev, "bad host1 (%hu != %hu)\n", |
---|
| 747 | + host1, le16_to_cpu(header->host1)); |
---|
| 748 | + return NULL; |
---|
| 749 | + } |
---|
| 750 | + |
---|
| 751 | + size = le32_to_cpu(header->size); |
---|
| 752 | + if (size != le32_to_cpu(entry->size)) { |
---|
| 753 | + dev_err(smem->dev, "bad partition size (%u != %u)\n", |
---|
| 754 | + size, le32_to_cpu(entry->size)); |
---|
| 755 | + return NULL; |
---|
| 756 | + } |
---|
| 757 | + |
---|
| 758 | + if (le32_to_cpu(header->offset_free_uncached) > size) { |
---|
| 759 | + dev_err(smem->dev, "bad partition free uncached (%u > %u)\n", |
---|
| 760 | + le32_to_cpu(header->offset_free_uncached), size); |
---|
| 761 | + return NULL; |
---|
| 762 | + } |
---|
| 763 | + |
---|
| 764 | + return header; |
---|
| 765 | +} |
---|
| 766 | + |
---|
725 | 767 | static int qcom_smem_set_global_partition(struct qcom_smem *smem) |
---|
726 | 768 | { |
---|
727 | 769 | struct smem_partition_header *header; |
---|
728 | 770 | struct smem_ptable_entry *entry; |
---|
729 | 771 | struct smem_ptable *ptable; |
---|
730 | | - u32 host0, host1, size; |
---|
731 | 772 | bool found = false; |
---|
732 | 773 | int i; |
---|
733 | 774 | |
---|
.. | .. |
---|
742 | 783 | |
---|
743 | 784 | for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { |
---|
744 | 785 | entry = &ptable->entry[i]; |
---|
745 | | - host0 = le16_to_cpu(entry->host0); |
---|
746 | | - host1 = le16_to_cpu(entry->host1); |
---|
| 786 | + if (!le32_to_cpu(entry->offset)) |
---|
| 787 | + continue; |
---|
| 788 | + if (!le32_to_cpu(entry->size)) |
---|
| 789 | + continue; |
---|
747 | 790 | |
---|
748 | | - if (host0 == SMEM_GLOBAL_HOST && host0 == host1) { |
---|
| 791 | + if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST) |
---|
| 792 | + continue; |
---|
| 793 | + |
---|
| 794 | + if (le16_to_cpu(entry->host1) == SMEM_GLOBAL_HOST) { |
---|
749 | 795 | found = true; |
---|
750 | 796 | break; |
---|
751 | 797 | } |
---|
.. | .. |
---|
756 | 802 | return -EINVAL; |
---|
757 | 803 | } |
---|
758 | 804 | |
---|
759 | | - if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) { |
---|
760 | | - dev_err(smem->dev, "Invalid entry for global partition\n"); |
---|
| 805 | + header = qcom_smem_partition_header(smem, entry, |
---|
| 806 | + SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST); |
---|
| 807 | + if (!header) |
---|
761 | 808 | return -EINVAL; |
---|
762 | | - } |
---|
763 | | - |
---|
764 | | - header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); |
---|
765 | | - host0 = le16_to_cpu(header->host0); |
---|
766 | | - host1 = le16_to_cpu(header->host1); |
---|
767 | | - |
---|
768 | | - if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) { |
---|
769 | | - dev_err(smem->dev, "Global partition has invalid magic\n"); |
---|
770 | | - return -EINVAL; |
---|
771 | | - } |
---|
772 | | - |
---|
773 | | - if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) { |
---|
774 | | - dev_err(smem->dev, "Global partition hosts are invalid\n"); |
---|
775 | | - return -EINVAL; |
---|
776 | | - } |
---|
777 | | - |
---|
778 | | - if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) { |
---|
779 | | - dev_err(smem->dev, "Global partition has invalid size\n"); |
---|
780 | | - return -EINVAL; |
---|
781 | | - } |
---|
782 | | - |
---|
783 | | - size = le32_to_cpu(header->offset_free_uncached); |
---|
784 | | - if (size > le32_to_cpu(header->size)) { |
---|
785 | | - dev_err(smem->dev, |
---|
786 | | - "Global partition has invalid free pointer\n"); |
---|
787 | | - return -EINVAL; |
---|
788 | | - } |
---|
789 | 809 | |
---|
790 | 810 | smem->global_partition = header; |
---|
791 | 811 | smem->global_cacheline = le32_to_cpu(entry->cacheline); |
---|
.. | .. |
---|
793 | 813 | return 0; |
---|
794 | 814 | } |
---|
795 | 815 | |
---|
796 | | -static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, |
---|
797 | | - unsigned int local_host) |
---|
| 816 | +static int |
---|
| 817 | +qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host) |
---|
798 | 818 | { |
---|
799 | 819 | struct smem_partition_header *header; |
---|
800 | 820 | struct smem_ptable_entry *entry; |
---|
801 | 821 | struct smem_ptable *ptable; |
---|
802 | 822 | unsigned int remote_host; |
---|
803 | | - u32 host0, host1; |
---|
| 823 | + u16 host0, host1; |
---|
804 | 824 | int i; |
---|
805 | 825 | |
---|
806 | 826 | ptable = qcom_smem_get_ptable(smem); |
---|
.. | .. |
---|
809 | 829 | |
---|
810 | 830 | for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { |
---|
811 | 831 | entry = &ptable->entry[i]; |
---|
812 | | - host0 = le16_to_cpu(entry->host0); |
---|
813 | | - host1 = le16_to_cpu(entry->host1); |
---|
814 | | - |
---|
815 | | - if (host0 != local_host && host1 != local_host) |
---|
816 | | - continue; |
---|
817 | | - |
---|
818 | 832 | if (!le32_to_cpu(entry->offset)) |
---|
819 | 833 | continue; |
---|
820 | | - |
---|
821 | 834 | if (!le32_to_cpu(entry->size)) |
---|
822 | 835 | continue; |
---|
823 | 836 | |
---|
| 837 | + host0 = le16_to_cpu(entry->host0); |
---|
| 838 | + host1 = le16_to_cpu(entry->host1); |
---|
824 | 839 | if (host0 == local_host) |
---|
825 | 840 | remote_host = host1; |
---|
826 | | - else |
---|
| 841 | + else if (host1 == local_host) |
---|
827 | 842 | remote_host = host0; |
---|
| 843 | + else |
---|
| 844 | + continue; |
---|
828 | 845 | |
---|
829 | 846 | if (remote_host >= SMEM_HOST_COUNT) { |
---|
830 | | - dev_err(smem->dev, |
---|
831 | | - "Invalid remote host %d\n", |
---|
832 | | - remote_host); |
---|
| 847 | + dev_err(smem->dev, "bad host %hu\n", remote_host); |
---|
833 | 848 | return -EINVAL; |
---|
834 | 849 | } |
---|
835 | 850 | |
---|
836 | 851 | if (smem->partitions[remote_host]) { |
---|
837 | | - dev_err(smem->dev, |
---|
838 | | - "Already found a partition for host %d\n", |
---|
839 | | - remote_host); |
---|
| 852 | + dev_err(smem->dev, "duplicate host %hu\n", remote_host); |
---|
840 | 853 | return -EINVAL; |
---|
841 | 854 | } |
---|
842 | 855 | |
---|
843 | | - header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); |
---|
844 | | - host0 = le16_to_cpu(header->host0); |
---|
845 | | - host1 = le16_to_cpu(header->host1); |
---|
846 | | - |
---|
847 | | - if (memcmp(header->magic, SMEM_PART_MAGIC, |
---|
848 | | - sizeof(header->magic))) { |
---|
849 | | - dev_err(smem->dev, |
---|
850 | | - "Partition %d has invalid magic\n", i); |
---|
| 856 | + header = qcom_smem_partition_header(smem, entry, host0, host1); |
---|
| 857 | + if (!header) |
---|
851 | 858 | return -EINVAL; |
---|
852 | | - } |
---|
853 | | - |
---|
854 | | - if (host0 != local_host && host1 != local_host) { |
---|
855 | | - dev_err(smem->dev, |
---|
856 | | - "Partition %d hosts are invalid\n", i); |
---|
857 | | - return -EINVAL; |
---|
858 | | - } |
---|
859 | | - |
---|
860 | | - if (host0 != remote_host && host1 != remote_host) { |
---|
861 | | - dev_err(smem->dev, |
---|
862 | | - "Partition %d hosts are invalid\n", i); |
---|
863 | | - return -EINVAL; |
---|
864 | | - } |
---|
865 | | - |
---|
866 | | - if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) { |
---|
867 | | - dev_err(smem->dev, |
---|
868 | | - "Partition %d has invalid size\n", i); |
---|
869 | | - return -EINVAL; |
---|
870 | | - } |
---|
871 | | - |
---|
872 | | - if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) { |
---|
873 | | - dev_err(smem->dev, |
---|
874 | | - "Partition %d has invalid free pointer\n", i); |
---|
875 | | - return -EINVAL; |
---|
876 | | - } |
---|
877 | 859 | |
---|
878 | 860 | smem->partitions[remote_host] = header; |
---|
879 | 861 | smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline); |
---|
.. | .. |
---|
887 | 869 | { |
---|
888 | 870 | struct device_node *np; |
---|
889 | 871 | struct resource r; |
---|
| 872 | + resource_size_t size; |
---|
890 | 873 | int ret; |
---|
891 | 874 | |
---|
892 | 875 | np = of_parse_phandle(dev->of_node, name, 0); |
---|
.. | .. |
---|
899 | 882 | of_node_put(np); |
---|
900 | 883 | if (ret) |
---|
901 | 884 | return ret; |
---|
| 885 | + size = resource_size(&r); |
---|
902 | 886 | |
---|
903 | | - smem->regions[i].aux_base = (u32)r.start; |
---|
904 | | - smem->regions[i].size = resource_size(&r); |
---|
905 | | - smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, resource_size(&r)); |
---|
| 887 | + smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, size); |
---|
906 | 888 | if (!smem->regions[i].virt_base) |
---|
907 | 889 | return -ENOMEM; |
---|
| 890 | + smem->regions[i].aux_base = (u32)r.start; |
---|
| 891 | + smem->regions[i].size = size; |
---|
908 | 892 | |
---|
909 | 893 | return 0; |
---|
910 | 894 | } |
---|
.. | .. |
---|
962 | 946 | return -EINVAL; |
---|
963 | 947 | } |
---|
964 | 948 | |
---|
| 949 | + BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT); |
---|
965 | 950 | ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); |
---|
966 | 951 | if (ret < 0 && ret != -ENOENT) |
---|
967 | 952 | return ret; |
---|
.. | .. |
---|
979 | 964 | |
---|
980 | 965 | __smem = smem; |
---|
981 | 966 | |
---|
| 967 | + smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo", |
---|
| 968 | + PLATFORM_DEVID_NONE, NULL, |
---|
| 969 | + 0); |
---|
| 970 | + if (IS_ERR(smem->socinfo)) |
---|
| 971 | + dev_dbg(&pdev->dev, "failed to register socinfo device\n"); |
---|
| 972 | + |
---|
982 | 973 | return 0; |
---|
983 | 974 | } |
---|
984 | 975 | |
---|
985 | 976 | static int qcom_smem_remove(struct platform_device *pdev) |
---|
986 | 977 | { |
---|
| 978 | + platform_device_unregister(__smem->socinfo); |
---|
| 979 | + |
---|
987 | 980 | hwspin_lock_free(__smem->hwlock); |
---|
988 | 981 | __smem = NULL; |
---|
989 | 982 | |
---|