162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic SH7786 PCI-Express operations. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 - 2010 Paul Mundt 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include "pcie-sh7786.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cienum { 1562306a36Sopenharmony_ci PCI_ACCESS_READ, 1662306a36Sopenharmony_ci PCI_ACCESS_WRITE, 1762306a36Sopenharmony_ci}; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int sh7786_pcie_config_access(unsigned char access_type, 2062306a36Sopenharmony_ci struct pci_bus *bus, unsigned int devfn, int where, u32 *data) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct pci_channel *chan = bus->sysdata; 2362306a36Sopenharmony_ci int dev, func, type, reg; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci dev = PCI_SLOT(devfn); 2662306a36Sopenharmony_ci func = PCI_FUNC(devfn); 2762306a36Sopenharmony_ci type = !!bus->parent; 2862306a36Sopenharmony_ci reg = where & ~3; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (bus->number > 255 || dev > 31 || func > 7) 3162306a36Sopenharmony_ci return PCIBIOS_FUNC_NOT_SUPPORTED; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* 3462306a36Sopenharmony_ci * While each channel has its own memory-mapped extended config 3562306a36Sopenharmony_ci * space, it's generally only accessible when in endpoint mode. 3662306a36Sopenharmony_ci * When in root complex mode, the controller is unable to target 3762306a36Sopenharmony_ci * itself with either type 0 or type 1 accesses, and indeed, any 3862306a36Sopenharmony_ci * controller initiated target transfer to its own config space 3962306a36Sopenharmony_ci * result in a completer abort. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Each channel effectively only supports a single device, but as 4262306a36Sopenharmony_ci * the same channel <-> device access works for any PCI_SLOT() 4362306a36Sopenharmony_ci * value, we cheat a bit here and bind the controller's config 4462306a36Sopenharmony_ci * space to devfn 0 in order to enable self-enumeration. In this 4562306a36Sopenharmony_ci * case the regular PAR/PDR path is sidelined and the mangled 4662306a36Sopenharmony_ci * config access itself is initiated as a SuperHyway transaction. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci if (pci_is_root_bus(bus)) { 4962306a36Sopenharmony_ci if (dev == 0) { 5062306a36Sopenharmony_ci if (access_type == PCI_ACCESS_READ) 5162306a36Sopenharmony_ci *data = pci_read_reg(chan, PCI_REG(reg)); 5262306a36Sopenharmony_ci else 5362306a36Sopenharmony_ci pci_write_reg(chan, *data, PCI_REG(reg)); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 5662306a36Sopenharmony_ci } else if (dev > 1) 5762306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* Clear errors */ 6162306a36Sopenharmony_ci pci_write_reg(chan, pci_read_reg(chan, SH4A_PCIEERRFR), SH4A_PCIEERRFR); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Set the PIO address */ 6462306a36Sopenharmony_ci pci_write_reg(chan, (bus->number << 24) | (dev << 19) | 6562306a36Sopenharmony_ci (func << 16) | reg, SH4A_PCIEPAR); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Enable the configuration access */ 6862306a36Sopenharmony_ci pci_write_reg(chan, (1 << 31) | (type << 8), SH4A_PCIEPCTLR); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Check for errors */ 7162306a36Sopenharmony_ci if (pci_read_reg(chan, SH4A_PCIEERRFR) & 0x10) 7262306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Check for master and target aborts */ 7562306a36Sopenharmony_ci if (pci_read_reg(chan, SH4A_PCIEPCICONF1) & ((1 << 29) | (1 << 28))) 7662306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (access_type == PCI_ACCESS_READ) 7962306a36Sopenharmony_ci *data = pci_read_reg(chan, SH4A_PCIEPDR); 8062306a36Sopenharmony_ci else 8162306a36Sopenharmony_ci pci_write_reg(chan, *data, SH4A_PCIEPDR); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Disable the configuration access */ 8462306a36Sopenharmony_ci pci_write_reg(chan, 0, SH4A_PCIEPCTLR); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int sh7786_pcie_read(struct pci_bus *bus, unsigned int devfn, 9062306a36Sopenharmony_ci int where, int size, u32 *val) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci unsigned long flags; 9362306a36Sopenharmony_ci int ret; 9462306a36Sopenharmony_ci u32 data; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if ((size == 2) && (where & 1)) 9762306a36Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 9862306a36Sopenharmony_ci else if ((size == 4) && (where & 3)) 9962306a36Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci raw_spin_lock_irqsave(&pci_config_lock, flags); 10262306a36Sopenharmony_ci ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus, 10362306a36Sopenharmony_ci devfn, where, &data); 10462306a36Sopenharmony_ci if (ret != PCIBIOS_SUCCESSFUL) { 10562306a36Sopenharmony_ci *val = 0xffffffff; 10662306a36Sopenharmony_ci goto out; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (size == 1) 11062306a36Sopenharmony_ci *val = (data >> ((where & 3) << 3)) & 0xff; 11162306a36Sopenharmony_ci else if (size == 2) 11262306a36Sopenharmony_ci *val = (data >> ((where & 2) << 3)) & 0xffff; 11362306a36Sopenharmony_ci else 11462306a36Sopenharmony_ci *val = data; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x " 11762306a36Sopenharmony_ci "where=0x%04x size=%d val=0x%08lx\n", bus->number, 11862306a36Sopenharmony_ci devfn, where, size, (unsigned long)*val); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciout: 12162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pci_config_lock, flags); 12262306a36Sopenharmony_ci return ret; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int sh7786_pcie_write(struct pci_bus *bus, unsigned int devfn, 12662306a36Sopenharmony_ci int where, int size, u32 val) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci unsigned long flags; 12962306a36Sopenharmony_ci int shift, ret; 13062306a36Sopenharmony_ci u32 data; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if ((size == 2) && (where & 1)) 13362306a36Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 13462306a36Sopenharmony_ci else if ((size == 4) && (where & 3)) 13562306a36Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci raw_spin_lock_irqsave(&pci_config_lock, flags); 13862306a36Sopenharmony_ci ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus, 13962306a36Sopenharmony_ci devfn, where, &data); 14062306a36Sopenharmony_ci if (ret != PCIBIOS_SUCCESSFUL) 14162306a36Sopenharmony_ci goto out; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci dev_dbg(&bus->dev, "pcie-config-write: bus=%3d devfn=0x%04x " 14462306a36Sopenharmony_ci "where=0x%04x size=%d val=%08lx\n", bus->number, 14562306a36Sopenharmony_ci devfn, where, size, (unsigned long)val); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (size == 1) { 14862306a36Sopenharmony_ci shift = (where & 3) << 3; 14962306a36Sopenharmony_ci data &= ~(0xff << shift); 15062306a36Sopenharmony_ci data |= ((val & 0xff) << shift); 15162306a36Sopenharmony_ci } else if (size == 2) { 15262306a36Sopenharmony_ci shift = (where & 2) << 3; 15362306a36Sopenharmony_ci data &= ~(0xffff << shift); 15462306a36Sopenharmony_ci data |= ((val & 0xffff) << shift); 15562306a36Sopenharmony_ci } else 15662306a36Sopenharmony_ci data = val; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ret = sh7786_pcie_config_access(PCI_ACCESS_WRITE, bus, 15962306a36Sopenharmony_ci devfn, where, &data); 16062306a36Sopenharmony_ciout: 16162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pci_config_lock, flags); 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistruct pci_ops sh7786_pci_ops = { 16662306a36Sopenharmony_ci .read = sh7786_pcie_read, 16762306a36Sopenharmony_ci .write = sh7786_pcie_write, 16862306a36Sopenharmony_ci}; 169