1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Driver for HiSilicon Hydra Home Agent (HHA). 4 * 5 * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. 6 * Author: Yicong Yang <yangyicong@hisilicon.com> 7 * Yushan Wang <wangyushan12@huawei.com> 8 * 9 * A system typically contains multiple HHAs. Each is responsible for a subset 10 * of the physical addresses in the system, but interleave can make the mapping 11 * from a particular cache line to a responsible HHA complex. As such no 12 * filtering is done in the driver, with the hardware being responsible for 13 * responding with success for even if it was not responsible for any addresses 14 * in the range on which the operation was requested. 15 */ 16 17 #include <linux/bitfield.h> 18 #include <linux/cache_coherency.h> 19 #include <linux/dev_printk.h> 20 #include <linux/init.h> 21 #include <linux/io.h> 22 #include <linux/iopoll.h> 23 #include <linux/kernel.h> 24 #include <linux/memregion.h> 25 #include <linux/module.h> 26 #include <linux/mutex.h> 27 #include <linux/platform_device.h> 28 29 #define HISI_HHA_CTRL 0x5004 30 #define HISI_HHA_CTRL_EN BIT(0) 31 #define HISI_HHA_CTRL_RANGE BIT(1) 32 #define HISI_HHA_CTRL_TYPE GENMASK(3, 2) 33 #define HISI_HHA_START_L 0x5008 34 #define HISI_HHA_START_H 0x500c 35 #define HISI_HHA_LEN_L 0x5010 36 #define HISI_HHA_LEN_H 0x5014 37 38 /* The maintain operation performs in a 128 Byte granularity */ 39 #define HISI_HHA_MAINT_ALIGN 128 40 41 #define HISI_HHA_POLL_GAP_US 10 42 #define HISI_HHA_POLL_TIMEOUT_US 50000 43 44 struct hisi_soc_hha { 45 /* Must be first element */ 46 struct cache_coherency_ops_inst cci; 47 /* Locks HHA instance to forbid overlapping access. */ 48 struct mutex lock; 49 void __iomem *base; 50 }; 51 52 static bool hisi_hha_cache_maintain_wait_finished(struct hisi_soc_hha *soc_hha) 53 { 54 u32 val; 55 56 return !readl_poll_timeout_atomic(soc_hha->base + HISI_HHA_CTRL, val, 57 !(val & HISI_HHA_CTRL_EN), 58 HISI_HHA_POLL_GAP_US, 59 HISI_HHA_POLL_TIMEOUT_US); 60 } 61 62 static int hisi_soc_hha_wbinv(struct cache_coherency_ops_inst *cci, 63 struct cc_inval_params *invp) 64 { 65 struct hisi_soc_hha *soc_hha = 66 container_of(cci, struct hisi_soc_hha, cci); 67 phys_addr_t top, addr = invp->addr; 68 size_t size = invp->size; 69 u32 reg; 70 71 if (!size) 72 return -EINVAL; 73 74 addr = ALIGN_DOWN(addr, HISI_HHA_MAINT_ALIGN); 75 top = ALIGN(addr + size, HISI_HHA_MAINT_ALIGN); 76 size = top - addr; 77 78 guard(mutex)(&soc_hha->lock); 79 80 if (!hisi_hha_cache_maintain_wait_finished(soc_hha)) 81 return -EBUSY; 82 83 /* 84 * Hardware will search for addresses ranging [addr, addr + size - 1], 85 * last byte included, and perform maintenance in 128 byte granules 86 * on those cachelines which contain the addresses. If a given instance 87 * is either not responsible for a cacheline or that cacheline is not 88 * currently present then the search will fail, no operation will be 89 * necessary and the device will report success. 90 */ 91 size -= 1; 92 93 writel(lower_32_bits(addr), soc_hha->base + HISI_HHA_START_L); 94 writel(upper_32_bits(addr), soc_hha->base + HISI_HHA_START_H); 95 writel(lower_32_bits(size), soc_hha->base + HISI_HHA_LEN_L); 96 writel(upper_32_bits(size), soc_hha->base + HISI_HHA_LEN_H); 97 98 reg = FIELD_PREP(HISI_HHA_CTRL_TYPE, 1); /* Clean Invalid */ 99 reg |= HISI_HHA_CTRL_RANGE | HISI_HHA_CTRL_EN; 100 writel(reg, soc_hha->base + HISI_HHA_CTRL); 101 102 return 0; 103 } 104 105 static int hisi_soc_hha_done(struct cache_coherency_ops_inst *cci) 106 { 107 struct hisi_soc_hha *soc_hha = 108 container_of(cci, struct hisi_soc_hha, cci); 109 110 guard(mutex)(&soc_hha->lock); 111 if (!hisi_hha_cache_maintain_wait_finished(soc_hha)) 112 return -ETIMEDOUT; 113 114 return 0; 115 } 116 117 static const struct cache_coherency_ops hha_ops = { 118 .wbinv = hisi_soc_hha_wbinv, 119 .done = hisi_soc_hha_done, 120 }; 121 122 static int hisi_soc_hha_probe(struct platform_device *pdev) 123 { 124 struct hisi_soc_hha *soc_hha; 125 struct resource *mem; 126 int ret; 127 128 soc_hha = cache_coherency_ops_instance_alloc(&hha_ops, 129 struct hisi_soc_hha, cci); 130 if (!soc_hha) 131 return -ENOMEM; 132 133 platform_set_drvdata(pdev, soc_hha); 134 135 mutex_init(&soc_hha->lock); 136 137 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 138 if (!mem) { 139 ret = -ENOMEM; 140 goto err_free_cci; 141 } 142 143 soc_hha->base = ioremap(mem->start, resource_size(mem)); 144 if (!soc_hha->base) { 145 ret = dev_err_probe(&pdev->dev, -ENOMEM, 146 "failed to remap io memory"); 147 goto err_free_cci; 148 } 149 150 ret = cache_coherency_ops_instance_register(&soc_hha->cci); 151 if (ret) 152 goto err_iounmap; 153 154 return 0; 155 156 err_iounmap: 157 iounmap(soc_hha->base); 158 err_free_cci: 159 cache_coherency_ops_instance_put(&soc_hha->cci); 160 return ret; 161 } 162 163 static void hisi_soc_hha_remove(struct platform_device *pdev) 164 { 165 struct hisi_soc_hha *soc_hha = platform_get_drvdata(pdev); 166 167 cache_coherency_ops_instance_unregister(&soc_hha->cci); 168 iounmap(soc_hha->base); 169 cache_coherency_ops_instance_put(&soc_hha->cci); 170 } 171 172 static const struct acpi_device_id hisi_soc_hha_ids[] = { 173 { "HISI0511", }, 174 { } 175 }; 176 MODULE_DEVICE_TABLE(acpi, hisi_soc_hha_ids); 177 178 static struct platform_driver hisi_soc_hha_driver = { 179 .driver = { 180 .name = "hisi_soc_hha", 181 .acpi_match_table = hisi_soc_hha_ids, 182 }, 183 .probe = hisi_soc_hha_probe, 184 .remove = hisi_soc_hha_remove, 185 }; 186 187 module_platform_driver(hisi_soc_hha_driver); 188 189 MODULE_IMPORT_NS("CACHE_COHERENCY"); 190 MODULE_DESCRIPTION("HiSilicon Hydra Home Agent driver supporting cache maintenance"); 191 MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>"); 192 MODULE_AUTHOR("Yushan Wang <wangyushan12@huawei.com>"); 193 MODULE_LICENSE("GPL"); 194