File Index
Files and directories
Sign in
FILE

hisi_soc_hha.c

File preview with syntax highlighting.

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