162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DMA translation between STA2x11 AMBA memory mapping and the x86 memory mapping 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * ST Microelectronics ConneXt (STA2X11/STA2X10) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2010-2011 Wind River Systems, Inc. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/pci_ids.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 1562306a36Sopenharmony_ci#include <linux/swiotlb.h> 1662306a36Sopenharmony_ci#include <asm/iommu.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define STA2X11_SWIOTLB_SIZE (4*1024*1024) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * We build a list of bus numbers that are under the ConneXt. The 2262306a36Sopenharmony_ci * main bridge hosts 4 busses, which are the 4 endpoints, in order. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#define STA2X11_NR_EP 4 /* 0..3 included */ 2562306a36Sopenharmony_ci#define STA2X11_NR_FUNCS 8 /* 0..7 included */ 2662306a36Sopenharmony_ci#define STA2X11_AMBA_SIZE (512 << 20) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct sta2x11_ahb_regs { /* saved during suspend */ 2962306a36Sopenharmony_ci u32 base, pexlbase, pexhbase, crw; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct sta2x11_mapping { 3362306a36Sopenharmony_ci int is_suspended; 3462306a36Sopenharmony_ci struct sta2x11_ahb_regs regs[STA2X11_NR_FUNCS]; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct sta2x11_instance { 3862306a36Sopenharmony_ci struct list_head list; 3962306a36Sopenharmony_ci int bus0; 4062306a36Sopenharmony_ci struct sta2x11_mapping map[STA2X11_NR_EP]; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic LIST_HEAD(sta2x11_instance_list); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* At probe time, record new instances of this bridge (likely one only) */ 4662306a36Sopenharmony_cistatic void sta2x11_new_instance(struct pci_dev *pdev) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct sta2x11_instance *instance; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci instance = kzalloc(sizeof(*instance), GFP_ATOMIC); 5162306a36Sopenharmony_ci if (!instance) 5262306a36Sopenharmony_ci return; 5362306a36Sopenharmony_ci /* This has a subordinate bridge, with 4 more-subordinate ones */ 5462306a36Sopenharmony_ci instance->bus0 = pdev->subordinate->number + 1; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (list_empty(&sta2x11_instance_list)) { 5762306a36Sopenharmony_ci int size = STA2X11_SWIOTLB_SIZE; 5862306a36Sopenharmony_ci /* First instance: register your own swiotlb area */ 5962306a36Sopenharmony_ci dev_info(&pdev->dev, "Using SWIOTLB (size %i)\n", size); 6062306a36Sopenharmony_ci if (swiotlb_init_late(size, GFP_DMA, NULL)) 6162306a36Sopenharmony_ci dev_emerg(&pdev->dev, "init swiotlb failed\n"); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci list_add(&instance->list, &sta2x11_instance_list); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ciDECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, 0xcc17, sta2x11_new_instance); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * Utility functions used in this file from below 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistatic struct sta2x11_instance *sta2x11_pdev_to_instance(struct pci_dev *pdev) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct sta2x11_instance *instance; 7362306a36Sopenharmony_ci int ep; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci list_for_each_entry(instance, &sta2x11_instance_list, list) { 7662306a36Sopenharmony_ci ep = pdev->bus->number - instance->bus0; 7762306a36Sopenharmony_ci if (ep >= 0 && ep < STA2X11_NR_EP) 7862306a36Sopenharmony_ci return instance; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci return NULL; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int sta2x11_pdev_to_ep(struct pci_dev *pdev) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct sta2x11_instance *instance; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci instance = sta2x11_pdev_to_instance(pdev); 8862306a36Sopenharmony_ci if (!instance) 8962306a36Sopenharmony_ci return -1; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return pdev->bus->number - instance->bus0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* This is exported, as some devices need to access the MFD registers */ 9562306a36Sopenharmony_cistruct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return sta2x11_pdev_to_instance(pdev); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ciEXPORT_SYMBOL(sta2x11_get_instance); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* At setup time, we use our own ops if the device is a ConneXt one */ 10262306a36Sopenharmony_cistatic void sta2x11_setup_pdev(struct pci_dev *pdev) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (!instance) /* either a sta2x11 bridge or another ST device */ 10762306a36Sopenharmony_ci return; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* We must enable all devices as master, for audio DMA to work */ 11062306a36Sopenharmony_ci pci_set_master(pdev); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ciDECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_setup_pdev); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * At boot we must set up the mappings for the pcie-to-amba bridge. 11662306a36Sopenharmony_ci * It involves device access, and the same happens at suspend/resume time 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define AHB_MAPB 0xCA4 12062306a36Sopenharmony_ci#define AHB_CRW(i) (AHB_MAPB + 0 + (i) * 0x10) 12162306a36Sopenharmony_ci#define AHB_CRW_SZMASK 0xfffffc00UL 12262306a36Sopenharmony_ci#define AHB_CRW_ENABLE (1 << 0) 12362306a36Sopenharmony_ci#define AHB_CRW_WTYPE_MEM (2 << 1) 12462306a36Sopenharmony_ci#define AHB_CRW_ROE (1UL << 3) /* Relax Order Ena */ 12562306a36Sopenharmony_ci#define AHB_CRW_NSE (1UL << 4) /* No Snoop Enable */ 12662306a36Sopenharmony_ci#define AHB_BASE(i) (AHB_MAPB + 4 + (i) * 0x10) 12762306a36Sopenharmony_ci#define AHB_PEXLBASE(i) (AHB_MAPB + 8 + (i) * 0x10) 12862306a36Sopenharmony_ci#define AHB_PEXHBASE(i) (AHB_MAPB + 12 + (i) * 0x10) 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* At probe time, enable mapping for each endpoint, using the pdev */ 13162306a36Sopenharmony_cistatic void sta2x11_map_ep(struct pci_dev *pdev) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev); 13462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 13562306a36Sopenharmony_ci u32 amba_base, max_amba_addr; 13662306a36Sopenharmony_ci int i, ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (!instance) 13962306a36Sopenharmony_ci return; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci pci_read_config_dword(pdev, AHB_BASE(0), &amba_base); 14262306a36Sopenharmony_ci max_amba_addr = amba_base + STA2X11_AMBA_SIZE - 1; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ret = dma_direct_set_offset(dev, 0, amba_base, STA2X11_AMBA_SIZE); 14562306a36Sopenharmony_ci if (ret) 14662306a36Sopenharmony_ci dev_err(dev, "sta2x11: could not set DMA offset\n"); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci dev->bus_dma_limit = max_amba_addr; 14962306a36Sopenharmony_ci dma_set_mask_and_coherent(&pdev->dev, max_amba_addr); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Configure AHB mapping */ 15262306a36Sopenharmony_ci pci_write_config_dword(pdev, AHB_PEXLBASE(0), 0); 15362306a36Sopenharmony_ci pci_write_config_dword(pdev, AHB_PEXHBASE(0), 0); 15462306a36Sopenharmony_ci pci_write_config_dword(pdev, AHB_CRW(0), STA2X11_AMBA_SIZE | 15562306a36Sopenharmony_ci AHB_CRW_WTYPE_MEM | AHB_CRW_ENABLE); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Disable all the other windows */ 15862306a36Sopenharmony_ci for (i = 1; i < STA2X11_NR_FUNCS; i++) 15962306a36Sopenharmony_ci pci_write_config_dword(pdev, AHB_CRW(i), 0); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci dev_info(&pdev->dev, 16262306a36Sopenharmony_ci "sta2x11: Map EP %i: AMBA address %#8x-%#8x\n", 16362306a36Sopenharmony_ci sta2x11_pdev_to_ep(pdev), amba_base, max_amba_addr); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ciDECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_map_ep); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#ifdef CONFIG_PM /* Some register values must be saved and restored */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct sta2x11_instance *instance; 17262306a36Sopenharmony_ci int ep; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci instance = sta2x11_pdev_to_instance(pdev); 17562306a36Sopenharmony_ci if (!instance) 17662306a36Sopenharmony_ci return NULL; 17762306a36Sopenharmony_ci ep = sta2x11_pdev_to_ep(pdev); 17862306a36Sopenharmony_ci return instance->map + ep; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void suspend_mapping(struct pci_dev *pdev) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev); 18462306a36Sopenharmony_ci int i; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!map) 18762306a36Sopenharmony_ci return; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (map->is_suspended) 19062306a36Sopenharmony_ci return; 19162306a36Sopenharmony_ci map->is_suspended = 1; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Save all window configs */ 19462306a36Sopenharmony_ci for (i = 0; i < STA2X11_NR_FUNCS; i++) { 19562306a36Sopenharmony_ci struct sta2x11_ahb_regs *regs = map->regs + i; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci pci_read_config_dword(pdev, AHB_BASE(i), ®s->base); 19862306a36Sopenharmony_ci pci_read_config_dword(pdev, AHB_PEXLBASE(i), ®s->pexlbase); 19962306a36Sopenharmony_ci pci_read_config_dword(pdev, AHB_PEXHBASE(i), ®s->pexhbase); 20062306a36Sopenharmony_ci pci_read_config_dword(pdev, AHB_CRW(i), ®s->crw); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ciDECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, suspend_mapping); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void resume_mapping(struct pci_dev *pdev) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev); 20862306a36Sopenharmony_ci int i; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (!map) 21162306a36Sopenharmony_ci return; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!map->is_suspended) 21562306a36Sopenharmony_ci goto out; 21662306a36Sopenharmony_ci map->is_suspended = 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Restore all window configs */ 21962306a36Sopenharmony_ci for (i = 0; i < STA2X11_NR_FUNCS; i++) { 22062306a36Sopenharmony_ci struct sta2x11_ahb_regs *regs = map->regs + i; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci pci_write_config_dword(pdev, AHB_BASE(i), regs->base); 22362306a36Sopenharmony_ci pci_write_config_dword(pdev, AHB_PEXLBASE(i), regs->pexlbase); 22462306a36Sopenharmony_ci pci_write_config_dword(pdev, AHB_PEXHBASE(i), regs->pexhbase); 22562306a36Sopenharmony_ci pci_write_config_dword(pdev, AHB_CRW(i), regs->crw); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ciout: 22862306a36Sopenharmony_ci pci_set_master(pdev); /* Like at boot, enable master on all devices */ 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ciDECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, resume_mapping); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci#endif /* CONFIG_PM */ 233