brintos

brintos / linux-shallow public Read only

0
0
Text · 8.5 KiB · afa1117 Raw
334 lines · c
1// SPDX-License-Identifier: GPL-2.0-only OR MIT2/*3 * Apple SART device driver4 * Copyright (C) The Asahi Linux Contributors5 *6 * Apple SART is a simple address filter for some DMA transactions.7 * Regions of physical memory must be added to the SART's allow8 * list before any DMA can target these. Unlike a proper9 * IOMMU no remapping can be done and special support in the10 * consumer driver is required since not all DMA transactions of11 * a single device are subject to SART filtering.12 */13 14#include <linux/soc/apple/sart.h>15#include <linux/atomic.h>16#include <linux/bits.h>17#include <linux/bitfield.h>18#include <linux/device.h>19#include <linux/io.h>20#include <linux/module.h>21#include <linux/of.h>22#include <linux/of_platform.h>23#include <linux/platform_device.h>24#include <linux/types.h>25 26#define APPLE_SART_MAX_ENTRIES 1627 28/* This is probably a bitfield but the exact meaning of each bit is unknown. */29#define APPLE_SART_FLAGS_ALLOW 0xff30 31/* SARTv2 registers */32#define APPLE_SART2_CONFIG(idx)	      (0x00 + 4 * (idx))33#define APPLE_SART2_CONFIG_FLAGS      GENMASK(31, 24)34#define APPLE_SART2_CONFIG_SIZE	      GENMASK(23, 0)35#define APPLE_SART2_CONFIG_SIZE_SHIFT 1236#define APPLE_SART2_CONFIG_SIZE_MAX   GENMASK(23, 0)37 38#define APPLE_SART2_PADDR(idx)	(0x40 + 4 * (idx))39#define APPLE_SART2_PADDR_SHIFT 1240 41/* SARTv3 registers */42#define APPLE_SART3_CONFIG(idx) (0x00 + 4 * (idx))43 44#define APPLE_SART3_PADDR(idx)	(0x40 + 4 * (idx))45#define APPLE_SART3_PADDR_SHIFT 1246 47#define APPLE_SART3_SIZE(idx)  (0x80 + 4 * (idx))48#define APPLE_SART3_SIZE_SHIFT 1249#define APPLE_SART3_SIZE_MAX   GENMASK(29, 0)50 51struct apple_sart_ops {52	void (*get_entry)(struct apple_sart *sart, int index, u8 *flags,53			  phys_addr_t *paddr, size_t *size);54	void (*set_entry)(struct apple_sart *sart, int index, u8 flags,55			  phys_addr_t paddr_shifted, size_t size_shifted);56	unsigned int size_shift;57	unsigned int paddr_shift;58	size_t size_max;59};60 61struct apple_sart {62	struct device *dev;63	void __iomem *regs;64 65	const struct apple_sart_ops *ops;66 67	unsigned long protected_entries;68	unsigned long used_entries;69};70 71static void sart2_get_entry(struct apple_sart *sart, int index, u8 *flags,72			    phys_addr_t *paddr, size_t *size)73{74	u32 cfg = readl(sart->regs + APPLE_SART2_CONFIG(index));75	phys_addr_t paddr_ = readl(sart->regs + APPLE_SART2_PADDR(index));76	size_t size_ = FIELD_GET(APPLE_SART2_CONFIG_SIZE, cfg);77 78	*flags = FIELD_GET(APPLE_SART2_CONFIG_FLAGS, cfg);79	*size = size_ << APPLE_SART2_CONFIG_SIZE_SHIFT;80	*paddr = paddr_ << APPLE_SART2_PADDR_SHIFT;81}82 83static void sart2_set_entry(struct apple_sart *sart, int index, u8 flags,84			    phys_addr_t paddr_shifted, size_t size_shifted)85{86	u32 cfg;87 88	cfg = FIELD_PREP(APPLE_SART2_CONFIG_FLAGS, flags);89	cfg |= FIELD_PREP(APPLE_SART2_CONFIG_SIZE, size_shifted);90 91	writel(paddr_shifted, sart->regs + APPLE_SART2_PADDR(index));92	writel(cfg, sart->regs + APPLE_SART2_CONFIG(index));93}94 95static struct apple_sart_ops sart_ops_v2 = {96	.get_entry = sart2_get_entry,97	.set_entry = sart2_set_entry,98	.size_shift = APPLE_SART2_CONFIG_SIZE_SHIFT,99	.paddr_shift = APPLE_SART2_PADDR_SHIFT,100	.size_max = APPLE_SART2_CONFIG_SIZE_MAX,101};102 103static void sart3_get_entry(struct apple_sart *sart, int index, u8 *flags,104			    phys_addr_t *paddr, size_t *size)105{106	phys_addr_t paddr_ = readl(sart->regs + APPLE_SART3_PADDR(index));107	size_t size_ = readl(sart->regs + APPLE_SART3_SIZE(index));108 109	*flags = readl(sart->regs + APPLE_SART3_CONFIG(index));110	*size = size_ << APPLE_SART3_SIZE_SHIFT;111	*paddr = paddr_ << APPLE_SART3_PADDR_SHIFT;112}113 114static void sart3_set_entry(struct apple_sart *sart, int index, u8 flags,115			    phys_addr_t paddr_shifted, size_t size_shifted)116{117	writel(paddr_shifted, sart->regs + APPLE_SART3_PADDR(index));118	writel(size_shifted, sart->regs + APPLE_SART3_SIZE(index));119	writel(flags, sart->regs + APPLE_SART3_CONFIG(index));120}121 122static struct apple_sart_ops sart_ops_v3 = {123	.get_entry = sart3_get_entry,124	.set_entry = sart3_set_entry,125	.size_shift = APPLE_SART3_SIZE_SHIFT,126	.paddr_shift = APPLE_SART3_PADDR_SHIFT,127	.size_max = APPLE_SART3_SIZE_MAX,128};129 130static int apple_sart_probe(struct platform_device *pdev)131{132	int i;133	struct apple_sart *sart;134	struct device *dev = &pdev->dev;135 136	sart = devm_kzalloc(dev, sizeof(*sart), GFP_KERNEL);137	if (!sart)138		return -ENOMEM;139 140	sart->dev = dev;141	sart->ops = of_device_get_match_data(dev);142 143	sart->regs = devm_platform_ioremap_resource(pdev, 0);144	if (IS_ERR(sart->regs))145		return PTR_ERR(sart->regs);146 147	for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {148		u8 flags;149		size_t size;150		phys_addr_t paddr;151 152		sart->ops->get_entry(sart, i, &flags, &paddr, &size);153 154		if (!flags)155			continue;156 157		dev_dbg(sart->dev,158			"SART bootloader entry: index %02d; flags: 0x%02x; paddr: %pa; size: 0x%zx\n",159			i, flags, &paddr, size);160		set_bit(i, &sart->protected_entries);161	}162 163	platform_set_drvdata(pdev, sart);164	return 0;165}166 167static void apple_sart_put_device(void *dev)168{169	put_device(dev);170}171 172struct apple_sart *devm_apple_sart_get(struct device *dev)173{174	struct device_node *sart_node;175	struct platform_device *sart_pdev;176	struct apple_sart *sart;177	int ret;178 179	sart_node = of_parse_phandle(dev->of_node, "apple,sart", 0);180	if (!sart_node)181		return ERR_PTR(-ENODEV);182 183	sart_pdev = of_find_device_by_node(sart_node);184	of_node_put(sart_node);185 186	if (!sart_pdev)187		return ERR_PTR(-ENODEV);188 189	sart = dev_get_drvdata(&sart_pdev->dev);190	if (!sart) {191		put_device(&sart_pdev->dev);192		return ERR_PTR(-EPROBE_DEFER);193	}194 195	ret = devm_add_action_or_reset(dev, apple_sart_put_device,196				       &sart_pdev->dev);197	if (ret)198		return ERR_PTR(ret);199 200	device_link_add(dev, &sart_pdev->dev,201			DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER);202 203	return sart;204}205EXPORT_SYMBOL_GPL(devm_apple_sart_get);206 207static int sart_set_entry(struct apple_sart *sart, int index, u8 flags,208			  phys_addr_t paddr, size_t size)209{210	if (size & ((1 << sart->ops->size_shift) - 1))211		return -EINVAL;212	if (paddr & ((1 << sart->ops->paddr_shift) - 1))213		return -EINVAL;214 215	paddr >>= sart->ops->size_shift;216	size >>= sart->ops->paddr_shift;217 218	if (size > sart->ops->size_max)219		return -EINVAL;220 221	sart->ops->set_entry(sart, index, flags, paddr, size);222	return 0;223}224 225int apple_sart_add_allowed_region(struct apple_sart *sart, phys_addr_t paddr,226				  size_t size)227{228	int i, ret;229 230	for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {231		if (test_bit(i, &sart->protected_entries))232			continue;233		if (test_and_set_bit(i, &sart->used_entries))234			continue;235 236		ret = sart_set_entry(sart, i, APPLE_SART_FLAGS_ALLOW, paddr,237				     size);238		if (ret) {239			dev_dbg(sart->dev,240				"unable to set entry %d to [%pa, 0x%zx]\n",241				i, &paddr, size);242			clear_bit(i, &sart->used_entries);243			return ret;244		}245 246		dev_dbg(sart->dev, "wrote [%pa, 0x%zx] to %d\n", &paddr, size,247			i);248		return 0;249	}250 251	dev_warn(sart->dev,252		 "no free entries left to add [paddr: 0x%pa, size: 0x%zx]\n",253		 &paddr, size);254 255	return -EBUSY;256}257EXPORT_SYMBOL_GPL(apple_sart_add_allowed_region);258 259int apple_sart_remove_allowed_region(struct apple_sart *sart, phys_addr_t paddr,260				     size_t size)261{262	int i;263 264	dev_dbg(sart->dev,265		"will remove [paddr: %pa, size: 0x%zx] from allowed regions\n",266		&paddr, size);267 268	for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {269		u8 eflags;270		size_t esize;271		phys_addr_t epaddr;272 273		if (test_bit(i, &sart->protected_entries))274			continue;275 276		sart->ops->get_entry(sart, i, &eflags, &epaddr, &esize);277 278		if (epaddr != paddr || esize != size)279			continue;280 281		sart->ops->set_entry(sart, i, 0, 0, 0);282 283		clear_bit(i, &sart->used_entries);284		dev_dbg(sart->dev, "cleared entry %d\n", i);285		return 0;286	}287 288	dev_warn(sart->dev, "entry [paddr: 0x%pa, size: 0x%zx] not found\n",289		 &paddr, size);290 291	return -EINVAL;292}293EXPORT_SYMBOL_GPL(apple_sart_remove_allowed_region);294 295static void apple_sart_shutdown(struct platform_device *pdev)296{297	struct apple_sart *sart = dev_get_drvdata(&pdev->dev);298	int i;299 300	for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {301		if (test_bit(i, &sart->protected_entries))302			continue;303 304		sart->ops->set_entry(sart, i, 0, 0, 0);305	}306}307 308static const struct of_device_id apple_sart_of_match[] = {309	{310		.compatible = "apple,t6000-sart",311		.data = &sart_ops_v3,312	},313	{314		.compatible = "apple,t8103-sart",315		.data = &sart_ops_v2,316	},317	{}318};319MODULE_DEVICE_TABLE(of, apple_sart_of_match);320 321static struct platform_driver apple_sart_driver = {322	.driver = {323		.name = "apple-sart",324		.of_match_table = apple_sart_of_match,325	},326	.probe = apple_sart_probe,327	.shutdown = apple_sart_shutdown,328};329module_platform_driver(apple_sart_driver);330 331MODULE_LICENSE("Dual MIT/GPL");332MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");333MODULE_DESCRIPTION("Apple SART driver");334