162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only OR MIT 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Apple SART device driver 462306a36Sopenharmony_ci * Copyright (C) The Asahi Linux Contributors 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Apple SART is a simple address filter for some DMA transactions. 762306a36Sopenharmony_ci * Regions of physical memory must be added to the SART's allow 862306a36Sopenharmony_ci * list before any DMA can target these. Unlike a proper 962306a36Sopenharmony_ci * IOMMU no remapping can be done and special support in the 1062306a36Sopenharmony_ci * consumer driver is required since not all DMA transactions of 1162306a36Sopenharmony_ci * a single device are subject to SART filtering. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/soc/apple/sart.h> 1562306a36Sopenharmony_ci#include <linux/atomic.h> 1662306a36Sopenharmony_ci#include <linux/bits.h> 1762306a36Sopenharmony_ci#include <linux/bitfield.h> 1862306a36Sopenharmony_ci#include <linux/device.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include <linux/of_platform.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/types.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define APPLE_SART_MAX_ENTRIES 16 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* This is probably a bitfield but the exact meaning of each bit is unknown. */ 2962306a36Sopenharmony_ci#define APPLE_SART_FLAGS_ALLOW 0xff 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* SARTv2 registers */ 3262306a36Sopenharmony_ci#define APPLE_SART2_CONFIG(idx) (0x00 + 4 * (idx)) 3362306a36Sopenharmony_ci#define APPLE_SART2_CONFIG_FLAGS GENMASK(31, 24) 3462306a36Sopenharmony_ci#define APPLE_SART2_CONFIG_SIZE GENMASK(23, 0) 3562306a36Sopenharmony_ci#define APPLE_SART2_CONFIG_SIZE_SHIFT 12 3662306a36Sopenharmony_ci#define APPLE_SART2_CONFIG_SIZE_MAX GENMASK(23, 0) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define APPLE_SART2_PADDR(idx) (0x40 + 4 * (idx)) 3962306a36Sopenharmony_ci#define APPLE_SART2_PADDR_SHIFT 12 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* SARTv3 registers */ 4262306a36Sopenharmony_ci#define APPLE_SART3_CONFIG(idx) (0x00 + 4 * (idx)) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define APPLE_SART3_PADDR(idx) (0x40 + 4 * (idx)) 4562306a36Sopenharmony_ci#define APPLE_SART3_PADDR_SHIFT 12 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define APPLE_SART3_SIZE(idx) (0x80 + 4 * (idx)) 4862306a36Sopenharmony_ci#define APPLE_SART3_SIZE_SHIFT 12 4962306a36Sopenharmony_ci#define APPLE_SART3_SIZE_MAX GENMASK(29, 0) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct apple_sart_ops { 5262306a36Sopenharmony_ci void (*get_entry)(struct apple_sart *sart, int index, u8 *flags, 5362306a36Sopenharmony_ci phys_addr_t *paddr, size_t *size); 5462306a36Sopenharmony_ci void (*set_entry)(struct apple_sart *sart, int index, u8 flags, 5562306a36Sopenharmony_ci phys_addr_t paddr_shifted, size_t size_shifted); 5662306a36Sopenharmony_ci unsigned int size_shift; 5762306a36Sopenharmony_ci unsigned int paddr_shift; 5862306a36Sopenharmony_ci size_t size_max; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct apple_sart { 6262306a36Sopenharmony_ci struct device *dev; 6362306a36Sopenharmony_ci void __iomem *regs; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci const struct apple_sart_ops *ops; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci unsigned long protected_entries; 6862306a36Sopenharmony_ci unsigned long used_entries; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void sart2_get_entry(struct apple_sart *sart, int index, u8 *flags, 7262306a36Sopenharmony_ci phys_addr_t *paddr, size_t *size) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci u32 cfg = readl(sart->regs + APPLE_SART2_CONFIG(index)); 7562306a36Sopenharmony_ci phys_addr_t paddr_ = readl(sart->regs + APPLE_SART2_PADDR(index)); 7662306a36Sopenharmony_ci size_t size_ = FIELD_GET(APPLE_SART2_CONFIG_SIZE, cfg); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci *flags = FIELD_GET(APPLE_SART2_CONFIG_FLAGS, cfg); 7962306a36Sopenharmony_ci *size = size_ << APPLE_SART2_CONFIG_SIZE_SHIFT; 8062306a36Sopenharmony_ci *paddr = paddr_ << APPLE_SART2_PADDR_SHIFT; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void sart2_set_entry(struct apple_sart *sart, int index, u8 flags, 8462306a36Sopenharmony_ci phys_addr_t paddr_shifted, size_t size_shifted) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci u32 cfg; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci cfg = FIELD_PREP(APPLE_SART2_CONFIG_FLAGS, flags); 8962306a36Sopenharmony_ci cfg |= FIELD_PREP(APPLE_SART2_CONFIG_SIZE, size_shifted); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci writel(paddr_shifted, sart->regs + APPLE_SART2_PADDR(index)); 9262306a36Sopenharmony_ci writel(cfg, sart->regs + APPLE_SART2_CONFIG(index)); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic struct apple_sart_ops sart_ops_v2 = { 9662306a36Sopenharmony_ci .get_entry = sart2_get_entry, 9762306a36Sopenharmony_ci .set_entry = sart2_set_entry, 9862306a36Sopenharmony_ci .size_shift = APPLE_SART2_CONFIG_SIZE_SHIFT, 9962306a36Sopenharmony_ci .paddr_shift = APPLE_SART2_PADDR_SHIFT, 10062306a36Sopenharmony_ci .size_max = APPLE_SART2_CONFIG_SIZE_MAX, 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void sart3_get_entry(struct apple_sart *sart, int index, u8 *flags, 10462306a36Sopenharmony_ci phys_addr_t *paddr, size_t *size) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci phys_addr_t paddr_ = readl(sart->regs + APPLE_SART3_PADDR(index)); 10762306a36Sopenharmony_ci size_t size_ = readl(sart->regs + APPLE_SART3_SIZE(index)); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci *flags = readl(sart->regs + APPLE_SART3_CONFIG(index)); 11062306a36Sopenharmony_ci *size = size_ << APPLE_SART3_SIZE_SHIFT; 11162306a36Sopenharmony_ci *paddr = paddr_ << APPLE_SART3_PADDR_SHIFT; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void sart3_set_entry(struct apple_sart *sart, int index, u8 flags, 11562306a36Sopenharmony_ci phys_addr_t paddr_shifted, size_t size_shifted) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci writel(paddr_shifted, sart->regs + APPLE_SART3_PADDR(index)); 11862306a36Sopenharmony_ci writel(size_shifted, sart->regs + APPLE_SART3_SIZE(index)); 11962306a36Sopenharmony_ci writel(flags, sart->regs + APPLE_SART3_CONFIG(index)); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic struct apple_sart_ops sart_ops_v3 = { 12362306a36Sopenharmony_ci .get_entry = sart3_get_entry, 12462306a36Sopenharmony_ci .set_entry = sart3_set_entry, 12562306a36Sopenharmony_ci .size_shift = APPLE_SART3_SIZE_SHIFT, 12662306a36Sopenharmony_ci .paddr_shift = APPLE_SART3_PADDR_SHIFT, 12762306a36Sopenharmony_ci .size_max = APPLE_SART3_SIZE_MAX, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int apple_sart_probe(struct platform_device *pdev) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci int i; 13362306a36Sopenharmony_ci struct apple_sart *sart; 13462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci sart = devm_kzalloc(dev, sizeof(*sart), GFP_KERNEL); 13762306a36Sopenharmony_ci if (!sart) 13862306a36Sopenharmony_ci return -ENOMEM; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci sart->dev = dev; 14162306a36Sopenharmony_ci sart->ops = of_device_get_match_data(dev); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci sart->regs = devm_platform_ioremap_resource(pdev, 0); 14462306a36Sopenharmony_ci if (IS_ERR(sart->regs)) 14562306a36Sopenharmony_ci return PTR_ERR(sart->regs); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { 14862306a36Sopenharmony_ci u8 flags; 14962306a36Sopenharmony_ci size_t size; 15062306a36Sopenharmony_ci phys_addr_t paddr; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci sart->ops->get_entry(sart, i, &flags, &paddr, &size); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!flags) 15562306a36Sopenharmony_ci continue; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci dev_dbg(sart->dev, 15862306a36Sopenharmony_ci "SART bootloader entry: index %02d; flags: 0x%02x; paddr: %pa; size: 0x%zx\n", 15962306a36Sopenharmony_ci i, flags, &paddr, size); 16062306a36Sopenharmony_ci set_bit(i, &sart->protected_entries); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci platform_set_drvdata(pdev, sart); 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void apple_sart_put_device(void *dev) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci put_device(dev); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistruct apple_sart *devm_apple_sart_get(struct device *dev) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct device_node *sart_node; 17562306a36Sopenharmony_ci struct platform_device *sart_pdev; 17662306a36Sopenharmony_ci struct apple_sart *sart; 17762306a36Sopenharmony_ci int ret; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci sart_node = of_parse_phandle(dev->of_node, "apple,sart", 0); 18062306a36Sopenharmony_ci if (!sart_node) 18162306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci sart_pdev = of_find_device_by_node(sart_node); 18462306a36Sopenharmony_ci of_node_put(sart_node); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!sart_pdev) 18762306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci sart = dev_get_drvdata(&sart_pdev->dev); 19062306a36Sopenharmony_ci if (!sart) { 19162306a36Sopenharmony_ci put_device(&sart_pdev->dev); 19262306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, apple_sart_put_device, 19662306a36Sopenharmony_ci &sart_pdev->dev); 19762306a36Sopenharmony_ci if (ret) 19862306a36Sopenharmony_ci return ERR_PTR(ret); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci device_link_add(dev, &sart_pdev->dev, 20162306a36Sopenharmony_ci DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return sart; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_apple_sart_get); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int sart_set_entry(struct apple_sart *sart, int index, u8 flags, 20862306a36Sopenharmony_ci phys_addr_t paddr, size_t size) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci if (size & ((1 << sart->ops->size_shift) - 1)) 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci if (paddr & ((1 << sart->ops->paddr_shift) - 1)) 21362306a36Sopenharmony_ci return -EINVAL; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci paddr >>= sart->ops->size_shift; 21662306a36Sopenharmony_ci size >>= sart->ops->paddr_shift; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (size > sart->ops->size_max) 21962306a36Sopenharmony_ci return -EINVAL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci sart->ops->set_entry(sart, index, flags, paddr, size); 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ciint apple_sart_add_allowed_region(struct apple_sart *sart, phys_addr_t paddr, 22662306a36Sopenharmony_ci size_t size) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci int i, ret; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { 23162306a36Sopenharmony_ci if (test_bit(i, &sart->protected_entries)) 23262306a36Sopenharmony_ci continue; 23362306a36Sopenharmony_ci if (test_and_set_bit(i, &sart->used_entries)) 23462306a36Sopenharmony_ci continue; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = sart_set_entry(sart, i, APPLE_SART_FLAGS_ALLOW, paddr, 23762306a36Sopenharmony_ci size); 23862306a36Sopenharmony_ci if (ret) { 23962306a36Sopenharmony_ci dev_dbg(sart->dev, 24062306a36Sopenharmony_ci "unable to set entry %d to [%pa, 0x%zx]\n", 24162306a36Sopenharmony_ci i, &paddr, size); 24262306a36Sopenharmony_ci clear_bit(i, &sart->used_entries); 24362306a36Sopenharmony_ci return ret; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci dev_dbg(sart->dev, "wrote [%pa, 0x%zx] to %d\n", &paddr, size, 24762306a36Sopenharmony_ci i); 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci dev_warn(sart->dev, 25262306a36Sopenharmony_ci "no free entries left to add [paddr: 0x%pa, size: 0x%zx]\n", 25362306a36Sopenharmony_ci &paddr, size); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return -EBUSY; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apple_sart_add_allowed_region); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciint apple_sart_remove_allowed_region(struct apple_sart *sart, phys_addr_t paddr, 26062306a36Sopenharmony_ci size_t size) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci int i; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci dev_dbg(sart->dev, 26562306a36Sopenharmony_ci "will remove [paddr: %pa, size: 0x%zx] from allowed regions\n", 26662306a36Sopenharmony_ci &paddr, size); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { 26962306a36Sopenharmony_ci u8 eflags; 27062306a36Sopenharmony_ci size_t esize; 27162306a36Sopenharmony_ci phys_addr_t epaddr; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (test_bit(i, &sart->protected_entries)) 27462306a36Sopenharmony_ci continue; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci sart->ops->get_entry(sart, i, &eflags, &epaddr, &esize); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (epaddr != paddr || esize != size) 27962306a36Sopenharmony_ci continue; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci sart->ops->set_entry(sart, i, 0, 0, 0); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci clear_bit(i, &sart->used_entries); 28462306a36Sopenharmony_ci dev_dbg(sart->dev, "cleared entry %d\n", i); 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci dev_warn(sart->dev, "entry [paddr: 0x%pa, size: 0x%zx] not found\n", 28962306a36Sopenharmony_ci &paddr, size); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return -EINVAL; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apple_sart_remove_allowed_region); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void apple_sart_shutdown(struct platform_device *pdev) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct apple_sart *sart = dev_get_drvdata(&pdev->dev); 29862306a36Sopenharmony_ci int i; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { 30162306a36Sopenharmony_ci if (test_bit(i, &sart->protected_entries)) 30262306a36Sopenharmony_ci continue; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci sart->ops->set_entry(sart, i, 0, 0, 0); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic const struct of_device_id apple_sart_of_match[] = { 30962306a36Sopenharmony_ci { 31062306a36Sopenharmony_ci .compatible = "apple,t6000-sart", 31162306a36Sopenharmony_ci .data = &sart_ops_v3, 31262306a36Sopenharmony_ci }, 31362306a36Sopenharmony_ci { 31462306a36Sopenharmony_ci .compatible = "apple,t8103-sart", 31562306a36Sopenharmony_ci .data = &sart_ops_v2, 31662306a36Sopenharmony_ci }, 31762306a36Sopenharmony_ci {} 31862306a36Sopenharmony_ci}; 31962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, apple_sart_of_match); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic struct platform_driver apple_sart_driver = { 32262306a36Sopenharmony_ci .driver = { 32362306a36Sopenharmony_ci .name = "apple-sart", 32462306a36Sopenharmony_ci .of_match_table = apple_sart_of_match, 32562306a36Sopenharmony_ci }, 32662306a36Sopenharmony_ci .probe = apple_sart_probe, 32762306a36Sopenharmony_ci .shutdown = apple_sart_shutdown, 32862306a36Sopenharmony_ci}; 32962306a36Sopenharmony_cimodule_platform_driver(apple_sart_driver); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL"); 33262306a36Sopenharmony_ciMODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>"); 33362306a36Sopenharmony_ciMODULE_DESCRIPTION("Apple SART driver"); 334