/*
* Copyright 2010 Ben Herrenschmidt, IBM Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/debugfs.h>
#include <asm/sections.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#include <asm/machdep.h>
#include <asm/ppc-pci.h>
#include <asm/iommu.h>
#include <asm/io-workarounds.h>
#include "wsp.h"
#include "wsp_pci.h"
#include "msi.h"
/* Max number of TVTs for one table. Only 32-bit tables can use
* multiple TVTs and so the max currently supported is thus 8
* since only 2G of DMA space is supported
*/
#define MAX_TABLE_TVT_COUNT 8
struct wsp_dma_table {
struct list_head link;
struct iommu_table table;
struct wsp_phb *phb;
struct page *tces[MAX_TABLE_TVT_COUNT];
};
/* We support DMA regions from 0...2G in 32bit space (no support for
* 64-bit DMA just yet). Each device gets a separate TCE table (TVT
* entry) with validation enabled (though not supported by SimiCS
* just yet).
*
* To simplify things, we divide this 2G space into N regions based
* on the constant below which could be turned into a tunable eventually
*
* We then assign dynamically those regions to devices as they show up.
*
* We use a bitmap as an allocator for these.
*
* Tables are allocated/created dynamically as devices are discovered,
* multiple TVT entries are used if needed
*
* When 64-bit DMA support is added we should simply use a separate set
* of larger regions (the HW supports 64 TVT entries). We can
* additionally create a bypass region in 64-bit space for performances
* though that would have a cost in term of security.
*
* If you set NUM_DMA32_REGIONS to 1, then a single table is shared
* for all devices and bus/dev/fn validation is disabled
*
* Note that a DMA32 region cannot be smaller than 256M so the max
* supported here for now is 8. We don't yet support sharing regions
* between multiple devices so the max number of devices supported
* is MAX_TABLE_TVT_COUNT.
*/
#define NUM_DMA32_REGIONS 1
struct wsp_phb {
struct pci_controller *hose;
/* Lock controlling access to the list of dma tables.
* It does -not- protect against dma_* operations on
* those tables, those should be stopped before an entry
* is removed from the list.
*
* The lock is also used for error handling operations
*/
spinlock_t lock;
struct list_head dma_tables;
unsigned long dma32_map;
unsigned long dma32_base;
unsigned int dma32_num_regions;
unsigned long dma32_region_size;
/* Debugfs stuff */
struct dentry *ddir;
struct list_head all;
};
static LIST_HEAD(wsp_phbs);
//#define cfg_debug(fmt...) pr_debug(fmt)
#define cfg_debug(fmt...)
static int wsp_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 *val)
{
struct pci_controller *hose;
int suboff;
u64 addr;
hose = pci_bus_to_host(bus);
if (hose == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
if (offset >= 0x1000)
return PCIBIOS_BAD_REGISTER_NUMBER;
addr = PCIE_REG_CA_ENABLE |
((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT |
((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT |
((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT;
suboff = offset & 3;
/*
* Note: the caller has already checked that offset is
* suitably aligned and that len is 1, 2 or 4.
*/
switch (len) {
case 1:
addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT;
out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
*val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA)
>> (suboff << 3)) & 0xff;
cfg_debug("read 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n",
bus->number, devfn >> 3, devfn & 7,
offset, suboff, addr, *val);
break;
case 2:
addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT;
out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
*val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA)
>> (suboff << 3)) & 0xffff;
cfg_debug(