Path: blob/master/arch/powerpc/platforms/cell/celleb_scc_epci.c
10818 views
/*1* Support for SCC external PCI2*3* (C) Copyright 2004-2007 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/threads.h>24#include <linux/pci.h>25#include <linux/init.h>26#include <linux/pci_regs.h>27#include <linux/bootmem.h>2829#include <asm/io.h>30#include <asm/irq.h>31#include <asm/prom.h>32#include <asm/pci-bridge.h>33#include <asm/ppc-pci.h>3435#include "celleb_scc.h"36#include "celleb_pci.h"3738#define MAX_PCI_DEVICES 3239#define MAX_PCI_FUNCTIONS 84041#define iob() __asm__ __volatile__("eieio; sync":::"memory")4243static inline PCI_IO_ADDR celleb_epci_get_epci_base(44struct pci_controller *hose)45{46/*47* Note:48* Celleb epci uses cfg_addr as a base address for49* epci control registers.50*/5152return hose->cfg_addr;53}5455static inline PCI_IO_ADDR celleb_epci_get_epci_cfg(56struct pci_controller *hose)57{58/*59* Note:60* Celleb epci uses cfg_data as a base address for61* configuration area for epci devices.62*/6364return hose->cfg_data;65}6667static inline void clear_and_disable_master_abort_interrupt(68struct pci_controller *hose)69{70PCI_IO_ADDR epci_base;71PCI_IO_ADDR reg;72epci_base = celleb_epci_get_epci_base(hose);73reg = epci_base + PCI_COMMAND;74out_be32(reg, in_be32(reg) | (PCI_STATUS_REC_MASTER_ABORT << 16));75}7677static int celleb_epci_check_abort(struct pci_controller *hose,78PCI_IO_ADDR addr)79{80PCI_IO_ADDR reg;81PCI_IO_ADDR epci_base;82u32 val;8384iob();85epci_base = celleb_epci_get_epci_base(hose);8687reg = epci_base + PCI_COMMAND;88val = in_be32(reg);8990if (val & (PCI_STATUS_REC_MASTER_ABORT << 16)) {91out_be32(reg,92(val & 0xffff) | (PCI_STATUS_REC_MASTER_ABORT << 16));9394/* clear PCI Controller error, FRE, PMFE */95reg = epci_base + SCC_EPCI_STATUS;96out_be32(reg, SCC_EPCI_INT_PAI);9798reg = epci_base + SCC_EPCI_VCSR;99val = in_be32(reg) & 0xffff;100val |= SCC_EPCI_VCSR_FRE;101out_be32(reg, val);102103reg = epci_base + SCC_EPCI_VISTAT;104out_be32(reg, SCC_EPCI_VISTAT_PMFE);105return PCIBIOS_DEVICE_NOT_FOUND;106}107108return PCIBIOS_SUCCESSFUL;109}110111static PCI_IO_ADDR celleb_epci_make_config_addr(struct pci_bus *bus,112struct pci_controller *hose, unsigned int devfn, int where)113{114PCI_IO_ADDR addr;115116if (bus != hose->bus)117addr = celleb_epci_get_epci_cfg(hose) +118(((bus->number & 0xff) << 16)119| ((devfn & 0xff) << 8)120| (where & 0xff)121| 0x01000000);122else123addr = celleb_epci_get_epci_cfg(hose) +124(((devfn & 0xff) << 8) | (where & 0xff));125126pr_debug("EPCI: config_addr = 0x%p\n", addr);127128return addr;129}130131static int celleb_epci_read_config(struct pci_bus *bus,132unsigned int devfn, int where, int size, u32 *val)133{134PCI_IO_ADDR epci_base;135PCI_IO_ADDR addr;136struct pci_controller *hose = pci_bus_to_host(bus);137138/* allignment check */139BUG_ON(where % size);140141if (!celleb_epci_get_epci_cfg(hose))142return PCIBIOS_DEVICE_NOT_FOUND;143144if (bus->number == hose->first_busno && devfn == 0) {145/* EPCI controller self */146147epci_base = celleb_epci_get_epci_base(hose);148addr = epci_base + where;149150switch (size) {151case 1:152*val = in_8(addr);153break;154case 2:155*val = in_be16(addr);156break;157case 4:158*val = in_be32(addr);159break;160default:161return PCIBIOS_DEVICE_NOT_FOUND;162}163164} else {165166clear_and_disable_master_abort_interrupt(hose);167addr = celleb_epci_make_config_addr(bus, hose, devfn, where);168169switch (size) {170case 1:171*val = in_8(addr);172break;173case 2:174*val = in_le16(addr);175break;176case 4:177*val = in_le32(addr);178break;179default:180return PCIBIOS_DEVICE_NOT_FOUND;181}182}183184pr_debug("EPCI: "185"addr=0x%p, devfn=0x%x, where=0x%x, size=0x%x, val=0x%x\n",186addr, devfn, where, size, *val);187188return celleb_epci_check_abort(hose, NULL);189}190191static int celleb_epci_write_config(struct pci_bus *bus,192unsigned int devfn, int where, int size, u32 val)193{194PCI_IO_ADDR epci_base;195PCI_IO_ADDR addr;196struct pci_controller *hose = pci_bus_to_host(bus);197198/* allignment check */199BUG_ON(where % size);200201if (!celleb_epci_get_epci_cfg(hose))202return PCIBIOS_DEVICE_NOT_FOUND;203204if (bus->number == hose->first_busno && devfn == 0) {205/* EPCI controller self */206207epci_base = celleb_epci_get_epci_base(hose);208addr = epci_base + where;209210switch (size) {211case 1:212out_8(addr, val);213break;214case 2:215out_be16(addr, val);216break;217case 4:218out_be32(addr, val);219break;220default:221return PCIBIOS_DEVICE_NOT_FOUND;222}223224} else {225226clear_and_disable_master_abort_interrupt(hose);227addr = celleb_epci_make_config_addr(bus, hose, devfn, where);228229switch (size) {230case 1:231out_8(addr, val);232break;233case 2:234out_le16(addr, val);235break;236case 4:237out_le32(addr, val);238break;239default:240return PCIBIOS_DEVICE_NOT_FOUND;241}242}243244return celleb_epci_check_abort(hose, addr);245}246247struct pci_ops celleb_epci_ops = {248.read = celleb_epci_read_config,249.write = celleb_epci_write_config,250};251252/* to be moved in FW */253static int __init celleb_epci_init(struct pci_controller *hose)254{255u32 val;256PCI_IO_ADDR reg;257PCI_IO_ADDR epci_base;258int hwres = 0;259260epci_base = celleb_epci_get_epci_base(hose);261262/* PCI core reset(Internal bus and PCI clock) */263reg = epci_base + SCC_EPCI_CKCTRL;264val = in_be32(reg);265if (val == 0x00030101)266hwres = 1;267else {268val &= ~(SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1);269out_be32(reg, val);270271/* set PCI core clock */272val = in_be32(reg);273val |= (SCC_EPCI_CKCTRL_OCLKEN | SCC_EPCI_CKCTRL_LCLKEN);274out_be32(reg, val);275276/* release PCI core reset (internal bus) */277val = in_be32(reg);278val |= SCC_EPCI_CKCTRL_CRST0;279out_be32(reg, val);280281/* set PCI clock select */282reg = epci_base + SCC_EPCI_CLKRST;283val = in_be32(reg);284val &= ~SCC_EPCI_CLKRST_CKS_MASK;285val |= SCC_EPCI_CLKRST_CKS_2;286out_be32(reg, val);287288/* set arbiter */289reg = epci_base + SCC_EPCI_ABTSET;290out_be32(reg, 0x0f1f001f); /* temporary value */291292/* buffer on */293reg = epci_base + SCC_EPCI_CLKRST;294val = in_be32(reg);295val |= SCC_EPCI_CLKRST_BC;296out_be32(reg, val);297298/* PCI clock enable */299val = in_be32(reg);300val |= SCC_EPCI_CLKRST_PCKEN;301out_be32(reg, val);302303/* release PCI core reset (all) */304reg = epci_base + SCC_EPCI_CKCTRL;305val = in_be32(reg);306val |= (SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1);307out_be32(reg, val);308309/* set base translation registers. (already set by Beat) */310311/* set base address masks. (already set by Beat) */312}313314/* release interrupt masks and clear all interrupts */315reg = epci_base + SCC_EPCI_INTSET;316out_be32(reg, 0x013f011f); /* all interrupts enable */317reg = epci_base + SCC_EPCI_VIENAB;318val = SCC_EPCI_VIENAB_PMPEE | SCC_EPCI_VIENAB_PMFEE;319out_be32(reg, val);320reg = epci_base + SCC_EPCI_STATUS;321out_be32(reg, 0xffffffff);322reg = epci_base + SCC_EPCI_VISTAT;323out_be32(reg, 0xffffffff);324325/* disable PCI->IB address translation */326reg = epci_base + SCC_EPCI_VCSR;327val = in_be32(reg);328val &= ~(SCC_EPCI_VCSR_DR | SCC_EPCI_VCSR_AT);329out_be32(reg, val);330331/* set base addresses. (no need to set?) */332333/* memory space, bus master enable */334reg = epci_base + PCI_COMMAND;335val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;336out_be32(reg, val);337338/* endian mode setup */339reg = epci_base + SCC_EPCI_ECMODE;340val = 0x00550155;341out_be32(reg, val);342343/* set control option */344reg = epci_base + SCC_EPCI_CNTOPT;345val = in_be32(reg);346val |= SCC_EPCI_CNTOPT_O2PMB;347out_be32(reg, val);348349/* XXX: temporay: set registers for address conversion setup */350reg = epci_base + SCC_EPCI_CNF10_REG;351out_be32(reg, 0x80000008);352reg = epci_base + SCC_EPCI_CNF14_REG;353out_be32(reg, 0x40000008);354355reg = epci_base + SCC_EPCI_BAM0;356out_be32(reg, 0x80000000);357reg = epci_base + SCC_EPCI_BAM1;358out_be32(reg, 0xe0000000);359360reg = epci_base + SCC_EPCI_PVBAT;361out_be32(reg, 0x80000000);362363if (!hwres) {364/* release external PCI reset */365reg = epci_base + SCC_EPCI_CLKRST;366val = in_be32(reg);367val |= SCC_EPCI_CLKRST_PCIRST;368out_be32(reg, val);369}370371return 0;372}373374static int __init celleb_setup_epci(struct device_node *node,375struct pci_controller *hose)376{377struct resource r;378379pr_debug("PCI: celleb_setup_epci()\n");380381/*382* Note:383* Celleb epci uses cfg_addr and cfg_data member of384* pci_controller structure in irregular way.385*386* cfg_addr is used to map for control registers of387* celleb epci.388*389* cfg_data is used for configuration area of devices390* on Celleb epci buses.391*/392393if (of_address_to_resource(node, 0, &r))394goto error;395hose->cfg_addr = ioremap(r.start, (r.end - r.start + 1));396if (!hose->cfg_addr)397goto error;398pr_debug("EPCI: cfg_addr map 0x%016llx->0x%016lx + 0x%016llx\n",399r.start, (unsigned long)hose->cfg_addr, (r.end - r.start + 1));400401if (of_address_to_resource(node, 2, &r))402goto error;403hose->cfg_data = ioremap(r.start, (r.end - r.start + 1));404if (!hose->cfg_data)405goto error;406pr_debug("EPCI: cfg_data map 0x%016llx->0x%016lx + 0x%016llx\n",407r.start, (unsigned long)hose->cfg_data, (r.end - r.start + 1));408409hose->ops = &celleb_epci_ops;410celleb_epci_init(hose);411412return 0;413414error:415if (hose->cfg_addr)416iounmap(hose->cfg_addr);417418if (hose->cfg_data)419iounmap(hose->cfg_data);420return 1;421}422423struct celleb_phb_spec celleb_epci_spec __initdata = {424.setup = celleb_setup_epci,425.ops = &spiderpci_ops,426.iowa_init = &spiderpci_iowa_init,427.iowa_data = (void *)0,428};429430431