| .. | .. |
|---|
| 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 | |
|---|