Path: blob/master/arch/powerpc/platforms/cell/celleb_scc_pciex.c
10818 views
/*1* Support for Celleb PCI-Express.2*3* (C) Copyright 2007-2008 TOSHIBA CORPORATION4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*10* This program is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License along16* with this program; if not, write to the Free Software Foundation, Inc.,17* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.18*/1920#undef DEBUG2122#include <linux/kernel.h>23#include <linux/pci.h>24#include <linux/string.h>25#include <linux/slab.h>26#include <linux/init.h>27#include <linux/bootmem.h>28#include <linux/delay.h>29#include <linux/interrupt.h>3031#include <asm/io.h>32#include <asm/irq.h>33#include <asm/iommu.h>34#include <asm/byteorder.h>3536#include "celleb_scc.h"37#include "celleb_pci.h"3839#define PEX_IN(base, off) in_be32((void __iomem *)(base) + (off))40#define PEX_OUT(base, off, data) out_be32((void __iomem *)(base) + (off), (data))4142static void scc_pciex_io_flush(struct iowa_bus *bus)43{44(void)PEX_IN(bus->phb->cfg_addr, PEXDMRDEN0);45}4647/*48* Memory space access to device on PCIEX49*/50#define PCIEX_MMIO_READ(name, ret) \51static ret scc_pciex_##name(const PCI_IO_ADDR addr) \52{ \53ret val = __do_##name(addr); \54scc_pciex_io_flush(iowa_mem_find_bus(addr)); \55return val; \56}5758#define PCIEX_MMIO_READ_STR(name) \59static void scc_pciex_##name(const PCI_IO_ADDR addr, void *buf, \60unsigned long count) \61{ \62__do_##name(addr, buf, count); \63scc_pciex_io_flush(iowa_mem_find_bus(addr)); \64}6566PCIEX_MMIO_READ(readb, u8)67PCIEX_MMIO_READ(readw, u16)68PCIEX_MMIO_READ(readl, u32)69PCIEX_MMIO_READ(readq, u64)70PCIEX_MMIO_READ(readw_be, u16)71PCIEX_MMIO_READ(readl_be, u32)72PCIEX_MMIO_READ(readq_be, u64)73PCIEX_MMIO_READ_STR(readsb)74PCIEX_MMIO_READ_STR(readsw)75PCIEX_MMIO_READ_STR(readsl)7677static void scc_pciex_memcpy_fromio(void *dest, const PCI_IO_ADDR src,78unsigned long n)79{80__do_memcpy_fromio(dest, src, n);81scc_pciex_io_flush(iowa_mem_find_bus(src));82}8384/*85* I/O port access to devices on PCIEX.86*/8788static inline unsigned long get_bus_address(struct pci_controller *phb,89unsigned long port)90{91return port - ((unsigned long)(phb->io_base_virt) - _IO_BASE);92}9394static u32 scc_pciex_read_port(struct pci_controller *phb,95unsigned long port, int size)96{97unsigned int byte_enable;98unsigned int cmd, shift;99unsigned long addr;100u32 data, ret;101102BUG_ON(((port & 0x3ul) + size) > 4);103104addr = get_bus_address(phb, port);105shift = addr & 0x3ul;106byte_enable = ((1 << size) - 1) << shift;107cmd = PEXDCMND_IO_READ | (byte_enable << PEXDCMND_BYTE_EN_SHIFT);108PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul));109PEX_OUT(phb->cfg_addr, PEXDCMND, cmd);110data = PEX_IN(phb->cfg_addr, PEXDRDATA);111ret = (data >> (shift * 8)) & (0xFFFFFFFF >> ((4 - size) * 8));112113pr_debug("PCIEX:PIO READ:port=0x%lx, addr=0x%lx, size=%d, be=%x,"114" cmd=%x, data=%x, ret=%x\n", port, addr, size, byte_enable,115cmd, data, ret);116117return ret;118}119120static void scc_pciex_write_port(struct pci_controller *phb,121unsigned long port, int size, u32 val)122{123unsigned int byte_enable;124unsigned int cmd, shift;125unsigned long addr;126u32 data;127128BUG_ON(((port & 0x3ul) + size) > 4);129130addr = get_bus_address(phb, port);131shift = addr & 0x3ul;132byte_enable = ((1 << size) - 1) << shift;133cmd = PEXDCMND_IO_WRITE | (byte_enable << PEXDCMND_BYTE_EN_SHIFT);134data = (val & (0xFFFFFFFF >> (4 - size) * 8)) << (shift * 8);135PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul));136PEX_OUT(phb->cfg_addr, PEXDCMND, cmd);137PEX_OUT(phb->cfg_addr, PEXDWDATA, data);138139pr_debug("PCIEX:PIO WRITE:port=0x%lx, addr=%lx, size=%d, val=%x,"140" be=%x, cmd=%x, data=%x\n", port, addr, size, val,141byte_enable, cmd, data);142}143144static u8 __scc_pciex_inb(struct pci_controller *phb, unsigned long port)145{146return (u8)scc_pciex_read_port(phb, port, 1);147}148149static u16 __scc_pciex_inw(struct pci_controller *phb, unsigned long port)150{151u32 data;152if ((port & 0x3ul) < 3)153data = scc_pciex_read_port(phb, port, 2);154else {155u32 d1 = scc_pciex_read_port(phb, port, 1);156u32 d2 = scc_pciex_read_port(phb, port + 1, 1);157data = d1 | (d2 << 8);158}159return (u16)data;160}161162static u32 __scc_pciex_inl(struct pci_controller *phb, unsigned long port)163{164unsigned int mod = port & 0x3ul;165u32 data;166if (mod == 0)167data = scc_pciex_read_port(phb, port, 4);168else {169u32 d1 = scc_pciex_read_port(phb, port, 4 - mod);170u32 d2 = scc_pciex_read_port(phb, port + 1, mod);171data = d1 | (d2 << (mod * 8));172}173return data;174}175176static void __scc_pciex_outb(struct pci_controller *phb,177u8 val, unsigned long port)178{179scc_pciex_write_port(phb, port, 1, (u32)val);180}181182static void __scc_pciex_outw(struct pci_controller *phb,183u16 val, unsigned long port)184{185if ((port & 0x3ul) < 3)186scc_pciex_write_port(phb, port, 2, (u32)val);187else {188u32 d1 = val & 0x000000FF;189u32 d2 = (val & 0x0000FF00) >> 8;190scc_pciex_write_port(phb, port, 1, d1);191scc_pciex_write_port(phb, port + 1, 1, d2);192}193}194195static void __scc_pciex_outl(struct pci_controller *phb,196u32 val, unsigned long port)197{198unsigned int mod = port & 0x3ul;199if (mod == 0)200scc_pciex_write_port(phb, port, 4, val);201else {202u32 d1 = val & (0xFFFFFFFFul >> (mod * 8));203u32 d2 = val >> ((4 - mod) * 8);204scc_pciex_write_port(phb, port, 4 - mod, d1);205scc_pciex_write_port(phb, port + 1, mod, d2);206}207}208209#define PCIEX_PIO_FUNC(size, name) \210static u##size scc_pciex_in##name(unsigned long port) \211{ \212struct iowa_bus *bus = iowa_pio_find_bus(port); \213u##size data = __scc_pciex_in##name(bus->phb, port); \214scc_pciex_io_flush(bus); \215return data; \216} \217static void scc_pciex_ins##name(unsigned long p, void *b, unsigned long c) \218{ \219struct iowa_bus *bus = iowa_pio_find_bus(p); \220__le##size *dst = b; \221for (; c != 0; c--, dst++) \222*dst = cpu_to_le##size(__scc_pciex_in##name(bus->phb, p)); \223scc_pciex_io_flush(bus); \224} \225static void scc_pciex_out##name(u##size val, unsigned long port) \226{ \227struct iowa_bus *bus = iowa_pio_find_bus(port); \228__scc_pciex_out##name(bus->phb, val, port); \229} \230static void scc_pciex_outs##name(unsigned long p, const void *b, \231unsigned long c) \232{ \233struct iowa_bus *bus = iowa_pio_find_bus(p); \234const __le##size *src = b; \235for (; c != 0; c--, src++) \236__scc_pciex_out##name(bus->phb, le##size##_to_cpu(*src), p); \237}238#define __le8 u8239#define cpu_to_le8(x) (x)240#define le8_to_cpu(x) (x)241PCIEX_PIO_FUNC(8, b)242PCIEX_PIO_FUNC(16, w)243PCIEX_PIO_FUNC(32, l)244245static struct ppc_pci_io scc_pciex_ops = {246.readb = scc_pciex_readb,247.readw = scc_pciex_readw,248.readl = scc_pciex_readl,249.readq = scc_pciex_readq,250.readw_be = scc_pciex_readw_be,251.readl_be = scc_pciex_readl_be,252.readq_be = scc_pciex_readq_be,253.readsb = scc_pciex_readsb,254.readsw = scc_pciex_readsw,255.readsl = scc_pciex_readsl,256.memcpy_fromio = scc_pciex_memcpy_fromio,257.inb = scc_pciex_inb,258.inw = scc_pciex_inw,259.inl = scc_pciex_inl,260.outb = scc_pciex_outb,261.outw = scc_pciex_outw,262.outl = scc_pciex_outl,263.insb = scc_pciex_insb,264.insw = scc_pciex_insw,265.insl = scc_pciex_insl,266.outsb = scc_pciex_outsb,267.outsw = scc_pciex_outsw,268.outsl = scc_pciex_outsl,269};270271static int __init scc_pciex_iowa_init(struct iowa_bus *bus, void *data)272{273dma_addr_t dummy_page_da;274void *dummy_page_va;275276dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL);277if (!dummy_page_va) {278pr_err("PCIEX:Alloc dummy_page_va failed\n");279return -1;280}281282dummy_page_da = dma_map_single(bus->phb->parent, dummy_page_va,283PAGE_SIZE, DMA_FROM_DEVICE);284if (dma_mapping_error(bus->phb->parent, dummy_page_da)) {285pr_err("PCIEX:Map dummy page failed.\n");286kfree(dummy_page_va);287return -1;288}289290PEX_OUT(bus->phb->cfg_addr, PEXDMRDADR0, dummy_page_da);291292return 0;293}294295/*296* config space access297*/298#define MK_PEXDADRS(bus_no, dev_no, func_no, addr) \299((uint32_t)(((addr) & ~0x3UL) | \300((bus_no) << PEXDADRS_BUSNO_SHIFT) | \301((dev_no) << PEXDADRS_DEVNO_SHIFT) | \302((func_no) << PEXDADRS_FUNCNO_SHIFT)))303304#define MK_PEXDCMND_BYTE_EN(addr, size) \305((((0x1 << (size))-1) << ((addr) & 0x3)) << PEXDCMND_BYTE_EN_SHIFT)306#define MK_PEXDCMND(cmd, addr, size) ((cmd) | MK_PEXDCMND_BYTE_EN(addr, size))307308static uint32_t config_read_pciex_dev(unsigned int __iomem *base,309uint64_t bus_no, uint64_t dev_no, uint64_t func_no,310uint64_t off, uint64_t size)311{312uint32_t ret;313uint32_t addr, cmd;314315addr = MK_PEXDADRS(bus_no, dev_no, func_no, off);316cmd = MK_PEXDCMND(PEXDCMND_CONFIG_READ, off, size);317PEX_OUT(base, PEXDADRS, addr);318PEX_OUT(base, PEXDCMND, cmd);319ret = (PEX_IN(base, PEXDRDATA)320>> ((off & (4-size)) * 8)) & ((0x1 << (size * 8)) - 1);321return ret;322}323324static void config_write_pciex_dev(unsigned int __iomem *base, uint64_t bus_no,325uint64_t dev_no, uint64_t func_no, uint64_t off, uint64_t size,326uint32_t data)327{328uint32_t addr, cmd;329330addr = MK_PEXDADRS(bus_no, dev_no, func_no, off);331cmd = MK_PEXDCMND(PEXDCMND_CONFIG_WRITE, off, size);332PEX_OUT(base, PEXDADRS, addr);333PEX_OUT(base, PEXDCMND, cmd);334PEX_OUT(base, PEXDWDATA,335(data & ((0x1 << (size * 8)) - 1)) << ((off & (4-size)) * 8));336}337338#define MK_PEXCADRS_BYTE_EN(off, len) \339((((0x1 << (len)) - 1) << ((off) & 0x3)) << PEXCADRS_BYTE_EN_SHIFT)340#define MK_PEXCADRS(cmd, addr, size) \341((cmd) | MK_PEXCADRS_BYTE_EN(addr, size) | ((addr) & ~0x3))342static uint32_t config_read_pciex_rc(unsigned int __iomem *base,343uint32_t where, uint32_t size)344{345PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_READ, where, size));346return (PEX_IN(base, PEXCRDATA)347>> ((where & (4 - size)) * 8)) & ((0x1 << (size * 8)) - 1);348}349350static void config_write_pciex_rc(unsigned int __iomem *base, uint32_t where,351uint32_t size, uint32_t val)352{353uint32_t data;354355data = (val & ((0x1 << (size * 8)) - 1)) << ((where & (4 - size)) * 8);356PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_WRITE, where, size));357PEX_OUT(base, PEXCWDATA, data);358}359360/* Interfaces */361/* Note: Work-around362* On SCC PCIEXC, one device is seen on all 32 dev_no.363* As SCC PCIEXC can have only one device on the bus, we look only one dev_no.364* (dev_no = 1)365*/366static int scc_pciex_read_config(struct pci_bus *bus, unsigned int devfn,367int where, int size, unsigned int *val)368{369struct pci_controller *phb = pci_bus_to_host(bus);370371if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1) {372*val = ~0;373return PCIBIOS_DEVICE_NOT_FOUND;374}375376if (bus->number == 0 && PCI_SLOT(devfn) == 0)377*val = config_read_pciex_rc(phb->cfg_addr, where, size);378else379*val = config_read_pciex_dev(phb->cfg_addr, bus->number,380PCI_SLOT(devfn), PCI_FUNC(devfn), where, size);381382return PCIBIOS_SUCCESSFUL;383}384385static int scc_pciex_write_config(struct pci_bus *bus, unsigned int devfn,386int where, int size, unsigned int val)387{388struct pci_controller *phb = pci_bus_to_host(bus);389390if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1)391return PCIBIOS_DEVICE_NOT_FOUND;392393if (bus->number == 0 && PCI_SLOT(devfn) == 0)394config_write_pciex_rc(phb->cfg_addr, where, size, val);395else396config_write_pciex_dev(phb->cfg_addr, bus->number,397PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val);398return PCIBIOS_SUCCESSFUL;399}400401static struct pci_ops scc_pciex_pci_ops = {402scc_pciex_read_config,403scc_pciex_write_config,404};405406static void pciex_clear_intr_all(unsigned int __iomem *base)407{408PEX_OUT(base, PEXAERRSTS, 0xffffffff);409PEX_OUT(base, PEXPRERRSTS, 0xffffffff);410PEX_OUT(base, PEXINTSTS, 0xffffffff);411}412413#if 0414static void pciex_disable_intr_all(unsigned int *base)415{416PEX_OUT(base, PEXINTMASK, 0x0);417PEX_OUT(base, PEXAERRMASK, 0x0);418PEX_OUT(base, PEXPRERRMASK, 0x0);419PEX_OUT(base, PEXVDMASK, 0x0);420}421#endif422423static void pciex_enable_intr_all(unsigned int __iomem *base)424{425PEX_OUT(base, PEXINTMASK, 0x0000e7f1);426PEX_OUT(base, PEXAERRMASK, 0x03ff01ff);427PEX_OUT(base, PEXPRERRMASK, 0x0001010f);428PEX_OUT(base, PEXVDMASK, 0x00000001);429}430431static void pciex_check_status(unsigned int __iomem *base)432{433uint32_t err = 0;434uint32_t intsts, aerr, prerr, rcvcp, lenerr;435uint32_t maea, maec;436437intsts = PEX_IN(base, PEXINTSTS);438aerr = PEX_IN(base, PEXAERRSTS);439prerr = PEX_IN(base, PEXPRERRSTS);440rcvcp = PEX_IN(base, PEXRCVCPLIDA);441lenerr = PEX_IN(base, PEXLENERRIDA);442443if (intsts || aerr || prerr || rcvcp || lenerr)444err = 1;445446pr_info("PCEXC interrupt!!\n");447pr_info("PEXINTSTS :0x%08x\n", intsts);448pr_info("PEXAERRSTS :0x%08x\n", aerr);449pr_info("PEXPRERRSTS :0x%08x\n", prerr);450pr_info("PEXRCVCPLIDA :0x%08x\n", rcvcp);451pr_info("PEXLENERRIDA :0x%08x\n", lenerr);452453/* print detail of Protection Error */454if (intsts & 0x00004000) {455uint32_t i, n;456for (i = 0; i < 4; i++) {457n = 1 << i;458if (prerr & n) {459maea = PEX_IN(base, PEXMAEA(i));460maec = PEX_IN(base, PEXMAEC(i));461pr_info("PEXMAEC%d :0x%08x\n", i, maec);462pr_info("PEXMAEA%d :0x%08x\n", i, maea);463}464}465}466467if (err)468pciex_clear_intr_all(base);469}470471static irqreturn_t pciex_handle_internal_irq(int irq, void *dev_id)472{473struct pci_controller *phb = dev_id;474475pr_debug("PCIEX:pciex_handle_internal_irq(irq=%d)\n", irq);476477BUG_ON(phb->cfg_addr == NULL);478479pciex_check_status(phb->cfg_addr);480481return IRQ_HANDLED;482}483484static __init int celleb_setup_pciex(struct device_node *node,485struct pci_controller *phb)486{487struct resource r;488struct of_irq oirq;489int virq;490491/* SMMIO registers; used inside this file */492if (of_address_to_resource(node, 0, &r)) {493pr_err("PCIEXC:Failed to get config resource.\n");494return 1;495}496phb->cfg_addr = ioremap(r.start, r.end - r.start + 1);497if (!phb->cfg_addr) {498pr_err("PCIEXC:Failed to remap SMMIO region.\n");499return 1;500}501502/* Not use cfg_data, cmd and data regs are near address reg */503phb->cfg_data = NULL;504505/* set pci_ops */506phb->ops = &scc_pciex_pci_ops;507508/* internal interrupt handler */509if (of_irq_map_one(node, 1, &oirq)) {510pr_err("PCIEXC:Failed to map irq\n");511goto error;512}513virq = irq_create_of_mapping(oirq.controller, oirq.specifier,514oirq.size);515if (request_irq(virq, pciex_handle_internal_irq,516IRQF_DISABLED, "pciex", (void *)phb)) {517pr_err("PCIEXC:Failed to request irq\n");518goto error;519}520521/* enable all interrupts */522pciex_clear_intr_all(phb->cfg_addr);523pciex_enable_intr_all(phb->cfg_addr);524/* MSI: TBD */525526return 0;527528error:529phb->cfg_data = NULL;530if (phb->cfg_addr)531iounmap(phb->cfg_addr);532phb->cfg_addr = NULL;533return 1;534}535536struct celleb_phb_spec celleb_pciex_spec __initdata = {537.setup = celleb_setup_pciex,538.ops = &scc_pciex_ops,539.iowa_init = &scc_pciex_iowa_init,540};541542543