aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/cell
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/cell')
-rw-r--r--arch/powerpc/platforms/cell/Kconfig103
-rw-r--r--arch/powerpc/platforms/cell/Makefile40
-rw-r--r--arch/powerpc/platforms/cell/axon_msi.c210
-rw-r--r--arch/powerpc/platforms/cell/beat.c264
-rw-r--r--arch/powerpc/platforms/cell/beat.h39
-rw-r--r--arch/powerpc/platforms/cell/beat_htab.c445
-rw-r--r--arch/powerpc/platforms/cell/beat_hvCall.S285
-rw-r--r--arch/powerpc/platforms/cell/beat_interrupt.c253
-rw-r--r--arch/powerpc/platforms/cell/beat_interrupt.h30
-rw-r--r--arch/powerpc/platforms/cell/beat_iommu.c115
-rw-r--r--arch/powerpc/platforms/cell/beat_spu_priv1.c205
-rw-r--r--arch/powerpc/platforms/cell/beat_syscall.h164
-rw-r--r--arch/powerpc/platforms/cell/beat_udbg.c98
-rw-r--r--arch/powerpc/platforms/cell/beat_wrapper.h290
-rw-r--r--arch/powerpc/platforms/cell/cbe_cpufreq.c208
-rw-r--r--arch/powerpc/platforms/cell/cbe_cpufreq.h24
-rw-r--r--arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c115
-rw-r--r--arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c149
-rw-r--r--arch/powerpc/platforms/cell/cbe_powerbutton.c118
-rw-r--r--arch/powerpc/platforms/cell/cbe_regs.c13
-rw-r--r--arch/powerpc/platforms/cell/cbe_thermal.c133
-rw-r--r--arch/powerpc/platforms/cell/celleb_pci.c500
-rw-r--r--arch/powerpc/platforms/cell/celleb_pci.h46
-rw-r--r--arch/powerpc/platforms/cell/celleb_scc.h232
-rw-r--r--arch/powerpc/platforms/cell/celleb_scc_epci.c429
-rw-r--r--arch/powerpc/platforms/cell/celleb_scc_pciex.c539
-rw-r--r--arch/powerpc/platforms/cell/celleb_scc_sio.c99
-rw-r--r--arch/powerpc/platforms/cell/celleb_scc_uhc.c95
-rw-r--r--arch/powerpc/platforms/cell/celleb_setup.c243
-rw-r--r--arch/powerpc/platforms/cell/cpufreq_spudemand.c171
-rw-r--r--arch/powerpc/platforms/cell/interrupt.c119
-rw-r--r--arch/powerpc/platforms/cell/interrupt.h2
-rw-r--r--arch/powerpc/platforms/cell/io-workarounds.c343
-rw-r--r--arch/powerpc/platforms/cell/iommu.c281
-rw-r--r--arch/powerpc/platforms/cell/pervasive.c40
-rw-r--r--arch/powerpc/platforms/cell/pervasive.h9
-rw-r--r--arch/powerpc/platforms/cell/pmu.c5
-rw-r--r--arch/powerpc/platforms/cell/qpace_setup.c148
-rw-r--r--arch/powerpc/platforms/cell/ras.c116
-rw-r--r--arch/powerpc/platforms/cell/setup.c87
-rw-r--r--arch/powerpc/platforms/cell/smp.c114
-rw-r--r--arch/powerpc/platforms/cell/spider-pci.c184
-rw-r--r--arch/powerpc/platforms/cell/spider-pic.c96
-rw-r--r--arch/powerpc/platforms/cell/spu_base.c259
-rw-r--r--arch/powerpc/platforms/cell/spu_callbacks.c17
-rw-r--r--arch/powerpc/platforms/cell/spu_fault.c52
-rw-r--r--arch/powerpc/platforms/cell/spu_manage.c32
-rw-r--r--arch/powerpc/platforms/cell/spu_notify.c3
-rw-r--r--arch/powerpc/platforms/cell/spu_priv1_mmio.c18
-rw-r--r--arch/powerpc/platforms/cell/spu_syscalls.c34
-rw-r--r--arch/powerpc/platforms/cell/spufs/.gitignore2
-rw-r--r--arch/powerpc/platforms/cell/spufs/Makefile12
-rw-r--r--arch/powerpc/platforms/cell/spufs/backing_ops.c1
-rw-r--r--arch/powerpc/platforms/cell/spufs/context.c25
-rw-r--r--arch/powerpc/platforms/cell/spufs/coredump.c142
-rw-r--r--arch/powerpc/platforms/cell/spufs/fault.c20
-rw-r--r--arch/powerpc/platforms/cell/spufs/file.c561
-rw-r--r--arch/powerpc/platforms/cell/spufs/hw_ops.c1
-rw-r--r--arch/powerpc/platforms/cell/spufs/inode.c349
-rw-r--r--arch/powerpc/platforms/cell/spufs/lscsa_alloc.c6
-rw-r--r--arch/powerpc/platforms/cell/spufs/run.c84
-rw-r--r--arch/powerpc/platforms/cell/spufs/sched.c144
-rw-r--r--arch/powerpc/platforms/cell/spufs/spu_restore.c2
-rw-r--r--arch/powerpc/platforms/cell/spufs/spufs.h65
-rw-r--r--arch/powerpc/platforms/cell/spufs/sputrace.c242
-rw-r--r--arch/powerpc/platforms/cell/spufs/sputrace.h39
-rw-r--r--arch/powerpc/platforms/cell/spufs/switch.c75
-rw-r--r--arch/powerpc/platforms/cell/spufs/syscalls.c33
68 files changed, 7081 insertions, 2336 deletions
diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig
index 2f169991896..9978f594cac 100644
--- a/arch/powerpc/platforms/cell/Kconfig
+++ b/arch/powerpc/platforms/cell/Kconfig
@@ -2,29 +2,58 @@ config PPC_CELL
bool
default n
-config PPC_CELL_NATIVE
+config PPC_CELL_COMMON
bool
select PPC_CELL
select PPC_DCR_MMIO
- select PPC_OF_PLATFORM_PCI
- select PPC_INDIRECT_IO
+ select PPC_INDIRECT_PIO
+ select PPC_INDIRECT_MMIO
select PPC_NATIVE
+ select PPC_RTAS
+ select IRQ_EDGE_EOI_HANDLER
+
+config PPC_CELL_NATIVE
+ bool
+ select PPC_CELL_COMMON
select MPIC
- select IBM_NEW_EMAC_EMAC4
- select IBM_NEW_EMAC_RGMII
- select IBM_NEW_EMAC_ZMII #test only
- select IBM_NEW_EMAC_TAH #test only
+ select PPC_IO_WORKAROUNDS
+ select IBM_EMAC_EMAC4
+ select IBM_EMAC_RGMII
+ select IBM_EMAC_ZMII #test only
+ select IBM_EMAC_TAH #test only
default n
config PPC_IBM_CELL_BLADE
bool "IBM Cell Blade"
- depends on PPC_MULTIPLATFORM && PPC64
+ depends on PPC64 && PPC_BOOK3S
select PPC_CELL_NATIVE
- select PPC_RTAS
+ select PPC_OF_PLATFORM_PCI
+ select PCI
select MMIO_NVRAM
select PPC_UDBG_16550
select UDBG_RTAS_CONSOLE
+config PPC_CELLEB
+ bool "Toshiba's Cell Reference Set 'Celleb' Architecture"
+ depends on PPC64 && PPC_BOOK3S
+ select PPC_CELL_NATIVE
+ select PPC_OF_PLATFORM_PCI
+ select PCI
+ select HAS_TXX9_SERIAL
+ select PPC_UDBG_BEAT
+ select USB_OHCI_BIG_ENDIAN_MMIO
+ select USB_EHCI_BIG_ENDIAN_MMIO
+
+config PPC_CELL_QPACE
+ bool "IBM Cell - QPACE"
+ depends on PPC64 && PPC_BOOK3S
+ select PPC_CELL_COMMON
+
+config AXON_MSI
+ bool
+ depends on PPC_IBM_CELL_BLADE && PCI_MSI
+ default y
+
menu "Cell Broadband Engine options"
depends on PPC_CELL
@@ -54,13 +83,6 @@ config SPU_FS_64K_LS
uses 4K pages. This can improve performances of applications
using multiple SPEs by lowering the TLB pressure on them.
-config SPU_TRACE
- tristate "SPU event tracing support"
- depends on SPU_FS && MARKERS
- help
- This option allows reading a trace of spu-related events through
- the sputrace file in procfs.
-
config SPU_BASE
bool
default n
@@ -70,33 +92,48 @@ config CBE_RAS
depends on PPC_CELL_NATIVE
default y
+config PPC_IBM_CELL_RESETBUTTON
+ bool "IBM Cell Blade Pinhole reset button"
+ depends on CBE_RAS && PPC_IBM_CELL_BLADE
+ default y
+ help
+ Support Pinhole Resetbutton on IBM Cell blades.
+ This adds a method to trigger system reset via front panel pinhole button.
+
+config PPC_IBM_CELL_POWERBUTTON
+ tristate "IBM Cell Blade power button"
+ depends on PPC_IBM_CELL_BLADE && INPUT_EVDEV
+ default y
+ help
+ Support Powerbutton on IBM Cell blades.
+ This will enable the powerbutton as an input device.
+
config CBE_THERM
tristate "CBE thermal support"
default m
- depends on CBE_RAS
+ depends on CBE_RAS && SPU_BASE
-config CBE_CPUFREQ
- tristate "CBE frequency scaling"
- depends on CBE_RAS && CPU_FREQ
- default m
+config PPC_PMI
+ tristate
+ default y
+ depends on CPU_FREQ_CBE_PMI || PPC_IBM_CELL_POWERBUTTON
help
- This adds the cpufreq driver for Cell BE processors.
- For details, take a look at <file:Documentation/cpu-freq/>.
- If you don't have such processor, say N
+ PMI (Platform Management Interrupt) is a way to
+ communicate with the BMC (Baseboard Management Controller).
+ It is used in some IBM Cell blades.
-config CBE_CPUFREQ_PMI
- tristate "CBE frequency scaling using PMI interface"
- depends on CBE_CPUFREQ && PPC_PMI && EXPERIMENTAL
- default n
+config CBE_CPUFREQ_SPU_GOVERNOR
+ tristate "CBE frequency scaling based on SPU usage"
+ depends on SPU_FS && CPU_FREQ
+ default m
help
- Select this, if you want to use the PMI interface
- to switch frequencies. Using PMI, the
- processor will not only be able to run at lower speed,
- but also at lower core voltage.
+ This governor checks for spu usage to adjust the cpu frequency.
+ If no spu is running on a given cpu, that cpu will be throttled to
+ the minimal possible frequency.
endmenu
config OPROFILE_CELL
def_bool y
- depends on PPC_CELL_NATIVE && (OPROFILE = m || OPROFILE = y)
+ depends on PPC_CELL_NATIVE && (OPROFILE = m || OPROFILE = y) && SPU_BASE
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile
index c89964c6fb1..fe053e7c73e 100644
--- a/arch/powerpc/platforms/cell/Makefile
+++ b/arch/powerpc/platforms/cell/Makefile
@@ -1,22 +1,22 @@
-obj-$(CONFIG_PPC_CELL_NATIVE) += interrupt.o iommu.o setup.o \
- cbe_regs.o spider-pic.o \
- pervasive.o pmu.o io-workarounds.o
+obj-$(CONFIG_PPC_CELL_COMMON) += cbe_regs.o interrupt.o pervasive.o
+
+obj-$(CONFIG_PPC_CELL_NATIVE) += iommu.o setup.o spider-pic.o \
+ pmu.o spider-pci.o
obj-$(CONFIG_CBE_RAS) += ras.o
obj-$(CONFIG_CBE_THERM) += cbe_thermal.o
-obj-$(CONFIG_CBE_CPUFREQ_PMI) += cbe_cpufreq_pmi.o
-obj-$(CONFIG_CBE_CPUFREQ) += cbe-cpufreq.o
-cbe-cpufreq-y += cbe_cpufreq_pervasive.o cbe_cpufreq.o
+obj-$(CONFIG_CBE_CPUFREQ_SPU_GOVERNOR) += cpufreq_spudemand.o
+
+obj-$(CONFIG_PPC_IBM_CELL_POWERBUTTON) += cbe_powerbutton.o
ifeq ($(CONFIG_SMP),y)
obj-$(CONFIG_PPC_CELL_NATIVE) += smp.o
+obj-$(CONFIG_PPC_CELL_QPACE) += smp.o
endif
# needed only when building loadable spufs.ko
-spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o
-
-spu-manage-$(CONFIG_PPC_CELLEB) += spu_manage.o
-spu-manage-$(CONFIG_PPC_CELL_NATIVE) += spu_manage.o
+spu-priv1-$(CONFIG_PPC_CELL_COMMON) += spu_priv1_mmio.o
+spu-manage-$(CONFIG_PPC_CELL_COMMON) += spu_manage.o
obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \
spu_notify.o \
@@ -25,4 +25,22 @@ obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \
$(spu-manage-y) \
spufs/
-obj-$(CONFIG_PCI_MSI) += axon_msi.o
+obj-$(CONFIG_AXON_MSI) += axon_msi.o
+
+# qpace setup
+obj-$(CONFIG_PPC_CELL_QPACE) += qpace_setup.o
+
+# celleb stuff
+ifeq ($(CONFIG_PPC_CELLEB),y)
+obj-y += celleb_setup.o \
+ celleb_pci.o celleb_scc_epci.o \
+ celleb_scc_pciex.o \
+ celleb_scc_uhc.o \
+ spider-pci.o beat.o beat_htab.o \
+ beat_hvCall.o beat_interrupt.o \
+ beat_iommu.o
+
+obj-$(CONFIG_PPC_UDBG_BEAT) += beat_udbg.o
+obj-$(CONFIG_SERIAL_TXX9) += celleb_scc_sio.o
+obj-$(CONFIG_SPU_BASE) += beat_spu_priv1.o
+endif
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
index d95e71dee91..85825b5401e 100644
--- a/arch/powerpc/platforms/cell/axon_msi.c
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -13,7 +13,10 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/msi.h>
+#include <linux/export.h>
#include <linux/of_platform.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
#include <asm/dcr.h>
#include <asm/machdep.h>
@@ -64,55 +67,92 @@
struct axon_msic {
- struct irq_host *irq_host;
+ struct irq_domain *irq_domain;
__le32 *fifo_virt;
dma_addr_t fifo_phys;
dcr_host_t dcr_host;
u32 read_offset;
+#ifdef DEBUG
+ u32 __iomem *trigger;
+#endif
};
+#ifdef DEBUG
+void axon_msi_debug_setup(struct device_node *dn, struct axon_msic *msic);
+#else
+static inline void axon_msi_debug_setup(struct device_node *dn,
+ struct axon_msic *msic) { }
+#endif
+
+
static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val)
{
- pr_debug("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n);
+ pr_devel("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n);
dcr_write(msic->dcr_host, dcr_n, val);
}
static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
{
- struct axon_msic *msic = get_irq_data(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct axon_msic *msic = irq_get_handler_data(irq);
u32 write_offset, msi;
int idx;
+ int retry = 0;
write_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG);
- pr_debug("axon_msi: original write_offset 0x%x\n", write_offset);
+ pr_devel("axon_msi: original write_offset 0x%x\n", write_offset);
/* write_offset doesn't wrap properly, so we have to mask it */
write_offset &= MSIC_FIFO_SIZE_MASK;
- while (msic->read_offset != write_offset) {
+ while (msic->read_offset != write_offset && retry < 100) {
idx = msic->read_offset / sizeof(__le32);
msi = le32_to_cpu(msic->fifo_virt[idx]);
msi &= 0xFFFF;
- pr_debug("axon_msi: woff %x roff %x msi %x\n",
+ pr_devel("axon_msi: woff %x roff %x msi %x\n",
write_offset, msic->read_offset, msi);
+ if (msi < nr_irqs && irq_get_chip_data(msi) == msic) {
+ generic_handle_irq(msi);
+ msic->fifo_virt[idx] = cpu_to_le32(0xffffffff);
+ } else {
+ /*
+ * Reading the MSIC_WRITE_OFFSET_REG does not
+ * reliably flush the outstanding DMA to the
+ * FIFO buffer. Here we were reading stale
+ * data, so we need to retry.
+ */
+ udelay(1);
+ retry++;
+ pr_devel("axon_msi: invalid irq 0x%x!\n", msi);
+ continue;
+ }
+
+ if (retry) {
+ pr_devel("axon_msi: late irq 0x%x, retry %d\n",
+ msi, retry);
+ retry = 0;
+ }
+
msic->read_offset += MSIC_FIFO_ENTRY_SIZE;
msic->read_offset &= MSIC_FIFO_SIZE_MASK;
+ }
- if (msi < NR_IRQS && irq_map[msi].host == msic->irq_host)
- generic_handle_irq(msi);
- else
- pr_debug("axon_msi: invalid irq 0x%x!\n", msi);
+ if (retry) {
+ printk(KERN_WARNING "axon_msi: irq timed out\n");
+
+ msic->read_offset += MSIC_FIFO_ENTRY_SIZE;
+ msic->read_offset &= MSIC_FIFO_SIZE_MASK;
}
- desc->chip->eoi(irq);
+ chip->irq_eoi(&desc->irq_data);
}
static struct axon_msic *find_msi_translator(struct pci_dev *dev)
{
- struct irq_host *irq_host;
+ struct irq_domain *irq_domain;
struct device_node *dn, *tmp;
const phandle *ph;
struct axon_msic *msic = NULL;
@@ -123,7 +163,7 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev)
return NULL;
}
- for (; dn; tmp = of_get_parent(dn), of_node_put(dn), dn = tmp) {
+ for (; dn; dn = of_get_next_parent(dn)) {
ph = of_get_property(dn, "msi-translator", NULL);
if (ph)
break;
@@ -144,14 +184,14 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev)
goto out_error;
}
- irq_host = irq_find_host(dn);
- if (!irq_host) {
- dev_dbg(&dev->dev, "axon_msi: no irq_host found for node %s\n",
+ irq_domain = irq_find_host(dn);
+ if (!irq_domain) {
+ dev_dbg(&dev->dev, "axon_msi: no irq_domain found for node %s\n",
dn->full_name);
goto out_error;
}
- msic = irq_host->host_data;
+ msic = irq_domain->host_data;
out_error:
of_node_put(dn);
@@ -169,7 +209,7 @@ static int axon_msi_check_device(struct pci_dev *dev, int nvec, int type)
static int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg)
{
- struct device_node *dn, *tmp;
+ struct device_node *dn;
struct msi_desc *entry;
int len;
const u32 *prop;
@@ -182,7 +222,7 @@ static int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg)
entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
- for (; dn; tmp = of_get_parent(dn), of_node_put(dn), dn = tmp) {
+ for (; dn; dn = of_get_next_parent(dn)) {
if (entry->msi_attrib.is_64) {
prop = of_get_property(dn, "msi-address-64", &len);
if (prop)
@@ -236,11 +276,8 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
if (rc)
return rc;
- /* We rely on being able to stash a virq in a u16 */
- BUILD_BUG_ON(NR_IRQS > 65536);
-
list_for_each_entry(entry, &dev->msi_list, list) {
- virq = irq_create_direct_mapping(msic->irq_host);
+ virq = irq_create_direct_mapping(msic->irq_domain);
if (virq == NO_IRQ) {
dev_warn(&dev->dev,
"axon_msi: virq allocation failed!\n");
@@ -248,7 +285,7 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
}
dev_dbg(&dev->dev, "axon_msi: allocated virq 0x%x\n", virq);
- set_irq_msi(virq, entry);
+ irq_set_msi_desc(virq, entry);
msg.data = virq;
write_msi_msg(virq, &msg);
}
@@ -266,53 +303,51 @@ static void axon_msi_teardown_msi_irqs(struct pci_dev *dev)
if (entry->irq == NO_IRQ)
continue;
- set_irq_msi(entry->irq, NULL);
+ irq_set_msi_desc(entry->irq, NULL);
irq_dispose_mapping(entry->irq);
}
}
static struct irq_chip msic_irq_chip = {
- .mask = mask_msi_irq,
- .unmask = unmask_msi_irq,
- .shutdown = unmask_msi_irq,
- .typename = "AXON-MSI",
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+ .irq_shutdown = mask_msi_irq,
+ .name = "AXON-MSI",
};
-static int msic_host_map(struct irq_host *h, unsigned int virq,
+static int msic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
- set_irq_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq);
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq);
return 0;
}
-static struct irq_host_ops msic_host_ops = {
+static const struct irq_domain_ops msic_host_ops = {
.map = msic_host_map,
};
-static int axon_msi_shutdown(struct of_device *device)
+static void axon_msi_shutdown(struct platform_device *device)
{
- struct axon_msic *msic = device->dev.platform_data;
+ struct axon_msic *msic = dev_get_drvdata(&device->dev);
u32 tmp;
- pr_debug("axon_msi: disabling %s\n",
- msic->irq_host->of_node->full_name);
+ pr_devel("axon_msi: disabling %s\n",
+ msic->irq_domain->of_node->full_name);
tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG);
tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
-
- return 0;
}
-static int axon_msi_probe(struct of_device *device,
- const struct of_device_id *device_id)
+static int axon_msi_probe(struct platform_device *device)
{
- struct device_node *dn = device->node;
+ struct device_node *dn = device->dev.of_node;
struct axon_msic *msic;
unsigned int virq;
int dcr_base, dcr_len;
- pr_debug("axon_msi: setting up dn %s\n", dn->full_name);
+ pr_devel("axon_msi: setting up dn %s\n", dn->full_name);
msic = kzalloc(sizeof(struct axon_msic), GFP_KERNEL);
if (!msic) {
@@ -328,7 +363,7 @@ static int axon_msi_probe(struct of_device *device,
printk(KERN_ERR
"axon_msi: couldn't parse dcr properties on %s\n",
dn->full_name);
- goto out;
+ goto out_free_msic;
}
msic->dcr_host = dcr_map(dn, dcr_base, dcr_len);
@@ -346,26 +381,25 @@ static int axon_msi_probe(struct of_device *device,
goto out_free_msic;
}
- msic->irq_host = irq_alloc_host(of_node_get(dn), IRQ_HOST_MAP_NOMAP,
- NR_IRQS, &msic_host_ops, 0);
- if (!msic->irq_host) {
- printk(KERN_ERR "axon_msi: couldn't allocate irq_host for %s\n",
+ virq = irq_of_parse_and_map(dn, 0);
+ if (virq == NO_IRQ) {
+ printk(KERN_ERR "axon_msi: irq parse and map failed for %s\n",
dn->full_name);
goto out_free_fifo;
}
+ memset(msic->fifo_virt, 0xff, MSIC_FIFO_SIZE_BYTES);
- msic->irq_host->host_data = msic;
-
- virq = irq_of_parse_and_map(dn, 0);
- if (virq == NO_IRQ) {
- printk(KERN_ERR "axon_msi: irq parse and map failed for %s\n",
+ /* We rely on being able to stash a virq in a u16, so limit irqs to < 65536 */
+ msic->irq_domain = irq_domain_add_nomap(dn, 65536, &msic_host_ops, msic);
+ if (!msic->irq_domain) {
+ printk(KERN_ERR "axon_msi: couldn't allocate irq_domain for %s\n",
dn->full_name);
- goto out_free_host;
+ goto out_free_fifo;
}
- set_irq_data(virq, msic);
- set_irq_chained_handler(virq, axon_msi_cascade);
- pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq);
+ irq_set_handler_data(virq, msic);
+ irq_set_chained_handler(virq, axon_msi_cascade);
+ pr_devel("axon_msi: irq 0x%x setup for axon_msi\n", virq);
/* Enable the MSIC hardware */
msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, msic->fifo_phys >> 32);
@@ -375,18 +409,21 @@ static int axon_msi_probe(struct of_device *device,
MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE |
MSIC_CTRL_FIFO_SIZE);
- device->dev.platform_data = msic;
+ msic->read_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG)
+ & MSIC_FIFO_SIZE_MASK;
+
+ dev_set_drvdata(&device->dev, msic);
ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
ppc_md.msi_check_device = axon_msi_check_device;
+ axon_msi_debug_setup(dn, msic);
+
printk(KERN_DEBUG "axon_msi: setup MSIC on %s\n", dn->full_name);
return 0;
-out_free_host:
- kfree(msic->irq_host);
out_free_fifo:
dma_free_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, msic->fifo_virt,
msic->fifo_phys);
@@ -404,17 +441,62 @@ static const struct of_device_id axon_msi_device_id[] = {
{}
};
-static struct of_platform_driver axon_msi_driver = {
- .match_table = axon_msi_device_id,
+static struct platform_driver axon_msi_driver = {
.probe = axon_msi_probe,
.shutdown = axon_msi_shutdown,
- .driver = {
- .name = "axon-msi"
+ .driver = {
+ .name = "axon-msi",
+ .owner = THIS_MODULE,
+ .of_match_table = axon_msi_device_id,
},
};
static int __init axon_msi_init(void)
{
- return of_register_platform_driver(&axon_msi_driver);
+ return platform_driver_register(&axon_msi_driver);
}
subsys_initcall(axon_msi_init);
+
+
+#ifdef DEBUG
+static int msic_set(void *data, u64 val)
+{
+ struct axon_msic *msic = data;
+ out_le32(msic->trigger, val);
+ return 0;
+}
+
+static int msic_get(void *data, u64 *val)
+{
+ *val = 0;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_msic, msic_get, msic_set, "%llu\n");
+
+void axon_msi_debug_setup(struct device_node *dn, struct axon_msic *msic)
+{
+ char name[8];
+ u64 addr;
+
+ addr = of_translate_address(dn, of_get_property(dn, "reg", NULL));
+ if (addr == OF_BAD_ADDR) {
+ pr_devel("axon_msi: couldn't translate reg property\n");
+ return;
+ }
+
+ msic->trigger = ioremap(addr, 0x4);
+ if (!msic->trigger) {
+ pr_devel("axon_msi: ioremap failed\n");
+ return;
+ }
+
+ snprintf(name, sizeof(name), "msic_%d", of_node_to_nid(dn));
+
+ if (!debugfs_create_file(name, 0600, powerpc_debugfs_root,
+ msic, &fops_msic)) {
+ pr_devel("axon_msi: debugfs_create_file failed!\n");
+ return;
+ }
+}
+#endif /* DEBUG */
diff --git a/arch/powerpc/platforms/cell/beat.c b/arch/powerpc/platforms/cell/beat.c
new file mode 100644
index 00000000000..affcf566d46
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat.c
@@ -0,0 +1,264 @@
+/*
+ * Simple routines for Celleb/Beat
+ *
+ * (C) Copyright 2006-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/rtc.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/reboot.h>
+
+#include <asm/hvconsole.h>
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/firmware.h>
+
+#include "beat_wrapper.h"
+#include "beat.h"
+#include "beat_interrupt.h"
+
+static int beat_pm_poweroff_flag;
+
+void beat_restart(char *cmd)
+{
+ beat_shutdown_logical_partition(!beat_pm_poweroff_flag);
+}
+
+void beat_power_off(void)
+{
+ beat_shutdown_logical_partition(0);
+}
+
+u64 beat_halt_code = 0x1000000000000000UL;
+EXPORT_SYMBOL(beat_halt_code);
+
+void beat_halt(void)
+{
+ beat_shutdown_logical_partition(beat_halt_code);
+}
+
+int beat_set_rtc_time(struct rtc_time *rtc_time)
+{
+ u64 tim;
+ tim = mktime(rtc_time->tm_year+1900,
+ rtc_time->tm_mon+1, rtc_time->tm_mday,
+ rtc_time->tm_hour, rtc_time->tm_min, rtc_time->tm_sec);
+ if (beat_rtc_write(tim))
+ return -1;
+ return 0;
+}
+
+void beat_get_rtc_time(struct rtc_time *rtc_time)
+{
+ u64 tim;
+
+ if (beat_rtc_read(&tim))
+ tim = 0;
+ to_tm(tim, rtc_time);
+ rtc_time->tm_year -= 1900;
+ rtc_time->tm_mon -= 1;
+}
+
+#define BEAT_NVRAM_SIZE 4096
+
+ssize_t beat_nvram_read(char *buf, size_t count, loff_t *index)
+{
+ unsigned int i;
+ unsigned long len;
+ char *p = buf;
+
+ if (*index >= BEAT_NVRAM_SIZE)
+ return -ENODEV;
+ i = *index;
+ if (i + count > BEAT_NVRAM_SIZE)
+ count = BEAT_NVRAM_SIZE - i;
+
+ for (; count != 0; count -= len) {
+ len = count;
+ if (len > BEAT_NVRW_CNT)
+ len = BEAT_NVRW_CNT;
+ if (beat_eeprom_read(i, len, p))
+ return -EIO;
+
+ p += len;
+ i += len;
+ }
+ *index = i;
+ return p - buf;
+}
+
+ssize_t beat_nvram_write(char *buf, size_t count, loff_t *index)
+{
+ unsigned int i;
+ unsigned long len;
+ char *p = buf;
+
+ if (*index >= BEAT_NVRAM_SIZE)
+ return -ENODEV;
+ i = *index;
+ if (i + count > BEAT_NVRAM_SIZE)
+ count = BEAT_NVRAM_SIZE - i;
+
+ for (; count != 0; count -= len) {
+ len = count;
+ if (len > BEAT_NVRW_CNT)
+ len = BEAT_NVRW_CNT;
+ if (beat_eeprom_write(i, len, p))
+ return -EIO;
+
+ p += len;
+ i += len;
+ }
+ *index = i;
+ return p - buf;
+}
+
+ssize_t beat_nvram_get_size(void)
+{
+ return BEAT_NVRAM_SIZE;
+}
+
+int beat_set_xdabr(unsigned long dabr, unsigned long dabrx)
+{
+ if (beat_set_dabr(dabr, dabrx))
+ return -1;
+ return 0;
+}
+
+int64_t beat_get_term_char(u64 vterm, u64 *len, u64 *t1, u64 *t2)
+{
+ u64 db[2];
+ s64 ret;
+
+ ret = beat_get_characters_from_console(vterm, len, (u8 *)db);
+ if (ret == 0) {
+ *t1 = db[0];
+ *t2 = db[1];
+ }
+ return ret;
+}
+EXPORT_SYMBOL(beat_get_term_char);
+
+int64_t beat_put_term_char(u64 vterm, u64 len, u64 t1, u64 t2)
+{
+ u64 db[2];
+
+ db[0] = t1;
+ db[1] = t2;
+ return beat_put_characters_to_console(vterm, len, (u8 *)db);
+}
+EXPORT_SYMBOL(beat_put_term_char);
+
+void beat_power_save(void)
+{
+ beat_pause(0);
+}
+
+#ifdef CONFIG_KEXEC
+void beat_kexec_cpu_down(int crash, int secondary)
+{
+ beatic_deinit_IRQ();
+}
+#endif
+
+static irqreturn_t beat_power_event(int virq, void *arg)
+{
+ printk(KERN_DEBUG "Beat: power button pressed\n");
+ beat_pm_poweroff_flag = 1;
+ ctrl_alt_del();
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t beat_reset_event(int virq, void *arg)
+{
+ printk(KERN_DEBUG "Beat: reset button pressed\n");
+ beat_pm_poweroff_flag = 0;
+ ctrl_alt_del();
+ return IRQ_HANDLED;
+}
+
+static struct beat_event_list {
+ const char *typecode;
+ irq_handler_t handler;
+ unsigned int virq;
+} beat_event_list[] = {
+ { "power", beat_power_event, 0 },
+ { "reset", beat_reset_event, 0 },
+};
+
+static int __init beat_register_event(void)
+{
+ u64 path[4], data[2];
+ int rc, i;
+ unsigned int virq;
+
+ for (i = 0; i < ARRAY_SIZE(beat_event_list); i++) {
+ struct beat_event_list *ev = &beat_event_list[i];
+
+ if (beat_construct_event_receive_port(data) != 0) {
+ printk(KERN_ERR "Beat: "
+ "cannot construct event receive port for %s\n",
+ ev->typecode);
+ return -EINVAL;
+ }
+
+ virq = irq_create_mapping(NULL, data[0]);
+ if (virq == NO_IRQ) {
+ printk(KERN_ERR "Beat: failed to get virtual IRQ"
+ " for event receive port for %s\n",
+ ev->typecode);
+ beat_destruct_event_receive_port(data[0]);
+ return -EIO;
+ }
+ ev->virq = virq;
+
+ rc = request_irq(virq, ev->handler, 0,
+ ev->typecode, NULL);
+ if (rc != 0) {
+ printk(KERN_ERR "Beat: failed to request virtual IRQ"
+ " for event receive port for %s\n",
+ ev->typecode);
+ beat_destruct_event_receive_port(data[0]);
+ return rc;
+ }
+
+ path[0] = 0x1000000065780000ul; /* 1,ex */
+ path[1] = 0x627574746f6e0000ul; /* button */
+ path[2] = 0;
+ strncpy((char *)&path[2], ev->typecode, 8);
+ path[3] = 0;
+ data[1] = 0;
+
+ beat_create_repository_node(path, data);
+ }
+ return 0;
+}
+
+static int __init beat_event_init(void)
+{
+ if (!firmware_has_feature(FW_FEATURE_BEAT))
+ return -EINVAL;
+
+ beat_pm_poweroff_flag = 0;
+ return beat_register_event();
+}
+
+device_initcall(beat_event_init);
diff --git a/arch/powerpc/platforms/cell/beat.h b/arch/powerpc/platforms/cell/beat.h
new file mode 100644
index 00000000000..bfcb8e351ae
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat.h
@@ -0,0 +1,39 @@
+/*
+ * Guest OS Interfaces.
+ *
+ * (C) Copyright 2006 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _CELLEB_BEAT_H
+#define _CELLEB_BEAT_H
+
+int64_t beat_get_term_char(uint64_t, uint64_t *, uint64_t *, uint64_t *);
+int64_t beat_put_term_char(uint64_t, uint64_t, uint64_t, uint64_t);
+int64_t beat_repository_encode(int, const char *, uint64_t[4]);
+void beat_restart(char *);
+void beat_power_off(void);
+void beat_halt(void);
+int beat_set_rtc_time(struct rtc_time *);
+void beat_get_rtc_time(struct rtc_time *);
+ssize_t beat_nvram_get_size(void);
+ssize_t beat_nvram_read(char *, size_t, loff_t *);
+ssize_t beat_nvram_write(char *, size_t, loff_t *);
+int beat_set_xdabr(unsigned long, unsigned long);
+void beat_power_save(void);
+void beat_kexec_cpu_down(int, int);
+
+#endif /* _CELLEB_BEAT_H */
diff --git a/arch/powerpc/platforms/cell/beat_htab.c b/arch/powerpc/platforms/cell/beat_htab.c
new file mode 100644
index 00000000000..d4d245c0d78
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat_htab.c
@@ -0,0 +1,445 @@
+/*
+ * "Cell Reference Set" HTAB support.
+ *
+ * (C) Copyright 2006-2007 TOSHIBA CORPORATION
+ *
+ * This code is based on arch/powerpc/platforms/pseries/lpar.c:
+ * Copyright (C) 2001 Todd Inglett, 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#undef DEBUG_LOW
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+
+#include <asm/mmu.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/machdep.h>
+#include <asm/udbg.h>
+
+#include "beat_wrapper.h"
+
+#ifdef DEBUG_LOW
+#define DBG_LOW(fmt...) do { udbg_printf(fmt); } while (0)
+#else
+#define DBG_LOW(fmt...) do { } while (0)
+#endif
+
+static DEFINE_RAW_SPINLOCK(beat_htab_lock);
+
+static inline unsigned int beat_read_mask(unsigned hpte_group)
+{
+ unsigned long rmask = 0;
+ u64 hpte_v[5];
+
+ beat_read_htab_entries(0, hpte_group + 0, hpte_v);
+ if (!(hpte_v[0] & HPTE_V_BOLTED))
+ rmask |= 0x8000;
+ if (!(hpte_v[1] & HPTE_V_BOLTED))
+ rmask |= 0x4000;
+ if (!(hpte_v[2] & HPTE_V_BOLTED))
+ rmask |= 0x2000;
+ if (!(hpte_v[3] & HPTE_V_BOLTED))
+ rmask |= 0x1000;
+ beat_read_htab_entries(0, hpte_group + 4, hpte_v);
+ if (!(hpte_v[0] & HPTE_V_BOLTED))
+ rmask |= 0x0800;
+ if (!(hpte_v[1] & HPTE_V_BOLTED))
+ rmask |= 0x0400;
+ if (!(hpte_v[2] & HPTE_V_BOLTED))
+ rmask |= 0x0200;
+ if (!(hpte_v[3] & HPTE_V_BOLTED))
+ rmask |= 0x0100;
+ hpte_group = ~hpte_group & (htab_hash_mask * HPTES_PER_GROUP);
+ beat_read_htab_entries(0, hpte_group + 0, hpte_v);
+ if (!(hpte_v[0] & HPTE_V_BOLTED))
+ rmask |= 0x80;
+ if (!(hpte_v[1] & HPTE_V_BOLTED))
+ rmask |= 0x40;
+ if (!(hpte_v[2] & HPTE_V_BOLTED))
+ rmask |= 0x20;
+ if (!(hpte_v[3] & HPTE_V_BOLTED))
+ rmask |= 0x10;
+ beat_read_htab_entries(0, hpte_group + 4, hpte_v);
+ if (!(hpte_v[0] & HPTE_V_BOLTED))
+ rmask |= 0x08;
+ if (!(hpte_v[1] & HPTE_V_BOLTED))
+ rmask |= 0x04;
+ if (!(hpte_v[2] & HPTE_V_BOLTED))
+ rmask |= 0x02;
+ if (!(hpte_v[3] & HPTE_V_BOLTED))
+ rmask |= 0x01;
+ return rmask;
+}
+
+static long beat_lpar_hpte_insert(unsigned long hpte_group,
+ unsigned long vpn, unsigned long pa,
+ unsigned long rflags, unsigned long vflags,
+ int psize, int apsize, int ssize)
+{
+ unsigned long lpar_rc;
+ u64 hpte_v, hpte_r, slot;
+
+ if (vflags & HPTE_V_SECONDARY)
+ return -1;
+
+ if (!(vflags & HPTE_V_BOLTED))
+ DBG_LOW("hpte_insert(group=%lx, va=%016lx, pa=%016lx, "
+ "rflags=%lx, vflags=%lx, psize=%d)\n",
+ hpte_group, va, pa, rflags, vflags, psize);
+
+ hpte_v = hpte_encode_v(vpn, psize, apsize, MMU_SEGSIZE_256M) |
+ vflags | HPTE_V_VALID;
+ hpte_r = hpte_encode_r(pa, psize, apsize) | rflags;
+
+ if (!(vflags & HPTE_V_BOLTED))
+ DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r);
+
+ if (rflags & _PAGE_NO_CACHE)
+ hpte_r &= ~HPTE_R_M;
+
+ raw_spin_lock(&beat_htab_lock);
+ lpar_rc = beat_read_mask(hpte_group);
+ if (lpar_rc == 0) {
+ if (!(vflags & HPTE_V_BOLTED))
+ DBG_LOW(" full\n");
+ raw_spin_unlock(&beat_htab_lock);
+ return -1;
+ }
+
+ lpar_rc = beat_insert_htab_entry(0, hpte_group, lpar_rc << 48,
+ hpte_v, hpte_r, &slot);
+ raw_spin_unlock(&beat_htab_lock);
+
+ /*
+ * Since we try and ioremap PHBs we don't own, the pte insert
+ * will fail. However we must catch the failure in hash_page
+ * or we will loop forever, so return -2 in this case.
+ */
+ if (unlikely(lpar_rc != 0)) {
+ if (!(vflags & HPTE_V_BOLTED))
+ DBG_LOW(" lpar err %lx\n", lpar_rc);
+ return -2;
+ }
+ if (!(vflags & HPTE_V_BOLTED))
+ DBG_LOW(" -> slot: %lx\n", slot);
+
+ /* We have to pass down the secondary bucket bit here as well */
+ return (slot ^ hpte_group) & 15;
+}
+
+static long beat_lpar_hpte_remove(unsigned long hpte_group)
+{
+ DBG_LOW("hpte_remove(group=%lx)\n", hpte_group);
+ return -1;
+}
+
+static unsigned long beat_lpar_hpte_getword0(unsigned long slot)
+{
+ unsigned long dword0;
+ unsigned long lpar_rc;
+ u64 dword[5];
+
+ lpar_rc = beat_read_htab_entries(0, slot & ~3UL, dword);
+
+ dword0 = dword[slot&3];
+
+ BUG_ON(lpar_rc != 0);
+
+ return dword0;
+}
+
+static void beat_lpar_hptab_clear(void)
+{
+ unsigned long size_bytes = 1UL << ppc64_pft_size;
+ unsigned long hpte_count = size_bytes >> 4;
+ int i;
+ u64 dummy0, dummy1;
+
+ /* TODO: Use bulk call */
+ for (i = 0; i < hpte_count; i++)
+ beat_write_htab_entry(0, i, 0, 0, -1UL, -1UL, &dummy0, &dummy1);
+}
+
+/*
+ * NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and
+ * the low 3 bits of flags happen to line up. So no transform is needed.
+ * We can probably optimize here and assume the high bits of newpp are
+ * already zero. For now I am paranoid.
+ */
+static long beat_lpar_hpte_updatepp(unsigned long slot,
+ unsigned long newpp,
+ unsigned long vpn,
+ int psize, int apsize,
+ int ssize, int local)
+{
+ unsigned long lpar_rc;
+ u64 dummy0, dummy1;
+ unsigned long want_v;
+
+ want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M);
+
+ DBG_LOW(" update: "
+ "avpnv=%016lx, slot=%016lx, psize: %d, newpp %016lx ... ",
+ want_v & HPTE_V_AVPN, slot, psize, newpp);
+
+ raw_spin_lock(&beat_htab_lock);
+ dummy0 = beat_lpar_hpte_getword0(slot);
+ if ((dummy0 & ~0x7FUL) != (want_v & ~0x7FUL)) {
+ DBG_LOW("not found !\n");
+ raw_spin_unlock(&beat_htab_lock);
+ return -1;
+ }
+
+ lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7, &dummy0,
+ &dummy1);
+ raw_spin_unlock(&beat_htab_lock);
+ if (lpar_rc != 0 || dummy0 == 0) {
+ DBG_LOW("not found !\n");
+ return -1;
+ }
+
+ DBG_LOW("ok %lx %lx\n", dummy0, dummy1);
+
+ BUG_ON(lpar_rc != 0);
+
+ return 0;
+}
+
+static long beat_lpar_hpte_find(unsigned long vpn, int psize)
+{
+ unsigned long hash;
+ unsigned long i, j;
+ long slot;
+ unsigned long want_v, hpte_v;
+
+ hash = hpt_hash(vpn, mmu_psize_defs[psize].shift, MMU_SEGSIZE_256M);
+ want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M);
+
+ for (j = 0; j < 2; j++) {
+ slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
+ for (i = 0; i < HPTES_PER_GROUP; i++) {
+ hpte_v = beat_lpar_hpte_getword0(slot);
+
+ if (HPTE_V_COMPARE(hpte_v, want_v)
+ && (hpte_v & HPTE_V_VALID)
+ && (!!(hpte_v & HPTE_V_SECONDARY) == j)) {
+ /* HPTE matches */
+ if (j)
+ slot = -slot;
+ return slot;
+ }
+ ++slot;
+ }
+ hash = ~hash;
+ }
+
+ return -1;
+}
+
+static void beat_lpar_hpte_updateboltedpp(unsigned long newpp,
+ unsigned long ea,
+ int psize, int ssize)
+{
+ unsigned long vpn;
+ unsigned long lpar_rc, slot, vsid;
+ u64 dummy0, dummy1;
+
+ vsid = get_kernel_vsid(ea, MMU_SEGSIZE_256M);
+ vpn = hpt_vpn(ea, vsid, MMU_SEGSIZE_256M);
+
+ raw_spin_lock(&beat_htab_lock);
+ slot = beat_lpar_hpte_find(vpn, psize);
+ BUG_ON(slot == -1);
+
+ lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7,
+ &dummy0, &dummy1);
+ raw_spin_unlock(&beat_htab_lock);
+
+ BUG_ON(lpar_rc != 0);
+}
+
+static void beat_lpar_hpte_invalidate(unsigned long slot, unsigned long vpn,
+ int psize, int apsize,
+ int ssize, int local)
+{
+ unsigned long want_v;
+ unsigned long lpar_rc;
+ u64 dummy1, dummy2;
+ unsigned long flags;
+
+ DBG_LOW(" inval : slot=%lx, va=%016lx, psize: %d, local: %d\n",
+ slot, va, psize, local);
+ want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M);
+
+ raw_spin_lock_irqsave(&beat_htab_lock, flags);
+ dummy1 = beat_lpar_hpte_getword0(slot);
+
+ if ((dummy1 & ~0x7FUL) != (want_v & ~0x7FUL)) {
+ DBG_LOW("not found !\n");
+ raw_spin_unlock_irqrestore(&beat_htab_lock, flags);
+ return;
+ }
+
+ lpar_rc = beat_write_htab_entry(0, slot, 0, 0, HPTE_V_VALID, 0,
+ &dummy1, &dummy2);
+ raw_spin_unlock_irqrestore(&beat_htab_lock, flags);
+
+ BUG_ON(lpar_rc != 0);
+}
+
+void __init hpte_init_beat(void)
+{
+ ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate;
+ ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp;
+ ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp;
+ ppc_md.hpte_insert = beat_lpar_hpte_insert;
+ ppc_md.hpte_remove = beat_lpar_hpte_remove;
+ ppc_md.hpte_clear_all = beat_lpar_hptab_clear;
+}
+
+static long beat_lpar_hpte_insert_v3(unsigned long hpte_group,
+ unsigned long vpn, unsigned long pa,
+ unsigned long rflags, unsigned long vflags,
+ int psize, int apsize, int ssize)
+{
+ unsigned long lpar_rc;
+ u64 hpte_v, hpte_r, slot;
+
+ if (vflags & HPTE_V_SECONDARY)
+ return -1;
+
+ if (!(vflags & HPTE_V_BOLTED))
+ DBG_LOW("hpte_insert(group=%lx, vpn=%016lx, pa=%016lx, "
+ "rflags=%lx, vflags=%lx, psize=%d)\n",
+ hpte_group, vpn, pa, rflags, vflags, psize);
+
+ hpte_v = hpte_encode_v(vpn, psize, apsize, MMU_SEGSIZE_256M) |
+ vflags | HPTE_V_VALID;
+ hpte_r = hpte_encode_r(pa, psize, apsize) | rflags;
+
+ if (!(vflags & HPTE_V_BOLTED))
+ DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r);
+
+ if (rflags & _PAGE_NO_CACHE)
+ hpte_r &= ~HPTE_R_M;
+
+ /* insert into not-volted entry */
+ lpar_rc = beat_insert_htab_entry3(0, hpte_group, hpte_v, hpte_r,
+ HPTE_V_BOLTED, 0, &slot);
+ /*
+ * Since we try and ioremap PHBs we don't own, the pte insert
+ * will fail. However we must catch the failure in hash_page
+ * or we will loop forever, so return -2 in this case.
+ */
+ if (unlikely(lpar_rc != 0)) {
+ if (!(vflags & HPTE_V_BOLTED))
+ DBG_LOW(" lpar err %lx\n", lpar_rc);
+ return -2;
+ }
+ if (!(vflags & HPTE_V_BOLTED))
+ DBG_LOW(" -> slot: %lx\n", slot);
+
+ /* We have to pass down the secondary bucket bit here as well */
+ return (slot ^ hpte_group) & 15;
+}
+
+/*
+ * NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and
+ * the low 3 bits of flags happen to line up. So no transform is needed.
+ * We can probably optimize here and assume the high bits of newpp are
+ * already zero. For now I am paranoid.
+ */
+static long beat_lpar_hpte_updatepp_v3(unsigned long slot,
+ unsigned long newpp,
+ unsigned long vpn,
+ int psize, int apsize,
+ int ssize, int local)
+{
+ unsigned long lpar_rc;
+ unsigned long want_v;
+ unsigned long pss;
+
+ want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M);
+ pss = (psize == MMU_PAGE_4K) ? -1UL : mmu_psize_defs[psize].penc[psize];
+
+ DBG_LOW(" update: "
+ "avpnv=%016lx, slot=%016lx, psize: %d, newpp %016lx ... ",
+ want_v & HPTE_V_AVPN, slot, psize, newpp);
+
+ lpar_rc = beat_update_htab_permission3(0, slot, want_v, pss, 7, newpp);
+
+ if (lpar_rc == 0xfffffff7) {
+ DBG_LOW("not found !\n");
+ return -1;
+ }
+
+ DBG_LOW("ok\n");
+
+ BUG_ON(lpar_rc != 0);
+
+ return 0;
+}
+
+static void beat_lpar_hpte_invalidate_v3(unsigned long slot, unsigned long vpn,
+ int psize, int apsize,
+ int ssize, int local)
+{
+ unsigned long want_v;
+ unsigned long lpar_rc;
+ unsigned long pss;
+
+ DBG_LOW(" inval : slot=%lx, vpn=%016lx, psize: %d, local: %d\n",
+ slot, vpn, psize, local);
+ want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M);
+ pss = (psize == MMU_PAGE_4K) ? -1UL : mmu_psize_defs[psize].penc[psize];
+
+ lpar_rc = beat_invalidate_htab_entry3(0, slot, want_v, pss);
+
+ /* E_busy can be valid output: page may be already replaced */
+ BUG_ON(lpar_rc != 0 && lpar_rc != 0xfffffff7);
+}
+
+static int64_t _beat_lpar_hptab_clear_v3(void)
+{
+ return beat_clear_htab3(0);
+}
+
+static void beat_lpar_hptab_clear_v3(void)
+{
+ _beat_lpar_hptab_clear_v3();
+}
+
+void __init hpte_init_beat_v3(void)
+{
+ if (_beat_lpar_hptab_clear_v3() == 0) {
+ ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate_v3;
+ ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp_v3;
+ ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp;
+ ppc_md.hpte_insert = beat_lpar_hpte_insert_v3;
+ ppc_md.hpte_remove = beat_lpar_hpte_remove;
+ ppc_md.hpte_clear_all = beat_lpar_hptab_clear_v3;
+ } else {
+ ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate;
+ ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp;
+ ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp;
+ ppc_md.hpte_insert = beat_lpar_hpte_insert;
+ ppc_md.hpte_remove = beat_lpar_hpte_remove;
+ ppc_md.hpte_clear_all = beat_lpar_hptab_clear;
+ }
+}
diff --git a/arch/powerpc/platforms/cell/beat_hvCall.S b/arch/powerpc/platforms/cell/beat_hvCall.S
new file mode 100644
index 00000000000..96c80190712
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat_hvCall.S
@@ -0,0 +1,285 @@
+/*
+ * Beat hypervisor call I/F
+ *
+ * (C) Copyright 2007 TOSHIBA CORPORATION
+ *
+ * This code is based on arch/powerpc/platforms/pseries/hvCall.S.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <asm/ppc_asm.h>
+
+/* Not implemented on Beat, now */
+#define HCALL_INST_PRECALL
+#define HCALL_INST_POSTCALL
+
+ .text
+
+#define HVSC .long 0x44000022
+
+/* Note: takes only 7 input parameters at maximum */
+_GLOBAL(beat_hcall_norets)
+ HMT_MEDIUM
+
+ mfcr r0
+ stw r0,8(r1)
+
+ HCALL_INST_PRECALL
+
+ mr r11,r3
+ mr r3,r4
+ mr r4,r5
+ mr r5,r6
+ mr r6,r7
+ mr r7,r8
+ mr r8,r9
+
+ HVSC /* invoke the hypervisor */
+
+ HCALL_INST_POSTCALL
+
+ lwz r0,8(r1)
+ mtcrf 0xff,r0
+
+ blr /* return r3 = status */
+
+/* Note: takes 8 input parameters at maximum */
+_GLOBAL(beat_hcall_norets8)
+ HMT_MEDIUM
+
+ mfcr r0
+ stw r0,8(r1)
+
+ HCALL_INST_PRECALL
+
+ mr r11,r3
+ mr r3,r4
+ mr r4,r5
+ mr r5,r6
+ mr r6,r7
+ mr r7,r8
+ mr r8,r9
+ ld r10,STK_PARAM(R10)(r1)
+
+ HVSC /* invoke the hypervisor */
+
+ HCALL_INST_POSTCALL
+
+ lwz r0,8(r1)
+ mtcrf 0xff,r0
+
+ blr /* return r3 = status */
+
+/* Note: takes only 6 input parameters, 1 output parameters at maximum */
+_GLOBAL(beat_hcall1)
+ HMT_MEDIUM
+
+ mfcr r0
+ stw r0,8(r1)
+
+ HCALL_INST_PRECALL
+
+ std r4,STK_PARAM(R4)(r1) /* save ret buffer */
+
+ mr r11,r3
+ mr r3,r5
+ mr r4,r6
+ mr r5,r7
+ mr r6,r8
+ mr r7,r9
+ mr r8,r10
+
+ HVSC /* invoke the hypervisor */
+
+ HCALL_INST_POSTCALL
+
+ ld r12,STK_PARAM(R4)(r1)
+ std r4, 0(r12)
+
+ lwz r0,8(r1)
+ mtcrf 0xff,r0
+
+ blr /* return r3 = status */
+
+/* Note: takes only 6 input parameters, 2 output parameters at maximum */
+_GLOBAL(beat_hcall2)
+ HMT_MEDIUM
+
+ mfcr r0
+ stw r0,8(r1)
+
+ HCALL_INST_PRECALL
+
+ std r4,STK_PARAM(R4)(r1) /* save ret buffer */
+
+ mr r11,r3
+ mr r3,r5
+ mr r4,r6
+ mr r5,r7
+ mr r6,r8
+ mr r7,r9
+ mr r8,r10
+
+ HVSC /* invoke the hypervisor */
+
+ HCALL_INST_POSTCALL
+
+ ld r12,STK_PARAM(R4)(r1)
+ std r4, 0(r12)
+ std r5, 8(r12)
+
+ lwz r0,8(r1)
+ mtcrf 0xff,r0
+
+ blr /* return r3 = status */
+
+/* Note: takes only 6 input parameters, 3 output parameters at maximum */
+_GLOBAL(beat_hcall3)
+ HMT_MEDIUM
+
+ mfcr r0
+ stw r0,8(r1)
+
+ HCALL_INST_PRECALL
+
+ std r4,STK_PARAM(R4)(r1) /* save ret buffer */
+
+ mr r11,r3
+ mr r3,r5
+ mr r4,r6
+ mr r5,r7
+ mr r6,r8
+ mr r7,r9
+ mr r8,r10
+
+ HVSC /* invoke the hypervisor */
+
+ HCALL_INST_POSTCALL
+
+ ld r12,STK_PARAM(R4)(r1)
+ std r4, 0(r12)
+ std r5, 8(r12)
+ std r6, 16(r12)
+
+ lwz r0,8(r1)
+ mtcrf 0xff,r0
+
+ blr /* return r3 = status */
+
+/* Note: takes only 6 input parameters, 4 output parameters at maximum */
+_GLOBAL(beat_hcall4)
+ HMT_MEDIUM
+
+ mfcr r0
+ stw r0,8(r1)
+
+ HCALL_INST_PRECALL
+
+ std r4,STK_PARAM(R4)(r1) /* save ret buffer */
+
+ mr r11,r3
+ mr r3,r5
+ mr r4,r6
+ mr r5,r7
+ mr r6,r8
+ mr r7,r9
+ mr r8,r10
+
+ HVSC /* invoke the hypervisor */
+
+ HCALL_INST_POSTCALL
+
+ ld r12,STK_PARAM(R4)(r1)
+ std r4, 0(r12)
+ std r5, 8(r12)
+ std r6, 16(r12)
+ std r7, 24(r12)
+
+ lwz r0,8(r1)
+ mtcrf 0xff,r0
+
+ blr /* return r3 = status */
+
+/* Note: takes only 6 input parameters, 5 output parameters at maximum */
+_GLOBAL(beat_hcall5)
+ HMT_MEDIUM
+
+ mfcr r0
+ stw r0,8(r1)
+
+ HCALL_INST_PRECALL
+
+ std r4,STK_PARAM(R4)(r1) /* save ret buffer */
+
+ mr r11,r3
+ mr r3,r5
+ mr r4,r6
+ mr r5,r7
+ mr r6,r8
+ mr r7,r9
+ mr r8,r10
+
+ HVSC /* invoke the hypervisor */
+
+ HCALL_INST_POSTCALL
+
+ ld r12,STK_PARAM(R4)(r1)
+ std r4, 0(r12)
+ std r5, 8(r12)
+ std r6, 16(r12)
+ std r7, 24(r12)
+ std r8, 32(r12)
+
+ lwz r0,8(r1)
+ mtcrf 0xff,r0
+
+ blr /* return r3 = status */
+
+/* Note: takes only 6 input parameters, 6 output parameters at maximum */
+_GLOBAL(beat_hcall6)
+ HMT_MEDIUM
+
+ mfcr r0
+ stw r0,8(r1)
+
+ HCALL_INST_PRECALL
+
+ std r4,STK_PARAM(R4)(r1) /* save ret buffer */
+
+ mr r11,r3
+ mr r3,r5
+ mr r4,r6
+ mr r5,r7
+ mr r6,r8
+ mr r7,r9
+ mr r8,r10
+
+ HVSC /* invoke the hypervisor */
+
+ HCALL_INST_POSTCALL
+
+ ld r12,STK_PARAM(R4)(r1)
+ std r4, 0(r12)
+ std r5, 8(r12)
+ std r6, 16(r12)
+ std r7, 24(r12)
+ std r8, 32(r12)
+ std r9, 40(r12)
+
+ lwz r0,8(r1)
+ mtcrf 0xff,r0
+
+ blr /* return r3 = status */
diff --git a/arch/powerpc/platforms/cell/beat_interrupt.c b/arch/powerpc/platforms/cell/beat_interrupt.c
new file mode 100644
index 00000000000..9e5dfbcc00a
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat_interrupt.c
@@ -0,0 +1,253 @@
+/*
+ * Celleb/Beat Interrupt controller
+ *
+ * (C) Copyright 2006-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/percpu.h>
+#include <linux/types.h>
+
+#include <asm/machdep.h>
+
+#include "beat_interrupt.h"
+#include "beat_wrapper.h"
+
+#define MAX_IRQS NR_IRQS
+static DEFINE_RAW_SPINLOCK(beatic_irq_mask_lock);
+static uint64_t beatic_irq_mask_enable[(MAX_IRQS+255)/64];
+static uint64_t beatic_irq_mask_ack[(MAX_IRQS+255)/64];
+
+static struct irq_domain *beatic_host;
+
+/*
+ * In this implementation, "virq" == "IRQ plug number",
+ * "(irq_hw_number_t)hwirq" == "IRQ outlet number".
+ */
+
+/* assumption: locked */
+static inline void beatic_update_irq_mask(unsigned int irq_plug)
+{
+ int off;
+ unsigned long masks[4];
+
+ off = (irq_plug / 256) * 4;
+ masks[0] = beatic_irq_mask_enable[off + 0]
+ & beatic_irq_mask_ack[off + 0];
+ masks[1] = beatic_irq_mask_enable[off + 1]
+ & beatic_irq_mask_ack[off + 1];
+ masks[2] = beatic_irq_mask_enable[off + 2]
+ & beatic_irq_mask_ack[off + 2];
+ masks[3] = beatic_irq_mask_enable[off + 3]
+ & beatic_irq_mask_ack[off + 3];
+ if (beat_set_interrupt_mask(irq_plug&~255UL,
+ masks[0], masks[1], masks[2], masks[3]) != 0)
+ panic("Failed to set mask IRQ!");
+}
+
+static void beatic_mask_irq(struct irq_data *d)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags);
+ beatic_irq_mask_enable[d->irq/64] &= ~(1UL << (63 - (d->irq%64)));
+ beatic_update_irq_mask(d->irq);
+ raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);
+}
+
+static void beatic_unmask_irq(struct irq_data *d)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags);
+ beatic_irq_mask_enable[d->irq/64] |= 1UL << (63 - (d->irq%64));
+ beatic_update_irq_mask(d->irq);
+ raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);
+}
+
+static void beatic_ack_irq(struct irq_data *d)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags);
+ beatic_irq_mask_ack[d->irq/64] &= ~(1UL << (63 - (d->irq%64)));
+ beatic_update_irq_mask(d->irq);
+ raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);
+}
+
+static void beatic_end_irq(struct irq_data *d)
+{
+ s64 err;
+ unsigned long flags;
+
+ err = beat_downcount_of_interrupt(d->irq);
+ if (err != 0) {
+ if ((err & 0xFFFFFFFF) != 0xFFFFFFF5) /* -11: wrong state */
+ panic("Failed to downcount IRQ! Error = %16llx", err);
+
+ printk(KERN_ERR "IRQ over-downcounted, plug %d\n", d->irq);
+ }
+ raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags);
+ beatic_irq_mask_ack[d->irq/64] |= 1UL << (63 - (d->irq%64));
+ beatic_update_irq_mask(d->irq);
+ raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);
+}
+
+static struct irq_chip beatic_pic = {
+ .name = "CELL-BEAT",
+ .irq_unmask = beatic_unmask_irq,
+ .irq_mask = beatic_mask_irq,
+ .irq_eoi = beatic_end_irq,
+};
+
+/*
+ * Dispose binding hardware IRQ number (hw) and Virtuql IRQ number (virq),
+ * update flags.
+ *
+ * Note that the number (virq) is already assigned at upper layer.
+ */
+static void beatic_pic_host_unmap(struct irq_domain *h, unsigned int virq)
+{
+ beat_destruct_irq_plug(virq);
+}
+
+/*
+ * Create or update binding hardware IRQ number (hw) and Virtuql
+ * IRQ number (virq). This is called only once for a given mapping.
+ *
+ * Note that the number (virq) is already assigned at upper layer.
+ */
+static int beatic_pic_host_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ int64_t err;
+
+ err = beat_construct_and_connect_irq_plug(virq, hw);
+ if (err < 0)
+ return -EIO;
+
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ irq_set_chip_and_handler(virq, &beatic_pic, handle_fasteoi_irq);
+ return 0;
+}
+
+/*
+ * Translate device-tree interrupt spec to irq_hw_number_t style (ulong),
+ * to pass away to irq_create_mapping().
+ *
+ * Called from irq_create_of_mapping() only.
+ * Note: We have only 1 entry to translate.
+ */
+static int beatic_pic_host_xlate(struct irq_domain *h, struct device_node *ct,
+ const u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_flags)
+{
+ const u64 *intspec2 = (const u64 *)intspec;
+
+ *out_hwirq = *intspec2;
+ *out_flags |= IRQ_TYPE_LEVEL_LOW;
+ return 0;
+}
+
+static int beatic_pic_host_match(struct irq_domain *h, struct device_node *np)
+{
+ /* Match all */
+ return 1;
+}
+
+static const struct irq_domain_ops beatic_pic_host_ops = {
+ .map = beatic_pic_host_map,
+ .unmap = beatic_pic_host_unmap,
+ .xlate = beatic_pic_host_xlate,
+ .match = beatic_pic_host_match,
+};
+
+/*
+ * Get an IRQ number
+ * Note: returns VIRQ
+ */
+static inline unsigned int beatic_get_irq_plug(void)
+{
+ int i;
+ uint64_t pending[4], ub;
+
+ for (i = 0; i < MAX_IRQS; i += 256) {
+ beat_detect_pending_interrupts(i, pending);
+ __asm__ ("cntlzd %0,%1":"=r"(ub):
+ "r"(pending[0] & beatic_irq_mask_enable[i/64+0]
+ & beatic_irq_mask_ack[i/64+0]));
+ if (ub != 64)
+ return i + ub + 0;
+ __asm__ ("cntlzd %0,%1":"=r"(ub):
+ "r"(pending[1] & beatic_irq_mask_enable[i/64+1]
+ & beatic_irq_mask_ack[i/64+1]));
+ if (ub != 64)
+ return i + ub + 64;
+ __asm__ ("cntlzd %0,%1":"=r"(ub):
+ "r"(pending[2] & beatic_irq_mask_enable[i/64+2]
+ & beatic_irq_mask_ack[i/64+2]));
+ if (ub != 64)
+ return i + ub + 128;
+ __asm__ ("cntlzd %0,%1":"=r"(ub):
+ "r"(pending[3] & beatic_irq_mask_enable[i/64+3]
+ & beatic_irq_mask_ack[i/64+3]));
+ if (ub != 64)
+ return i + ub + 192;
+ }
+
+ return NO_IRQ;
+}
+unsigned int beatic_get_irq(void)
+{
+ unsigned int ret;
+
+ ret = beatic_get_irq_plug();
+ if (ret != NO_IRQ)
+ beatic_ack_irq(irq_get_irq_data(ret));
+ return ret;
+}
+
+/*
+ */
+void __init beatic_init_IRQ(void)
+{
+ int i;
+
+ memset(beatic_irq_mask_enable, 0, sizeof(beatic_irq_mask_enable));
+ memset(beatic_irq_mask_ack, 255, sizeof(beatic_irq_mask_ack));
+ for (i = 0; i < MAX_IRQS; i += 256)
+ beat_set_interrupt_mask(i, 0L, 0L, 0L, 0L);
+
+ /* Set out get_irq function */
+ ppc_md.get_irq = beatic_get_irq;
+
+ /* Allocate an irq host */
+ beatic_host = irq_domain_add_nomap(NULL, ~0, &beatic_pic_host_ops, NULL);
+ BUG_ON(beatic_host == NULL);
+ irq_set_default_host(beatic_host);
+}
+
+void beatic_deinit_IRQ(void)
+{
+ int i;
+
+ for (i = 1; i < nr_irqs; i++)
+ beat_destruct_irq_plug(i);
+}
diff --git a/arch/powerpc/platforms/cell/beat_interrupt.h b/arch/powerpc/platforms/cell/beat_interrupt.h
new file mode 100644
index 00000000000..a7e52f91a07
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat_interrupt.h
@@ -0,0 +1,30 @@
+/*
+ * Celleb/Beat Interrupt controller
+ *
+ * (C) Copyright 2006 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef ASM_BEAT_PIC_H
+#define ASM_BEAT_PIC_H
+#ifdef __KERNEL__
+
+extern void beatic_init_IRQ(void);
+extern unsigned int beatic_get_irq(void);
+extern void beatic_deinit_IRQ(void);
+
+#endif
+#endif /* ASM_BEAT_PIC_H */
diff --git a/arch/powerpc/platforms/cell/beat_iommu.c b/arch/powerpc/platforms/cell/beat_iommu.c
new file mode 100644
index 00000000000..3ce68556893
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat_iommu.c
@@ -0,0 +1,115 @@
+/*
+ * Support for IOMMU on Celleb platform.
+ *
+ * (C) Copyright 2006-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/of_platform.h>
+
+#include <asm/machdep.h>
+
+#include "beat_wrapper.h"
+
+#define DMA_FLAGS 0xf800000000000000UL /* r/w permitted, coherency required,
+ strongest order */
+
+static int __init find_dma_window(u64 *io_space_id, u64 *ioid,
+ u64 *base, u64 *size, u64 *io_page_size)
+{
+ struct device_node *dn;
+ const unsigned long *dma_window;
+
+ for_each_node_by_type(dn, "ioif") {
+ dma_window = of_get_property(dn, "toshiba,dma-window", NULL);
+ if (dma_window) {
+ *io_space_id = (dma_window[0] >> 32) & 0xffffffffUL;
+ *ioid = dma_window[0] & 0x7ffUL;
+ *base = dma_window[1];
+ *size = dma_window[2];
+ *io_page_size = 1 << dma_window[3];
+ of_node_put(dn);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static unsigned long celleb_dma_direct_offset;
+
+static void __init celleb_init_direct_mapping(void)
+{
+ u64 lpar_addr, io_addr;
+ u64 io_space_id, ioid, dma_base, dma_size, io_page_size;
+
+ if (!find_dma_window(&io_space_id, &ioid, &dma_base, &dma_size,
+ &io_page_size)) {
+ pr_info("No dma window found !\n");
+ return;
+ }
+
+ for (lpar_addr = 0; lpar_addr < dma_size; lpar_addr += io_page_size) {
+ io_addr = lpar_addr + dma_base;
+ (void)beat_put_iopte(io_space_id, io_addr, lpar_addr,
+ ioid, DMA_FLAGS);
+ }
+
+ celleb_dma_direct_offset = dma_base;
+}
+
+static void celleb_dma_dev_setup(struct device *dev)
+{
+ set_dma_ops(dev, &dma_direct_ops);
+ set_dma_offset(dev, celleb_dma_direct_offset);
+}
+
+static void celleb_pci_dma_dev_setup(struct pci_dev *pdev)
+{
+ celleb_dma_dev_setup(&pdev->dev);
+}
+
+static int celleb_of_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+
+ /* We are only intereted in device addition */
+ if (action != BUS_NOTIFY_ADD_DEVICE)
+ return 0;
+
+ celleb_dma_dev_setup(dev);
+
+ return 0;
+}
+
+static struct notifier_block celleb_of_bus_notifier = {
+ .notifier_call = celleb_of_bus_notify
+};
+
+static int __init celleb_init_iommu(void)
+{
+ celleb_init_direct_mapping();
+ ppc_md.pci_dma_dev_setup = celleb_pci_dma_dev_setup;
+ bus_register_notifier(&platform_bus_type, &celleb_of_bus_notifier);
+
+ return 0;
+}
+
+machine_arch_initcall(celleb_beat, celleb_init_iommu);
diff --git a/arch/powerpc/platforms/cell/beat_spu_priv1.c b/arch/powerpc/platforms/cell/beat_spu_priv1.c
new file mode 100644
index 00000000000..13f52589d3a
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat_spu_priv1.c
@@ -0,0 +1,205 @@
+/*
+ * spu hypervisor abstraction for Beat
+ *
+ * (C) Copyright 2006-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <asm/types.h>
+#include <asm/spu.h>
+#include <asm/spu_priv1.h>
+
+#include "beat_wrapper.h"
+
+static inline void _int_mask_set(struct spu *spu, int class, u64 mask)
+{
+ spu->shadow_int_mask_RW[class] = mask;
+ beat_set_irq_mask_for_spe(spu->spe_id, class, mask);
+}
+
+static inline u64 _int_mask_get(struct spu *spu, int class)
+{
+ return spu->shadow_int_mask_RW[class];
+}
+
+static void int_mask_set(struct spu *spu, int class, u64 mask)
+{
+ _int_mask_set(spu, class, mask);
+}
+
+static u64 int_mask_get(struct spu *spu, int class)
+{
+ return _int_mask_get(spu, class);
+}
+
+static void int_mask_and(struct spu *spu, int class, u64 mask)
+{
+ u64 old_mask;
+ old_mask = _int_mask_get(spu, class);
+ _int_mask_set(spu, class, old_mask & mask);
+}
+
+static void int_mask_or(struct spu *spu, int class, u64 mask)
+{
+ u64 old_mask;
+ old_mask = _int_mask_get(spu, class);
+ _int_mask_set(spu, class, old_mask | mask);
+}
+
+static void int_stat_clear(struct spu *spu, int class, u64 stat)
+{
+ beat_clear_interrupt_status_of_spe(spu->spe_id, class, stat);
+}
+
+static u64 int_stat_get(struct spu *spu, int class)
+{
+ u64 int_stat;
+ beat_get_interrupt_status_of_spe(spu->spe_id, class, &int_stat);
+ return int_stat;
+}
+
+static void cpu_affinity_set(struct spu *spu, int cpu)
+{
+ return;
+}
+
+static u64 mfc_dar_get(struct spu *spu)
+{
+ u64 dar;
+ beat_get_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, mfc_dar_RW), &dar);
+ return dar;
+}
+
+static u64 mfc_dsisr_get(struct spu *spu)
+{
+ u64 dsisr;
+ beat_get_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, mfc_dsisr_RW), &dsisr);
+ return dsisr;
+}
+
+static void mfc_dsisr_set(struct spu *spu, u64 dsisr)
+{
+ beat_set_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, mfc_dsisr_RW), dsisr);
+}
+
+static void mfc_sdr_setup(struct spu *spu)
+{
+ return;
+}
+
+static void mfc_sr1_set(struct spu *spu, u64 sr1)
+{
+ beat_set_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, mfc_sr1_RW), sr1);
+}
+
+static u64 mfc_sr1_get(struct spu *spu)
+{
+ u64 sr1;
+ beat_get_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, mfc_sr1_RW), &sr1);
+ return sr1;
+}
+
+static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id)
+{
+ beat_set_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, mfc_tclass_id_RW), tclass_id);
+}
+
+static u64 mfc_tclass_id_get(struct spu *spu)
+{
+ u64 tclass_id;
+ beat_get_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, mfc_tclass_id_RW), &tclass_id);
+ return tclass_id;
+}
+
+static void tlb_invalidate(struct spu *spu)
+{
+ beat_set_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, tlb_invalidate_entry_W), 0ul);
+}
+
+static void resource_allocation_groupID_set(struct spu *spu, u64 id)
+{
+ beat_set_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, resource_allocation_groupID_RW),
+ id);
+}
+
+static u64 resource_allocation_groupID_get(struct spu *spu)
+{
+ u64 id;
+ beat_get_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, resource_allocation_groupID_RW),
+ &id);
+ return id;
+}
+
+static void resource_allocation_enable_set(struct spu *spu, u64 enable)
+{
+ beat_set_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, resource_allocation_enable_RW),
+ enable);
+}
+
+static u64 resource_allocation_enable_get(struct spu *spu)
+{
+ u64 enable;
+ beat_get_spe_privileged_state_1_registers(
+ spu->spe_id,
+ offsetof(struct spu_priv1, resource_allocation_enable_RW),
+ &enable);
+ return enable;
+}
+
+const struct spu_priv1_ops spu_priv1_beat_ops = {
+ .int_mask_and = int_mask_and,
+ .int_mask_or = int_mask_or,
+ .int_mask_set = int_mask_set,
+ .int_mask_get = int_mask_get,
+ .int_stat_clear = int_stat_clear,
+ .int_stat_get = int_stat_get,
+ .cpu_affinity_set = cpu_affinity_set,
+ .mfc_dar_get = mfc_dar_get,
+ .mfc_dsisr_get = mfc_dsisr_get,
+ .mfc_dsisr_set = mfc_dsisr_set,
+ .mfc_sdr_setup = mfc_sdr_setup,
+ .mfc_sr1_set = mfc_sr1_set,
+ .mfc_sr1_get = mfc_sr1_get,
+ .mfc_tclass_id_set = mfc_tclass_id_set,
+ .mfc_tclass_id_get = mfc_tclass_id_get,
+ .tlb_invalidate = tlb_invalidate,
+ .resource_allocation_groupID_set = resource_allocation_groupID_set,
+ .resource_allocation_groupID_get = resource_allocation_groupID_get,
+ .resource_allocation_enable_set = resource_allocation_enable_set,
+ .resource_allocation_enable_get = resource_allocation_enable_get,
+};
diff --git a/arch/powerpc/platforms/cell/beat_syscall.h b/arch/powerpc/platforms/cell/beat_syscall.h
new file mode 100644
index 00000000000..8580dc7e179
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat_syscall.h
@@ -0,0 +1,164 @@
+/*
+ * Beat hypervisor call numbers
+ *
+ * (C) Copyright 2004-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef BEAT_BEAT_syscall_H
+#define BEAT_BEAT_syscall_H
+
+#ifdef __ASSEMBLY__
+#define __BEAT_ADD_VENDOR_ID(__x, __v) ((__v)<<60|(__x))
+#else
+#define __BEAT_ADD_VENDOR_ID(__x, __v) ((u64)(__v)<<60|(__x))
+#endif
+#define HV_allocate_memory __BEAT_ADD_VENDOR_ID(0, 0)
+#define HV_construct_virtual_address_space __BEAT_ADD_VENDOR_ID(2, 0)
+#define HV_destruct_virtual_address_space __BEAT_ADD_VENDOR_ID(10, 0)
+#define HV_get_virtual_address_space_id_of_ppe __BEAT_ADD_VENDOR_ID(4, 0)
+#define HV_query_logical_partition_address_region_info \
+ __BEAT_ADD_VENDOR_ID(6, 0)
+#define HV_release_memory __BEAT_ADD_VENDOR_ID(13, 0)
+#define HV_select_virtual_address_space __BEAT_ADD_VENDOR_ID(7, 0)
+#define HV_load_range_registers __BEAT_ADD_VENDOR_ID(68, 0)
+#define HV_set_ppe_l2cache_rmt_entry __BEAT_ADD_VENDOR_ID(70, 0)
+#define HV_set_ppe_tlb_rmt_entry __BEAT_ADD_VENDOR_ID(71, 0)
+#define HV_set_spe_tlb_rmt_entry __BEAT_ADD_VENDOR_ID(72, 0)
+#define HV_get_io_address_translation_fault_info __BEAT_ADD_VENDOR_ID(14, 0)
+#define HV_get_iopte __BEAT_ADD_VENDOR_ID(16, 0)
+#define HV_preload_iopt_cache __BEAT_ADD_VENDOR_ID(17, 0)
+#define HV_put_iopte __BEAT_ADD_VENDOR_ID(15, 0)
+#define HV_connect_event_ports __BEAT_ADD_VENDOR_ID(21, 0)
+#define HV_construct_event_receive_port __BEAT_ADD_VENDOR_ID(18, 0)
+#define HV_destruct_event_receive_port __BEAT_ADD_VENDOR_ID(19, 0)
+#define HV_destruct_event_send_port __BEAT_ADD_VENDOR_ID(22, 0)
+#define HV_get_state_of_event_send_port __BEAT_ADD_VENDOR_ID(25, 0)
+#define HV_request_to_connect_event_ports __BEAT_ADD_VENDOR_ID(20, 0)
+#define HV_send_event_externally __BEAT_ADD_VENDOR_ID(23, 0)
+#define HV_send_event_locally __BEAT_ADD_VENDOR_ID(24, 0)
+#define HV_construct_and_connect_irq_plug __BEAT_ADD_VENDOR_ID(28, 0)
+#define HV_destruct_irq_plug __BEAT_ADD_VENDOR_ID(29, 0)
+#define HV_detect_pending_interrupts __BEAT_ADD_VENDOR_ID(26, 0)
+#define HV_end_of_interrupt __BEAT_ADD_VENDOR_ID(27, 0)
+#define HV_assign_control_signal_notification_port __BEAT_ADD_VENDOR_ID(45, 0)
+#define HV_end_of_control_signal_processing __BEAT_ADD_VENDOR_ID(48, 0)
+#define HV_get_control_signal __BEAT_ADD_VENDOR_ID(46, 0)
+#define HV_set_irq_mask_for_spe __BEAT_ADD_VENDOR_ID(61, 0)
+#define HV_shutdown_logical_partition __BEAT_ADD_VENDOR_ID(44, 0)
+#define HV_connect_message_ports __BEAT_ADD_VENDOR_ID(35, 0)
+#define HV_destruct_message_port __BEAT_ADD_VENDOR_ID(36, 0)
+#define HV_receive_message __BEAT_ADD_VENDOR_ID(37, 0)
+#define HV_get_message_port_info __BEAT_ADD_VENDOR_ID(34, 0)
+#define HV_request_to_connect_message_ports __BEAT_ADD_VENDOR_ID(33, 0)
+#define HV_send_message __BEAT_ADD_VENDOR_ID(32, 0)
+#define HV_get_logical_ppe_id __BEAT_ADD_VENDOR_ID(69, 0)
+#define HV_pause __BEAT_ADD_VENDOR_ID(9, 0)
+#define HV_destruct_shared_memory_handle __BEAT_ADD_VENDOR_ID(51, 0)
+#define HV_get_shared_memory_info __BEAT_ADD_VENDOR_ID(52, 0)
+#define HV_permit_sharing_memory __BEAT_ADD_VENDOR_ID(50, 0)
+#define HV_request_to_attach_shared_memory __BEAT_ADD_VENDOR_ID(49, 0)
+#define HV_enable_logical_spe_execution __BEAT_ADD_VENDOR_ID(55, 0)
+#define HV_construct_logical_spe __BEAT_ADD_VENDOR_ID(53, 0)
+#define HV_disable_logical_spe_execution __BEAT_ADD_VENDOR_ID(56, 0)
+#define HV_destruct_logical_spe __BEAT_ADD_VENDOR_ID(54, 0)
+#define HV_sense_spe_execution_status __BEAT_ADD_VENDOR_ID(58, 0)
+#define HV_insert_htab_entry __BEAT_ADD_VENDOR_ID(101, 0)
+#define HV_read_htab_entries __BEAT_ADD_VENDOR_ID(95, 0)
+#define HV_write_htab_entry __BEAT_ADD_VENDOR_ID(94, 0)
+#define HV_assign_io_address_translation_fault_port \
+ __BEAT_ADD_VENDOR_ID(100, 0)
+#define HV_set_interrupt_mask __BEAT_ADD_VENDOR_ID(73, 0)
+#define HV_get_logical_partition_id __BEAT_ADD_VENDOR_ID(74, 0)
+#define HV_create_repository_node2 __BEAT_ADD_VENDOR_ID(90, 0)
+#define HV_create_repository_node __BEAT_ADD_VENDOR_ID(90, 0) /* alias */
+#define HV_get_repository_node_value2 __BEAT_ADD_VENDOR_ID(91, 0)
+#define HV_get_repository_node_value __BEAT_ADD_VENDOR_ID(91, 0) /* alias */
+#define HV_modify_repository_node_value2 __BEAT_ADD_VENDOR_ID(92, 0)
+#define HV_modify_repository_node_value __BEAT_ADD_VENDOR_ID(92, 0) /* alias */
+#define HV_remove_repository_node2 __BEAT_ADD_VENDOR_ID(93, 0)
+#define HV_remove_repository_node __BEAT_ADD_VENDOR_ID(93, 0) /* alias */
+#define HV_cancel_shared_memory __BEAT_ADD_VENDOR_ID(104, 0)
+#define HV_clear_interrupt_status_of_spe __BEAT_ADD_VENDOR_ID(206, 0)
+#define HV_construct_spe_irq_outlet __BEAT_ADD_VENDOR_ID(80, 0)
+#define HV_destruct_spe_irq_outlet __BEAT_ADD_VENDOR_ID(81, 0)
+#define HV_disconnect_ipspc_service __BEAT_ADD_VENDOR_ID(88, 0)
+#define HV_execute_ipspc_command __BEAT_ADD_VENDOR_ID(86, 0)
+#define HV_get_interrupt_status_of_spe __BEAT_ADD_VENDOR_ID(205, 0)
+#define HV_get_spe_privileged_state_1_registers __BEAT_ADD_VENDOR_ID(208, 0)
+#define HV_permit_use_of_ipspc_service __BEAT_ADD_VENDOR_ID(85, 0)
+#define HV_reinitialize_logical_spe __BEAT_ADD_VENDOR_ID(82, 0)
+#define HV_request_ipspc_service __BEAT_ADD_VENDOR_ID(84, 0)
+#define HV_stop_ipspc_command __BEAT_ADD_VENDOR_ID(87, 0)
+#define HV_set_spe_privileged_state_1_registers __BEAT_ADD_VENDOR_ID(204, 0)
+#define HV_get_status_of_ipspc_service __BEAT_ADD_VENDOR_ID(203, 0)
+#define HV_put_characters_to_console __BEAT_ADD_VENDOR_ID(0x101, 1)
+#define HV_get_characters_from_console __BEAT_ADD_VENDOR_ID(0x102, 1)
+#define HV_get_base_clock __BEAT_ADD_VENDOR_ID(0x111, 1)
+#define HV_set_base_clock __BEAT_ADD_VENDOR_ID(0x112, 1)
+#define HV_get_frame_cycle __BEAT_ADD_VENDOR_ID(0x114, 1)
+#define HV_disable_console __BEAT_ADD_VENDOR_ID(0x115, 1)
+#define HV_disable_all_console __BEAT_ADD_VENDOR_ID(0x116, 1)
+#define HV_oneshot_timer __BEAT_ADD_VENDOR_ID(0x117, 1)
+#define HV_set_dabr __BEAT_ADD_VENDOR_ID(0x118, 1)
+#define HV_get_dabr __BEAT_ADD_VENDOR_ID(0x119, 1)
+#define HV_start_hv_stats __BEAT_ADD_VENDOR_ID(0x21c, 1)
+#define HV_stop_hv_stats __BEAT_ADD_VENDOR_ID(0x21d, 1)
+#define HV_get_hv_stats __BEAT_ADD_VENDOR_ID(0x21e, 1)
+#define HV_get_hv_error_stats __BEAT_ADD_VENDOR_ID(0x221, 1)
+#define HV_get_stats __BEAT_ADD_VENDOR_ID(0x224, 1)
+#define HV_get_heap_stats __BEAT_ADD_VENDOR_ID(0x225, 1)
+#define HV_get_memory_stats __BEAT_ADD_VENDOR_ID(0x227, 1)
+#define HV_get_memory_detail __BEAT_ADD_VENDOR_ID(0x228, 1)
+#define HV_set_priority_of_irq_outlet __BEAT_ADD_VENDOR_ID(0x122, 1)
+#define HV_get_physical_spe_by_reservation_id __BEAT_ADD_VENDOR_ID(0x128, 1)
+#define HV_get_spe_context __BEAT_ADD_VENDOR_ID(0x129, 1)
+#define HV_set_spe_context __BEAT_ADD_VENDOR_ID(0x12a, 1)
+#define HV_downcount_of_interrupt __BEAT_ADD_VENDOR_ID(0x12e, 1)
+#define HV_peek_spe_context __BEAT_ADD_VENDOR_ID(0x12f, 1)
+#define HV_read_bpa_register __BEAT_ADD_VENDOR_ID(0x131, 1)
+#define HV_write_bpa_register __BEAT_ADD_VENDOR_ID(0x132, 1)
+#define HV_map_context_table_of_spe __BEAT_ADD_VENDOR_ID(0x137, 1)
+#define HV_get_slb_for_logical_spe __BEAT_ADD_VENDOR_ID(0x138, 1)
+#define HV_set_slb_for_logical_spe __BEAT_ADD_VENDOR_ID(0x139, 1)
+#define HV_init_pm __BEAT_ADD_VENDOR_ID(0x150, 1)
+#define HV_set_pm_signal __BEAT_ADD_VENDOR_ID(0x151, 1)
+#define HV_get_pm_signal __BEAT_ADD_VENDOR_ID(0x152, 1)
+#define HV_set_pm_config __BEAT_ADD_VENDOR_ID(0x153, 1)
+#define HV_get_pm_config __BEAT_ADD_VENDOR_ID(0x154, 1)
+#define HV_get_inner_trace_data __BEAT_ADD_VENDOR_ID(0x155, 1)
+#define HV_set_ext_trace_buffer __BEAT_ADD_VENDOR_ID(0x156, 1)
+#define HV_get_ext_trace_buffer __BEAT_ADD_VENDOR_ID(0x157, 1)
+#define HV_set_pm_interrupt __BEAT_ADD_VENDOR_ID(0x158, 1)
+#define HV_get_pm_interrupt __BEAT_ADD_VENDOR_ID(0x159, 1)
+#define HV_kick_pm __BEAT_ADD_VENDOR_ID(0x160, 1)
+#define HV_construct_pm_context __BEAT_ADD_VENDOR_ID(0x164, 1)
+#define HV_destruct_pm_context __BEAT_ADD_VENDOR_ID(0x165, 1)
+#define HV_be_slow __BEAT_ADD_VENDOR_ID(0x170, 1)
+#define HV_assign_ipspc_server_connection_status_notification_port \
+ __BEAT_ADD_VENDOR_ID(0x173, 1)
+#define HV_get_raid_of_physical_spe __BEAT_ADD_VENDOR_ID(0x174, 1)
+#define HV_set_physical_spe_to_rag __BEAT_ADD_VENDOR_ID(0x175, 1)
+#define HV_release_physical_spe_from_rag __BEAT_ADD_VENDOR_ID(0x176, 1)
+#define HV_rtc_read __BEAT_ADD_VENDOR_ID(0x190, 1)
+#define HV_rtc_write __BEAT_ADD_VENDOR_ID(0x191, 1)
+#define HV_eeprom_read __BEAT_ADD_VENDOR_ID(0x192, 1)
+#define HV_eeprom_write __BEAT_ADD_VENDOR_ID(0x193, 1)
+#define HV_insert_htab_entry3 __BEAT_ADD_VENDOR_ID(0x104, 1)
+#define HV_invalidate_htab_entry3 __BEAT_ADD_VENDOR_ID(0x105, 1)
+#define HV_update_htab_permission3 __BEAT_ADD_VENDOR_ID(0x106, 1)
+#define HV_clear_htab3 __BEAT_ADD_VENDOR_ID(0x107, 1)
+#endif
diff --git a/arch/powerpc/platforms/cell/beat_udbg.c b/arch/powerpc/platforms/cell/beat_udbg.c
new file mode 100644
index 00000000000..350735bc888
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat_udbg.c
@@ -0,0 +1,98 @@
+/*
+ * udbg function for Beat
+ *
+ * (C) Copyright 2006 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/console.h>
+
+#include <asm/machdep.h>
+#include <asm/prom.h>
+#include <asm/udbg.h>
+
+#include "beat.h"
+
+#define celleb_vtermno 0
+
+static void udbg_putc_beat(char c)
+{
+ unsigned long rc;
+
+ if (c == '\n')
+ udbg_putc_beat('\r');
+
+ rc = beat_put_term_char(celleb_vtermno, 1, (uint64_t)c << 56, 0);
+}
+
+/* Buffered chars getc */
+static u64 inbuflen;
+static u64 inbuf[2]; /* must be 2 u64s */
+
+static int udbg_getc_poll_beat(void)
+{
+ /* The interface is tricky because it may return up to 16 chars.
+ * We save them statically for future calls to udbg_getc().
+ */
+ char ch, *buf = (char *)inbuf;
+ int i;
+ long rc;
+ if (inbuflen == 0) {
+ /* get some more chars. */
+ inbuflen = 0;
+ rc = beat_get_term_char(celleb_vtermno, &inbuflen,
+ inbuf+0, inbuf+1);
+ if (rc != 0)
+ inbuflen = 0; /* otherwise inbuflen is garbage */
+ }
+ if (inbuflen <= 0 || inbuflen > 16) {
+ /* Catch error case as well as other oddities (corruption) */
+ inbuflen = 0;
+ return -1;
+ }
+ ch = buf[0];
+ for (i = 1; i < inbuflen; i++) /* shuffle them down. */
+ buf[i-1] = buf[i];
+ inbuflen--;
+ return ch;
+}
+
+static int udbg_getc_beat(void)
+{
+ int ch;
+ for (;;) {
+ ch = udbg_getc_poll_beat();
+ if (ch == -1) {
+ /* This shouldn't be needed...but... */
+ volatile unsigned long delay;
+ for (delay = 0; delay < 2000000; delay++)
+ ;
+ } else {
+ return ch;
+ }
+ }
+}
+
+/* call this from early_init() for a working debug console on
+ * vterm capable LPAR machines
+ */
+void __init udbg_init_debug_beat(void)
+{
+ udbg_putc = udbg_putc_beat;
+ udbg_getc = udbg_getc_beat;
+ udbg_getc_poll = udbg_getc_poll_beat;
+}
diff --git a/arch/powerpc/platforms/cell/beat_wrapper.h b/arch/powerpc/platforms/cell/beat_wrapper.h
new file mode 100644
index 00000000000..c1109969f24
--- /dev/null
+++ b/arch/powerpc/platforms/cell/beat_wrapper.h
@@ -0,0 +1,290 @@
+/*
+ * Beat hypervisor call I/F
+ *
+ * (C) Copyright 2007 TOSHIBA CORPORATION
+ *
+ * This code is based on arch/powerpc/platforms/pseries/plpar_wrapper.h.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef BEAT_HCALL
+#include <linux/string.h>
+#include "beat_syscall.h"
+
+/* defined in hvCall.S */
+extern s64 beat_hcall_norets(u64 opcode, ...);
+extern s64 beat_hcall_norets8(u64 opcode, u64 arg1, u64 arg2, u64 arg3,
+ u64 arg4, u64 arg5, u64 arg6, u64 arg7, u64 arg8);
+extern s64 beat_hcall1(u64 opcode, u64 retbuf[1], ...);
+extern s64 beat_hcall2(u64 opcode, u64 retbuf[2], ...);
+extern s64 beat_hcall3(u64 opcode, u64 retbuf[3], ...);
+extern s64 beat_hcall4(u64 opcode, u64 retbuf[4], ...);
+extern s64 beat_hcall5(u64 opcode, u64 retbuf[5], ...);
+extern s64 beat_hcall6(u64 opcode, u64 retbuf[6], ...);
+
+static inline s64 beat_downcount_of_interrupt(u64 plug_id)
+{
+ return beat_hcall_norets(HV_downcount_of_interrupt, plug_id);
+}
+
+static inline s64 beat_set_interrupt_mask(u64 index,
+ u64 val0, u64 val1, u64 val2, u64 val3)
+{
+ return beat_hcall_norets(HV_set_interrupt_mask, index,
+ val0, val1, val2, val3);
+}
+
+static inline s64 beat_destruct_irq_plug(u64 plug_id)
+{
+ return beat_hcall_norets(HV_destruct_irq_plug, plug_id);
+}
+
+static inline s64 beat_construct_and_connect_irq_plug(u64 plug_id,
+ u64 outlet_id)
+{
+ return beat_hcall_norets(HV_construct_and_connect_irq_plug, plug_id,
+ outlet_id);
+}
+
+static inline s64 beat_detect_pending_interrupts(u64 index, u64 *retbuf)
+{
+ return beat_hcall4(HV_detect_pending_interrupts, retbuf, index);
+}
+
+static inline s64 beat_pause(u64 style)
+{
+ return beat_hcall_norets(HV_pause, style);
+}
+
+static inline s64 beat_read_htab_entries(u64 htab_id, u64 index, u64 *retbuf)
+{
+ return beat_hcall5(HV_read_htab_entries, retbuf, htab_id, index);
+}
+
+static inline s64 beat_insert_htab_entry(u64 htab_id, u64 group,
+ u64 bitmask, u64 hpte_v, u64 hpte_r, u64 *slot)
+{
+ u64 dummy[3];
+ s64 ret;
+
+ ret = beat_hcall3(HV_insert_htab_entry, dummy, htab_id, group,
+ bitmask, hpte_v, hpte_r);
+ *slot = dummy[0];
+ return ret;
+}
+
+static inline s64 beat_write_htab_entry(u64 htab_id, u64 slot,
+ u64 hpte_v, u64 hpte_r, u64 mask_v, u64 mask_r,
+ u64 *ret_v, u64 *ret_r)
+{
+ u64 dummy[2];
+ s64 ret;
+
+ ret = beat_hcall2(HV_write_htab_entry, dummy, htab_id, slot,
+ hpte_v, hpte_r, mask_v, mask_r);
+ *ret_v = dummy[0];
+ *ret_r = dummy[1];
+ return ret;
+}
+
+static inline s64 beat_insert_htab_entry3(u64 htab_id, u64 group,
+ u64 hpte_v, u64 hpte_r, u64 mask_v, u64 value_v, u64 *slot)
+{
+ u64 dummy[1];
+ s64 ret;
+
+ ret = beat_hcall1(HV_insert_htab_entry3, dummy, htab_id, group,
+ hpte_v, hpte_r, mask_v, value_v);
+ *slot = dummy[0];
+ return ret;
+}
+
+static inline s64 beat_invalidate_htab_entry3(u64 htab_id, u64 group,
+ u64 va, u64 pss)
+{
+ return beat_hcall_norets(HV_invalidate_htab_entry3,
+ htab_id, group, va, pss);
+}
+
+static inline s64 beat_update_htab_permission3(u64 htab_id, u64 group,
+ u64 va, u64 pss, u64 ptel_mask, u64 ptel_value)
+{
+ return beat_hcall_norets(HV_update_htab_permission3,
+ htab_id, group, va, pss, ptel_mask, ptel_value);
+}
+
+static inline s64 beat_clear_htab3(u64 htab_id)
+{
+ return beat_hcall_norets(HV_clear_htab3, htab_id);
+}
+
+static inline void beat_shutdown_logical_partition(u64 code)
+{
+ (void)beat_hcall_norets(HV_shutdown_logical_partition, code);
+}
+
+static inline s64 beat_rtc_write(u64 time_from_epoch)
+{
+ return beat_hcall_norets(HV_rtc_write, time_from_epoch);
+}
+
+static inline s64 beat_rtc_read(u64 *time_from_epoch)
+{
+ u64 dummy[1];
+ s64 ret;
+
+ ret = beat_hcall1(HV_rtc_read, dummy);
+ *time_from_epoch = dummy[0];
+ return ret;
+}
+
+#define BEAT_NVRW_CNT (sizeof(u64) * 6)
+
+static inline s64 beat_eeprom_write(u64 index, u64 length, u8 *buffer)
+{
+ u64 b[6];
+
+ if (length > BEAT_NVRW_CNT)
+ return -1;
+ memcpy(b, buffer, sizeof(b));
+ return beat_hcall_norets8(HV_eeprom_write, index, length,
+ b[0], b[1], b[2], b[3], b[4], b[5]);
+}
+
+static inline s64 beat_eeprom_read(u64 index, u64 length, u8 *buffer)
+{
+ u64 b[6];
+ s64 ret;
+
+ if (length > BEAT_NVRW_CNT)
+ return -1;
+ ret = beat_hcall6(HV_eeprom_read, b, index, length);
+ memcpy(buffer, b, length);
+ return ret;
+}
+
+static inline s64 beat_set_dabr(u64 value, u64 style)
+{
+ return beat_hcall_norets(HV_set_dabr, value, style);
+}
+
+static inline s64 beat_get_characters_from_console(u64 termno, u64 *len,
+ u8 *buffer)
+{
+ u64 dummy[3];
+ s64 ret;
+
+ ret = beat_hcall3(HV_get_characters_from_console, dummy, termno, len);
+ *len = dummy[0];
+ memcpy(buffer, dummy + 1, *len);
+ return ret;
+}
+
+static inline s64 beat_put_characters_to_console(u64 termno, u64 len,
+ u8 *buffer)
+{
+ u64 b[2];
+
+ memcpy(b, buffer, len);
+ return beat_hcall_norets(HV_put_characters_to_console, termno, len,
+ b[0], b[1]);
+}
+
+static inline s64 beat_get_spe_privileged_state_1_registers(
+ u64 id, u64 offsetof, u64 *value)
+{
+ u64 dummy[1];
+ s64 ret;
+
+ ret = beat_hcall1(HV_get_spe_privileged_state_1_registers, dummy, id,
+ offsetof);
+ *value = dummy[0];
+ return ret;
+}
+
+static inline s64 beat_set_irq_mask_for_spe(u64 id, u64 class, u64 mask)
+{
+ return beat_hcall_norets(HV_set_irq_mask_for_spe, id, class, mask);
+}
+
+static inline s64 beat_clear_interrupt_status_of_spe(u64 id, u64 class,
+ u64 mask)
+{
+ return beat_hcall_norets(HV_clear_interrupt_status_of_spe,
+ id, class, mask);
+}
+
+static inline s64 beat_set_spe_privileged_state_1_registers(
+ u64 id, u64 offsetof, u64 value)
+{
+ return beat_hcall_norets(HV_set_spe_privileged_state_1_registers,
+ id, offsetof, value);
+}
+
+static inline s64 beat_get_interrupt_status_of_spe(u64 id, u64 class, u64 *val)
+{
+ u64 dummy[1];
+ s64 ret;
+
+ ret = beat_hcall1(HV_get_interrupt_status_of_spe, dummy, id, class);
+ *val = dummy[0];
+ return ret;
+}
+
+static inline s64 beat_put_iopte(u64 ioas_id, u64 io_addr, u64 real_addr,
+ u64 ioid, u64 flags)
+{
+ return beat_hcall_norets(HV_put_iopte, ioas_id, io_addr, real_addr,
+ ioid, flags);
+}
+
+static inline s64 beat_construct_event_receive_port(u64 *port)
+{
+ u64 dummy[1];
+ s64 ret;
+
+ ret = beat_hcall1(HV_construct_event_receive_port, dummy);
+ *port = dummy[0];
+ return ret;
+}
+
+static inline s64 beat_destruct_event_receive_port(u64 port)
+{
+ s64 ret;
+
+ ret = beat_hcall_norets(HV_destruct_event_receive_port, port);
+ return ret;
+}
+
+static inline s64 beat_create_repository_node(u64 path[4], u64 data[2])
+{
+ s64 ret;
+
+ ret = beat_hcall_norets(HV_create_repository_node2,
+ path[0], path[1], path[2], path[3], data[0], data[1]);
+ return ret;
+}
+
+static inline s64 beat_get_repository_node_value(u64 lpid, u64 path[4],
+ u64 data[2])
+{
+ s64 ret;
+
+ ret = beat_hcall2(HV_get_repository_node_value2, data,
+ lpid, path[0], path[1], path[2], path[3]);
+ return ret;
+}
+
+#endif
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq.c b/arch/powerpc/platforms/cell/cbe_cpufreq.c
deleted file mode 100644
index ec7c8f45a21..00000000000
--- a/arch/powerpc/platforms/cell/cbe_cpufreq.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * cpufreq driver for the cell processor
- *
- * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
- *
- * Author: Christian Krafft <krafft@de.ibm.com>
- *
- * 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, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/cpufreq.h>
-#include <linux/of_platform.h>
-
-#include <asm/machdep.h>
-#include <asm/prom.h>
-#include <asm/cell-regs.h>
-#include "cbe_cpufreq.h"
-
-static DEFINE_MUTEX(cbe_switch_mutex);
-
-
-/* the CBE supports an 8 step frequency scaling */
-static struct cpufreq_frequency_table cbe_freqs[] = {
- {1, 0},
- {2, 0},
- {3, 0},
- {4, 0},
- {5, 0},
- {6, 0},
- {8, 0},
- {10, 0},
- {0, CPUFREQ_TABLE_END},
-};
-
-/*
- * hardware specific functions
- */
-
-static int set_pmode(unsigned int cpu, unsigned int slow_mode)
-{
- int rc;
-
- if (cbe_cpufreq_has_pmi)
- rc = cbe_cpufreq_set_pmode_pmi(cpu, slow_mode);
- else
- rc = cbe_cpufreq_set_pmode(cpu, slow_mode);
-
- pr_debug("register contains slow mode %d\n", cbe_cpufreq_get_pmode(cpu));
-
- return rc;
-}
-
-/*
- * cpufreq functions
- */
-
-static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
-{
- const u32 *max_freqp;
- u32 max_freq;
- int i, cur_pmode;
- struct device_node *cpu;
-
- cpu = of_get_cpu_node(policy->cpu, NULL);
-
- if (!cpu)
- return -ENODEV;
-
- pr_debug("init cpufreq on CPU %d\n", policy->cpu);
-
- /*
- * Let's check we can actually get to the CELL regs
- */
- if (!cbe_get_cpu_pmd_regs(policy->cpu) ||
- !cbe_get_cpu_mic_tm_regs(policy->cpu)) {
- pr_info("invalid CBE regs pointers for cpufreq\n");
- return -EINVAL;
- }
-
- max_freqp = of_get_property(cpu, "clock-frequency", NULL);
-
- of_node_put(cpu);
-
- if (!max_freqp)
- return -EINVAL;
-
- /* we need the freq in kHz */
- max_freq = *max_freqp / 1000;
-
- pr_debug("max clock-frequency is at %u kHz\n", max_freq);
- pr_debug("initializing frequency table\n");
-
- /* initialize frequency table */
- for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
- cbe_freqs[i].frequency = max_freq / cbe_freqs[i].index;
- pr_debug("%d: %d\n", i, cbe_freqs[i].frequency);
- }
-
- /* if DEBUG is enabled set_pmode() measures the latency
- * of a transition */
- policy->cpuinfo.transition_latency = 25000;
-
- cur_pmode = cbe_cpufreq_get_pmode(policy->cpu);
- pr_debug("current pmode is at %d\n",cur_pmode);
-
- policy->cur = cbe_freqs[cur_pmode].frequency;
-
-#ifdef CONFIG_SMP
- policy->cpus = per_cpu(cpu_sibling_map, policy->cpu);
-#endif
-
- cpufreq_frequency_table_get_attr(cbe_freqs, policy->cpu);
-
- /* this ensures that policy->cpuinfo_min
- * and policy->cpuinfo_max are set correctly */
- return cpufreq_frequency_table_cpuinfo(policy, cbe_freqs);
-}
-
-static int cbe_cpufreq_cpu_exit(struct cpufreq_policy *policy)
-{
- cpufreq_frequency_table_put_attr(policy->cpu);
- return 0;
-}
-
-static int cbe_cpufreq_verify(struct cpufreq_policy *policy)
-{
- return cpufreq_frequency_table_verify(policy, cbe_freqs);
-}
-
-static int cbe_cpufreq_target(struct cpufreq_policy *policy,
- unsigned int target_freq,
- unsigned int relation)
-{
- int rc;
- struct cpufreq_freqs freqs;
- unsigned int cbe_pmode_new;
-
- cpufreq_frequency_table_target(policy,
- cbe_freqs,
- target_freq,
- relation,
- &cbe_pmode_new);
-
- freqs.old = policy->cur;
- freqs.new = cbe_freqs[cbe_pmode_new].frequency;
- freqs.cpu = policy->cpu;
-
- mutex_lock(&cbe_switch_mutex);
- cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
-
- pr_debug("setting frequency for cpu %d to %d kHz, " \
- "1/%d of max frequency\n",
- policy->cpu,
- cbe_freqs[cbe_pmode_new].frequency,
- cbe_freqs[cbe_pmode_new].index);
-
- rc = set_pmode(policy->cpu, cbe_pmode_new);
-
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- mutex_unlock(&cbe_switch_mutex);
-
- return rc;
-}
-
-static struct cpufreq_driver cbe_cpufreq_driver = {
- .verify = cbe_cpufreq_verify,
- .target = cbe_cpufreq_target,
- .init = cbe_cpufreq_cpu_init,
- .exit = cbe_cpufreq_cpu_exit,
- .name = "cbe-cpufreq",
- .owner = THIS_MODULE,
- .flags = CPUFREQ_CONST_LOOPS,
-};
-
-/*
- * module init and destoy
- */
-
-static int __init cbe_cpufreq_init(void)
-{
- if (!machine_is(cell))
- return -ENODEV;
-
- return cpufreq_register_driver(&cbe_cpufreq_driver);
-}
-
-static void __exit cbe_cpufreq_exit(void)
-{
- cpufreq_unregister_driver(&cbe_cpufreq_driver);
-}
-
-module_init(cbe_cpufreq_init);
-module_exit(cbe_cpufreq_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq.h b/arch/powerpc/platforms/cell/cbe_cpufreq.h
deleted file mode 100644
index c1d86bfa92f..00000000000
--- a/arch/powerpc/platforms/cell/cbe_cpufreq.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * cbe_cpufreq.h
- *
- * This file contains the definitions used by the cbe_cpufreq driver.
- *
- * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
- *
- * Author: Christian Krafft <krafft@de.ibm.com>
- *
- */
-
-#include <linux/cpufreq.h>
-#include <linux/types.h>
-
-int cbe_cpufreq_set_pmode(int cpu, unsigned int pmode);
-int cbe_cpufreq_get_pmode(int cpu);
-
-int cbe_cpufreq_set_pmode_pmi(int cpu, unsigned int pmode);
-
-#if defined(CONFIG_CBE_CPUFREQ_PMI) || defined(CONFIG_CBE_CPUFREQ_PMI_MODULE)
-extern bool cbe_cpufreq_has_pmi;
-#else
-#define cbe_cpufreq_has_pmi (0)
-#endif
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c b/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c
deleted file mode 100644
index 70fa7aef5ed..00000000000
--- a/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * pervasive backend for the cbe_cpufreq driver
- *
- * This driver makes use of the pervasive unit to
- * engage the desired frequency.
- *
- * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
- *
- * Author: Christian Krafft <krafft@de.ibm.com>
- *
- * 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, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/time.h>
-#include <asm/machdep.h>
-#include <asm/hw_irq.h>
-#include <asm/cell-regs.h>
-
-#include "cbe_cpufreq.h"
-
-/* to write to MIC register */
-static u64 MIC_Slow_Fast_Timer_table[] = {
- [0 ... 7] = 0x007fc00000000000ull,
-};
-
-/* more values for the MIC */
-static u64 MIC_Slow_Next_Timer_table[] = {
- 0x0000240000000000ull,
- 0x0000268000000000ull,
- 0x000029C000000000ull,
- 0x00002D0000000000ull,
- 0x0000300000000000ull,
- 0x0000334000000000ull,
- 0x000039C000000000ull,
- 0x00003FC000000000ull,
-};
-
-
-int cbe_cpufreq_set_pmode(int cpu, unsigned int pmode)
-{
- struct cbe_pmd_regs __iomem *pmd_regs;
- struct cbe_mic_tm_regs __iomem *mic_tm_regs;
- u64 flags;
- u64 value;
-#ifdef DEBUG
- long time;
-#endif
-
- local_irq_save(flags);
-
- mic_tm_regs = cbe_get_cpu_mic_tm_regs(cpu);
- pmd_regs = cbe_get_cpu_pmd_regs(cpu);
-
-#ifdef DEBUG
- time = jiffies;
-#endif
-
- out_be64(&mic_tm_regs->slow_fast_timer_0, MIC_Slow_Fast_Timer_table[pmode]);
- out_be64(&mic_tm_regs->slow_fast_timer_1, MIC_Slow_Fast_Timer_table[pmode]);
-
- out_be64(&mic_tm_regs->slow_next_timer_0, MIC_Slow_Next_Timer_table[pmode]);
- out_be64(&mic_tm_regs->slow_next_timer_1, MIC_Slow_Next_Timer_table[pmode]);
-
- value = in_be64(&pmd_regs->pmcr);
- /* set bits to zero */
- value &= 0xFFFFFFFFFFFFFFF8ull;
- /* set bits to next pmode */
- value |= pmode;
-
- out_be64(&pmd_regs->pmcr, value);
-
-#ifdef DEBUG
- /* wait until new pmode appears in status register */
- value = in_be64(&pmd_regs->pmsr) & 0x07;
- while (value != pmode) {
- cpu_relax();
- value = in_be64(&pmd_regs->pmsr) & 0x07;
- }
-
- time = jiffies - time;
- time = jiffies_to_msecs(time);
- pr_debug("had to wait %lu ms for a transition using " \
- "pervasive unit\n", time);
-#endif
- local_irq_restore(flags);
-
- return 0;
-}
-
-
-int cbe_cpufreq_get_pmode(int cpu)
-{
- int ret;
- struct cbe_pmd_regs __iomem *pmd_regs;
-
- pmd_regs = cbe_get_cpu_pmd_regs(cpu);
- ret = in_be64(&pmd_regs->pmsr) & 0x07;
-
- return ret;
-}
-
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c b/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c
deleted file mode 100644
index 69288f65314..00000000000
--- a/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * pmi backend for the cbe_cpufreq driver
- *
- * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
- *
- * Author: Christian Krafft <krafft@de.ibm.com>
- *
- * 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, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/of_platform.h>
-
-#include <asm/processor.h>
-#include <asm/prom.h>
-#include <asm/pmi.h>
-#include <asm/cell-regs.h>
-
-#ifdef DEBUG
-#include <asm/time.h>
-#endif
-
-#include "cbe_cpufreq.h"
-
-static u8 pmi_slow_mode_limit[MAX_CBE];
-
-bool cbe_cpufreq_has_pmi = false;
-EXPORT_SYMBOL_GPL(cbe_cpufreq_has_pmi);
-
-/*
- * hardware specific functions
- */
-
-int cbe_cpufreq_set_pmode_pmi(int cpu, unsigned int pmode)
-{
- int ret;
- pmi_message_t pmi_msg;
-#ifdef DEBUG
- long time;
-#endif
- pmi_msg.type = PMI_TYPE_FREQ_CHANGE;
- pmi_msg.data1 = cbe_cpu_to_node(cpu);
- pmi_msg.data2 = pmode;
-
-#ifdef DEBUG
- time = jiffies;
-#endif
- pmi_send_message(pmi_msg);
-
-#ifdef DEBUG
- time = jiffies - time;
- time = jiffies_to_msecs(time);
- pr_debug("had to wait %lu ms for a transition using " \
- "PMI\n", time);
-#endif
- ret = pmi_msg.data2;
- pr_debug("PMI returned slow mode %d\n", ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(cbe_cpufreq_set_pmode_pmi);
-
-
-static void cbe_cpufreq_handle_pmi(pmi_message_t pmi_msg)
-{
- u8 node, slow_mode;
-
- BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE);
-
- node = pmi_msg.data1;
- slow_mode = pmi_msg.data2;
-
- pmi_slow_mode_limit[node] = slow_mode;
-
- pr_debug("cbe_handle_pmi: node: %d max_freq: %d\n", node, slow_mode);
-}
-
-static int pmi_notifier(struct notifier_block *nb,
- unsigned long event, void *data)
-{
- struct cpufreq_policy *policy = data;
- struct cpufreq_frequency_table *cbe_freqs;
- u8 node;
-
- cbe_freqs = cpufreq_frequency_get_table(policy->cpu);
- node = cbe_cpu_to_node(policy->cpu);
-
- pr_debug("got notified, event=%lu, node=%u\n", event, node);
-
- if (pmi_slow_mode_limit[node] != 0) {
- pr_debug("limiting node %d to slow mode %d\n",
- node, pmi_slow_mode_limit[node]);
-
- cpufreq_verify_within_limits(policy, 0,
-
- cbe_freqs[pmi_slow_mode_limit[node]].frequency);
- }
-
- return 0;
-}
-
-static struct notifier_block pmi_notifier_block = {
- .notifier_call = pmi_notifier,
-};
-
-static struct pmi_handler cbe_pmi_handler = {
- .type = PMI_TYPE_FREQ_CHANGE,
- .handle_pmi_message = cbe_cpufreq_handle_pmi,
-};
-
-
-
-static int __init cbe_cpufreq_pmi_init(void)
-{
- cbe_cpufreq_has_pmi = pmi_register_handler(&cbe_pmi_handler) == 0;
-
- if (!cbe_cpufreq_has_pmi)
- return -ENODEV;
-
- cpufreq_register_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER);
-
- return 0;
-}
-
-static void __exit cbe_cpufreq_pmi_exit(void)
-{
- cpufreq_unregister_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER);
- pmi_unregister_handler(&cbe_pmi_handler);
-}
-
-module_init(cbe_cpufreq_pmi_init);
-module_exit(cbe_cpufreq_pmi_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
diff --git a/arch/powerpc/platforms/cell/cbe_powerbutton.c b/arch/powerpc/platforms/cell/cbe_powerbutton.c
new file mode 100644
index 00000000000..2bb8031303f
--- /dev/null
+++ b/arch/powerpc/platforms/cell/cbe_powerbutton.c
@@ -0,0 +1,118 @@
+/*
+ * driver for powerbutton on IBM cell blades
+ *
+ * (C) Copyright IBM Corp. 2005-2008
+ *
+ * Author: Christian Krafft <krafft@de.ibm.com>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <asm/pmi.h>
+#include <asm/prom.h>
+
+static struct input_dev *button_dev;
+static struct platform_device *button_pdev;
+
+static void cbe_powerbutton_handle_pmi(pmi_message_t pmi_msg)
+{
+ BUG_ON(pmi_msg.type != PMI_TYPE_POWER_BUTTON);
+
+ input_report_key(button_dev, KEY_POWER, 1);
+ input_sync(button_dev);
+ input_report_key(button_dev, KEY_POWER, 0);
+ input_sync(button_dev);
+}
+
+static struct pmi_handler cbe_pmi_handler = {
+ .type = PMI_TYPE_POWER_BUTTON,
+ .handle_pmi_message = cbe_powerbutton_handle_pmi,
+};
+
+static int __init cbe_powerbutton_init(void)
+{
+ int ret = 0;
+ struct input_dev *dev;
+
+ if (!of_machine_is_compatible("IBM,CBPLUS-1.0")) {
+ printk(KERN_ERR "%s: Not a cell blade.\n", __func__);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ dev = input_allocate_device();
+ if (!dev) {
+ ret = -ENOMEM;
+ printk(KERN_ERR "%s: Not enough memory.\n", __func__);
+ goto out;
+ }
+
+ set_bit(EV_KEY, dev->evbit);
+ set_bit(KEY_POWER, dev->keybit);
+
+ dev->name = "Power Button";
+ dev->id.bustype = BUS_HOST;
+
+ /* this makes the button look like an acpi power button
+ * no clue whether anyone relies on that though */
+ dev->id.product = 0x02;
+ dev->phys = "LNXPWRBN/button/input0";
+
+ button_pdev = platform_device_register_simple("power_button", 0, NULL, 0);
+ if (IS_ERR(button_pdev)) {
+ ret = PTR_ERR(button_pdev);
+ goto out_free_input;
+ }
+
+ dev->dev.parent = &button_pdev->dev;
+ ret = input_register_device(dev);
+ if (ret) {
+ printk(KERN_ERR "%s: Failed to register device\n", __func__);
+ goto out_free_pdev;
+ }
+
+ button_dev = dev;
+
+ ret = pmi_register_handler(&cbe_pmi_handler);
+ if (ret) {
+ printk(KERN_ERR "%s: Failed to register with pmi.\n", __func__);
+ goto out_free_pdev;
+ }
+
+ goto out;
+
+out_free_pdev:
+ platform_device_unregister(button_pdev);
+out_free_input:
+ input_free_device(dev);
+out:
+ return ret;
+}
+
+static void __exit cbe_powerbutton_exit(void)
+{
+ pmi_unregister_handler(&cbe_pmi_handler);
+ platform_device_unregister(button_pdev);
+ input_free_device(button_dev);
+}
+
+module_init(cbe_powerbutton_init);
+module_exit(cbe_powerbutton_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
diff --git a/arch/powerpc/platforms/cell/cbe_regs.c b/arch/powerpc/platforms/cell/cbe_regs.c
index dbc338f187a..1428d583c23 100644
--- a/arch/powerpc/platforms/cell/cbe_regs.c
+++ b/arch/powerpc/platforms/cell/cbe_regs.c
@@ -8,7 +8,7 @@
#include <linux/percpu.h>
#include <linux/types.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
@@ -45,8 +45,8 @@ static struct cbe_thread_map
unsigned int cbe_id;
} cbe_thread_map[NR_CPUS];
-static cpumask_t cbe_local_mask[MAX_CBE] = { [0 ... MAX_CBE-1] = CPU_MASK_NONE };
-static cpumask_t cbe_first_online_cpu = CPU_MASK_NONE;
+static cpumask_t cbe_local_mask[MAX_CBE] = { [0 ... MAX_CBE-1] = {CPU_BITS_NONE} };
+static cpumask_t cbe_first_online_cpu = { CPU_BITS_NONE };
static struct cbe_regs_map *cbe_find_map(struct device_node *np)
{
@@ -159,7 +159,8 @@ EXPORT_SYMBOL_GPL(cbe_cpu_to_node);
u32 cbe_node_to_cpu(int node)
{
- return find_first_bit( (unsigned long *) &cbe_local_mask[node], sizeof(cpumask_t));
+ return cpumask_first(&cbe_local_mask[node]);
+
}
EXPORT_SYMBOL_GPL(cbe_node_to_cpu);
@@ -268,9 +269,9 @@ void __init cbe_regs_init(void)
thread->regs = map;
thread->cbe_id = cbe_id;
map->be_node = thread->be_node;
- cpu_set(i, cbe_local_mask[cbe_id]);
+ cpumask_set_cpu(i, &cbe_local_mask[cbe_id]);
if(thread->thread_id == 0)
- cpu_set(i, cbe_first_online_cpu);
+ cpumask_set_cpu(i, &cbe_first_online_cpu);
}
}
diff --git a/arch/powerpc/platforms/cell/cbe_thermal.c b/arch/powerpc/platforms/cell/cbe_thermal.c
index 4852bf312d8..2c15ff09448 100644
--- a/arch/powerpc/platforms/cell/cbe_thermal.c
+++ b/arch/powerpc/platforms/cell/cbe_thermal.c
@@ -46,7 +46,7 @@
*/
#include <linux/module.h>
-#include <linux/sysdev.h>
+#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/cpu.h>
#include <asm/spu.h>
@@ -59,8 +59,8 @@
#define TEMP_MIN 65
#define TEMP_MAX 125
-#define SYSDEV_PREFIX_ATTR(_prefix,_name,_mode) \
-struct sysdev_attribute attr_ ## _prefix ## _ ## _name = { \
+#define DEVICE_PREFIX_ATTR(_prefix,_name,_mode) \
+struct device_attribute attr_ ## _prefix ## _ ## _name = { \
.attr = { .name = __stringify(_name), .mode = _mode }, \
.show = _prefix ## _show_ ## _name, \
.store = _prefix ## _store_ ## _name, \
@@ -76,35 +76,36 @@ static inline u8 temp_to_reg(u8 temp)
return ((temp - TEMP_MIN) >> 1) & 0x3f;
}
-static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev)
+static struct cbe_pmd_regs __iomem *get_pmd_regs(struct device *dev)
{
struct spu *spu;
- spu = container_of(sysdev, struct spu, sysdev);
+ spu = container_of(dev, struct spu, dev);
return cbe_get_pmd_regs(spu_devnode(spu));
}
/* returns the value for a given spu in a given register */
-static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iomem *reg)
+static u8 spu_read_register_value(struct device *dev, union spe_reg __iomem *reg)
{
union spe_reg value;
struct spu *spu;
- spu = container_of(sysdev, struct spu, sysdev);
+ spu = container_of(dev, struct spu, dev);
value.val = in_be64(&reg->val);
return value.spe[spu->spe_id];
}
-static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf)
+static ssize_t spu_show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
u8 value;
struct cbe_pmd_regs __iomem *pmd_regs;
- pmd_regs = get_pmd_regs(sysdev);
+ pmd_regs = get_pmd_regs(dev);
- value = spu_read_register_value(sysdev, &pmd_regs->ts_ctsr1);
+ value = spu_read_register_value(dev, &pmd_regs->ts_ctsr1);
return sprintf(buf, "%d\n", reg_to_temp(value));
}
@@ -124,7 +125,7 @@ static ssize_t show_throttle(struct cbe_pmd_regs __iomem *pmd_regs, char *buf, i
static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char *buf, size_t size, int pos)
{
u64 reg_value;
- int temp;
+ unsigned int temp;
u64 new_value;
int ret;
@@ -146,42 +147,48 @@ static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char
return size;
}
-static ssize_t spu_show_throttle_end(struct sys_device *sysdev, char *buf)
+static ssize_t spu_show_throttle_end(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return show_throttle(get_pmd_regs(sysdev), buf, 0);
+ return show_throttle(get_pmd_regs(dev), buf, 0);
}
-static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, char *buf)
+static ssize_t spu_show_throttle_begin(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return show_throttle(get_pmd_regs(sysdev), buf, 8);
+ return show_throttle(get_pmd_regs(dev), buf, 8);
}
-static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, char *buf)
+static ssize_t spu_show_throttle_full_stop(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return show_throttle(get_pmd_regs(sysdev), buf, 16);
+ return show_throttle(get_pmd_regs(dev), buf, 16);
}
-static ssize_t spu_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size)
+static ssize_t spu_store_throttle_end(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
{
- return store_throttle(get_pmd_regs(sysdev), buf, size, 0);
+ return store_throttle(get_pmd_regs(dev), buf, size, 0);
}
-static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size)
+static ssize_t spu_store_throttle_begin(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
{
- return store_throttle(get_pmd_regs(sysdev), buf, size, 8);
+ return store_throttle(get_pmd_regs(dev), buf, size, 8);
}
-static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size)
+static ssize_t spu_store_throttle_full_stop(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
{
- return store_throttle(get_pmd_regs(sysdev), buf, size, 16);
+ return store_throttle(get_pmd_regs(dev), buf, size, 16);
}
-static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos)
+static ssize_t ppe_show_temp(struct device *dev, char *buf, int pos)
{
struct cbe_pmd_regs __iomem *pmd_regs;
u64 value;
- pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id);
+ pmd_regs = cbe_get_cpu_pmd_regs(dev->id);
value = in_be64(&pmd_regs->ts_ctsr2);
value = (value >> pos) & 0x3f;
@@ -192,56 +199,64 @@ static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos)
/* shows the temperature of the DTS on the PPE,
* located near the linear thermal sensor */
-static ssize_t ppe_show_temp0(struct sys_device *sysdev, char *buf)
+static ssize_t ppe_show_temp0(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return ppe_show_temp(sysdev, buf, 32);
+ return ppe_show_temp(dev, buf, 32);
}
/* shows the temperature of the second DTS on the PPE */
-static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf)
+static ssize_t ppe_show_temp1(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return ppe_show_temp(sysdev, buf, 0);
+ return ppe_show_temp(dev, buf, 0);
}
-static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, char *buf)
+static ssize_t ppe_show_throttle_end(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 32);
+ return show_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, 32);
}
-static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, char *buf)
+static ssize_t ppe_show_throttle_begin(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 40);
+ return show_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, 40);
}
-static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, char *buf)
+static ssize_t ppe_show_throttle_full_stop(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 48);
+ return show_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, 48);
}
-static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size)
+static ssize_t ppe_store_throttle_end(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
{
- return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 32);
+ return store_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, size, 32);
}
-static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size)
+static ssize_t ppe_store_throttle_begin(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
{
- return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 40);
+ return store_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, size, 40);
}
-static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size)
+static ssize_t ppe_store_throttle_full_stop(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
{
- return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 48);
+ return store_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, size, 48);
}
-static struct sysdev_attribute attr_spu_temperature = {
+static struct device_attribute attr_spu_temperature = {
.attr = {.name = "temperature", .mode = 0400 },
.show = spu_show_temp,
};
-static SYSDEV_PREFIX_ATTR(spu, throttle_end, 0600);
-static SYSDEV_PREFIX_ATTR(spu, throttle_begin, 0600);
-static SYSDEV_PREFIX_ATTR(spu, throttle_full_stop, 0600);
+static DEVICE_PREFIX_ATTR(spu, throttle_end, 0600);
+static DEVICE_PREFIX_ATTR(spu, throttle_begin, 0600);
+static DEVICE_PREFIX_ATTR(spu, throttle_full_stop, 0600);
static struct attribute *spu_attributes[] = {
@@ -257,19 +272,19 @@ static struct attribute_group spu_attribute_group = {
.attrs = spu_attributes,
};
-static struct sysdev_attribute attr_ppe_temperature0 = {
+static struct device_attribute attr_ppe_temperature0 = {
.attr = {.name = "temperature0", .mode = 0400 },
.show = ppe_show_temp0,
};
-static struct sysdev_attribute attr_ppe_temperature1 = {
+static struct device_attribute attr_ppe_temperature1 = {
.attr = {.name = "temperature1", .mode = 0400 },
.show = ppe_show_temp1,
};
-static SYSDEV_PREFIX_ATTR(ppe, throttle_end, 0600);
-static SYSDEV_PREFIX_ATTR(ppe, throttle_begin, 0600);
-static SYSDEV_PREFIX_ATTR(ppe, throttle_full_stop, 0600);
+static DEVICE_PREFIX_ATTR(ppe, throttle_end, 0600);
+static DEVICE_PREFIX_ATTR(ppe, throttle_begin, 0600);
+static DEVICE_PREFIX_ATTR(ppe, throttle_full_stop, 0600);
static struct attribute *ppe_attributes[] = {
&attr_ppe_temperature0.attr,
@@ -292,7 +307,7 @@ static int __init init_default_values(void)
{
int cpu;
struct cbe_pmd_regs __iomem *pmd_regs;
- struct sys_device *sysdev;
+ struct device *dev;
union ppe_spe_reg tpr;
union spe_reg str1;
u64 str2;
@@ -334,14 +349,14 @@ static int __init init_default_values(void)
for_each_possible_cpu (cpu) {
pr_debug("processing cpu %d\n", cpu);
- sysdev = get_cpu_sysdev(cpu);
+ dev = get_cpu_device(cpu);
- if (!sysdev) {
- pr_info("invalid sysdev pointer for cbe_thermal\n");
+ if (!dev) {
+ pr_info("invalid dev pointer for cbe_thermal\n");
return -EINVAL;
}
- pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id);
+ pmd_regs = cbe_get_cpu_pmd_regs(dev->id);
if (!pmd_regs) {
pr_info("invalid CBE regs pointer for cbe_thermal\n");
@@ -364,8 +379,8 @@ static int __init thermal_init(void)
int rc = init_default_values();
if (rc == 0) {
- spu_add_sysdev_attr_group(&spu_attribute_group);
- cpu_add_sysdev_attr_group(&ppe_attribute_group);
+ spu_add_dev_attr_group(&spu_attribute_group);
+ cpu_add_dev_attr_group(&ppe_attribute_group);
}
return rc;
@@ -374,8 +389,8 @@ module_init(thermal_init);
static void __exit thermal_exit(void)
{
- spu_remove_sysdev_attr_group(&spu_attribute_group);
- cpu_remove_sysdev_attr_group(&ppe_attribute_group);
+ spu_remove_dev_attr_group(&spu_attribute_group);
+ cpu_remove_dev_attr_group(&ppe_attribute_group);
}
module_exit(thermal_exit);
diff --git a/arch/powerpc/platforms/cell/celleb_pci.c b/arch/powerpc/platforms/cell/celleb_pci.c
new file mode 100644
index 00000000000..173568140a3
--- /dev/null
+++ b/arch/powerpc/platforms/cell/celleb_pci.c
@@ -0,0 +1,500 @@
+/*
+ * Support for PCI on Celleb platform.
+ *
+ * (C) Copyright 2006-2007 TOSHIBA CORPORATION
+ *
+ * This code is based on arch/powerpc/kernel/rtas_pci.c:
+ * Copyright (C) 2001 Dave Engebretsen, IBM Corporation
+ * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/threads.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/pci_regs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/ppc-pci.h>
+
+#include "celleb_pci.h"
+
+#define MAX_PCI_DEVICES 32
+#define MAX_PCI_FUNCTIONS 8
+#define MAX_PCI_BASE_ADDRS 3 /* use 64 bit address */
+
+/* definition for fake pci configuration area for GbE, .... ,and etc. */
+
+struct celleb_pci_resource {
+ struct resource r[MAX_PCI_BASE_ADDRS];
+};
+
+struct celleb_pci_private {
+ unsigned char *fake_config[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS];
+ struct celleb_pci_resource *res[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS];
+};
+
+static inline u8 celleb_fake_config_readb(void *addr)
+{
+ u8 *p = addr;
+ return *p;
+}
+
+static inline u16 celleb_fake_config_readw(void *addr)
+{
+ __le16 *p = addr;
+ return le16_to_cpu(*p);
+}
+
+static inline u32 celleb_fake_config_readl(void *addr)
+{
+ __le32 *p = addr;
+ return le32_to_cpu(*p);
+}
+
+static inline void celleb_fake_config_writeb(u32 val, void *addr)
+{
+ u8 *p = addr;
+ *p = val;
+}
+
+static inline void celleb_fake_config_writew(u32 val, void *addr)
+{
+ __le16 val16;
+ __le16 *p = addr;
+ val16 = cpu_to_le16(val);
+ *p = val16;
+}
+
+static inline void celleb_fake_config_writel(u32 val, void *addr)
+{
+ __le32 val32;
+ __le32 *p = addr;
+ val32 = cpu_to_le32(val);
+ *p = val32;
+}
+
+static unsigned char *get_fake_config_start(struct pci_controller *hose,
+ int devno, int fn)
+{
+ struct celleb_pci_private *private = hose->private_data;
+
+ if (private == NULL)
+ return NULL;
+
+ return private->fake_config[devno][fn];
+}
+
+static struct celleb_pci_resource *get_resource_start(
+ struct pci_controller *hose,
+ int devno, int fn)
+{
+ struct celleb_pci_private *private = hose->private_data;
+
+ if (private == NULL)
+ return NULL;
+
+ return private->res[devno][fn];
+}
+
+
+static void celleb_config_read_fake(unsigned char *config, int where,
+ int size, u32 *val)
+{
+ char *p = config + where;
+
+ switch (size) {
+ case 1:
+ *val = celleb_fake_config_readb(p);
+ break;
+ case 2:
+ *val = celleb_fake_config_readw(p);
+ break;
+ case 4:
+ *val = celleb_fake_config_readl(p);
+ break;
+ }
+}
+
+static void celleb_config_write_fake(unsigned char *config, int where,
+ int size, u32 val)
+{
+ char *p = config + where;
+
+ switch (size) {
+ case 1:
+ celleb_fake_config_writeb(val, p);
+ break;
+ case 2:
+ celleb_fake_config_writew(val, p);
+ break;
+ case 4:
+ celleb_fake_config_writel(val, p);
+ break;
+ }
+}
+
+static int celleb_fake_pci_read_config(struct pci_bus *bus,
+ unsigned int devfn, int where, int size, u32 *val)
+{
+ char *config;
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ unsigned int devno = devfn >> 3;
+ unsigned int fn = devfn & 0x7;
+
+ /* allignment check */
+ BUG_ON(where % size);
+
+ pr_debug(" fake read: bus=0x%x, ", bus->number);
+ config = get_fake_config_start(hose, devno, fn);
+
+ pr_debug("devno=0x%x, where=0x%x, size=0x%x, ", devno, where, size);
+ if (!config) {
+ pr_debug("failed\n");
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ celleb_config_read_fake(config, where, size, val);
+ pr_debug("val=0x%x\n", *val);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int celleb_fake_pci_write_config(struct pci_bus *bus,
+ unsigned int devfn, int where, int size, u32 val)
+{
+ char *config;
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ struct celleb_pci_resource *res;
+ unsigned int devno = devfn >> 3;
+ unsigned int fn = devfn & 0x7;
+
+ /* allignment check */
+ BUG_ON(where % size);
+
+ config = get_fake_config_start(hose, devno, fn);
+
+ if (!config)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (val == ~0) {
+ int i = (where - PCI_BASE_ADDRESS_0) >> 3;
+
+ switch (where) {
+ case PCI_BASE_ADDRESS_0:
+ case PCI_BASE_ADDRESS_2:
+ if (size != 4)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ res = get_resource_start(hose, devno, fn);
+ if (!res)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ celleb_config_write_fake(config, where, size,
+ (res->r[i].end - res->r[i].start));
+ return PCIBIOS_SUCCESSFUL;
+ case PCI_BASE_ADDRESS_1:
+ case PCI_BASE_ADDRESS_3:
+ case PCI_BASE_ADDRESS_4:
+ case PCI_BASE_ADDRESS_5:
+ break;
+ default:
+ break;
+ }
+ }
+
+ celleb_config_write_fake(config, where, size, val);
+ pr_debug(" fake write: where=%x, size=%d, val=%x\n",
+ where, size, val);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops celleb_fake_pci_ops = {
+ .read = celleb_fake_pci_read_config,
+ .write = celleb_fake_pci_write_config,
+};
+
+static inline void celleb_setup_pci_base_addrs(struct pci_controller *hose,
+ unsigned int devno, unsigned int fn,
+ unsigned int num_base_addr)
+{
+ u32 val;
+ unsigned char *config;
+ struct celleb_pci_resource *res;
+
+ config = get_fake_config_start(hose, devno, fn);
+ res = get_resource_start(hose, devno, fn);
+
+ if (!config || !res)
+ return;
+
+ switch (num_base_addr) {
+ case 3:
+ val = (res->r[2].start & 0xfffffff0)
+ | PCI_BASE_ADDRESS_MEM_TYPE_64;
+ celleb_config_write_fake(config, PCI_BASE_ADDRESS_4, 4, val);
+ val = res->r[2].start >> 32;
+ celleb_config_write_fake(config, PCI_BASE_ADDRESS_5, 4, val);
+ /* FALLTHROUGH */
+ case 2:
+ val = (res->r[1].start & 0xfffffff0)
+ | PCI_BASE_ADDRESS_MEM_TYPE_64;
+ celleb_config_write_fake(config, PCI_BASE_ADDRESS_2, 4, val);
+ val = res->r[1].start >> 32;
+ celleb_config_write_fake(config, PCI_BASE_ADDRESS_3, 4, val);
+ /* FALLTHROUGH */
+ case 1:
+ val = (res->r[0].start & 0xfffffff0)
+ | PCI_BASE_ADDRESS_MEM_TYPE_64;
+ celleb_config_write_fake(config, PCI_BASE_ADDRESS_0, 4, val);
+ val = res->r[0].start >> 32;
+ celleb_config_write_fake(config, PCI_BASE_ADDRESS_1, 4, val);
+ break;
+ }
+
+ val = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+ celleb_config_write_fake(config, PCI_COMMAND, 2, val);
+}
+
+static int __init celleb_setup_fake_pci_device(struct device_node *node,
+ struct pci_controller *hose)
+{
+ unsigned int rlen;
+ int num_base_addr = 0;
+ u32 val;
+ const u32 *wi0, *wi1, *wi2, *wi3, *wi4;
+ unsigned int devno, fn;
+ struct celleb_pci_private *private = hose->private_data;
+ unsigned char **config = NULL;
+ struct celleb_pci_resource **res = NULL;
+ const char *name;
+ const unsigned long *li;
+ int size, result;
+
+ if (private == NULL) {
+ printk(KERN_ERR "PCI: "
+ "memory space for pci controller is not assigned\n");
+ goto error;
+ }
+
+ name = of_get_property(node, "model", &rlen);
+ if (!name) {
+ printk(KERN_ERR "PCI: model property not found.\n");
+ goto error;
+ }
+
+ wi4 = of_get_property(node, "reg", &rlen);
+ if (wi4 == NULL)
+ goto error;
+
+ devno = ((wi4[0] >> 8) & 0xff) >> 3;
+ fn = (wi4[0] >> 8) & 0x7;
+
+ pr_debug("PCI: celleb_setup_fake_pci() %s devno=%x fn=%x\n", name,
+ devno, fn);
+
+ size = 256;
+ config = &private->fake_config[devno][fn];
+ *config = zalloc_maybe_bootmem(size, GFP_KERNEL);
+ if (*config == NULL) {
+ printk(KERN_ERR "PCI: "
+ "not enough memory for fake configuration space\n");
+ goto error;
+ }
+ pr_debug("PCI: fake config area assigned 0x%016lx\n",
+ (unsigned long)*config);
+
+ size = sizeof(struct celleb_pci_resource);
+ res = &private->res[devno][fn];
+ *res = zalloc_maybe_bootmem(size, GFP_KERNEL);
+ if (*res == NULL) {
+ printk(KERN_ERR
+ "PCI: not enough memory for resource data space\n");
+ goto error;
+ }
+ pr_debug("PCI: res assigned 0x%016lx\n", (unsigned long)*res);
+
+ wi0 = of_get_property(node, "device-id", NULL);
+ wi1 = of_get_property(node, "vendor-id", NULL);
+ wi2 = of_get_property(node, "class-code", NULL);
+ wi3 = of_get_property(node, "revision-id", NULL);
+ if (!wi0 || !wi1 || !wi2 || !wi3) {
+ printk(KERN_ERR "PCI: Missing device tree properties.\n");
+ goto error;
+ }
+
+ celleb_config_write_fake(*config, PCI_DEVICE_ID, 2, wi0[0] & 0xffff);
+ celleb_config_write_fake(*config, PCI_VENDOR_ID, 2, wi1[0] & 0xffff);
+ pr_debug("class-code = 0x%08x\n", wi2[0]);
+
+ celleb_config_write_fake(*config, PCI_CLASS_PROG, 1, wi2[0] & 0xff);
+ celleb_config_write_fake(*config, PCI_CLASS_DEVICE, 2,
+ (wi2[0] >> 8) & 0xffff);
+ celleb_config_write_fake(*config, PCI_REVISION_ID, 1, wi3[0]);
+
+ while (num_base_addr < MAX_PCI_BASE_ADDRS) {
+ result = of_address_to_resource(node,
+ num_base_addr, &(*res)->r[num_base_addr]);
+ if (result)
+ break;
+ num_base_addr++;
+ }
+
+ celleb_setup_pci_base_addrs(hose, devno, fn, num_base_addr);
+
+ li = of_get_property(node, "interrupts", &rlen);
+ if (!li) {
+ printk(KERN_ERR "PCI: interrupts not found.\n");
+ goto error;
+ }
+ val = li[0];
+ celleb_config_write_fake(*config, PCI_INTERRUPT_PIN, 1, 1);
+ celleb_config_write_fake(*config, PCI_INTERRUPT_LINE, 1, val);
+
+#ifdef DEBUG
+ pr_debug("PCI: %s irq=%ld\n", name, li[0]);
+ for (i = 0; i < 6; i++) {
+ celleb_config_read_fake(*config,
+ PCI_BASE_ADDRESS_0 + 0x4 * i, 4,
+ &val);
+ pr_debug("PCI: %s fn=%d base_address_%d=0x%x\n",
+ name, fn, i, val);
+ }
+#endif
+
+ celleb_config_write_fake(*config, PCI_HEADER_TYPE, 1,
+ PCI_HEADER_TYPE_NORMAL);
+
+ return 0;
+
+error:
+ if (mem_init_done) {
+ if (config && *config)
+ kfree(*config);
+ if (res && *res)
+ kfree(*res);
+
+ } else {
+ if (config && *config) {
+ size = 256;
+ free_bootmem(__pa(*config), size);
+ }
+ if (res && *res) {
+ size = sizeof(struct celleb_pci_resource);
+ free_bootmem(__pa(*res), size);
+ }
+ }
+
+ return 1;
+}
+
+static int __init phb_set_bus_ranges(struct device_node *dev,
+ struct pci_controller *phb)
+{
+ const int *bus_range;
+ unsigned int len;
+
+ bus_range = of_get_property(dev, "bus-range", &len);
+ if (bus_range == NULL || len < 2 * sizeof(int))
+ return 1;
+
+ phb->first_busno = bus_range[0];
+ phb->last_busno = bus_range[1];
+
+ return 0;
+}
+
+static void __init celleb_alloc_private_mem(struct pci_controller *hose)
+{
+ hose->private_data =
+ zalloc_maybe_bootmem(sizeof(struct celleb_pci_private),
+ GFP_KERNEL);
+}
+
+static int __init celleb_setup_fake_pci(struct device_node *dev,
+ struct pci_controller *phb)
+{
+ struct device_node *node;
+
+ phb->ops = &celleb_fake_pci_ops;
+ celleb_alloc_private_mem(phb);
+
+ for (node = of_get_next_child(dev, NULL);
+ node != NULL; node = of_get_next_child(dev, node))
+ celleb_setup_fake_pci_device(node, phb);
+
+ return 0;
+}
+
+static struct celleb_phb_spec celleb_fake_pci_spec __initdata = {
+ .setup = celleb_setup_fake_pci,
+};
+
+static struct of_device_id celleb_phb_match[] __initdata = {
+ {
+ .name = "pci-pseudo",
+ .data = &celleb_fake_pci_spec,
+ }, {
+ .name = "epci",
+ .data = &celleb_epci_spec,
+ }, {
+ .name = "pcie",
+ .data = &celleb_pciex_spec,
+ }, {
+ },
+};
+
+int __init celleb_setup_phb(struct pci_controller *phb)
+{
+ struct device_node *dev = phb->dn;
+ const struct of_device_id *match;
+ const struct celleb_phb_spec *phb_spec;
+ int rc;
+
+ match = of_match_node(celleb_phb_match, dev);
+ if (!match)
+ return 1;
+
+ phb_set_bus_ranges(dev, phb);
+ phb->buid = 1;
+
+ phb_spec = match->data;
+ rc = (*phb_spec->setup)(dev, phb);
+ if (rc)
+ return 1;
+
+ if (phb_spec->ops)
+ iowa_register_bus(phb, phb_spec->ops,
+ phb_spec->iowa_init,
+ phb_spec->iowa_data);
+ return 0;
+}
+
+int celleb_pci_probe_mode(struct pci_bus *bus)
+{
+ return PCI_PROBE_DEVTREE;
+}
diff --git a/arch/powerpc/platforms/cell/celleb_pci.h b/arch/powerpc/platforms/cell/celleb_pci.h
new file mode 100644
index 00000000000..a801fcc5f38
--- /dev/null
+++ b/arch/powerpc/platforms/cell/celleb_pci.h
@@ -0,0 +1,46 @@
+/*
+ * pci prototypes for Celleb platform
+ *
+ * (C) Copyright 2006-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _CELLEB_PCI_H
+#define _CELLEB_PCI_H
+
+#include <linux/pci.h>
+
+#include <asm/pci-bridge.h>
+#include <asm/prom.h>
+#include <asm/ppc-pci.h>
+#include <asm/io-workarounds.h>
+
+struct iowa_bus;
+
+struct celleb_phb_spec {
+ int (*setup)(struct device_node *, struct pci_controller *);
+ struct ppc_pci_io *ops;
+ int (*iowa_init)(struct iowa_bus *, void *);
+ void *iowa_data;
+};
+
+extern int celleb_setup_phb(struct pci_controller *);
+extern int celleb_pci_probe_mode(struct pci_bus *);
+
+extern struct celleb_phb_spec celleb_epci_spec;
+extern struct celleb_phb_spec celleb_pciex_spec;
+
+#endif /* _CELLEB_PCI_H */
diff --git a/arch/powerpc/platforms/cell/celleb_scc.h b/arch/powerpc/platforms/cell/celleb_scc.h
new file mode 100644
index 00000000000..b596a711c34
--- /dev/null
+++ b/arch/powerpc/platforms/cell/celleb_scc.h
@@ -0,0 +1,232 @@
+/*
+ * SCC (Super Companion Chip) definitions
+ *
+ * (C) Copyright 2004-2006 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _CELLEB_SCC_H
+#define _CELLEB_SCC_H
+
+#define PCI_VENDOR_ID_TOSHIBA_2 0x102f
+#define PCI_DEVICE_ID_TOSHIBA_SCC_PCIEXC_BRIDGE 0x01b0
+#define PCI_DEVICE_ID_TOSHIBA_SCC_EPCI_BRIDGE 0x01b1
+#define PCI_DEVICE_ID_TOSHIBA_SCC_BRIDGE 0x01b2
+#define PCI_DEVICE_ID_TOSHIBA_SCC_GBE 0x01b3
+#define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4
+#define PCI_DEVICE_ID_TOSHIBA_SCC_USB2 0x01b5
+#define PCI_DEVICE_ID_TOSHIBA_SCC_USB 0x01b6
+#define PCI_DEVICE_ID_TOSHIBA_SCC_ENCDEC 0x01b7
+
+#define SCC_EPCI_REG 0x0000d000
+
+/* EPCI registers */
+#define SCC_EPCI_CNF10_REG 0x010
+#define SCC_EPCI_CNF14_REG 0x014
+#define SCC_EPCI_CNF18_REG 0x018
+#define SCC_EPCI_PVBAT 0x100
+#define SCC_EPCI_VPMBAT 0x104
+#define SCC_EPCI_VPIBAT 0x108
+#define SCC_EPCI_VCSR 0x110
+#define SCC_EPCI_VIENAB 0x114
+#define SCC_EPCI_VISTAT 0x118
+#define SCC_EPCI_VRDCOUNT 0x124
+#define SCC_EPCI_BAM0 0x12c
+#define SCC_EPCI_BAM1 0x134
+#define SCC_EPCI_BAM2 0x13c
+#define SCC_EPCI_IADR 0x164
+#define SCC_EPCI_CLKRST 0x800
+#define SCC_EPCI_INTSET 0x804
+#define SCC_EPCI_STATUS 0x808
+#define SCC_EPCI_ABTSET 0x80c
+#define SCC_EPCI_WATRP 0x810
+#define SCC_EPCI_DUMYRADR 0x814
+#define SCC_EPCI_SWRESP 0x818
+#define SCC_EPCI_CNTOPT 0x81c
+#define SCC_EPCI_ECMODE 0xf00
+#define SCC_EPCI_IOM_AC_NUM 5
+#define SCC_EPCI_IOM_ACTE(n) (0xf10 + (n) * 4)
+#define SCC_EPCI_IOT_AC_NUM 4
+#define SCC_EPCI_IOT_ACTE(n) (0xf30 + (n) * 4)
+#define SCC_EPCI_MAEA 0xf50
+#define SCC_EPCI_MAEC 0xf54
+#define SCC_EPCI_CKCTRL 0xff0
+
+/* bits for SCC_EPCI_VCSR */
+#define SCC_EPCI_VCSR_FRE 0x00020000
+#define SCC_EPCI_VCSR_FWE 0x00010000
+#define SCC_EPCI_VCSR_DR 0x00000400
+#define SCC_EPCI_VCSR_SR 0x00000008
+#define SCC_EPCI_VCSR_AT 0x00000004
+
+/* bits for SCC_EPCI_VIENAB/SCC_EPCI_VISTAT */
+#define SCC_EPCI_VISTAT_PMPE 0x00000008
+#define SCC_EPCI_VISTAT_PMFE 0x00000004
+#define SCC_EPCI_VISTAT_PRA 0x00000002
+#define SCC_EPCI_VISTAT_PRD 0x00000001
+#define SCC_EPCI_VISTAT_ALL 0x0000000f
+
+#define SCC_EPCI_VIENAB_PMPEE 0x00000008
+#define SCC_EPCI_VIENAB_PMFEE 0x00000004
+#define SCC_EPCI_VIENAB_PRA 0x00000002
+#define SCC_EPCI_VIENAB_PRD 0x00000001
+#define SCC_EPCI_VIENAB_ALL 0x0000000f
+
+/* bits for SCC_EPCI_CLKRST */
+#define SCC_EPCI_CLKRST_CKS_MASK 0x00030000
+#define SCC_EPCI_CLKRST_CKS_2 0x00000000
+#define SCC_EPCI_CLKRST_CKS_4 0x00010000
+#define SCC_EPCI_CLKRST_CKS_8 0x00020000
+#define SCC_EPCI_CLKRST_PCICRST 0x00000400
+#define SCC_EPCI_CLKRST_BC 0x00000200
+#define SCC_EPCI_CLKRST_PCIRST 0x00000100
+#define SCC_EPCI_CLKRST_PCKEN 0x00000001
+
+/* bits for SCC_EPCI_INTSET/SCC_EPCI_STATUS */
+#define SCC_EPCI_INT_2M 0x01000000
+#define SCC_EPCI_INT_RERR 0x00200000
+#define SCC_EPCI_INT_SERR 0x00100000
+#define SCC_EPCI_INT_PRTER 0x00080000
+#define SCC_EPCI_INT_SER 0x00040000
+#define SCC_EPCI_INT_PER 0x00020000
+#define SCC_EPCI_INT_PAI 0x00010000
+#define SCC_EPCI_INT_1M 0x00000100
+#define SCC_EPCI_INT_PME 0x00000010
+#define SCC_EPCI_INT_INTD 0x00000008
+#define SCC_EPCI_INT_INTC 0x00000004
+#define SCC_EPCI_INT_INTB 0x00000002
+#define SCC_EPCI_INT_INTA 0x00000001
+#define SCC_EPCI_INT_DEVINT 0x0000000f
+#define SCC_EPCI_INT_ALL 0x003f001f
+#define SCC_EPCI_INT_ALLERR 0x003f0000
+
+/* bits for SCC_EPCI_CKCTRL */
+#define SCC_EPCI_CKCTRL_CRST0 0x00010000
+#define SCC_EPCI_CKCTRL_CRST1 0x00020000
+#define SCC_EPCI_CKCTRL_OCLKEN 0x00000100
+#define SCC_EPCI_CKCTRL_LCLKEN 0x00000001
+
+#define SCC_EPCI_IDSEL_AD_TO_SLOT(ad) ((ad) - 10)
+#define SCC_EPCI_MAX_DEVNU SCC_EPCI_IDSEL_AD_TO_SLOT(32)
+
+/* bits for SCC_EPCI_CNTOPT */
+#define SCC_EPCI_CNTOPT_O2PMB 0x00000002
+
+/* SCC PCIEXC SMMIO registers */
+#define PEXCADRS 0x000
+#define PEXCWDATA 0x004
+#define PEXCRDATA 0x008
+#define PEXDADRS 0x010
+#define PEXDCMND 0x014
+#define PEXDWDATA 0x018
+#define PEXDRDATA 0x01c
+#define PEXREQID 0x020
+#define PEXTIDMAP 0x024
+#define PEXINTMASK 0x028
+#define PEXINTSTS 0x02c
+#define PEXAERRMASK 0x030
+#define PEXAERRSTS 0x034
+#define PEXPRERRMASK 0x040
+#define PEXPRERRSTS 0x044
+#define PEXPRERRID01 0x048
+#define PEXPRERRID23 0x04c
+#define PEXVDMASK 0x050
+#define PEXVDSTS 0x054
+#define PEXRCVCPLIDA 0x060
+#define PEXLENERRIDA 0x068
+#define PEXPHYPLLST 0x070
+#define PEXDMRDEN0 0x100
+#define PEXDMRDADR0 0x104
+#define PEXDMRDENX 0x110
+#define PEXDMRDADRX 0x114
+#define PEXECMODE 0xf00
+#define PEXMAEA(n) (0xf50 + (8 * n))
+#define PEXMAEC(n) (0xf54 + (8 * n))
+#define PEXCCRCTRL 0xff0
+
+/* SCC PCIEXC bits and shifts for PEXCADRS */
+#define PEXCADRS_BYTE_EN_SHIFT 20
+#define PEXCADRS_CMD_SHIFT 16
+#define PEXCADRS_CMD_READ (0xa << PEXCADRS_CMD_SHIFT)
+#define PEXCADRS_CMD_WRITE (0xb << PEXCADRS_CMD_SHIFT)
+
+/* SCC PCIEXC shifts for PEXDADRS */
+#define PEXDADRS_BUSNO_SHIFT 20
+#define PEXDADRS_DEVNO_SHIFT 15
+#define PEXDADRS_FUNCNO_SHIFT 12
+
+/* SCC PCIEXC bits and shifts for PEXDCMND */
+#define PEXDCMND_BYTE_EN_SHIFT 4
+#define PEXDCMND_IO_READ 0x2
+#define PEXDCMND_IO_WRITE 0x3
+#define PEXDCMND_CONFIG_READ 0xa
+#define PEXDCMND_CONFIG_WRITE 0xb
+
+/* SCC PCIEXC bits for PEXPHYPLLST */
+#define PEXPHYPLLST_PEXPHYAPLLST 0x00000001
+
+/* SCC PCIEXC bits for PEXECMODE */
+#define PEXECMODE_ALL_THROUGH 0x00000000
+#define PEXECMODE_ALL_8BIT 0x00550155
+#define PEXECMODE_ALL_16BIT 0x00aa02aa
+
+/* SCC PCIEXC bits for PEXCCRCTRL */
+#define PEXCCRCTRL_PEXIPCOREEN 0x00040000
+#define PEXCCRCTRL_PEXIPCONTEN 0x00020000
+#define PEXCCRCTRL_PEXPHYPLLEN 0x00010000
+#define PEXCCRCTRL_PCIEXCAOCKEN 0x00000100
+
+/* SCC PCIEXC port configuration registers */
+#define PEXTCERRCHK 0x21c
+#define PEXTAMAPB0 0x220
+#define PEXTAMAPL0 0x224
+#define PEXTAMAPB(n) (PEXTAMAPB0 + 8 * (n))
+#define PEXTAMAPL(n) (PEXTAMAPL0 + 8 * (n))
+#define PEXCHVC0P 0x500
+#define PEXCHVC0NP 0x504
+#define PEXCHVC0C 0x508
+#define PEXCDVC0P 0x50c
+#define PEXCDVC0NP 0x510
+#define PEXCDVC0C 0x514
+#define PEXCHVCXP 0x518
+#define PEXCHVCXNP 0x51c
+#define PEXCHVCXC 0x520
+#define PEXCDVCXP 0x524
+#define PEXCDVCXNP 0x528
+#define PEXCDVCXC 0x52c
+#define PEXCTTRG 0x530
+#define PEXTSCTRL 0x700
+#define PEXTSSTS 0x704
+#define PEXSKPCTRL 0x708
+
+/* UHC registers */
+#define SCC_UHC_CKRCTRL 0xff0
+#define SCC_UHC_ECMODE 0xf00
+
+/* bits for SCC_UHC_CKRCTRL */
+#define SCC_UHC_F48MCKLEN 0x00000001
+#define SCC_UHC_P_SUSPEND 0x00000002
+#define SCC_UHC_PHY_SUSPEND_SEL 0x00000004
+#define SCC_UHC_HCLKEN 0x00000100
+#define SCC_UHC_USBEN 0x00010000
+#define SCC_UHC_USBCEN 0x00020000
+#define SCC_UHC_PHYEN 0x00040000
+
+/* bits for SCC_UHC_ECMODE */
+#define SCC_UHC_ECMODE_BY_BYTE 0x00000555
+#define SCC_UHC_ECMODE_BY_WORD 0x00000aaa
+
+#endif /* _CELLEB_SCC_H */
diff --git a/arch/powerpc/platforms/cell/celleb_scc_epci.c b/arch/powerpc/platforms/cell/celleb_scc_epci.c
new file mode 100644
index 00000000000..844c0facb4f
--- /dev/null
+++ b/arch/powerpc/platforms/cell/celleb_scc_epci.c
@@ -0,0 +1,429 @@
+/*
+ * Support for SCC external PCI
+ *
+ * (C) Copyright 2004-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/threads.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/pci_regs.h>
+#include <linux/bootmem.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/ppc-pci.h>
+
+#include "celleb_scc.h"
+#include "celleb_pci.h"
+
+#define MAX_PCI_DEVICES 32
+#define MAX_PCI_FUNCTIONS 8
+
+#define iob() __asm__ __volatile__("eieio; sync":::"memory")
+
+static inline PCI_IO_ADDR celleb_epci_get_epci_base(
+ struct pci_controller *hose)
+{
+ /*
+ * Note:
+ * Celleb epci uses cfg_addr as a base address for
+ * epci control registers.
+ */
+
+ return hose->cfg_addr;
+}
+
+static inline PCI_IO_ADDR celleb_epci_get_epci_cfg(
+ struct pci_controller *hose)
+{
+ /*
+ * Note:
+ * Celleb epci uses cfg_data as a base address for
+ * configuration area for epci devices.
+ */
+
+ return hose->cfg_data;
+}
+
+static inline void clear_and_disable_master_abort_interrupt(
+ struct pci_controller *hose)
+{
+ PCI_IO_ADDR epci_base;
+ PCI_IO_ADDR reg;
+ epci_base = celleb_epci_get_epci_base(hose);
+ reg = epci_base + PCI_COMMAND;
+ out_be32(reg, in_be32(reg) | (PCI_STATUS_REC_MASTER_ABORT << 16));
+}
+
+static int celleb_epci_check_abort(struct pci_controller *hose,
+ PCI_IO_ADDR addr)
+{
+ PCI_IO_ADDR reg;
+ PCI_IO_ADDR epci_base;
+ u32 val;
+
+ iob();
+ epci_base = celleb_epci_get_epci_base(hose);
+
+ reg = epci_base + PCI_COMMAND;
+ val = in_be32(reg);
+
+ if (val & (PCI_STATUS_REC_MASTER_ABORT << 16)) {
+ out_be32(reg,
+ (val & 0xffff) | (PCI_STATUS_REC_MASTER_ABORT << 16));
+
+ /* clear PCI Controller error, FRE, PMFE */
+ reg = epci_base + SCC_EPCI_STATUS;
+ out_be32(reg, SCC_EPCI_INT_PAI);
+
+ reg = epci_base + SCC_EPCI_VCSR;
+ val = in_be32(reg) & 0xffff;
+ val |= SCC_EPCI_VCSR_FRE;
+ out_be32(reg, val);
+
+ reg = epci_base + SCC_EPCI_VISTAT;
+ out_be32(reg, SCC_EPCI_VISTAT_PMFE);
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static PCI_IO_ADDR celleb_epci_make_config_addr(struct pci_bus *bus,
+ struct pci_controller *hose, unsigned int devfn, int where)
+{
+ PCI_IO_ADDR addr;
+
+ if (bus != hose->bus)
+ addr = celleb_epci_get_epci_cfg(hose) +
+ (((bus->number & 0xff) << 16)
+ | ((devfn & 0xff) << 8)
+ | (where & 0xff)
+ | 0x01000000);
+ else
+ addr = celleb_epci_get_epci_cfg(hose) +
+ (((devfn & 0xff) << 8) | (where & 0xff));
+
+ pr_debug("EPCI: config_addr = 0x%p\n", addr);
+
+ return addr;
+}
+
+static int celleb_epci_read_config(struct pci_bus *bus,
+ unsigned int devfn, int where, int size, u32 *val)
+{
+ PCI_IO_ADDR epci_base;
+ PCI_IO_ADDR addr;
+ struct pci_controller *hose = pci_bus_to_host(bus);
+
+ /* allignment check */
+ BUG_ON(where % size);
+
+ if (!celleb_epci_get_epci_cfg(hose))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (bus->number == hose->first_busno && devfn == 0) {
+ /* EPCI controller self */
+
+ epci_base = celleb_epci_get_epci_base(hose);
+ addr = epci_base + where;
+
+ switch (size) {
+ case 1:
+ *val = in_8(addr);
+ break;
+ case 2:
+ *val = in_be16(addr);
+ break;
+ case 4:
+ *val = in_be32(addr);
+ break;
+ default:
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ } else {
+
+ clear_and_disable_master_abort_interrupt(hose);
+ addr = celleb_epci_make_config_addr(bus, hose, devfn, where);
+
+ switch (size) {
+ case 1:
+ *val = in_8(addr);
+ break;
+ case 2:
+ *val = in_le16(addr);
+ break;
+ case 4:
+ *val = in_le32(addr);
+ break;
+ default:
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ }
+
+ pr_debug("EPCI: "
+ "addr=0x%p, devfn=0x%x, where=0x%x, size=0x%x, val=0x%x\n",
+ addr, devfn, where, size, *val);
+
+ return celleb_epci_check_abort(hose, NULL);
+}
+
+static int celleb_epci_write_config(struct pci_bus *bus,
+ unsigned int devfn, int where, int size, u32 val)
+{
+ PCI_IO_ADDR epci_base;
+ PCI_IO_ADDR addr;
+ struct pci_controller *hose = pci_bus_to_host(bus);
+
+ /* allignment check */
+ BUG_ON(where % size);
+
+ if (!celleb_epci_get_epci_cfg(hose))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (bus->number == hose->first_busno && devfn == 0) {
+ /* EPCI controller self */
+
+ epci_base = celleb_epci_get_epci_base(hose);
+ addr = epci_base + where;
+
+ switch (size) {
+ case 1:
+ out_8(addr, val);
+ break;
+ case 2:
+ out_be16(addr, val);
+ break;
+ case 4:
+ out_be32(addr, val);
+ break;
+ default:
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ } else {
+
+ clear_and_disable_master_abort_interrupt(hose);
+ addr = celleb_epci_make_config_addr(bus, hose, devfn, where);
+
+ switch (size) {
+ case 1:
+ out_8(addr, val);
+ break;
+ case 2:
+ out_le16(addr, val);
+ break;
+ case 4:
+ out_le32(addr, val);
+ break;
+ default:
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ }
+
+ return celleb_epci_check_abort(hose, addr);
+}
+
+struct pci_ops celleb_epci_ops = {
+ .read = celleb_epci_read_config,
+ .write = celleb_epci_write_config,
+};
+
+/* to be moved in FW */
+static int __init celleb_epci_init(struct pci_controller *hose)
+{
+ u32 val;
+ PCI_IO_ADDR reg;
+ PCI_IO_ADDR epci_base;
+ int hwres = 0;
+
+ epci_base = celleb_epci_get_epci_base(hose);
+
+ /* PCI core reset(Internal bus and PCI clock) */
+ reg = epci_base + SCC_EPCI_CKCTRL;
+ val = in_be32(reg);
+ if (val == 0x00030101)
+ hwres = 1;
+ else {
+ val &= ~(SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1);
+ out_be32(reg, val);
+
+ /* set PCI core clock */
+ val = in_be32(reg);
+ val |= (SCC_EPCI_CKCTRL_OCLKEN | SCC_EPCI_CKCTRL_LCLKEN);
+ out_be32(reg, val);
+
+ /* release PCI core reset (internal bus) */
+ val = in_be32(reg);
+ val |= SCC_EPCI_CKCTRL_CRST0;
+ out_be32(reg, val);
+
+ /* set PCI clock select */
+ reg = epci_base + SCC_EPCI_CLKRST;
+ val = in_be32(reg);
+ val &= ~SCC_EPCI_CLKRST_CKS_MASK;
+ val |= SCC_EPCI_CLKRST_CKS_2;
+ out_be32(reg, val);
+
+ /* set arbiter */
+ reg = epci_base + SCC_EPCI_ABTSET;
+ out_be32(reg, 0x0f1f001f); /* temporary value */
+
+ /* buffer on */
+ reg = epci_base + SCC_EPCI_CLKRST;
+ val = in_be32(reg);
+ val |= SCC_EPCI_CLKRST_BC;
+ out_be32(reg, val);
+
+ /* PCI clock enable */
+ val = in_be32(reg);
+ val |= SCC_EPCI_CLKRST_PCKEN;
+ out_be32(reg, val);
+
+ /* release PCI core reset (all) */
+ reg = epci_base + SCC_EPCI_CKCTRL;
+ val = in_be32(reg);
+ val |= (SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1);
+ out_be32(reg, val);
+
+ /* set base translation registers. (already set by Beat) */
+
+ /* set base address masks. (already set by Beat) */
+ }
+
+ /* release interrupt masks and clear all interrupts */
+ reg = epci_base + SCC_EPCI_INTSET;
+ out_be32(reg, 0x013f011f); /* all interrupts enable */
+ reg = epci_base + SCC_EPCI_VIENAB;
+ val = SCC_EPCI_VIENAB_PMPEE | SCC_EPCI_VIENAB_PMFEE;
+ out_be32(reg, val);
+ reg = epci_base + SCC_EPCI_STATUS;
+ out_be32(reg, 0xffffffff);
+ reg = epci_base + SCC_EPCI_VISTAT;
+ out_be32(reg, 0xffffffff);
+
+ /* disable PCI->IB address translation */
+ reg = epci_base + SCC_EPCI_VCSR;
+ val = in_be32(reg);
+ val &= ~(SCC_EPCI_VCSR_DR | SCC_EPCI_VCSR_AT);
+ out_be32(reg, val);
+
+ /* set base addresses. (no need to set?) */
+
+ /* memory space, bus master enable */
+ reg = epci_base + PCI_COMMAND;
+ val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+ out_be32(reg, val);
+
+ /* endian mode setup */
+ reg = epci_base + SCC_EPCI_ECMODE;
+ val = 0x00550155;
+ out_be32(reg, val);
+
+ /* set control option */
+ reg = epci_base + SCC_EPCI_CNTOPT;
+ val = in_be32(reg);
+ val |= SCC_EPCI_CNTOPT_O2PMB;
+ out_be32(reg, val);
+
+ /* XXX: temporay: set registers for address conversion setup */
+ reg = epci_base + SCC_EPCI_CNF10_REG;
+ out_be32(reg, 0x80000008);
+ reg = epci_base + SCC_EPCI_CNF14_REG;
+ out_be32(reg, 0x40000008);
+
+ reg = epci_base + SCC_EPCI_BAM0;
+ out_be32(reg, 0x80000000);
+ reg = epci_base + SCC_EPCI_BAM1;
+ out_be32(reg, 0xe0000000);
+
+ reg = epci_base + SCC_EPCI_PVBAT;
+ out_be32(reg, 0x80000000);
+
+ if (!hwres) {
+ /* release external PCI reset */
+ reg = epci_base + SCC_EPCI_CLKRST;
+ val = in_be32(reg);
+ val |= SCC_EPCI_CLKRST_PCIRST;
+ out_be32(reg, val);
+ }
+
+ return 0;
+}
+
+static int __init celleb_setup_epci(struct device_node *node,
+ struct pci_controller *hose)
+{
+ struct resource r;
+
+ pr_debug("PCI: celleb_setup_epci()\n");
+
+ /*
+ * Note:
+ * Celleb epci uses cfg_addr and cfg_data member of
+ * pci_controller structure in irregular way.
+ *
+ * cfg_addr is used to map for control registers of
+ * celleb epci.
+ *
+ * cfg_data is used for configuration area of devices
+ * on Celleb epci buses.
+ */
+
+ if (of_address_to_resource(node, 0, &r))
+ goto error;
+ hose->cfg_addr = ioremap(r.start, resource_size(&r));
+ if (!hose->cfg_addr)
+ goto error;
+ pr_debug("EPCI: cfg_addr map 0x%016llx->0x%016lx + 0x%016llx\n",
+ r.start, (unsigned long)hose->cfg_addr, resource_size(&r));
+
+ if (of_address_to_resource(node, 2, &r))
+ goto error;
+ hose->cfg_data = ioremap(r.start, resource_size(&r));
+ if (!hose->cfg_data)
+ goto error;
+ pr_debug("EPCI: cfg_data map 0x%016llx->0x%016lx + 0x%016llx\n",
+ r.start, (unsigned long)hose->cfg_data, resource_size(&r));
+
+ hose->ops = &celleb_epci_ops;
+ celleb_epci_init(hose);
+
+ return 0;
+
+error:
+ if (hose->cfg_addr)
+ iounmap(hose->cfg_addr);
+
+ if (hose->cfg_data)
+ iounmap(hose->cfg_data);
+ return 1;
+}
+
+struct celleb_phb_spec celleb_epci_spec __initdata = {
+ .setup = celleb_setup_epci,
+ .ops = &spiderpci_ops,
+ .iowa_init = &spiderpci_iowa_init,
+ .iowa_data = (void *)0,
+};
diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c
new file mode 100644
index 00000000000..4278acfa2ed
--- /dev/null
+++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c
@@ -0,0 +1,539 @@
+/*
+ * Support for Celleb PCI-Express.
+ *
+ * (C) Copyright 2007-2008 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/iommu.h>
+#include <asm/byteorder.h>
+
+#include "celleb_scc.h"
+#include "celleb_pci.h"
+
+#define PEX_IN(base, off) in_be32((void __iomem *)(base) + (off))
+#define PEX_OUT(base, off, data) out_be32((void __iomem *)(base) + (off), (data))
+
+static void scc_pciex_io_flush(struct iowa_bus *bus)
+{
+ (void)PEX_IN(bus->phb->cfg_addr, PEXDMRDEN0);
+}
+
+/*
+ * Memory space access to device on PCIEX
+ */
+#define PCIEX_MMIO_READ(name, ret) \
+static ret scc_pciex_##name(const PCI_IO_ADDR addr) \
+{ \
+ ret val = __do_##name(addr); \
+ scc_pciex_io_flush(iowa_mem_find_bus(addr)); \
+ return val; \
+}
+
+#define PCIEX_MMIO_READ_STR(name) \
+static void scc_pciex_##name(const PCI_IO_ADDR addr, void *buf, \
+ unsigned long count) \
+{ \
+ __do_##name(addr, buf, count); \
+ scc_pciex_io_flush(iowa_mem_find_bus(addr)); \
+}
+
+PCIEX_MMIO_READ(readb, u8)
+PCIEX_MMIO_READ(readw, u16)
+PCIEX_MMIO_READ(readl, u32)
+PCIEX_MMIO_READ(readq, u64)
+PCIEX_MMIO_READ(readw_be, u16)
+PCIEX_MMIO_READ(readl_be, u32)
+PCIEX_MMIO_READ(readq_be, u64)
+PCIEX_MMIO_READ_STR(readsb)
+PCIEX_MMIO_READ_STR(readsw)
+PCIEX_MMIO_READ_STR(readsl)
+
+static void scc_pciex_memcpy_fromio(void *dest, const PCI_IO_ADDR src,
+ unsigned long n)
+{
+ __do_memcpy_fromio(dest, src, n);
+ scc_pciex_io_flush(iowa_mem_find_bus(src));
+}
+
+/*
+ * I/O port access to devices on PCIEX.
+ */
+
+static inline unsigned long get_bus_address(struct pci_controller *phb,
+ unsigned long port)
+{
+ return port - ((unsigned long)(phb->io_base_virt) - _IO_BASE);
+}
+
+static u32 scc_pciex_read_port(struct pci_controller *phb,
+ unsigned long port, int size)
+{
+ unsigned int byte_enable;
+ unsigned int cmd, shift;
+ unsigned long addr;
+ u32 data, ret;
+
+ BUG_ON(((port & 0x3ul) + size) > 4);
+
+ addr = get_bus_address(phb, port);
+ shift = addr & 0x3ul;
+ byte_enable = ((1 << size) - 1) << shift;
+ cmd = PEXDCMND_IO_READ | (byte_enable << PEXDCMND_BYTE_EN_SHIFT);
+ PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul));
+ PEX_OUT(phb->cfg_addr, PEXDCMND, cmd);
+ data = PEX_IN(phb->cfg_addr, PEXDRDATA);
+ ret = (data >> (shift * 8)) & (0xFFFFFFFF >> ((4 - size) * 8));
+
+ pr_debug("PCIEX:PIO READ:port=0x%lx, addr=0x%lx, size=%d, be=%x,"
+ " cmd=%x, data=%x, ret=%x\n", port, addr, size, byte_enable,
+ cmd, data, ret);
+
+ return ret;
+}
+
+static void scc_pciex_write_port(struct pci_controller *phb,
+ unsigned long port, int size, u32 val)
+{
+ unsigned int byte_enable;
+ unsigned int cmd, shift;
+ unsigned long addr;
+ u32 data;
+
+ BUG_ON(((port & 0x3ul) + size) > 4);
+
+ addr = get_bus_address(phb, port);
+ shift = addr & 0x3ul;
+ byte_enable = ((1 << size) - 1) << shift;
+ cmd = PEXDCMND_IO_WRITE | (byte_enable << PEXDCMND_BYTE_EN_SHIFT);
+ data = (val & (0xFFFFFFFF >> (4 - size) * 8)) << (shift * 8);
+ PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul));
+ PEX_OUT(phb->cfg_addr, PEXDCMND, cmd);
+ PEX_OUT(phb->cfg_addr, PEXDWDATA, data);
+
+ pr_debug("PCIEX:PIO WRITE:port=0x%lx, addr=%lx, size=%d, val=%x,"
+ " be=%x, cmd=%x, data=%x\n", port, addr, size, val,
+ byte_enable, cmd, data);
+}
+
+static u8 __scc_pciex_inb(struct pci_controller *phb, unsigned long port)
+{
+ return (u8)scc_pciex_read_port(phb, port, 1);
+}
+
+static u16 __scc_pciex_inw(struct pci_controller *phb, unsigned long port)
+{
+ u32 data;
+ if ((port & 0x3ul) < 3)
+ data = scc_pciex_read_port(phb, port, 2);
+ else {
+ u32 d1 = scc_pciex_read_port(phb, port, 1);
+ u32 d2 = scc_pciex_read_port(phb, port + 1, 1);
+ data = d1 | (d2 << 8);
+ }
+ return (u16)data;
+}
+
+static u32 __scc_pciex_inl(struct pci_controller *phb, unsigned long port)
+{
+ unsigned int mod = port & 0x3ul;
+ u32 data;
+ if (mod == 0)
+ data = scc_pciex_read_port(phb, port, 4);
+ else {
+ u32 d1 = scc_pciex_read_port(phb, port, 4 - mod);
+ u32 d2 = scc_pciex_read_port(phb, port + 1, mod);
+ data = d1 | (d2 << (mod * 8));
+ }
+ return data;
+}
+
+static void __scc_pciex_outb(struct pci_controller *phb,
+ u8 val, unsigned long port)
+{
+ scc_pciex_write_port(phb, port, 1, (u32)val);
+}
+
+static void __scc_pciex_outw(struct pci_controller *phb,
+ u16 val, unsigned long port)
+{
+ if ((port & 0x3ul) < 3)
+ scc_pciex_write_port(phb, port, 2, (u32)val);
+ else {
+ u32 d1 = val & 0x000000FF;
+ u32 d2 = (val & 0x0000FF00) >> 8;
+ scc_pciex_write_port(phb, port, 1, d1);
+ scc_pciex_write_port(phb, port + 1, 1, d2);
+ }
+}
+
+static void __scc_pciex_outl(struct pci_controller *phb,
+ u32 val, unsigned long port)
+{
+ unsigned int mod = port & 0x3ul;
+ if (mod == 0)
+ scc_pciex_write_port(phb, port, 4, val);
+ else {
+ u32 d1 = val & (0xFFFFFFFFul >> (mod * 8));
+ u32 d2 = val >> ((4 - mod) * 8);
+ scc_pciex_write_port(phb, port, 4 - mod, d1);
+ scc_pciex_write_port(phb, port + 1, mod, d2);
+ }
+}
+
+#define PCIEX_PIO_FUNC(size, name) \
+static u##size scc_pciex_in##name(unsigned long port) \
+{ \
+ struct iowa_bus *bus = iowa_pio_find_bus(port); \
+ u##size data = __scc_pciex_in##name(bus->phb, port); \
+ scc_pciex_io_flush(bus); \
+ return data; \
+} \
+static void scc_pciex_ins##name(unsigned long p, void *b, unsigned long c) \
+{ \
+ struct iowa_bus *bus = iowa_pio_find_bus(p); \
+ __le##size *dst = b; \
+ for (; c != 0; c--, dst++) \
+ *dst = cpu_to_le##size(__scc_pciex_in##name(bus->phb, p)); \
+ scc_pciex_io_flush(bus); \
+} \
+static void scc_pciex_out##name(u##size val, unsigned long port) \
+{ \
+ struct iowa_bus *bus = iowa_pio_find_bus(port); \
+ __scc_pciex_out##name(bus->phb, val, port); \
+} \
+static void scc_pciex_outs##name(unsigned long p, const void *b, \
+ unsigned long c) \
+{ \
+ struct iowa_bus *bus = iowa_pio_find_bus(p); \
+ const __le##size *src = b; \
+ for (; c != 0; c--, src++) \
+ __scc_pciex_out##name(bus->phb, le##size##_to_cpu(*src), p); \
+}
+#define __le8 u8
+#define cpu_to_le8(x) (x)
+#define le8_to_cpu(x) (x)
+PCIEX_PIO_FUNC(8, b)
+PCIEX_PIO_FUNC(16, w)
+PCIEX_PIO_FUNC(32, l)
+
+static struct ppc_pci_io scc_pciex_ops = {
+ .readb = scc_pciex_readb,
+ .readw = scc_pciex_readw,
+ .readl = scc_pciex_readl,
+ .readq = scc_pciex_readq,
+ .readw_be = scc_pciex_readw_be,
+ .readl_be = scc_pciex_readl_be,
+ .readq_be = scc_pciex_readq_be,
+ .readsb = scc_pciex_readsb,
+ .readsw = scc_pciex_readsw,
+ .readsl = scc_pciex_readsl,
+ .memcpy_fromio = scc_pciex_memcpy_fromio,
+ .inb = scc_pciex_inb,
+ .inw = scc_pciex_inw,
+ .inl = scc_pciex_inl,
+ .outb = scc_pciex_outb,
+ .outw = scc_pciex_outw,
+ .outl = scc_pciex_outl,
+ .insb = scc_pciex_insb,
+ .insw = scc_pciex_insw,
+ .insl = scc_pciex_insl,
+ .outsb = scc_pciex_outsb,
+ .outsw = scc_pciex_outsw,
+ .outsl = scc_pciex_outsl,
+};
+
+static int __init scc_pciex_iowa_init(struct iowa_bus *bus, void *data)
+{
+ dma_addr_t dummy_page_da;
+ void *dummy_page_va;
+
+ dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!dummy_page_va) {
+ pr_err("PCIEX:Alloc dummy_page_va failed\n");
+ return -1;
+ }
+
+ dummy_page_da = dma_map_single(bus->phb->parent, dummy_page_va,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(bus->phb->parent, dummy_page_da)) {
+ pr_err("PCIEX:Map dummy page failed.\n");
+ kfree(dummy_page_va);
+ return -1;
+ }
+
+ PEX_OUT(bus->phb->cfg_addr, PEXDMRDADR0, dummy_page_da);
+
+ return 0;
+}
+
+/*
+ * config space access
+ */
+#define MK_PEXDADRS(bus_no, dev_no, func_no, addr) \
+ ((uint32_t)(((addr) & ~0x3UL) | \
+ ((bus_no) << PEXDADRS_BUSNO_SHIFT) | \
+ ((dev_no) << PEXDADRS_DEVNO_SHIFT) | \
+ ((func_no) << PEXDADRS_FUNCNO_SHIFT)))
+
+#define MK_PEXDCMND_BYTE_EN(addr, size) \
+ ((((0x1 << (size))-1) << ((addr) & 0x3)) << PEXDCMND_BYTE_EN_SHIFT)
+#define MK_PEXDCMND(cmd, addr, size) ((cmd) | MK_PEXDCMND_BYTE_EN(addr, size))
+
+static uint32_t config_read_pciex_dev(unsigned int __iomem *base,
+ uint64_t bus_no, uint64_t dev_no, uint64_t func_no,
+ uint64_t off, uint64_t size)
+{
+ uint32_t ret;
+ uint32_t addr, cmd;
+
+ addr = MK_PEXDADRS(bus_no, dev_no, func_no, off);
+ cmd = MK_PEXDCMND(PEXDCMND_CONFIG_READ, off, size);
+ PEX_OUT(base, PEXDADRS, addr);
+ PEX_OUT(base, PEXDCMND, cmd);
+ ret = (PEX_IN(base, PEXDRDATA)
+ >> ((off & (4-size)) * 8)) & ((0x1 << (size * 8)) - 1);
+ return ret;
+}
+
+static void config_write_pciex_dev(unsigned int __iomem *base, uint64_t bus_no,
+ uint64_t dev_no, uint64_t func_no, uint64_t off, uint64_t size,
+ uint32_t data)
+{
+ uint32_t addr, cmd;
+
+ addr = MK_PEXDADRS(bus_no, dev_no, func_no, off);
+ cmd = MK_PEXDCMND(PEXDCMND_CONFIG_WRITE, off, size);
+ PEX_OUT(base, PEXDADRS, addr);
+ PEX_OUT(base, PEXDCMND, cmd);
+ PEX_OUT(base, PEXDWDATA,
+ (data & ((0x1 << (size * 8)) - 1)) << ((off & (4-size)) * 8));
+}
+
+#define MK_PEXCADRS_BYTE_EN(off, len) \
+ ((((0x1 << (len)) - 1) << ((off) & 0x3)) << PEXCADRS_BYTE_EN_SHIFT)
+#define MK_PEXCADRS(cmd, addr, size) \
+ ((cmd) | MK_PEXCADRS_BYTE_EN(addr, size) | ((addr) & ~0x3))
+static uint32_t config_read_pciex_rc(unsigned int __iomem *base,
+ uint32_t where, uint32_t size)
+{
+ PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_READ, where, size));
+ return (PEX_IN(base, PEXCRDATA)
+ >> ((where & (4 - size)) * 8)) & ((0x1 << (size * 8)) - 1);
+}
+
+static void config_write_pciex_rc(unsigned int __iomem *base, uint32_t where,
+ uint32_t size, uint32_t val)
+{
+ uint32_t data;
+
+ data = (val & ((0x1 << (size * 8)) - 1)) << ((where & (4 - size)) * 8);
+ PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_WRITE, where, size));
+ PEX_OUT(base, PEXCWDATA, data);
+}
+
+/* Interfaces */
+/* Note: Work-around
+ * On SCC PCIEXC, one device is seen on all 32 dev_no.
+ * As SCC PCIEXC can have only one device on the bus, we look only one dev_no.
+ * (dev_no = 1)
+ */
+static int scc_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, unsigned int *val)
+{
+ struct pci_controller *phb = pci_bus_to_host(bus);
+
+ if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1) {
+ *val = ~0;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ if (bus->number == 0 && PCI_SLOT(devfn) == 0)
+ *val = config_read_pciex_rc(phb->cfg_addr, where, size);
+ else
+ *val = config_read_pciex_dev(phb->cfg_addr, bus->number,
+ PCI_SLOT(devfn), PCI_FUNC(devfn), where, size);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int scc_pciex_write_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, unsigned int val)
+{
+ struct pci_controller *phb = pci_bus_to_host(bus);
+
+ if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (bus->number == 0 && PCI_SLOT(devfn) == 0)
+ config_write_pciex_rc(phb->cfg_addr, where, size, val);
+ else
+ config_write_pciex_dev(phb->cfg_addr, bus->number,
+ PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops scc_pciex_pci_ops = {
+ scc_pciex_read_config,
+ scc_pciex_write_config,
+};
+
+static void pciex_clear_intr_all(unsigned int __iomem *base)
+{
+ PEX_OUT(base, PEXAERRSTS, 0xffffffff);
+ PEX_OUT(base, PEXPRERRSTS, 0xffffffff);
+ PEX_OUT(base, PEXINTSTS, 0xffffffff);
+}
+
+#if 0
+static void pciex_disable_intr_all(unsigned int *base)
+{
+ PEX_OUT(base, PEXINTMASK, 0x0);
+ PEX_OUT(base, PEXAERRMASK, 0x0);
+ PEX_OUT(base, PEXPRERRMASK, 0x0);
+ PEX_OUT(base, PEXVDMASK, 0x0);
+}
+#endif
+
+static void pciex_enable_intr_all(unsigned int __iomem *base)
+{
+ PEX_OUT(base, PEXINTMASK, 0x0000e7f1);
+ PEX_OUT(base, PEXAERRMASK, 0x03ff01ff);
+ PEX_OUT(base, PEXPRERRMASK, 0x0001010f);
+ PEX_OUT(base, PEXVDMASK, 0x00000001);
+}
+
+static void pciex_check_status(unsigned int __iomem *base)
+{
+ uint32_t err = 0;
+ uint32_t intsts, aerr, prerr, rcvcp, lenerr;
+ uint32_t maea, maec;
+
+ intsts = PEX_IN(base, PEXINTSTS);
+ aerr = PEX_IN(base, PEXAERRSTS);
+ prerr = PEX_IN(base, PEXPRERRSTS);
+ rcvcp = PEX_IN(base, PEXRCVCPLIDA);
+ lenerr = PEX_IN(base, PEXLENERRIDA);
+
+ if (intsts || aerr || prerr || rcvcp || lenerr)
+ err = 1;
+
+ pr_info("PCEXC interrupt!!\n");
+ pr_info("PEXINTSTS :0x%08x\n", intsts);
+ pr_info("PEXAERRSTS :0x%08x\n", aerr);
+ pr_info("PEXPRERRSTS :0x%08x\n", prerr);
+ pr_info("PEXRCVCPLIDA :0x%08x\n", rcvcp);
+ pr_info("PEXLENERRIDA :0x%08x\n", lenerr);
+
+ /* print detail of Protection Error */
+ if (intsts & 0x00004000) {
+ uint32_t i, n;
+ for (i = 0; i < 4; i++) {
+ n = 1 << i;
+ if (prerr & n) {
+ maea = PEX_IN(base, PEXMAEA(i));
+ maec = PEX_IN(base, PEXMAEC(i));
+ pr_info("PEXMAEC%d :0x%08x\n", i, maec);
+ pr_info("PEXMAEA%d :0x%08x\n", i, maea);
+ }
+ }
+ }
+
+ if (err)
+ pciex_clear_intr_all(base);
+}
+
+static irqreturn_t pciex_handle_internal_irq(int irq, void *dev_id)
+{
+ struct pci_controller *phb = dev_id;
+
+ pr_debug("PCIEX:pciex_handle_internal_irq(irq=%d)\n", irq);
+
+ BUG_ON(phb->cfg_addr == NULL);
+
+ pciex_check_status(phb->cfg_addr);
+
+ return IRQ_HANDLED;
+}
+
+static __init int celleb_setup_pciex(struct device_node *node,
+ struct pci_controller *phb)
+{
+ struct resource r;
+ int virq;
+
+ /* SMMIO registers; used inside this file */
+ if (of_address_to_resource(node, 0, &r)) {
+ pr_err("PCIEXC:Failed to get config resource.\n");
+ return 1;
+ }
+ phb->cfg_addr = ioremap(r.start, resource_size(&r));
+ if (!phb->cfg_addr) {
+ pr_err("PCIEXC:Failed to remap SMMIO region.\n");
+ return 1;
+ }
+
+ /* Not use cfg_data, cmd and data regs are near address reg */
+ phb->cfg_data = NULL;
+
+ /* set pci_ops */
+ phb->ops = &scc_pciex_pci_ops;
+
+ /* internal interrupt handler */
+ virq = irq_of_parse_and_map(node, 1);
+ if (!virq) {
+ pr_err("PCIEXC:Failed to map irq\n");
+ goto error;
+ }
+ if (request_irq(virq, pciex_handle_internal_irq,
+ 0, "pciex", (void *)phb)) {
+ pr_err("PCIEXC:Failed to request irq\n");
+ goto error;
+ }
+
+ /* enable all interrupts */
+ pciex_clear_intr_all(phb->cfg_addr);
+ pciex_enable_intr_all(phb->cfg_addr);
+ /* MSI: TBD */
+
+ return 0;
+
+error:
+ phb->cfg_data = NULL;
+ if (phb->cfg_addr)
+ iounmap(phb->cfg_addr);
+ phb->cfg_addr = NULL;
+ return 1;
+}
+
+struct celleb_phb_spec celleb_pciex_spec __initdata = {
+ .setup = celleb_setup_pciex,
+ .ops = &scc_pciex_ops,
+ .iowa_init = &scc_pciex_iowa_init,
+};
diff --git a/arch/powerpc/platforms/cell/celleb_scc_sio.c b/arch/powerpc/platforms/cell/celleb_scc_sio.c
new file mode 100644
index 00000000000..c8eb5719382
--- /dev/null
+++ b/arch/powerpc/platforms/cell/celleb_scc_sio.c
@@ -0,0 +1,99 @@
+/*
+ * setup serial port in SCC
+ *
+ * (C) Copyright 2006-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/console.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+
+/* sio irq0=0xb00010022 irq0=0xb00010023 irq2=0xb00010024
+ mmio=0xfff000-0x1000,0xff2000-0x1000 */
+static int txx9_serial_bitmap __initdata;
+
+static struct {
+ uint32_t offset;
+ uint32_t index;
+} txx9_scc_tab[3] __initdata = {
+ { 0x300, 0 }, /* 0xFFF300 */
+ { 0x400, 0 }, /* 0xFFF400 */
+ { 0x800, 1 } /* 0xFF2800 */
+};
+
+static int __init txx9_serial_init(void)
+{
+ extern int early_serial_txx9_setup(struct uart_port *port);
+ struct device_node *node;
+ int i;
+ struct uart_port req;
+ struct of_phandle_args irq;
+ struct resource res;
+
+ for_each_compatible_node(node, "serial", "toshiba,sio-scc") {
+ for (i = 0; i < ARRAY_SIZE(txx9_scc_tab); i++) {
+ if (!(txx9_serial_bitmap & (1<<i)))
+ continue;
+
+ if (of_irq_parse_one(node, i, &irq))
+ continue;
+ if (of_address_to_resource(node,
+ txx9_scc_tab[i].index, &res))
+ continue;
+
+ memset(&req, 0, sizeof(req));
+ req.line = i;
+ req.iotype = UPIO_MEM;
+ req.mapbase = res.start + txx9_scc_tab[i].offset;
+#ifdef CONFIG_SERIAL_TXX9_CONSOLE
+ req.membase = ioremap(req.mapbase, 0x24);
+#endif
+ req.irq = irq_create_of_mapping(&irq);
+ req.flags |= UPF_IOREMAP | UPF_BUGGY_UART
+ /*HAVE_CTS_LINE*/;
+ req.uartclk = 83300000;
+ early_serial_txx9_setup(&req);
+ }
+ }
+
+ return 0;
+}
+
+static int __init txx9_serial_config(char *ptr)
+{
+ int i;
+
+ for (;;) {
+ switch (get_option(&ptr, &i)) {
+ default:
+ return 0;
+ case 2:
+ txx9_serial_bitmap |= 1 << i;
+ break;
+ case 1:
+ txx9_serial_bitmap |= 1 << i;
+ return 0;
+ }
+ }
+}
+__setup("txx9_serial=", txx9_serial_config);
+
+console_initcall(txx9_serial_init);
diff --git a/arch/powerpc/platforms/cell/celleb_scc_uhc.c b/arch/powerpc/platforms/cell/celleb_scc_uhc.c
new file mode 100644
index 00000000000..d63b720bfe3
--- /dev/null
+++ b/arch/powerpc/platforms/cell/celleb_scc_uhc.c
@@ -0,0 +1,95 @@
+/*
+ * SCC (Super Companion Chip) UHC setup
+ *
+ * (C) Copyright 2006-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+
+#include "celleb_scc.h"
+
+#define UHC_RESET_WAIT_MAX 10000
+
+static inline int uhc_clkctrl_ready(u32 val)
+{
+ const u32 mask = SCC_UHC_USBCEN | SCC_UHC_USBCEN;
+ return((val & mask) == mask);
+}
+
+/*
+ * UHC(usb host controller) enable function.
+ * affect to both of OHCI and EHCI core module.
+ */
+static void enable_scc_uhc(struct pci_dev *dev)
+{
+ void __iomem *uhc_base;
+ u32 __iomem *uhc_clkctrl;
+ u32 __iomem *uhc_ecmode;
+ u32 val = 0;
+ int i;
+
+ if (!machine_is(celleb_beat) &&
+ !machine_is(celleb_native))
+ return;
+
+ uhc_base = ioremap(pci_resource_start(dev, 0),
+ pci_resource_len(dev, 0));
+ if (!uhc_base) {
+ printk(KERN_ERR "failed to map UHC register base.\n");
+ return;
+ }
+ uhc_clkctrl = uhc_base + SCC_UHC_CKRCTRL;
+ uhc_ecmode = uhc_base + SCC_UHC_ECMODE;
+
+ /* setup for normal mode */
+ val |= SCC_UHC_F48MCKLEN;
+ out_be32(uhc_clkctrl, val);
+ val |= SCC_UHC_PHY_SUSPEND_SEL;
+ out_be32(uhc_clkctrl, val);
+ udelay(10);
+ val |= SCC_UHC_PHYEN;
+ out_be32(uhc_clkctrl, val);
+ udelay(50);
+
+ /* disable reset */
+ val |= SCC_UHC_HCLKEN;
+ out_be32(uhc_clkctrl, val);
+ val |= (SCC_UHC_USBCEN | SCC_UHC_USBEN);
+ out_be32(uhc_clkctrl, val);
+ i = 0;
+ while (!uhc_clkctrl_ready(in_be32(uhc_clkctrl))) {
+ udelay(10);
+ if (i++ > UHC_RESET_WAIT_MAX) {
+ printk(KERN_ERR "Failed to disable UHC reset %x\n",
+ in_be32(uhc_clkctrl));
+ break;
+ }
+ }
+
+ /* Endian Conversion Mode for Master ALL area */
+ out_be32(uhc_ecmode, SCC_UHC_ECMODE_BY_BYTE);
+
+ iounmap(uhc_base);
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA_2,
+ PCI_DEVICE_ID_TOSHIBA_SCC_USB, enable_scc_uhc);
diff --git a/arch/powerpc/platforms/cell/celleb_setup.c b/arch/powerpc/platforms/cell/celleb_setup.c
new file mode 100644
index 00000000000..1d5a4d8ddad
--- /dev/null
+++ b/arch/powerpc/platforms/cell/celleb_setup.c
@@ -0,0 +1,243 @@
+/*
+ * Celleb setup code
+ *
+ * (C) Copyright 2006-2007 TOSHIBA CORPORATION
+ *
+ * This code is based on arch/powerpc/platforms/cell/setup.c:
+ * Copyright (C) 1995 Linus Torvalds
+ * Adapted from 'alpha' version by Gary Thomas
+ * Modified by Cort Dougan (cort@cs.nmt.edu)
+ * Modified by PPC64 Team, IBM Corp
+ * Modified by Cell Team, IBM Deutschland Entwicklung GmbH
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#undef DEBUG
+
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/console.h>
+#include <linux/of_platform.h>
+
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/cputable.h>
+#include <asm/irq.h>
+#include <asm/time.h>
+#include <asm/spu_priv1.h>
+#include <asm/firmware.h>
+#include <asm/rtas.h>
+#include <asm/cell-regs.h>
+
+#include "beat_interrupt.h"
+#include "beat_wrapper.h"
+#include "beat.h"
+#include "celleb_pci.h"
+#include "interrupt.h"
+#include "pervasive.h"
+#include "ras.h"
+
+static char celleb_machine_type[128] = "Celleb";
+
+static void celleb_show_cpuinfo(struct seq_file *m)
+{
+ struct device_node *root;
+ const char *model = "";
+
+ root = of_find_node_by_path("/");
+ if (root)
+ model = of_get_property(root, "model", NULL);
+ /* using "CHRP" is to trick anaconda into installing FCx into Celleb */
+ seq_printf(m, "machine\t\t: %s %s\n", celleb_machine_type, model);
+ of_node_put(root);
+}
+
+static int __init celleb_machine_type_hack(char *ptr)
+{
+ strlcpy(celleb_machine_type, ptr, sizeof(celleb_machine_type));
+ return 0;
+}
+
+__setup("celleb_machine_type_hack=", celleb_machine_type_hack);
+
+static void celleb_progress(char *s, unsigned short hex)
+{
+ printk("*** %04x : %s\n", hex, s ? s : "");
+}
+
+static void __init celleb_setup_arch_common(void)
+{
+ /* init to some ~sane value until calibrate_delay() runs */
+ loops_per_jiffy = 50000000;
+
+#ifdef CONFIG_DUMMY_CONSOLE
+ conswitchp = &dummy_con;
+#endif
+}
+
+static struct of_device_id celleb_bus_ids[] __initdata = {
+ { .type = "scc", },
+ { .type = "ioif", }, /* old style */
+ {},
+};
+
+static int __init celleb_publish_devices(void)
+{
+ /* Publish OF platform devices for southbridge IOs */
+ of_platform_bus_probe(NULL, celleb_bus_ids, NULL);
+
+ return 0;
+}
+machine_device_initcall(celleb_beat, celleb_publish_devices);
+machine_device_initcall(celleb_native, celleb_publish_devices);
+
+
+/*
+ * functions for Celleb-Beat
+ */
+static void __init celleb_setup_arch_beat(void)
+{
+#ifdef CONFIG_SPU_BASE
+ spu_priv1_ops = &spu_priv1_beat_ops;
+ spu_management_ops = &spu_management_of_ops;
+#endif
+
+ celleb_setup_arch_common();
+}
+
+static int __init celleb_probe_beat(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ if (!of_flat_dt_is_compatible(root, "Beat"))
+ return 0;
+
+ powerpc_firmware_features |= FW_FEATURE_CELLEB_ALWAYS
+ | FW_FEATURE_BEAT | FW_FEATURE_LPAR;
+ hpte_init_beat_v3();
+
+ return 1;
+}
+
+
+/*
+ * functions for Celleb-native
+ */
+static void __init celleb_init_IRQ_native(void)
+{
+ iic_init_IRQ();
+ spider_init_IRQ();
+}
+
+static void __init celleb_setup_arch_native(void)
+{
+#ifdef CONFIG_SPU_BASE
+ spu_priv1_ops = &spu_priv1_mmio_ops;
+ spu_management_ops = &spu_management_of_ops;
+#endif
+
+ cbe_regs_init();
+
+#ifdef CONFIG_CBE_RAS
+ cbe_ras_init();
+#endif
+
+#ifdef CONFIG_SMP
+ smp_init_cell();
+#endif
+
+ cbe_pervasive_init();
+
+ /* XXX: nvram initialization should be added */
+
+ celleb_setup_arch_common();
+}
+
+static int __init celleb_probe_native(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ if (of_flat_dt_is_compatible(root, "Beat") ||
+ !of_flat_dt_is_compatible(root, "TOSHIBA,Celleb"))
+ return 0;
+
+ powerpc_firmware_features |= FW_FEATURE_CELLEB_ALWAYS;
+ hpte_init_native();
+
+ return 1;
+}
+
+
+/*
+ * machine definitions
+ */
+define_machine(celleb_beat) {
+ .name = "Cell Reference Set (Beat)",
+ .probe = celleb_probe_beat,
+ .setup_arch = celleb_setup_arch_beat,
+ .show_cpuinfo = celleb_show_cpuinfo,
+ .restart = beat_restart,
+ .power_off = beat_power_off,
+ .halt = beat_halt,
+ .get_rtc_time = beat_get_rtc_time,
+ .set_rtc_time = beat_set_rtc_time,
+ .calibrate_decr = generic_calibrate_decr,
+ .progress = celleb_progress,
+ .power_save = beat_power_save,
+ .nvram_size = beat_nvram_get_size,
+ .nvram_read = beat_nvram_read,
+ .nvram_write = beat_nvram_write,
+ .set_dabr = beat_set_xdabr,
+ .init_IRQ = beatic_init_IRQ,
+ .get_irq = beatic_get_irq,
+ .pci_probe_mode = celleb_pci_probe_mode,
+ .pci_setup_phb = celleb_setup_phb,
+#ifdef CONFIG_KEXEC
+ .kexec_cpu_down = beat_kexec_cpu_down,
+#endif
+};
+
+define_machine(celleb_native) {
+ .name = "Cell Reference Set (native)",
+ .probe = celleb_probe_native,
+ .setup_arch = celleb_setup_arch_native,
+ .show_cpuinfo = celleb_show_cpuinfo,
+ .restart = rtas_restart,
+ .power_off = rtas_power_off,
+ .halt = rtas_halt,
+ .get_boot_time = rtas_get_boot_time,
+ .get_rtc_time = rtas_get_rtc_time,
+ .set_rtc_time = rtas_set_rtc_time,
+ .calibrate_decr = generic_calibrate_decr,
+ .progress = celleb_progress,
+ .pci_probe_mode = celleb_pci_probe_mode,
+ .pci_setup_phb = celleb_setup_phb,
+ .init_IRQ = celleb_init_IRQ_native,
+};
diff --git a/arch/powerpc/platforms/cell/cpufreq_spudemand.c b/arch/powerpc/platforms/cell/cpufreq_spudemand.c
new file mode 100644
index 00000000000..82607d621ac
--- /dev/null
+++ b/arch/powerpc/platforms/cell/cpufreq_spudemand.c
@@ -0,0 +1,171 @@
+/*
+ * spu aware cpufreq governor for the cell processor
+ *
+ * © Copyright IBM Corporation 2006-2008
+ *
+ * Author: Christian Krafft <krafft@de.ibm.com>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/atomic.h>
+#include <asm/machdep.h>
+#include <asm/spu.h>
+
+#define POLL_TIME 100000 /* in µs */
+#define EXP 753 /* exp(-1) in fixed-point */
+
+struct spu_gov_info_struct {
+ unsigned long busy_spus; /* fixed-point */
+ struct cpufreq_policy *policy;
+ struct delayed_work work;
+ unsigned int poll_int; /* µs */
+};
+static DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info);
+
+static int calc_freq(struct spu_gov_info_struct *info)
+{
+ int cpu;
+ int busy_spus;
+
+ cpu = info->policy->cpu;
+ busy_spus = atomic_read(&cbe_spu_info[cpu_to_node(cpu)].busy_spus);
+
+ CALC_LOAD(info->busy_spus, EXP, busy_spus * FIXED_1);
+ pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n",
+ cpu, busy_spus, info->busy_spus);
+
+ return info->policy->max * info->busy_spus / FIXED_1;
+}
+
+static void spu_gov_work(struct work_struct *work)
+{
+ struct spu_gov_info_struct *info;
+ int delay;
+ unsigned long target_freq;
+
+ info = container_of(work, struct spu_gov_info_struct, work.work);
+
+ /* after cancel_delayed_work_sync we unset info->policy */
+ BUG_ON(info->policy == NULL);
+
+ target_freq = calc_freq(info);
+ __cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H);
+
+ delay = usecs_to_jiffies(info->poll_int);
+ schedule_delayed_work_on(info->policy->cpu, &info->work, delay);
+}
+
+static void spu_gov_init_work(struct spu_gov_info_struct *info)
+{
+ int delay = usecs_to_jiffies(info->poll_int);
+ INIT_DEFERRABLE_WORK(&info->work, spu_gov_work);
+ schedule_delayed_work_on(info->policy->cpu, &info->work, delay);
+}
+
+static void spu_gov_cancel_work(struct spu_gov_info_struct *info)
+{
+ cancel_delayed_work_sync(&info->work);
+}
+
+static int spu_gov_govern(struct cpufreq_policy *policy, unsigned int event)
+{
+ unsigned int cpu = policy->cpu;
+ struct spu_gov_info_struct *info, *affected_info;
+ int i;
+ int ret = 0;
+
+ info = &per_cpu(spu_gov_info, cpu);
+
+ switch (event) {
+ case CPUFREQ_GOV_START:
+ if (!cpu_online(cpu)) {
+ printk(KERN_ERR "cpu %d is not online\n", cpu);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!policy->cur) {
+ printk(KERN_ERR "no cpu specified in policy\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* initialize spu_gov_info for all affected cpus */
+ for_each_cpu(i, policy->cpus) {
+ affected_info = &per_cpu(spu_gov_info, i);
+ affected_info->policy = policy;
+ }
+
+ info->poll_int = POLL_TIME;
+
+ /* setup timer */
+ spu_gov_init_work(info);
+
+ break;
+
+ case CPUFREQ_GOV_STOP:
+ /* cancel timer */
+ spu_gov_cancel_work(info);
+
+ /* clean spu_gov_info for all affected cpus */
+ for_each_cpu (i, policy->cpus) {
+ info = &per_cpu(spu_gov_info, i);
+ info->policy = NULL;
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+static struct cpufreq_governor spu_governor = {
+ .name = "spudemand",
+ .governor = spu_gov_govern,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * module init and destoy
+ */
+
+static int __init spu_gov_init(void)
+{
+ int ret;
+
+ ret = cpufreq_register_governor(&spu_governor);
+ if (ret)
+ printk(KERN_ERR "registration of governor failed\n");
+ return ret;
+}
+
+static void __exit spu_gov_exit(void)
+{
+ cpufreq_unregister_governor(&spu_governor);
+}
+
+
+module_init(spu_gov_init);
+module_exit(spu_gov_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
+
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c
index 04f74f9f9ab..8a106b4172e 100644
--- a/arch/powerpc/platforms/cell/interrupt.c
+++ b/arch/powerpc/platforms/cell/interrupt.c
@@ -31,10 +31,11 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/percpu.h>
#include <linux/types.h>
#include <linux/ioport.h>
+#include <linux/kernel_stat.h>
#include <asm/io.h>
#include <asm/pgtable.h>
@@ -53,9 +54,9 @@ struct iic {
struct device_node *node;
};
-static DEFINE_PER_CPU(struct iic, iic);
+static DEFINE_PER_CPU(struct iic, cpu_iic);
#define IIC_NODE_COUNT 2
-static struct irq_host *iic_host;
+static struct irq_domain *iic_host;
/* Convert between "pending" bits and hw irq number */
static irq_hw_number_t iic_pending_to_hwnum(struct cbe_iic_pending_bits bits)
@@ -71,36 +72,38 @@ static irq_hw_number_t iic_pending_to_hwnum(struct cbe_iic_pending_bits bits)
return (node << IIC_IRQ_NODE_SHIFT) | (class << 4) | unit;
}
-static void iic_mask(unsigned int irq)
+static void iic_mask(struct irq_data *d)
{
}
-static void iic_unmask(unsigned int irq)
+static void iic_unmask(struct irq_data *d)
{
}
-static void iic_eoi(unsigned int irq)
+static void iic_eoi(struct irq_data *d)
{
- struct iic *iic = &__get_cpu_var(iic);
+ struct iic *iic = &__get_cpu_var(cpu_iic);
out_be64(&iic->regs->prio, iic->eoi_stack[--iic->eoi_ptr]);
BUG_ON(iic->eoi_ptr < 0);
}
static struct irq_chip iic_chip = {
- .typename = " CELL-IIC ",
- .mask = iic_mask,
- .unmask = iic_unmask,
- .eoi = iic_eoi,
+ .name = "CELL-IIC",
+ .irq_mask = iic_mask,
+ .irq_unmask = iic_unmask,
+ .irq_eoi = iic_eoi,
};
-static void iic_ioexc_eoi(unsigned int irq)
+static void iic_ioexc_eoi(struct irq_data *d)
{
}
static void iic_ioexc_cascade(unsigned int irq, struct irq_desc *desc)
{
- struct cbe_iic_regs __iomem *node_iic = (void __iomem *)desc->handler_data;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct cbe_iic_regs __iomem *node_iic =
+ (void __iomem *)irq_desc_get_handler_data(desc);
unsigned int base = (irq & 0xffffff00) | IIC_IRQ_TYPE_IOEXC;
unsigned long bits, ack;
int cascade;
@@ -127,15 +130,15 @@ static void iic_ioexc_cascade(unsigned int irq, struct irq_desc *desc)
if (ack)
out_be64(&node_iic->iic_is, ack);
}
- desc->chip->eoi(irq);
+ chip->irq_eoi(&desc->irq_data);
}
static struct irq_chip iic_ioexc_chip = {
- .typename = " CELL-IOEX",
- .mask = iic_mask,
- .unmask = iic_unmask,
- .eoi = iic_ioexc_eoi,
+ .name = "CELL-IOEX",
+ .irq_mask = iic_mask,
+ .irq_unmask = iic_unmask,
+ .irq_eoi = iic_ioexc_eoi,
};
/* Get an IRQ number from the pending state register of the IIC */
@@ -145,9 +148,9 @@ static unsigned int iic_get_irq(void)
struct iic *iic;
unsigned int virq;
- iic = &__get_cpu_var(iic);
+ iic = &__get_cpu_var(cpu_iic);
*(unsigned long *) &pending =
- in_be64((unsigned long __iomem *) &iic->regs->pending_destr);
+ in_be64((u64 __iomem *) &iic->regs->pending_destr);
if (!(pending.flags & CBE_IIC_IRQ_VALID))
return NO_IRQ;
virq = irq_linear_revmap(iic_host, iic_pending_to_hwnum(pending));
@@ -160,12 +163,12 @@ static unsigned int iic_get_irq(void)
void iic_setup_cpu(void)
{
- out_be64(&__get_cpu_var(iic).regs->prio, 0xff);
+ out_be64(&__get_cpu_var(cpu_iic).regs->prio, 0xff);
}
u8 iic_get_target_id(int cpu)
{
- return per_cpu(iic, cpu).target_id;
+ return per_cpu(cpu_iic, cpu).target_id;
}
EXPORT_SYMBOL_GPL(iic_get_target_id);
@@ -173,83 +176,77 @@ EXPORT_SYMBOL_GPL(iic_get_target_id);
#ifdef CONFIG_SMP
/* Use the highest interrupt priorities for IPI */
-static inline int iic_ipi_to_irq(int ipi)
+static inline int iic_msg_to_irq(int msg)
{
- return IIC_IRQ_TYPE_IPI + 0xf - ipi;
+ return IIC_IRQ_TYPE_IPI + 0xf - msg;
}
-void iic_cause_IPI(int cpu, int mesg)
+void iic_message_pass(int cpu, int msg)
{
- out_be64(&per_cpu(iic, cpu).regs->generate, (0xf - mesg) << 4);
+ out_be64(&per_cpu(cpu_iic, cpu).regs->generate, (0xf - msg) << 4);
}
-struct irq_host *iic_get_irq_host(int node)
+struct irq_domain *iic_get_irq_host(int node)
{
return iic_host;
}
EXPORT_SYMBOL_GPL(iic_get_irq_host);
-static irqreturn_t iic_ipi_action(int irq, void *dev_id)
-{
- int ipi = (int)(long)dev_id;
-
- smp_message_recv(ipi);
-
- return IRQ_HANDLED;
-}
-static void iic_request_ipi(int ipi, const char *name)
+static void iic_request_ipi(int msg)
{
int virq;
- virq = irq_create_mapping(iic_host, iic_ipi_to_irq(ipi));
+ virq = irq_create_mapping(iic_host, iic_msg_to_irq(msg));
if (virq == NO_IRQ) {
printk(KERN_ERR
- "iic: failed to map IPI %s\n", name);
+ "iic: failed to map IPI %s\n", smp_ipi_name[msg]);
return;
}
- if (request_irq(virq, iic_ipi_action, IRQF_DISABLED, name,
- (void *)(long)ipi))
- printk(KERN_ERR
- "iic: failed to request IPI %s\n", name);
+
+ /*
+ * If smp_request_message_ipi encounters an error it will notify
+ * the error. If a message is not needed it will return non-zero.
+ */
+ if (smp_request_message_ipi(virq, msg))
+ irq_dispose_mapping(virq);
}
void iic_request_IPIs(void)
{
- iic_request_ipi(PPC_MSG_CALL_FUNCTION, "IPI-call");
- iic_request_ipi(PPC_MSG_RESCHEDULE, "IPI-resched");
-#ifdef CONFIG_DEBUGGER
- iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug");
-#endif /* CONFIG_DEBUGGER */
+ iic_request_ipi(PPC_MSG_CALL_FUNCTION);
+ iic_request_ipi(PPC_MSG_RESCHEDULE);
+ iic_request_ipi(PPC_MSG_TICK_BROADCAST);
+ iic_request_ipi(PPC_MSG_DEBUGGER_BREAK);
}
#endif /* CONFIG_SMP */
-static int iic_host_match(struct irq_host *h, struct device_node *node)
+static int iic_host_match(struct irq_domain *h, struct device_node *node)
{
return of_device_is_compatible(node,
"IBM,CBEA-Internal-Interrupt-Controller");
}
-static int iic_host_map(struct irq_host *h, unsigned int virq,
+static int iic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
switch (hw & IIC_IRQ_TYPE_MASK) {
case IIC_IRQ_TYPE_IPI:
- set_irq_chip_and_handler(virq, &iic_chip, handle_percpu_irq);
+ irq_set_chip_and_handler(virq, &iic_chip, handle_percpu_irq);
break;
case IIC_IRQ_TYPE_IOEXC:
- set_irq_chip_and_handler(virq, &iic_ioexc_chip,
- handle_fasteoi_irq);
+ irq_set_chip_and_handler(virq, &iic_ioexc_chip,
+ handle_edge_eoi_irq);
break;
default:
- set_irq_chip_and_handler(virq, &iic_chip, handle_fasteoi_irq);
+ irq_set_chip_and_handler(virq, &iic_chip, handle_edge_eoi_irq);
}
return 0;
}
-static int iic_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
+static int iic_host_xlate(struct irq_domain *h, struct device_node *ct,
+ const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
{
@@ -288,7 +285,7 @@ static int iic_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
-static struct irq_host_ops iic_host_ops = {
+static const struct irq_domain_ops iic_host_ops = {
.match = iic_host_match,
.map = iic_host_map,
.xlate = iic_host_xlate,
@@ -300,7 +297,7 @@ static void __init init_one_iic(unsigned int hw_cpu, unsigned long addr,
/* XXX FIXME: should locate the linux CPU number from the HW cpu
* number properly. We are lucky for now
*/
- struct iic *iic = &per_cpu(iic, hw_cpu);
+ struct iic *iic = &per_cpu(cpu_iic, hw_cpu);
iic->regs = ioremap(addr, sizeof(struct cbe_iic_thread_regs));
BUG_ON(iic->regs == NULL);
@@ -360,8 +357,8 @@ static int __init setup_iic(void)
* irq_data is a generic pointer that gets passed back
* to us later, so the forced cast is fine.
*/
- set_irq_data(cascade, (void __force *)node_iic);
- set_irq_chained_handler(cascade , iic_ioexc_cascade);
+ irq_set_handler_data(cascade, (void __force *)node_iic);
+ irq_set_chained_handler(cascade, iic_ioexc_cascade);
out_be64(&node_iic->iic_ir,
(1 << 12) /* priority */ |
(node << 4) /* dest node */ |
@@ -381,8 +378,8 @@ static int __init setup_iic(void)
void __init iic_init_IRQ(void)
{
/* Setup an irq host data structure */
- iic_host = irq_alloc_host(NULL, IRQ_HOST_MAP_LINEAR, IIC_SOURCE_COUNT,
- &iic_host_ops, IIC_IRQ_INVALID);
+ iic_host = irq_domain_add_linear(NULL, IIC_SOURCE_COUNT, &iic_host_ops,
+ NULL);
BUG_ON(iic_host == NULL);
irq_set_default_host(iic_host);
diff --git a/arch/powerpc/platforms/cell/interrupt.h b/arch/powerpc/platforms/cell/interrupt.h
index 942dc39d604..4f60ae6ca35 100644
--- a/arch/powerpc/platforms/cell/interrupt.h
+++ b/arch/powerpc/platforms/cell/interrupt.h
@@ -75,7 +75,7 @@ enum {
};
extern void iic_init_IRQ(void);
-extern void iic_cause_IPI(int cpu, int mesg);
+extern void iic_message_pass(int cpu, int msg);
extern void iic_request_IPIs(void);
extern void iic_setup_cpu(void);
diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c
deleted file mode 100644
index 979d4b67efb..00000000000
--- a/arch/powerpc/platforms/cell/io-workarounds.c
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Copyright (C) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>
- * IBM, Corp.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#undef DEBUG
-
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-#include <asm/io.h>
-#include <asm/machdep.h>
-#include <asm/pci-bridge.h>
-#include <asm/ppc-pci.h>
-
-
-#define SPIDER_PCI_REG_BASE 0xd000
-#define SPIDER_PCI_VCI_CNTL_STAT 0x0110
-#define SPIDER_PCI_DUMMY_READ 0x0810
-#define SPIDER_PCI_DUMMY_READ_BASE 0x0814
-
-/* Undefine that to re-enable bogus prefetch
- *
- * Without that workaround, the chip will do bogus prefetch past
- * page boundary from system memory. This setting will disable that,
- * though the documentation is unclear as to the consequences of doing
- * so, either purely performances, or possible misbehaviour... It's not
- * clear wether the chip can handle unaligned accesses at all without
- * prefetching enabled.
- *
- * For now, things appear to be behaving properly with that prefetching
- * disabled and IDE, possibly because IDE isn't doing any unaligned
- * access.
- */
-#define SPIDER_DISABLE_PREFETCH
-
-#define MAX_SPIDERS 3
-
-static struct spider_pci_bus {
- void __iomem *regs;
- unsigned long mmio_start;
- unsigned long mmio_end;
- unsigned long pio_vstart;
- unsigned long pio_vend;
-} spider_pci_busses[MAX_SPIDERS];
-static int spider_pci_count;
-
-static struct spider_pci_bus *spider_pci_find(unsigned long vaddr,
- unsigned long paddr)
-{
- int i;
-
- for (i = 0; i < spider_pci_count; i++) {
- struct spider_pci_bus *bus = &spider_pci_busses[i];
- if (paddr && paddr >= bus->mmio_start && paddr < bus->mmio_end)
- return bus;
- if (vaddr && vaddr >= bus->pio_vstart && vaddr < bus->pio_vend)
- return bus;
- }
- return NULL;
-}
-
-static void spider_io_flush(const volatile void __iomem *addr)
-{
- struct spider_pci_bus *bus;
- int token;
-
- /* Get platform token (set by ioremap) from address */
- token = PCI_GET_ADDR_TOKEN(addr);
-
- /* Fast path if we have a non-0 token, it indicates which bus we
- * are on.
- *
- * If the token is 0, that means either that the ioremap was done
- * before we initialized this layer, or it's a PIO operation. We
- * fallback to a low path in this case. Hopefully, internal devices
- * which are ioremap'ed early should use in_XX/out_XX functions
- * instead of the PCI ones and thus not suffer from the slowdown.
- *
- * Also note that currently, the workaround will not work for areas
- * that are not mapped with PTEs (bolted in the hash table). This
- * is the case for ioremaps done very early at boot (before
- * mem_init_done) and includes the mapping of the ISA IO space.
- *
- * Fortunately, none of the affected devices is expected to do DMA
- * and thus there should be no problem in practice.
- *
- * In order to improve performances, we only do the PTE search for
- * addresses falling in the PHB IO space area. That means it will
- * not work for hotplug'ed PHBs but those don't exist with Spider.
- */
- if (token && token <= spider_pci_count)
- bus = &spider_pci_busses[token - 1];
- else {
- unsigned long vaddr, paddr;
- pte_t *ptep;
-
- /* Fixup physical address */
- vaddr = (unsigned long)PCI_FIX_ADDR(addr);
-
- /* Check if it's in allowed range for PIO */
- if (vaddr < PHB_IO_BASE || vaddr > PHB_IO_END)
- return;
-
- /* Try to find a PTE. If not, clear the paddr, we'll do
- * a vaddr only lookup (PIO only)
- */
- ptep = find_linux_pte(init_mm.pgd, vaddr);
- if (ptep == NULL)
- paddr = 0;
- else
- paddr = pte_pfn(*ptep) << PAGE_SHIFT;
-
- bus = spider_pci_find(vaddr, paddr);
- if (bus == NULL)
- return;
- }
-
- /* Now do the workaround
- */
- (void)in_be32(bus->regs + SPIDER_PCI_DUMMY_READ);
-}
-
-static u8 spider_readb(const volatile void __iomem *addr)
-{
- u8 val = __do_readb(addr);
- spider_io_flush(addr);
- return val;
-}
-
-static u16 spider_readw(const volatile void __iomem *addr)
-{
- u16 val = __do_readw(addr);
- spider_io_flush(addr);
- return val;
-}
-
-static u32 spider_readl(const volatile void __iomem *addr)
-{
- u32 val = __do_readl(addr);
- spider_io_flush(addr);
- return val;
-}
-
-static u64 spider_readq(const volatile void __iomem *addr)
-{
- u64 val = __do_readq(addr);
- spider_io_flush(addr);
- return val;
-}
-
-static u16 spider_readw_be(const volatile void __iomem *addr)
-{
- u16 val = __do_readw_be(addr);
- spider_io_flush(addr);
- return val;
-}
-
-static u32 spider_readl_be(const volatile void __iomem *addr)
-{
- u32 val = __do_readl_be(addr);
- spider_io_flush(addr);
- return val;
-}
-
-static u64 spider_readq_be(const volatile void __iomem *addr)
-{
- u64 val = __do_readq_be(addr);
- spider_io_flush(addr);
- return val;
-}
-
-static void spider_readsb(const volatile void __iomem *addr, void *buf,
- unsigned long count)
-{
- __do_readsb(addr, buf, count);
- spider_io_flush(addr);
-}
-
-static void spider_readsw(const volatile void __iomem *addr, void *buf,
- unsigned long count)
-{
- __do_readsw(addr, buf, count);
- spider_io_flush(addr);
-}
-
-static void spider_readsl(const volatile void __iomem *addr, void *buf,
- unsigned long count)
-{
- __do_readsl(addr, buf, count);
- spider_io_flush(addr);
-}
-
-static void spider_memcpy_fromio(void *dest, const volatile void __iomem *src,
- unsigned long n)
-{
- __do_memcpy_fromio(dest, src, n);
- spider_io_flush(src);
-}
-
-
-static void __iomem * spider_ioremap(unsigned long addr, unsigned long size,
- unsigned long flags)
-{
- struct spider_pci_bus *bus;
- void __iomem *res = __ioremap(addr, size, flags);
- int busno;
-
- pr_debug("spider_ioremap(0x%lx, 0x%lx, 0x%lx) -> 0x%p\n",
- addr, size, flags, res);
-
- bus = spider_pci_find(0, addr);
- if (bus != NULL) {
- busno = bus - spider_pci_busses;
- pr_debug(" found bus %d, setting token\n", busno);
- PCI_SET_ADDR_TOKEN(res, busno + 1);
- }
- pr_debug(" result=0x%p\n", res);
-
- return res;
-}
-
-static void __init spider_pci_setup_chip(struct spider_pci_bus *bus)
-{
-#ifdef SPIDER_DISABLE_PREFETCH
- u32 val = in_be32(bus->regs + SPIDER_PCI_VCI_CNTL_STAT);
- pr_debug(" PVCI_Control_Status was 0x%08x\n", val);
- out_be32(bus->regs + SPIDER_PCI_VCI_CNTL_STAT, val | 0x8);
-#endif
-
- /* Configure the dummy address for the workaround */
- out_be32(bus->regs + SPIDER_PCI_DUMMY_READ_BASE, 0x80000000);
-}
-
-static void __init spider_pci_add_one(struct pci_controller *phb)
-{
- struct spider_pci_bus *bus = &spider_pci_busses[spider_pci_count];
- struct device_node *np = phb->dn;
- struct resource rsrc;
- void __iomem *regs;
-
- if (spider_pci_count >= MAX_SPIDERS) {
- printk(KERN_ERR "Too many spider bridges, workarounds"
- " disabled for %s\n", np->full_name);
- return;
- }
-
- /* Get the registers for the beast */
- if (of_address_to_resource(np, 0, &rsrc)) {
- printk(KERN_ERR "Failed to get registers for spider %s"
- " workarounds disabled\n", np->full_name);
- return;
- }
-
- /* Mask out some useless bits in there to get to the base of the
- * spider chip
- */
- rsrc.start &= ~0xfffffffful;
-
- /* Map them */
- regs = ioremap(rsrc.start + SPIDER_PCI_REG_BASE, 0x1000);
- if (regs == NULL) {
- printk(KERN_ERR "Failed to map registers for spider %s"
- " workarounds disabled\n", np->full_name);
- return;
- }
-
- spider_pci_count++;
-
- /* We assume spiders only have one MMIO resource */
- bus->mmio_start = phb->mem_resources[0].start;
- bus->mmio_end = phb->mem_resources[0].end + 1;
-
- bus->pio_vstart = (unsigned long)phb->io_base_virt;
- bus->pio_vend = bus->pio_vstart + phb->pci_io_size;
-
- bus->regs = regs;
-
- printk(KERN_INFO "PCI: Spider MMIO workaround for %s\n",np->full_name);
-
- pr_debug(" mmio (P) = 0x%016lx..0x%016lx\n",
- bus->mmio_start, bus->mmio_end);
- pr_debug(" pio (V) = 0x%016lx..0x%016lx\n",
- bus->pio_vstart, bus->pio_vend);
- pr_debug(" regs (P) = 0x%016lx (V) = 0x%p\n",
- rsrc.start + SPIDER_PCI_REG_BASE, bus->regs);
-
- spider_pci_setup_chip(bus);
-}
-
-static struct ppc_pci_io __initdata spider_pci_io = {
- .readb = spider_readb,
- .readw = spider_readw,
- .readl = spider_readl,
- .readq = spider_readq,
- .readw_be = spider_readw_be,
- .readl_be = spider_readl_be,
- .readq_be = spider_readq_be,
- .readsb = spider_readsb,
- .readsw = spider_readsw,
- .readsl = spider_readsl,
- .memcpy_fromio = spider_memcpy_fromio,
-};
-
-static int __init spider_pci_workaround_init(void)
-{
- struct pci_controller *phb;
-
- /* Find spider bridges. We assume they have been all probed
- * in setup_arch(). If that was to change, we would need to
- * update this code to cope with dynamically added busses
- */
- list_for_each_entry(phb, &hose_list, list_node) {
- struct device_node *np = phb->dn;
- const char *model = of_get_property(np, "model", NULL);
-
- /* If no model property or name isn't exactly "pci", skip */
- if (model == NULL || strcmp(np->name, "pci"))
- continue;
- /* If model is not "Spider", skip */
- if (strcmp(model, "Spider"))
- continue;
- spider_pci_add_one(phb);
- }
-
- /* No Spider PCI found, exit */
- if (spider_pci_count == 0)
- return 0;
-
- /* Setup IO callbacks. We only setup MMIO reads. PIO reads will
- * fallback to MMIO reads (though without a token, thus slower)
- */
- ppc_pci_io = spider_pci_io;
-
- /* Setup ioremap callback */
- ppc_md.ioremap = spider_ioremap;
-
- return 0;
-}
-machine_arch_initcall(cell, spider_pci_workaround_init);
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index d75ccded7f1..2b90ff8a93b 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -28,13 +28,14 @@
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/memblock.h>
#include <asm/prom.h>
#include <asm/iommu.h>
#include <asm/machdep.h>
#include <asm/pci-bridge.h>
#include <asm/udbg.h>
-#include <asm/lmb.h>
#include <asm/firmware.h>
#include <asm/cell-regs.h>
@@ -74,7 +75,7 @@
#define IOC_IO_ExcpStat_V 0x8000000000000000ul
#define IOC_IO_ExcpStat_SPF_Mask 0x6000000000000000ul
#define IOC_IO_ExcpStat_SPF_S 0x6000000000000000ul
-#define IOC_IO_ExcpStat_SPF_P 0x4000000000000000ul
+#define IOC_IO_ExcpStat_SPF_P 0x2000000000000000ul
#define IOC_IO_ExcpStat_ADDR_Mask 0x00000007fffff000ul
#define IOC_IO_ExcpStat_RW_Mask 0x0000000000000800ul
#define IOC_IO_ExcpStat_IOID_Mask 0x00000000000007fful
@@ -100,16 +101,6 @@
#define IOSTE_PS_1M 0x0000000000000005ul /* - 1MB */
#define IOSTE_PS_16M 0x0000000000000007ul /* - 16MB */
-/* Page table entries */
-#define IOPTE_PP_W 0x8000000000000000ul /* protection: write */
-#define IOPTE_PP_R 0x4000000000000000ul /* protection: read */
-#define IOPTE_M 0x2000000000000000ul /* coherency required */
-#define IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */
-#define IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */
-#define IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */
-#define IOPTE_H 0x0000000000000800ul /* cache hint */
-#define IOPTE_IOID_Mask 0x00000000000007fful /* ioid */
-
/* IOMMU sizing */
#define IO_SEGMENT_SHIFT 28
@@ -150,8 +141,8 @@ static int cbe_nr_iommus;
static void invalidate_tce_cache(struct cbe_iommu *iommu, unsigned long *pte,
long n_ptes)
{
- unsigned long __iomem *reg;
- unsigned long val;
+ u64 __iomem *reg;
+ u64 val;
long n;
reg = iommu->xlate_regs + IOC_IOPT_CacheInvd;
@@ -172,8 +163,9 @@ static void invalidate_tce_cache(struct cbe_iommu *iommu, unsigned long *pte,
}
}
-static void tce_build_cell(struct iommu_table *tbl, long index, long npages,
- unsigned long uaddr, enum dma_data_direction direction)
+static int tce_build_cell(struct iommu_table *tbl, long index, long npages,
+ unsigned long uaddr, enum dma_data_direction direction,
+ struct dma_attrs *attrs)
{
int i;
unsigned long *io_pte, base_pte;
@@ -192,17 +184,21 @@ static void tce_build_cell(struct iommu_table *tbl, long index, long npages,
*/
const unsigned long prot = 0xc48;
base_pte =
- ((prot << (52 + 4 * direction)) & (IOPTE_PP_W | IOPTE_PP_R))
- | IOPTE_M | IOPTE_SO_RW | (window->ioid & IOPTE_IOID_Mask);
+ ((prot << (52 + 4 * direction)) &
+ (CBE_IOPTE_PP_W | CBE_IOPTE_PP_R)) |
+ CBE_IOPTE_M | CBE_IOPTE_SO_RW |
+ (window->ioid & CBE_IOPTE_IOID_Mask);
#else
- base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW |
- (window->ioid & IOPTE_IOID_Mask);
+ base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M |
+ CBE_IOPTE_SO_RW | (window->ioid & CBE_IOPTE_IOID_Mask);
#endif
+ if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)))
+ base_pte &= ~CBE_IOPTE_SO_RW;
io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset);
- for (i = 0; i < npages; i++, uaddr += IOMMU_PAGE_SIZE)
- io_pte[i] = base_pte | (__pa(uaddr) & IOPTE_RPN_Mask);
+ for (i = 0; i < npages; i++, uaddr += tbl->it_page_shift)
+ io_pte[i] = base_pte | (__pa(uaddr) & CBE_IOPTE_RPN_Mask);
mb();
@@ -210,6 +206,7 @@ static void tce_build_cell(struct iommu_table *tbl, long index, long npages,
pr_debug("tce_build_cell(index=%lx,n=%lx,dir=%d,base_pte=%lx)\n",
index, npages, direction, base_pte);
+ return 0;
}
static void tce_free_cell(struct iommu_table *tbl, long index, long npages)
@@ -227,8 +224,9 @@ static void tce_free_cell(struct iommu_table *tbl, long index, long npages)
#else
/* spider bridge does PCI reads after freeing - insert a mapping
* to a scratch page instead of an invalid entry */
- pte = IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | __pa(window->iommu->pad_page)
- | (window->ioid & IOPTE_IOID_Mask);
+ pte = CBE_IOPTE_PP_R | CBE_IOPTE_M | CBE_IOPTE_SO_RW |
+ __pa(window->iommu->pad_page) |
+ (window->ioid & CBE_IOPTE_IOID_Mask);
#endif
io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset);
@@ -243,17 +241,18 @@ static void tce_free_cell(struct iommu_table *tbl, long index, long npages)
static irqreturn_t ioc_interrupt(int irq, void *data)
{
- unsigned long stat;
+ unsigned long stat, spf;
struct cbe_iommu *iommu = data;
stat = in_be64(iommu->xlate_regs + IOC_IO_ExcpStat);
+ spf = stat & IOC_IO_ExcpStat_SPF_Mask;
/* Might want to rate limit it */
printk(KERN_ERR "iommu: DMA exception 0x%016lx\n", stat);
printk(KERN_ERR " V=%d, SPF=[%c%c], RW=%s, IOID=0x%04x\n",
!!(stat & IOC_IO_ExcpStat_V),
- (stat & IOC_IO_ExcpStat_SPF_S) ? 'S' : ' ',
- (stat & IOC_IO_ExcpStat_SPF_P) ? 'P' : ' ',
+ (spf == IOC_IO_ExcpStat_SPF_S) ? 'S' : ' ',
+ (spf == IOC_IO_ExcpStat_SPF_P) ? 'P' : ' ',
(stat & IOC_IO_ExcpStat_RW_Mask) ? "Read" : "Write",
(unsigned int)(stat & IOC_IO_ExcpStat_IOID_Mask));
printk(KERN_ERR " page=0x%016lx\n",
@@ -316,7 +315,7 @@ static void cell_iommu_setup_stab(struct cbe_iommu *iommu,
segments = max(dbase + dsize, fbase + fsize) >> IO_SEGMENT_SHIFT;
pr_debug("%s: iommu[%d]: segments: %lu\n",
- __FUNCTION__, iommu->nid, segments);
+ __func__, iommu->nid, segments);
/* set up the segment table */
stab_size = segments * sizeof(unsigned long);
@@ -343,7 +342,7 @@ static unsigned long *cell_iommu_alloc_ptab(struct cbe_iommu *iommu,
(1 << 12) / sizeof(unsigned long));
ptab_size = segments * pages_per_segment * sizeof(unsigned long);
- pr_debug("%s: iommu[%d]: ptab_size: %lu, order: %d\n", __FUNCTION__,
+ pr_debug("%s: iommu[%d]: ptab_size: %lu, order: %d\n", __func__,
iommu->nid, ptab_size, get_order(ptab_size));
page = alloc_pages_node(iommu->nid, GFP_KERNEL, get_order(ptab_size));
BUG_ON(!page);
@@ -355,7 +354,7 @@ static unsigned long *cell_iommu_alloc_ptab(struct cbe_iommu *iommu,
n_pte_pages = (pages_per_segment * sizeof(unsigned long)) >> 12;
pr_debug("%s: iommu[%d]: stab at %p, ptab at %p, n_pte_pages: %lu\n",
- __FUNCTION__, iommu->nid, iommu->stab, ptab,
+ __func__, iommu->nid, iommu->stab, ptab,
n_pte_pages);
/* initialise the STEs */
@@ -394,7 +393,7 @@ static void cell_iommu_enable_hardware(struct cbe_iommu *iommu)
if (cell_iommu_find_ioc(iommu->nid, &xlate_base))
panic("%s: missing IOC register mappings for node %d\n",
- __FUNCTION__, iommu->nid);
+ __func__, iommu->nid);
iommu->xlate_regs = ioremap(xlate_base, IOC_Reg_Size);
iommu->cmd_regs = iommu->xlate_regs + IOC_IOCmd_Offset;
@@ -413,8 +412,7 @@ static void cell_iommu_enable_hardware(struct cbe_iommu *iommu)
IIC_IRQ_IOEX_ATI | (iommu->nid << IIC_IRQ_NODE_SHIFT));
BUG_ON(virq == NO_IRQ);
- ret = request_irq(virq, ioc_interrupt, IRQF_DISABLED,
- iommu->name, iommu);
+ ret = request_irq(virq, ioc_interrupt, 0, iommu->name, iommu);
BUG_ON(ret);
/* set the IOC segment table origin register (and turn on the iommu) */
@@ -432,7 +430,7 @@ static void cell_iommu_setup_hardware(struct cbe_iommu *iommu,
{
cell_iommu_setup_stab(iommu, base, size, 0, 0);
iommu->ptab = cell_iommu_alloc_ptab(iommu, base, size, 0, 0,
- IOMMU_PAGE_SHIFT);
+ IOMMU_PAGE_SHIFT_4K);
cell_iommu_enable_hardware(iommu);
}
@@ -478,7 +476,7 @@ cell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np,
ioid = cell_iommu_get_ioid(np);
- window = kmalloc_node(sizeof(*window), GFP_KERNEL, iommu->nid);
+ window = kzalloc_node(sizeof(*window), GFP_KERNEL, iommu->nid);
BUG_ON(window == NULL);
window->offset = offset;
@@ -489,8 +487,10 @@ cell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np,
window->table.it_blocksize = 16;
window->table.it_base = (unsigned long)iommu->ptab;
window->table.it_index = iommu->nid;
- window->table.it_offset = (offset >> IOMMU_PAGE_SHIFT) + pte_offset;
- window->table.it_size = size >> IOMMU_PAGE_SHIFT;
+ window->table.it_page_shift = IOMMU_PAGE_SHIFT_4K;
+ window->table.it_offset =
+ (offset >> window->table.it_page_shift) + pte_offset;
+ window->table.it_size = size >> window->table.it_page_shift;
iommu_init_table(&window->table, iommu->nid);
@@ -519,8 +519,7 @@ cell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np,
__set_bit(0, window->table.it_map);
tce_build_cell(&window->table, window->table.it_offset, 1,
- (unsigned long)iommu->pad_page, DMA_TO_DEVICE);
- window->table.it_hint = window->table.it_blocksize;
+ (unsigned long)iommu->pad_page, DMA_TO_DEVICE, NULL);
return window;
}
@@ -538,43 +537,134 @@ static struct cbe_iommu *cell_iommu_for_node(int nid)
static unsigned long cell_dma_direct_offset;
static unsigned long dma_iommu_fixed_base;
-struct dma_mapping_ops dma_iommu_fixed_ops;
-static void cell_dma_dev_setup_iommu(struct device *dev)
+/* iommu_fixed_is_weak is set if booted with iommu_fixed=weak */
+static int iommu_fixed_is_weak;
+
+static struct iommu_table *cell_get_iommu_table(struct device *dev)
{
struct iommu_window *window;
struct cbe_iommu *iommu;
- struct dev_archdata *archdata = &dev->archdata;
/* Current implementation uses the first window available in that
* node's iommu. We -might- do something smarter later though it may
* never be necessary
*/
- iommu = cell_iommu_for_node(archdata->numa_node);
+ iommu = cell_iommu_for_node(dev_to_node(dev));
if (iommu == NULL || list_empty(&iommu->windows)) {
- printk(KERN_ERR "iommu: missing iommu for %s (node %d)\n",
- archdata->of_node ? archdata->of_node->full_name : "?",
- archdata->numa_node);
- return;
+ dev_err(dev, "iommu: missing iommu for %s (node %d)\n",
+ of_node_full_name(dev->of_node), dev_to_node(dev));
+ return NULL;
}
window = list_entry(iommu->windows.next, struct iommu_window, list);
- archdata->dma_data = &window->table;
+ return &window->table;
+}
+
+/* A coherent allocation implies strong ordering */
+
+static void *dma_fixed_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag,
+ struct dma_attrs *attrs)
+{
+ if (iommu_fixed_is_weak)
+ return iommu_alloc_coherent(dev, cell_get_iommu_table(dev),
+ size, dma_handle,
+ device_to_mask(dev), flag,
+ dev_to_node(dev));
+ else
+ return dma_direct_ops.alloc(dev, size, dma_handle, flag,
+ attrs);
+}
+
+static void dma_fixed_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle,
+ struct dma_attrs *attrs)
+{
+ if (iommu_fixed_is_weak)
+ iommu_free_coherent(cell_get_iommu_table(dev), size, vaddr,
+ dma_handle);
+ else
+ dma_direct_ops.free(dev, size, vaddr, dma_handle, attrs);
+}
+
+static dma_addr_t dma_fixed_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+ return dma_direct_ops.map_page(dev, page, offset, size,
+ direction, attrs);
+ else
+ return iommu_map_page(dev, cell_get_iommu_table(dev), page,
+ offset, size, device_to_mask(dev),
+ direction, attrs);
+}
+
+static void dma_fixed_unmap_page(struct device *dev, dma_addr_t dma_addr,
+ size_t size, enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+ dma_direct_ops.unmap_page(dev, dma_addr, size, direction,
+ attrs);
+ else
+ iommu_unmap_page(cell_get_iommu_table(dev), dma_addr, size,
+ direction, attrs);
+}
+
+static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+ return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs);
+ else
+ return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents,
+ device_to_mask(dev), direction, attrs);
+}
+
+static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+ dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs);
+ else
+ iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction,
+ attrs);
+}
+
+static int dma_fixed_dma_supported(struct device *dev, u64 mask)
+{
+ return mask == DMA_BIT_MASK(64);
}
+static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask);
+
+struct dma_map_ops dma_iommu_fixed_ops = {
+ .alloc = dma_fixed_alloc_coherent,
+ .free = dma_fixed_free_coherent,
+ .map_sg = dma_fixed_map_sg,
+ .unmap_sg = dma_fixed_unmap_sg,
+ .dma_supported = dma_fixed_dma_supported,
+ .set_dma_mask = dma_set_mask_and_switch,
+ .map_page = dma_fixed_map_page,
+ .unmap_page = dma_fixed_unmap_page,
+};
+
static void cell_dma_dev_setup_fixed(struct device *dev);
static void cell_dma_dev_setup(struct device *dev)
{
- struct dev_archdata *archdata = &dev->archdata;
-
/* Order is important here, these are not mutually exclusive */
if (get_dma_ops(dev) == &dma_iommu_fixed_ops)
cell_dma_dev_setup_fixed(dev);
else if (get_pci_dma_ops() == &dma_iommu_ops)
- cell_dma_dev_setup_iommu(dev);
+ set_iommu_table_base(dev, cell_get_iommu_table(dev));
else if (get_pci_dma_ops() == &dma_direct_ops)
- archdata->dma_data = (void *)cell_dma_direct_offset;
+ set_dma_offset(dev, cell_dma_direct_offset);
else
BUG();
}
@@ -609,7 +699,7 @@ static int __init cell_iommu_get_window(struct device_node *np,
unsigned long *base,
unsigned long *size)
{
- const void *dma_window;
+ const __be32 *dma_window;
unsigned long index;
/* Use ibm,dma-window if available, else, hard code ! */
@@ -640,7 +730,7 @@ static struct cbe_iommu * __init cell_iommu_alloc(struct device_node *np)
nid, np->full_name);
/* XXX todo: If we can have multiple windows on the same IOMMU, which
- * isn't the case today, we probably want here to check wether the
+ * isn't the case today, we probably want here to check whether the
* iommu for that node is already setup.
* However, there might be issue with getting the size right so let's
* ignore that for now. We might want to completely get rid of the
@@ -685,7 +775,7 @@ static void __init cell_iommu_init_one(struct device_node *np,
/* Setup the iommu_table */
cell_iommu_setup_window(iommu, np, base, size,
- offset >> IOMMU_PAGE_SHIFT);
+ offset >> IOMMU_PAGE_SHIFT_4K);
}
static void __init cell_disable_iommus(void)
@@ -756,10 +846,10 @@ static int __init cell_iommu_init_disabled(void)
/* If we found a DMA window, we check if it's big enough to enclose
* all of physical memory. If not, we force enable IOMMU
*/
- if (np && size < lmb_end_of_DRAM()) {
+ if (np && size < memblock_end_of_DRAM()) {
printk(KERN_WARNING "iommu: force-enabled, dma window"
- " (%ldMB) smaller than total memory (%ldMB)\n",
- size >> 20, lmb_end_of_DRAM() >> 20);
+ " (%ldMB) smaller than total memory (%lldMB)\n",
+ size >> 20, memblock_end_of_DRAM() >> 20);
return -ENODEV;
}
@@ -807,7 +897,7 @@ static u64 cell_iommu_get_fixed_address(struct device *dev)
const u32 *ranges = NULL;
int i, len, best, naddr, nsize, pna, range_size;
- np = of_node_get(dev->archdata.of_node);
+ np = of_node_get(dev->of_node);
while (1) {
naddr = of_n_addr_cells(np);
nsize = of_n_size_cells(np);
@@ -882,13 +972,12 @@ static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask)
static void cell_dma_dev_setup_fixed(struct device *dev)
{
- struct dev_archdata *archdata = &dev->archdata;
u64 addr;
addr = cell_iommu_get_fixed_address(dev) + dma_iommu_fixed_base;
- archdata->dma_data = (void *)addr;
+ set_dma_offset(dev, addr);
- dev_dbg(dev, "iommu: fixed addr = %lx\n", addr);
+ dev_dbg(dev, "iommu: fixed addr = %llx\n", addr);
}
static void insert_16M_pte(unsigned long addr, unsigned long *ptab,
@@ -903,7 +992,7 @@ static void insert_16M_pte(unsigned long addr, unsigned long *ptab,
pr_debug("iommu: addr %lx ptab %p segment %lx offset %lx\n",
addr, ptab, segment, offset);
- ptab[offset] = base_pte | (__pa(addr) & IOPTE_RPN_Mask);
+ ptab[offset] = base_pte | (__pa(addr) & CBE_IOPTE_RPN_Mask);
}
static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu,
@@ -918,8 +1007,15 @@ static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu,
pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase);
- base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW
- | (cell_iommu_get_ioid(np) & IOPTE_IOID_Mask);
+ base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M |
+ (cell_iommu_get_ioid(np) & CBE_IOPTE_IOID_Mask);
+
+ if (iommu_fixed_is_weak)
+ pr_info("IOMMU: Using weak ordering for fixed mapping\n");
+ else {
+ pr_info("IOMMU: Using strong ordering for fixed mapping\n");
+ base_pte |= CBE_IOPTE_SO_RW;
+ }
for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) {
/* Don't touch the dynamic region */
@@ -943,16 +1039,15 @@ static int __init cell_iommu_fixed_mapping_init(void)
/* The fixed mapping is only supported on axon machines */
np = of_find_node_by_name(NULL, "axon");
+ of_node_put(np);
+
if (!np) {
pr_debug("iommu: fixed mapping disabled, no axons found\n");
return -1;
}
/* We must have dma-ranges properties for fixed mapping to work */
- for (np = NULL; (np = of_find_all_nodes(np));) {
- if (of_find_property(np, "dma-ranges", NULL))
- break;
- }
+ np = of_find_node_with_property(NULL, "dma-ranges");
of_node_put(np);
if (!np) {
@@ -972,9 +1067,9 @@ static int __init cell_iommu_fixed_mapping_init(void)
}
fbase = _ALIGN_UP(fbase, 1 << IO_SEGMENT_SHIFT);
- fsize = lmb_phys_mem_size();
+ fsize = memblock_phys_mem_size();
- if ((fbase + fsize) <= 0x800000000)
+ if ((fbase + fsize) <= 0x800000000ul)
hbase = 0; /* use the device tree window */
else {
/* If we're over 32 GB we need to cheat. We can't map all of
@@ -1029,16 +1124,13 @@ static int __init cell_iommu_fixed_mapping_init(void)
cell_iommu_setup_stab(iommu, dbase, dsize, fbase, fsize);
iommu->ptab = cell_iommu_alloc_ptab(iommu, dbase, dsize, 0, 0,
- IOMMU_PAGE_SHIFT);
+ IOMMU_PAGE_SHIFT_4K);
cell_iommu_setup_fixed_ptab(iommu, np, dbase, dsize,
fbase, fsize);
cell_iommu_enable_hardware(iommu);
cell_iommu_setup_window(iommu, np, dbase, dsize, 0);
}
- dma_iommu_fixed_ops = dma_direct_ops;
- dma_iommu_fixed_ops.set_dma_mask = dma_set_mask_and_switch;
-
dma_iommu_ops.set_dma_mask = dma_set_mask_and_switch;
set_pci_dma_ops(&dma_iommu_ops);
@@ -1049,13 +1141,47 @@ static int iommu_fixed_disabled;
static int __init setup_iommu_fixed(char *str)
{
+ struct device_node *pciep;
+
if (strcmp(str, "off") == 0)
iommu_fixed_disabled = 1;
+ /* If we can find a pcie-endpoint in the device tree assume that
+ * we're on a triblade or a CAB so by default the fixed mapping
+ * should be set to be weakly ordered; but only if the boot
+ * option WASN'T set for strong ordering
+ */
+ pciep = of_find_node_by_type(NULL, "pcie-endpoint");
+
+ if (strcmp(str, "weak") == 0 || (pciep && strcmp(str, "strong") != 0))
+ iommu_fixed_is_weak = 1;
+
+ of_node_put(pciep);
+
return 1;
}
__setup("iommu_fixed=", setup_iommu_fixed);
+static u64 cell_dma_get_required_mask(struct device *dev)
+{
+ struct dma_map_ops *dma_ops;
+
+ if (!dev->dma_mask)
+ return 0;
+
+ if (!iommu_fixed_disabled &&
+ cell_iommu_get_fixed_address(dev) != OF_BAD_ADDR)
+ return DMA_BIT_MASK(64);
+
+ dma_ops = get_dma_ops(dev);
+ if (dma_ops->get_required_mask)
+ return dma_ops->get_required_mask(dev);
+
+ WARN_ONCE(1, "no get_required_mask in %p ops", dma_ops);
+
+ return DMA_BIT_MASK(64);
+}
+
static int __init cell_iommu_init(void)
{
struct device_node *np;
@@ -1066,12 +1192,13 @@ static int __init cell_iommu_init(void)
* Note: should we make sure we have the IOMMU actually disabled ?
*/
if (iommu_is_off ||
- (!iommu_force_on && lmb_end_of_DRAM() <= 0x80000000ull))
+ (!iommu_force_on && memblock_end_of_DRAM() <= 0x80000000ull))
if (cell_iommu_init_disabled() == 0)
goto bail;
/* Setup various ppc_md. callbacks */
ppc_md.pci_dma_dev_setup = cell_pci_dma_dev_setup;
+ ppc_md.dma_get_required_mask = cell_dma_get_required_mask;
ppc_md.tce_build = tce_build_cell;
ppc_md.tce_free = tce_free_cell;
@@ -1101,7 +1228,7 @@ static int __init cell_iommu_init(void)
/* Register callbacks on OF platform device addition/removal
* to handle linking them to the right DMA operations
*/
- bus_register_notifier(&of_platform_bus_type, &cell_of_bus_notifier);
+ bus_register_notifier(&platform_bus_type, &cell_of_bus_notifier);
return 0;
}
diff --git a/arch/powerpc/platforms/cell/pervasive.c b/arch/powerpc/platforms/cell/pervasive.c
index 0304589c0a8..d17e98bc0c1 100644
--- a/arch/powerpc/platforms/cell/pervasive.c
+++ b/arch/powerpc/platforms/cell/pervasive.c
@@ -38,17 +38,13 @@
#include "pervasive.h"
-static int sysreset_hack;
-
static void cbe_power_save(void)
{
unsigned long ctrl, thread_switch_control;
- /*
- * We need to hard disable interrupts, the local_irq_enable() done by
- * our caller upon return will hard re-enable.
- */
- hard_irq_disable();
+ /* Ensure our interrupt state is properly tracked */
+ if (!prep_irq_for_idle())
+ return;
ctrl = mfspr(SPRN_CTRLF);
@@ -65,7 +61,7 @@ static void cbe_power_save(void)
break;
default:
printk(KERN_WARNING "%s: unknown configuration\n",
- __FUNCTION__);
+ __func__);
break;
}
mtspr(SPRN_TSC_CELL, thread_switch_control);
@@ -83,13 +79,13 @@ static void cbe_power_save(void)
*/
ctrl &= ~(CTRL_RUNLATCH | CTRL_TE);
mtspr(SPRN_CTRLT, ctrl);
+
+ /* Re-enable interrupts in MSR */
+ __hard_irq_enable();
}
static int cbe_system_reset_exception(struct pt_regs *regs)
{
- int cpu;
- struct cbe_pmd_regs __iomem *pmd;
-
switch (regs->msr & SRR1_WAKEMASK) {
case SRR1_WAKEEE:
do_IRQ(regs);
@@ -98,19 +94,7 @@ static int cbe_system_reset_exception(struct pt_regs *regs)
timer_interrupt(regs);
break;
case SRR1_WAKEMT:
- /*
- * The BMC can inject user triggered system reset exceptions,
- * but cannot set the system reset reason in srr1,
- * so check an extra register here.
- */
- if (sysreset_hack && (cpu = smp_processor_id()) == 0) {
- pmd = cbe_get_cpu_pmd_regs(cpu);
- if (in_be64(&pmd->ras_esc_0) & 0xffff) {
- out_be64(&pmd->ras_esc_0, 0);
- return 0;
- }
- }
- break;
+ return cbe_sysreset_hack();
#ifdef CONFIG_CBE_RAS
case SRR1_WAKESYSERR:
cbe_system_error_exception(regs);
@@ -134,8 +118,6 @@ void __init cbe_pervasive_init(void)
if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO))
return;
- sysreset_hack = machine_is_compatible("IBM,CBPLUS-1.0");
-
for_each_possible_cpu(cpu) {
struct cbe_pmd_regs __iomem *regs = cbe_get_cpu_pmd_regs(cpu);
if (!regs)
@@ -144,12 +126,6 @@ void __init cbe_pervasive_init(void)
/* Enable Pause(0) control bit */
out_be64(&regs->pmcr, in_be64(&regs->pmcr) |
CBE_PMD_PAUSE_ZERO_CONTROL);
-
- /* Enable JTAG system-reset hack */
- if (sysreset_hack)
- out_be32(&regs->fir_mode_reg,
- in_be32(&regs->fir_mode_reg) |
- CBE_PMD_FIR_MODE_M8);
}
ppc_md.power_save = cbe_power_save;
diff --git a/arch/powerpc/platforms/cell/pervasive.h b/arch/powerpc/platforms/cell/pervasive.h
index 7b50947f804..fd4d7b7092b 100644
--- a/arch/powerpc/platforms/cell/pervasive.h
+++ b/arch/powerpc/platforms/cell/pervasive.h
@@ -30,4 +30,13 @@ extern void cbe_system_error_exception(struct pt_regs *regs);
extern void cbe_maintenance_exception(struct pt_regs *regs);
extern void cbe_thermal_exception(struct pt_regs *regs);
+#ifdef CONFIG_PPC_IBM_CELL_RESETBUTTON
+extern int cbe_sysreset_hack(void);
+#else
+static inline int cbe_sysreset_hack(void)
+{
+ return 1;
+}
+#endif /* CONFIG_PPC_IBM_CELL_RESETBUTTON */
+
#endif
diff --git a/arch/powerpc/platforms/cell/pmu.c b/arch/powerpc/platforms/cell/pmu.c
index 69ed0d7f164..348a27b1251 100644
--- a/arch/powerpc/platforms/cell/pmu.c
+++ b/arch/powerpc/platforms/cell/pmu.c
@@ -24,6 +24,7 @@
#include <linux/interrupt.h>
#include <linux/types.h>
+#include <linux/export.h>
#include <asm/io.h>
#include <asm/irq_regs.h>
#include <asm/machdep.h>
@@ -381,7 +382,7 @@ static int __init cbe_init_pm_irq(void)
unsigned int irq;
int rc, node;
- for_each_node(node) {
+ for_each_online_node(node) {
irq = irq_create_mapping(NULL, IIC_IRQ_IOEX_PMI |
(node << IIC_IRQ_NODE_SHIFT));
if (irq == NO_IRQ) {
@@ -391,7 +392,7 @@ static int __init cbe_init_pm_irq(void)
}
rc = request_irq(irq, cbe_pm_irq,
- IRQF_DISABLED, "cbe-pmu-0", NULL);
+ 0, "cbe-pmu-0", NULL);
if (rc) {
printk("ERROR: Request for irq on node %d failed\n",
node);
diff --git a/arch/powerpc/platforms/cell/qpace_setup.c b/arch/powerpc/platforms/cell/qpace_setup.c
new file mode 100644
index 00000000000..6e3409d590a
--- /dev/null
+++ b/arch/powerpc/platforms/cell/qpace_setup.c
@@ -0,0 +1,148 @@
+/*
+ * linux/arch/powerpc/platforms/cell/qpace_setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Adapted from 'alpha' version by Gary Thomas
+ * Modified by Cort Dougan (cort@cs.nmt.edu)
+ * Modified by PPC64 Team, IBM Corp
+ * Modified by Cell Team, IBM Deutschland Entwicklung GmbH
+ * Modified by Benjamin Krill <ben@codiert.org>, IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/console.h>
+#include <linux/of_platform.h>
+
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <asm/kexec.h>
+#include <asm/pgtable.h>
+#include <asm/prom.h>
+#include <asm/rtas.h>
+#include <asm/dma.h>
+#include <asm/machdep.h>
+#include <asm/time.h>
+#include <asm/cputable.h>
+#include <asm/irq.h>
+#include <asm/spu.h>
+#include <asm/spu_priv1.h>
+#include <asm/udbg.h>
+#include <asm/cell-regs.h>
+
+#include "interrupt.h"
+#include "pervasive.h"
+#include "ras.h"
+
+static void qpace_show_cpuinfo(struct seq_file *m)
+{
+ struct device_node *root;
+ const char *model = "";
+
+ root = of_find_node_by_path("/");
+ if (root)
+ model = of_get_property(root, "model", NULL);
+ seq_printf(m, "machine\t\t: CHRP %s\n", model);
+ of_node_put(root);
+}
+
+static void qpace_progress(char *s, unsigned short hex)
+{
+ printk("*** %04x : %s\n", hex, s ? s : "");
+}
+
+static const struct of_device_id qpace_bus_ids[] __initconst = {
+ { .type = "soc", },
+ { .compatible = "soc", },
+ { .type = "spider", },
+ { .type = "axon", },
+ { .type = "plb5", },
+ { .type = "plb4", },
+ { .type = "opb", },
+ { .type = "ebc", },
+ {},
+};
+
+static int __init qpace_publish_devices(void)
+{
+ int node;
+
+ /* Publish OF platform devices for southbridge IOs */
+ of_platform_bus_probe(NULL, qpace_bus_ids, NULL);
+
+ /* There is no device for the MIC memory controller, thus we create
+ * a platform device for it to attach the EDAC driver to.
+ */
+ for_each_online_node(node) {
+ if (cbe_get_cpu_mic_tm_regs(cbe_node_to_cpu(node)) == NULL)
+ continue;
+ platform_device_register_simple("cbe-mic", node, NULL, 0);
+ }
+
+ return 0;
+}
+machine_subsys_initcall(qpace, qpace_publish_devices);
+
+static void __init qpace_setup_arch(void)
+{
+#ifdef CONFIG_SPU_BASE
+ spu_priv1_ops = &spu_priv1_mmio_ops;
+ spu_management_ops = &spu_management_of_ops;
+#endif
+
+ cbe_regs_init();
+
+#ifdef CONFIG_CBE_RAS
+ cbe_ras_init();
+#endif
+
+#ifdef CONFIG_SMP
+ smp_init_cell();
+#endif
+
+ /* init to some ~sane value until calibrate_delay() runs */
+ loops_per_jiffy = 50000000;
+
+ cbe_pervasive_init();
+#ifdef CONFIG_DUMMY_CONSOLE
+ conswitchp = &dummy_con;
+#endif
+}
+
+static int __init qpace_probe(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ if (!of_flat_dt_is_compatible(root, "IBM,QPACE"))
+ return 0;
+
+ hpte_init_native();
+
+ return 1;
+}
+
+define_machine(qpace) {
+ .name = "QPACE",
+ .probe = qpace_probe,
+ .setup_arch = qpace_setup_arch,
+ .show_cpuinfo = qpace_show_cpuinfo,
+ .restart = rtas_restart,
+ .power_off = rtas_power_off,
+ .halt = rtas_halt,
+ .get_boot_time = rtas_get_boot_time,
+ .get_rtc_time = rtas_get_rtc_time,
+ .set_rtc_time = rtas_set_rtc_time,
+ .calibrate_decr = generic_calibrate_decr,
+ .progress = qpace_progress,
+ .init_IRQ = iic_init_IRQ,
+};
diff --git a/arch/powerpc/platforms/cell/ras.c b/arch/powerpc/platforms/cell/ras.c
index e43024c0392..e865d748179 100644
--- a/arch/powerpc/platforms/cell/ras.c
+++ b/arch/powerpc/platforms/cell/ras.c
@@ -11,9 +11,13 @@
#include <linux/types.h>
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/reboot.h>
+#include <linux/kexec.h>
+#include <linux/crash_dump.h>
+#include <asm/kexec.h>
#include <asm/reg.h>
#include <asm/io.h>
#include <asm/prom.h>
@@ -35,16 +39,16 @@ static void dump_fir(int cpu)
/* Todo: do some nicer parsing of bits and based on them go down
* to other sub-units FIRs and not only IIC
*/
- printk(KERN_ERR "Global Checkstop FIR : 0x%016lx\n",
+ printk(KERN_ERR "Global Checkstop FIR : 0x%016llx\n",
in_be64(&pregs->checkstop_fir));
- printk(KERN_ERR "Global Recoverable FIR : 0x%016lx\n",
+ printk(KERN_ERR "Global Recoverable FIR : 0x%016llx\n",
in_be64(&pregs->checkstop_fir));
- printk(KERN_ERR "Global MachineCheck FIR : 0x%016lx\n",
+ printk(KERN_ERR "Global MachineCheck FIR : 0x%016llx\n",
in_be64(&pregs->spec_att_mchk_fir));
if (iregs == NULL)
return;
- printk(KERN_ERR "IOC FIR : 0x%016lx\n",
+ printk(KERN_ERR "IOC FIR : 0x%016llx\n",
in_be64(&iregs->ioc_fir));
}
@@ -110,9 +114,8 @@ static int __init cbe_ptcal_enable_on_node(int nid, int order)
int ret = -ENOMEM;
unsigned long addr;
-#ifdef CONFIG_CRASH_DUMP
- rtas_call(ptcal_stop_tok, 1, 1, NULL, nid);
-#endif
+ if (is_kdump_kernel())
+ rtas_call(ptcal_stop_tok, 1, 1, NULL, nid);
area = kmalloc(sizeof(*area), GFP_KERNEL);
if (!area)
@@ -120,19 +123,31 @@ static int __init cbe_ptcal_enable_on_node(int nid, int order)
area->nid = nid;
area->order = order;
- area->pages = alloc_pages_node(area->nid, GFP_KERNEL, area->order);
+ area->pages = alloc_pages_exact_node(area->nid,
+ GFP_KERNEL|__GFP_THISNODE,
+ area->order);
- if (!area->pages)
+ if (!area->pages) {
+ printk(KERN_WARNING "%s: no page on node %d\n",
+ __func__, area->nid);
goto out_free_area;
+ }
- addr = __pa(page_address(area->pages));
+ /*
+ * We move the ptcal area to the middle of the allocated
+ * page, in order to avoid prefetches in memcpy and similar
+ * functions stepping on it.
+ */
+ addr = __pa(page_address(area->pages)) + (PAGE_SIZE >> 1);
+ printk(KERN_DEBUG "%s: enabling PTCAL on node %d address=0x%016lx\n",
+ __func__, area->nid, addr);
ret = -EIO;
if (rtas_call(ptcal_start_tok, 3, 1, NULL, area->nid,
(unsigned int)(addr >> 32),
(unsigned int)(addr & 0xffffffff))) {
printk(KERN_ERR "%s: error enabling PTCAL on node %d!\n",
- __FUNCTION__, nid);
+ __func__, nid);
goto out_free_pages;
}
@@ -159,10 +174,12 @@ static int __init cbe_ptcal_enable(void)
return -ENODEV;
size = of_get_property(np, "ibm,cbe-ptcal-size", NULL);
- if (!size)
+ if (!size) {
+ of_node_put(np);
return -ENODEV;
+ }
- pr_debug("%s: enabling PTCAL, size = 0x%x\n", __FUNCTION__, *size);
+ pr_debug("%s: enabling PTCAL, size = 0x%x\n", __func__, *size);
order = get_order(*size);
of_node_put(np);
@@ -180,7 +197,7 @@ static int __init cbe_ptcal_enable(void)
const u32 *nid = of_get_property(np, "node-id", NULL);
if (!nid) {
printk(KERN_ERR "%s: node %s is missing node-id?\n",
- __FUNCTION__, np->full_name);
+ __func__, np->full_name);
continue;
}
cbe_ptcal_enable_on_node(*nid, order);
@@ -195,13 +212,13 @@ static int cbe_ptcal_disable(void)
struct ptcal_area *area, *tmp;
int ret = 0;
- pr_debug("%s: disabling PTCAL\n", __FUNCTION__);
+ pr_debug("%s: disabling PTCAL\n", __func__);
list_for_each_entry_safe(area, tmp, &ptcal_list, list) {
/* disable ptcal on this node */
if (rtas_call(ptcal_stop_tok, 1, 1, NULL, area->nid)) {
printk(KERN_ERR "%s: error disabling PTCAL "
- "on node %d!\n", __FUNCTION__,
+ "on node %d!\n", __func__,
area->nid);
ret = -EIO;
continue;
@@ -226,10 +243,61 @@ static int cbe_ptcal_notify_reboot(struct notifier_block *nb,
return cbe_ptcal_disable();
}
+static void cbe_ptcal_crash_shutdown(void)
+{
+ cbe_ptcal_disable();
+}
+
static struct notifier_block cbe_ptcal_reboot_notifier = {
.notifier_call = cbe_ptcal_notify_reboot
};
+#ifdef CONFIG_PPC_IBM_CELL_RESETBUTTON
+static int sysreset_hack;
+
+static int __init cbe_sysreset_init(void)
+{
+ struct cbe_pmd_regs __iomem *regs;
+
+ sysreset_hack = of_machine_is_compatible("IBM,CBPLUS-1.0");
+ if (!sysreset_hack)
+ return 0;
+
+ regs = cbe_get_cpu_pmd_regs(0);
+ if (!regs)
+ return 0;
+
+ /* Enable JTAG system-reset hack */
+ out_be32(&regs->fir_mode_reg,
+ in_be32(&regs->fir_mode_reg) |
+ CBE_PMD_FIR_MODE_M8);
+
+ return 0;
+}
+device_initcall(cbe_sysreset_init);
+
+int cbe_sysreset_hack(void)
+{
+ struct cbe_pmd_regs __iomem *regs;
+
+ /*
+ * The BMC can inject user triggered system reset exceptions,
+ * but cannot set the system reset reason in srr1,
+ * so check an extra register here.
+ */
+ if (sysreset_hack && (smp_processor_id() == 0)) {
+ regs = cbe_get_cpu_pmd_regs(0);
+ if (!regs)
+ return 0;
+ if (in_be64(&regs->ras_esc_0) & 0x0000ffff) {
+ out_be64(&regs->ras_esc_0, 0);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif /* CONFIG_PPC_IBM_CELL_RESETBUTTON */
+
int __init cbe_ptcal_init(void)
{
int ret;
@@ -241,12 +309,20 @@ int __init cbe_ptcal_init(void)
return -ENODEV;
ret = register_reboot_notifier(&cbe_ptcal_reboot_notifier);
- if (ret) {
- printk(KERN_ERR "Can't disable PTCAL, so not enabling\n");
- return ret;
- }
+ if (ret)
+ goto out1;
+
+ ret = crash_shutdown_register(&cbe_ptcal_crash_shutdown);
+ if (ret)
+ goto out2;
return cbe_ptcal_enable();
+
+out2:
+ unregister_reboot_notifier(&cbe_ptcal_reboot_notifier);
+out1:
+ printk(KERN_ERR "Can't disable PTCAL, so not enabling\n");
+ return ret;
}
arch_initcall(cbe_ptcal_init);
diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c
index 5c531e8f9f6..6ae25fb6201 100644
--- a/arch/powerpc/platforms/cell/setup.c
+++ b/arch/powerpc/platforms/cell/setup.c
@@ -18,8 +18,8 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
+#include <linux/export.h>
#include <linux/unistd.h>
-#include <linux/slab.h>
#include <linux/user.h>
#include <linux/reboot.h>
#include <linux/init.h>
@@ -35,7 +35,6 @@
#include <asm/mmu.h>
#include <asm/processor.h>
#include <asm/io.h>
-#include <asm/kexec.h>
#include <asm/pgtable.h>
#include <asm/prom.h>
#include <asm/rtas.h>
@@ -53,6 +52,7 @@
#include <asm/udbg.h>
#include <asm/mpic.h>
#include <asm/cell-regs.h>
+#include <asm/io-workarounds.h>
#include "interrupt.h"
#include "pervasive.h"
@@ -117,12 +117,59 @@ static void cell_fixup_pcie_rootcomplex(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, cell_fixup_pcie_rootcomplex);
+static int cell_setup_phb(struct pci_controller *phb)
+{
+ const char *model;
+ struct device_node *np;
+
+ int rc = rtas_setup_phb(phb);
+ if (rc)
+ return rc;
+
+ np = phb->dn;
+ model = of_get_property(np, "model", NULL);
+ if (model == NULL || strcmp(np->name, "pci"))
+ return 0;
+
+ /* Setup workarounds for spider */
+ if (strcmp(model, "Spider"))
+ return 0;
+
+ iowa_register_bus(phb, &spiderpci_ops, &spiderpci_iowa_init,
+ (void *)SPIDER_PCI_REG_BASE);
+ return 0;
+}
+
+static const struct of_device_id cell_bus_ids[] __initconst = {
+ { .type = "soc", },
+ { .compatible = "soc", },
+ { .type = "spider", },
+ { .type = "axon", },
+ { .type = "plb5", },
+ { .type = "plb4", },
+ { .type = "opb", },
+ { .type = "ebc", },
+ {},
+};
+
static int __init cell_publish_devices(void)
{
+ struct device_node *root = of_find_node_by_path("/");
+ struct device_node *np;
int node;
/* Publish OF platform devices for southbridge IOs */
- of_platform_bus_probe(NULL, NULL, NULL);
+ of_platform_bus_probe(NULL, cell_bus_ids, NULL);
+
+ /* On spider based blades, we need to manually create the OF
+ * platform devices for the PCI host bridges
+ */
+ for_each_child_of_node(root, np) {
+ if (np->type == NULL || (strcmp(np->type, "pci") != 0 &&
+ strcmp(np->type, "pciex") != 0))
+ continue;
+ of_platform_device_create(np, NULL, NULL);
+ }
/* There is no device for the MIC memory controller, thus we create
* a platform device for it to attach the EDAC driver to.
@@ -132,26 +179,15 @@ static int __init cell_publish_devices(void)
continue;
platform_device_register_simple("cbe-mic", node, NULL, 0);
}
+
return 0;
}
machine_subsys_initcall(cell, cell_publish_devices);
-static void cell_mpic_cascade(unsigned int irq, struct irq_desc *desc)
-{
- struct mpic *mpic = desc->handler_data;
- unsigned int virq;
-
- virq = mpic_get_one_irq(mpic);
- if (virq != NO_IRQ)
- generic_handle_irq(virq);
- desc->chip->eoi(irq);
-}
-
static void __init mpic_init_IRQ(void)
{
struct device_node *dn;
struct mpic *mpic;
- unsigned int virq;
for (dn = NULL;
(dn = of_find_node_by_name(dn, "interrupt-controller"));) {
@@ -161,19 +197,11 @@ static void __init mpic_init_IRQ(void)
/* The MPIC driver will get everything it needs from the
* device-tree, just pass 0 to all arguments
*/
- mpic = mpic_alloc(dn, 0, 0, 0, 0, " MPIC ");
+ mpic = mpic_alloc(dn, 0, MPIC_SECONDARY | MPIC_NO_RESET,
+ 0, 0, " MPIC ");
if (mpic == NULL)
continue;
mpic_init(mpic);
-
- virq = irq_of_parse_and_map(dn, 0);
- if (virq == NO_IRQ)
- continue;
-
- printk(KERN_INFO "%s : hooking up to IRQ %d\n",
- dn->full_name, virq);
- set_irq_data(virq, mpic);
- set_irq_chained_handler(virq, cell_mpic_cascade);
}
}
@@ -213,7 +241,7 @@ static void __init cell_setup_arch(void)
/* Find and initialize PCI host bridges */
init_pci_config_tokens();
- find_and_init_phbs();
+
cbe_pervasive_init();
#ifdef CONFIG_DUMMY_CONSOLE
conswitchp = &dummy_con;
@@ -249,10 +277,5 @@ define_machine(cell) {
.calibrate_decr = generic_calibrate_decr,
.progress = cell_progress,
.init_IRQ = cell_init_irq,
- .pci_setup_phb = rtas_setup_phb,
-#ifdef CONFIG_KEXEC
- .machine_kexec = default_machine_kexec,
- .machine_kexec_prepare = default_machine_kexec_prepare,
- .machine_crash_shutdown = default_machine_crash_shutdown,
-#endif
+ .pci_setup_phb = cell_setup_phb,
};
diff --git a/arch/powerpc/platforms/cell/smp.c b/arch/powerpc/platforms/cell/smp.c
index efb3964457b..c8017a7bcab 100644
--- a/arch/powerpc/platforms/cell/smp.c
+++ b/arch/powerpc/platforms/cell/smp.c
@@ -15,7 +15,6 @@
#undef DEBUG
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
@@ -24,11 +23,11 @@
#include <linux/spinlock.h>
#include <linux/cache.h>
#include <linux/err.h>
-#include <linux/sysdev.h>
+#include <linux/device.h>
#include <linux/cpu.h>
#include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/irq.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -36,13 +35,12 @@
#include <asm/prom.h>
#include <asm/smp.h>
#include <asm/paca.h>
-#include <asm/time.h>
#include <asm/machdep.h>
#include <asm/cputable.h>
#include <asm/firmware.h>
-#include <asm/system.h>
#include <asm/rtas.h>
#include <asm/cputhreads.h>
+#include <asm/code-patching.h>
#include "interrupt.h"
#include <asm/udbg.h>
@@ -54,13 +52,11 @@
#endif
/*
- * The primary thread of each non-boot processor is recorded here before
- * smp init.
+ * The Primary thread of each non-boot processor was started from the OF client
+ * interface by prom_hold_cpus and is spinning on secondary_hold_spinloop.
*/
static cpumask_t of_spin_map;
-extern void generic_secondary_smp_init(unsigned long);
-
/**
* smp_startup_cpu() - start the given cpu
*
@@ -72,15 +68,15 @@ extern void generic_secondary_smp_init(unsigned long);
* 0 - failure
* 1 - success
*/
-static inline int __devinit smp_startup_cpu(unsigned int lcpu)
+static inline int smp_startup_cpu(unsigned int lcpu)
{
int status;
- unsigned long start_here = __pa((u32)*((unsigned long *)
- generic_secondary_smp_init));
+ unsigned long start_here =
+ __pa(ppc_function_entry(generic_secondary_smp_init));
unsigned int pcpu;
int start_cpu;
- if (cpu_isset(lcpu, of_spin_map))
+ if (cpumask_test_cpu(lcpu, &of_spin_map))
/* Already started by OF and sitting in spin loop */
return 1;
@@ -106,66 +102,30 @@ static inline int __devinit smp_startup_cpu(unsigned int lcpu)
return 1;
}
-static void smp_iic_message_pass(int target, int msg)
-{
- unsigned int i;
-
- if (target < NR_CPUS) {
- iic_cause_IPI(target, msg);
- } else {
- for_each_online_cpu(i) {
- if (target == MSG_ALL_BUT_SELF
- && i == smp_processor_id())
- continue;
- iic_cause_IPI(i, msg);
- }
- }
-}
-
static int __init smp_iic_probe(void)
{
iic_request_IPIs();
- return cpus_weight(cpu_possible_map);
+ return cpumask_weight(cpu_possible_mask);
}
-static void __devinit smp_iic_setup_cpu(int cpu)
+static void smp_cell_setup_cpu(int cpu)
{
if (cpu != boot_cpuid)
iic_setup_cpu();
-}
-
-static DEFINE_SPINLOCK(timebase_lock);
-static unsigned long timebase = 0;
-
-static void __devinit cell_give_timebase(void)
-{
- spin_lock(&timebase_lock);
- rtas_call(rtas_token("freeze-time-base"), 0, 1, NULL);
- timebase = get_tb();
- spin_unlock(&timebase_lock);
-
- while (timebase)
- barrier();
- rtas_call(rtas_token("thaw-time-base"), 0, 1, NULL);
-}
-static void __devinit cell_take_timebase(void)
-{
- while (!timebase)
- barrier();
- spin_lock(&timebase_lock);
- set_tb(timebase >> 32, timebase & 0xffffffff);
- timebase = 0;
- spin_unlock(&timebase_lock);
+ /*
+ * change default DABRX to allow user watchpoints
+ */
+ mtspr(SPRN_DABRX, DABRX_KERNEL | DABRX_USER);
}
-static void __devinit smp_cell_kick_cpu(int nr)
+static int smp_cell_kick_cpu(int nr)
{
BUG_ON(nr < 0 || nr >= NR_CPUS);
if (!smp_startup_cpu(nr))
- return;
+ return -ENOENT;
/*
* The processor is currently spinning, waiting for the
@@ -173,27 +133,16 @@ static void __devinit smp_cell_kick_cpu(int nr)
* the processor will continue on to secondary_start
*/
paca[nr].cpu_start = 1;
-}
-static int smp_cell_cpu_bootable(unsigned int nr)
-{
- /* Special case - we inhibit secondary thread startup
- * during boot if the user requests it. Odd-numbered
- * cpus are assumed to be secondary threads.
- */
- if (system_state < SYSTEM_RUNNING &&
- cpu_has_feature(CPU_FTR_SMT) &&
- !smt_enabled_at_boot && cpu_thread_in_core(nr) != 0)
- return 0;
-
- return 1;
+ return 0;
}
+
static struct smp_ops_t bpa_iic_smp_ops = {
- .message_pass = smp_iic_message_pass,
+ .message_pass = iic_message_pass,
.probe = smp_iic_probe,
.kick_cpu = smp_cell_kick_cpu,
- .setup_cpu = smp_iic_setup_cpu,
- .cpu_bootable = smp_cell_cpu_bootable,
+ .setup_cpu = smp_cell_setup_cpu,
+ .cpu_bootable = smp_generic_cpu_bootable,
};
/* This is called very early */
@@ -208,23 +157,18 @@ void __init smp_init_cell(void)
/* Mark threads which are still spinning in hold loops. */
if (cpu_has_feature(CPU_FTR_SMT)) {
for_each_present_cpu(i) {
- if (i % 2 == 0)
- /*
- * Even-numbered logical cpus correspond to
- * primary threads.
- */
- cpu_set(i, of_spin_map);
+ if (cpu_thread_in_core(i) == 0)
+ cpumask_set_cpu(i, &of_spin_map);
}
- } else {
- of_spin_map = cpu_present_map;
- }
+ } else
+ cpumask_copy(&of_spin_map, cpu_present_mask);
- cpu_clear(boot_cpuid, of_spin_map);
+ cpumask_clear_cpu(boot_cpuid, &of_spin_map);
/* Non-lpar has additional take/give timebase */
if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) {
- smp_ops->give_timebase = cell_give_timebase;
- smp_ops->take_timebase = cell_take_timebase;
+ smp_ops->give_timebase = rtas_give_timebase;
+ smp_ops->take_timebase = rtas_take_timebase;
}
DBG(" <- smp_init_cell()\n");
diff --git a/arch/powerpc/platforms/cell/spider-pci.c b/arch/powerpc/platforms/cell/spider-pci.c
new file mode 100644
index 00000000000..f1f7878893f
--- /dev/null
+++ b/arch/powerpc/platforms/cell/spider-pci.c
@@ -0,0 +1,184 @@
+/*
+ * IO workarounds for PCI on Celleb/Cell platform
+ *
+ * (C) Copyright 2006-2007 TOSHIBA 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <asm/ppc-pci.h>
+#include <asm/pci-bridge.h>
+#include <asm/io-workarounds.h>
+
+#define SPIDER_PCI_DISABLE_PREFETCH
+
+struct spiderpci_iowa_private {
+ void __iomem *regs;
+};
+
+static void spiderpci_io_flush(struct iowa_bus *bus)
+{
+ struct spiderpci_iowa_private *priv;
+ u32 val;
+
+ priv = bus->private;
+ val = in_be32(priv->regs + SPIDER_PCI_DUMMY_READ);
+ iosync();
+}
+
+#define SPIDER_PCI_MMIO_READ(name, ret) \
+static ret spiderpci_##name(const PCI_IO_ADDR addr) \
+{ \
+ ret val = __do_##name(addr); \
+ spiderpci_io_flush(iowa_mem_find_bus(addr)); \
+ return val; \
+}
+
+#define SPIDER_PCI_MMIO_READ_STR(name) \
+static void spiderpci_##name(const PCI_IO_ADDR addr, void *buf, \
+ unsigned long count) \
+{ \
+ __do_##name(addr, buf, count); \
+ spiderpci_io_flush(iowa_mem_find_bus(addr)); \
+}
+
+SPIDER_PCI_MMIO_READ(readb, u8)
+SPIDER_PCI_MMIO_READ(readw, u16)
+SPIDER_PCI_MMIO_READ(readl, u32)
+SPIDER_PCI_MMIO_READ(readq, u64)
+SPIDER_PCI_MMIO_READ(readw_be, u16)
+SPIDER_PCI_MMIO_READ(readl_be, u32)
+SPIDER_PCI_MMIO_READ(readq_be, u64)
+SPIDER_PCI_MMIO_READ_STR(readsb)
+SPIDER_PCI_MMIO_READ_STR(readsw)
+SPIDER_PCI_MMIO_READ_STR(readsl)
+
+static void spiderpci_memcpy_fromio(void *dest, const PCI_IO_ADDR src,
+ unsigned long n)
+{
+ __do_memcpy_fromio(dest, src, n);
+ spiderpci_io_flush(iowa_mem_find_bus(src));
+}
+
+static int __init spiderpci_pci_setup_chip(struct pci_controller *phb,
+ void __iomem *regs)
+{
+ void *dummy_page_va;
+ dma_addr_t dummy_page_da;
+
+#ifdef SPIDER_PCI_DISABLE_PREFETCH
+ u32 val = in_be32(regs + SPIDER_PCI_VCI_CNTL_STAT);
+ pr_debug("SPIDER_IOWA:PVCI_Control_Status was 0x%08x\n", val);
+ out_be32(regs + SPIDER_PCI_VCI_CNTL_STAT, val | 0x8);
+#endif /* SPIDER_PCI_DISABLE_PREFETCH */
+
+ /* setup dummy read */
+ /*
+ * On CellBlade, we can't know that which XDR memory is used by
+ * kmalloc() to allocate dummy_page_va.
+ * In order to imporve the performance, the XDR which is used to
+ * allocate dummy_page_va is the nearest the spider-pci.
+ * We have to select the CBE which is the nearest the spider-pci
+ * to allocate memory from the best XDR, but I don't know that
+ * how to do.
+ *
+ * Celleb does not have this problem, because it has only one XDR.
+ */
+ dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!dummy_page_va) {
+ pr_err("SPIDERPCI-IOWA:Alloc dummy_page_va failed.\n");
+ return -1;
+ }
+
+ dummy_page_da = dma_map_single(phb->parent, dummy_page_va,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(phb->parent, dummy_page_da)) {
+ pr_err("SPIDER-IOWA:Map dummy page filed.\n");
+ kfree(dummy_page_va);
+ return -1;
+ }
+
+ out_be32(regs + SPIDER_PCI_DUMMY_READ_BASE, dummy_page_da);
+
+ return 0;
+}
+
+int __init spiderpci_iowa_init(struct iowa_bus *bus, void *data)
+{
+ void __iomem *regs = NULL;
+ struct spiderpci_iowa_private *priv;
+ struct device_node *np = bus->phb->dn;
+ struct resource r;
+ unsigned long offset = (unsigned long)data;
+
+ pr_debug("SPIDERPCI-IOWA:Bus initialize for spider(%s)\n",
+ np->full_name);
+
+ priv = kzalloc(sizeof(struct spiderpci_iowa_private), GFP_KERNEL);
+ if (!priv) {
+ pr_err("SPIDERPCI-IOWA:"
+ "Can't allocate struct spiderpci_iowa_private");
+ return -1;
+ }
+
+ if (of_address_to_resource(np, 0, &r)) {
+ pr_err("SPIDERPCI-IOWA:Can't get resource.\n");
+ goto error;
+ }
+
+ regs = ioremap(r.start + offset, SPIDER_PCI_REG_SIZE);
+ if (!regs) {
+ pr_err("SPIDERPCI-IOWA:ioremap failed.\n");
+ goto error;
+ }
+ priv->regs = regs;
+ bus->private = priv;
+
+ if (spiderpci_pci_setup_chip(bus->phb, regs))
+ goto error;
+
+ return 0;
+
+error:
+ kfree(priv);
+ bus->private = NULL;
+
+ if (regs)
+ iounmap(regs);
+
+ return -1;
+}
+
+struct ppc_pci_io spiderpci_ops = {
+ .readb = spiderpci_readb,
+ .readw = spiderpci_readw,
+ .readl = spiderpci_readl,
+ .readq = spiderpci_readq,
+ .readw_be = spiderpci_readw_be,
+ .readl_be = spiderpci_readl_be,
+ .readq_be = spiderpci_readq_be,
+ .readsb = spiderpci_readsb,
+ .readsw = spiderpci_readsw,
+ .readsl = spiderpci_readsl,
+ .memcpy_fromio = spiderpci_memcpy_fromio,
+};
+
diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c
index 3f4b4aef756..1f72f4ab635 100644
--- a/arch/powerpc/platforms/cell/spider-pic.c
+++ b/arch/powerpc/platforms/cell/spider-pic.c
@@ -62,15 +62,15 @@ enum {
#define SPIDER_IRQ_INVALID 63
struct spider_pic {
- struct irq_host *host;
+ struct irq_domain *host;
void __iomem *regs;
unsigned int node_id;
};
static struct spider_pic spider_pics[SPIDER_CHIP_COUNT];
-static struct spider_pic *spider_virq_to_pic(unsigned int virq)
+static struct spider_pic *spider_irq_data_to_pic(struct irq_data *d)
{
- return irq_map[virq].host->host_data;
+ return irq_data_get_irq_chip_data(d);
}
static void __iomem *spider_get_irq_config(struct spider_pic *pic,
@@ -79,30 +79,30 @@ static void __iomem *spider_get_irq_config(struct spider_pic *pic,
return pic->regs + TIR_CFGA + 8 * src;
}
-static void spider_unmask_irq(unsigned int virq)
+static void spider_unmask_irq(struct irq_data *d)
{
- struct spider_pic *pic = spider_virq_to_pic(virq);
- void __iomem *cfg = spider_get_irq_config(pic, irq_map[virq].hwirq);
+ struct spider_pic *pic = spider_irq_data_to_pic(d);
+ void __iomem *cfg = spider_get_irq_config(pic, irqd_to_hwirq(d));
out_be32(cfg, in_be32(cfg) | 0x30000000u);
}
-static void spider_mask_irq(unsigned int virq)
+static void spider_mask_irq(struct irq_data *d)
{
- struct spider_pic *pic = spider_virq_to_pic(virq);
- void __iomem *cfg = spider_get_irq_config(pic, irq_map[virq].hwirq);
+ struct spider_pic *pic = spider_irq_data_to_pic(d);
+ void __iomem *cfg = spider_get_irq_config(pic, irqd_to_hwirq(d));
out_be32(cfg, in_be32(cfg) & ~0x30000000u);
}
-static void spider_ack_irq(unsigned int virq)
+static void spider_ack_irq(struct irq_data *d)
{
- struct spider_pic *pic = spider_virq_to_pic(virq);
- unsigned int src = irq_map[virq].hwirq;
+ struct spider_pic *pic = spider_irq_data_to_pic(d);
+ unsigned int src = irqd_to_hwirq(d);
/* Reset edge detection logic if necessary
*/
- if (get_irq_desc(virq)->status & IRQ_LEVEL)
+ if (irqd_is_level_type(d))
return;
/* Only interrupts 47 to 50 can be set to edge */
@@ -113,13 +113,12 @@ static void spider_ack_irq(unsigned int virq)
out_be32(pic->regs + TIR_EDC, 0x100 | (src & 0xf));
}
-static int spider_set_irq_type(unsigned int virq, unsigned int type)
+static int spider_set_irq_type(struct irq_data *d, unsigned int type)
{
unsigned int sense = type & IRQ_TYPE_SENSE_MASK;
- struct spider_pic *pic = spider_virq_to_pic(virq);
- unsigned int hw = irq_map[virq].hwirq;
+ struct spider_pic *pic = spider_irq_data_to_pic(d);
+ unsigned int hw = irqd_to_hwirq(d);
void __iomem *cfg = spider_get_irq_config(pic, hw);
- struct irq_desc *desc = get_irq_desc(virq);
u32 old_mask;
u32 ic;
@@ -147,15 +146,9 @@ static int spider_set_irq_type(unsigned int virq, unsigned int type)
return -EINVAL;
}
- /* Update irq_desc */
- desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
- desc->status |= type & IRQ_TYPE_SENSE_MASK;
- if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
- desc->status |= IRQ_LEVEL;
-
/* Configure the source. One gross hack that was there before and
* that I've kept around is the priority to the BE which I set to
- * be the same as the interrupt source number. I don't know wether
+ * be the same as the interrupt source number. I don't know whether
* that's supposed to make any kind of sense however, we'll have to
* decide that, but for now, I'm not changing the behaviour.
*/
@@ -168,26 +161,27 @@ static int spider_set_irq_type(unsigned int virq, unsigned int type)
}
static struct irq_chip spider_pic = {
- .typename = " SPIDER ",
- .unmask = spider_unmask_irq,
- .mask = spider_mask_irq,
- .ack = spider_ack_irq,
- .set_type = spider_set_irq_type,
+ .name = "SPIDER",
+ .irq_unmask = spider_unmask_irq,
+ .irq_mask = spider_mask_irq,
+ .irq_ack = spider_ack_irq,
+ .irq_set_type = spider_set_irq_type,
};
-static int spider_host_map(struct irq_host *h, unsigned int virq,
+static int spider_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
- set_irq_chip_and_handler(virq, &spider_pic, handle_level_irq);
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &spider_pic, handle_level_irq);
/* Set default irq type */
- set_irq_type(virq, IRQ_TYPE_NONE);
+ irq_set_irq_type(virq, IRQ_TYPE_NONE);
return 0;
}
-static int spider_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
+static int spider_host_xlate(struct irq_domain *h, struct device_node *ct,
+ const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
{
@@ -200,14 +194,15 @@ static int spider_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
-static struct irq_host_ops spider_host_ops = {
+static const struct irq_domain_ops spider_host_ops = {
.map = spider_host_map,
.xlate = spider_host_xlate,
};
static void spider_irq_cascade(unsigned int irq, struct irq_desc *desc)
{
- struct spider_pic *pic = desc->handler_data;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct spider_pic *pic = irq_desc_get_handler_data(desc);
unsigned int cs, virq;
cs = in_be32(pic->regs + TIR_CS) >> 24;
@@ -215,15 +210,17 @@ static void spider_irq_cascade(unsigned int irq, struct irq_desc *desc)
virq = NO_IRQ;
else
virq = irq_linear_revmap(pic->host, cs);
+
if (virq != NO_IRQ)
generic_handle_irq(virq);
- desc->chip->eoi(irq);
+
+ chip->irq_eoi(&desc->irq_data);
}
/* For hooking up the cascace we have a problem. Our device-tree is
* crap and we don't know on which BE iic interrupt we are hooked on at
* least not the "standard" way. We can reconstitute it based on two
- * informations though: which BE node we are connected to and wether
+ * informations though: which BE node we are connected to and whether
* we are connected to IOIF0 or IOIF1. Right now, we really only care
* about the IBM cell blade and we know that its firmware gives us an
* interrupt-map property which is pretty strange.
@@ -235,15 +232,12 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic)
int imaplen, intsize, unit;
struct device_node *iic;
- /* First, we check wether we have a real "interrupts" in the device
+ /* First, we check whether we have a real "interrupts" in the device
* tree in case the device-tree is ever fixed
*/
- struct of_irq oirq;
- if (of_irq_map_one(pic->host->of_node, 0, &oirq) == 0) {
- virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
- oirq.size);
+ virq = irq_of_parse_and_map(pic->host->of_node, 0);
+ if (virq)
return virq;
- }
/* Now do the horrible hacks */
tmp = of_get_property(pic->host->of_node, "#interrupt-cells", NULL);
@@ -258,8 +252,10 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic)
return NO_IRQ;
imap += intsize + 1;
tmp = of_get_property(iic, "#interrupt-cells", NULL);
- if (tmp == NULL)
+ if (tmp == NULL) {
+ of_node_put(iic);
return NO_IRQ;
+ }
intsize = *tmp;
/* Assume unit is last entry of interrupt specifier */
unit = imap[intsize - 1];
@@ -300,12 +296,10 @@ static void __init spider_init_one(struct device_node *of_node, int chip,
panic("spider_pic: can't map registers !");
/* Allocate a host */
- pic->host = irq_alloc_host(of_node_get(of_node), IRQ_HOST_MAP_LINEAR,
- SPIDER_SRC_COUNT, &spider_host_ops,
- SPIDER_IRQ_INVALID);
+ pic->host = irq_domain_add_linear(of_node, SPIDER_SRC_COUNT,
+ &spider_host_ops, pic);
if (pic->host == NULL)
panic("spider_pic: can't allocate irq host !");
- pic->host->host_data = pic;
/* Go through all sources and disable them */
for (i = 0; i < SPIDER_SRC_COUNT; i++) {
@@ -323,8 +317,8 @@ static void __init spider_init_one(struct device_node *of_node, int chip,
virq = spider_find_cascade_and_node(pic);
if (virq == NO_IRQ)
return;
- set_irq_data(virq, pic);
- set_irq_chained_handler(virq, spider_irq_cascade);
+ irq_set_handler_data(virq, pic);
+ irq_set_chained_handler(virq, spider_irq_cascade);
printk(KERN_INFO "spider_pic: node %d, addr: 0x%lx %s\n",
pic->node_id, addr, of_node->full_name);
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c
index 712001f6b7d..f85db3a69b4 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -32,11 +32,13 @@
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/linux_logo.h>
+#include <linux/syscore_ops.h>
#include <asm/spu.h>
#include <asm/spu_priv1.h>
#include <asm/spu_csa.h>
#include <asm/xmon.h>
#include <asm/prom.h>
+#include <asm/kexec.h>
const struct spu_management_ops *spu_management_ops;
EXPORT_SYMBOL_GPL(spu_management_ops);
@@ -114,7 +116,7 @@ static inline void mm_needs_global_tlbie(struct mm_struct *mm)
int nr = (NR_CPUS > 1) ? NR_CPUS : NR_CPUS + 1;
/* Global TLBIE broadcast required with SPEs. */
- __cpus_setall(&mm->cpu_vm_mask, nr);
+ bitmap_fill(cpumask_bits(mm_cpumask(mm)), nr);
}
void spu_associate_mm(struct spu *spu, struct mm_struct *mm)
@@ -141,13 +143,17 @@ static void spu_restart_dma(struct spu *spu)
if (!test_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags))
out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND);
+ else {
+ set_bit(SPU_CONTEXT_FAULT_PENDING, &spu->flags);
+ mb();
+ }
}
static inline void spu_load_slb(struct spu *spu, int slbe, struct spu_slb *slb)
{
struct spu_priv2 __iomem *priv2 = spu->priv2;
- pr_debug("%s: adding SLB[%d] 0x%016lx 0x%016lx\n",
+ pr_debug("%s: adding SLB[%d] 0x%016llx 0x%016llx\n",
__func__, slbe, slb->vsid, slb->esid);
out_be64(&priv2->slb_index_W, slbe);
@@ -165,7 +171,7 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea)
struct spu_slb slb;
int psize;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
slb.esid = (ea & ESID_MASK) | SLB_ESID_V;
@@ -215,22 +221,34 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea)
extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap); //XXX
static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr)
{
- pr_debug("%s, %lx, %lx\n", __FUNCTION__, dsisr, ea);
-
- /* Handle kernel space hash faults immediately.
- User hash faults need to be deferred to process context. */
- if ((dsisr & MFC_DSISR_PTE_NOT_FOUND)
- && REGION_ID(ea) != USER_REGION_ID
- && hash_page(ea, _PAGE_PRESENT, 0x300) == 0) {
- spu_restart_dma(spu);
- return 0;
+ int ret;
+
+ pr_debug("%s, %llx, %lx\n", __func__, dsisr, ea);
+
+ /*
+ * Handle kernel space hash faults immediately. User hash
+ * faults need to be deferred to process context.
+ */
+ if ((dsisr & MFC_DSISR_PTE_NOT_FOUND) &&
+ (REGION_ID(ea) != USER_REGION_ID)) {
+
+ spin_unlock(&spu->register_lock);
+ ret = hash_page(ea, _PAGE_PRESENT, 0x300);
+ spin_lock(&spu->register_lock);
+
+ if (!ret) {
+ spu_restart_dma(spu);
+ return 0;
+ }
}
- spu->class_0_pending = 0;
- spu->dar = ea;
- spu->dsisr = dsisr;
+ spu->class_1_dar = ea;
+ spu->class_1_dsisr = dsisr;
+
+ spu->stop_callback(spu, 1);
- spu->stop_callback(spu);
+ spu->class_1_dar = 0;
+ spu->class_1_dsisr = 0;
return 0;
}
@@ -318,13 +336,13 @@ spu_irq_class_0(int irq, void *data)
stat = spu_int_stat_get(spu, 0) & mask;
spu->class_0_pending |= stat;
- spu->dsisr = spu_mfc_dsisr_get(spu);
- spu->dar = spu_mfc_dar_get(spu);
- spin_unlock(&spu->register_lock);
-
- spu->stop_callback(spu);
+ spu->class_0_dar = spu_mfc_dar_get(spu);
+ spu->stop_callback(spu, 0);
+ spu->class_0_pending = 0;
+ spu->class_0_dar = 0;
spu_int_stat_clear(spu, 0, stat);
+ spin_unlock(&spu->register_lock);
return IRQ_HANDLED;
}
@@ -347,13 +365,12 @@ spu_irq_class_1(int irq, void *data)
spu_mfc_dsisr_set(spu, 0ul);
spu_int_stat_clear(spu, 1, stat);
+ pr_debug("%s: %lx %lx %lx %lx\n", __func__, mask, stat,
+ dar, dsisr);
+
if (stat & CLASS1_SEGMENT_FAULT_INTR)
__spu_trap_data_seg(spu, dar);
- spin_unlock(&spu->register_lock);
- pr_debug("%s: %lx %lx %lx %lx\n", __FUNCTION__, mask, stat,
- dar, dsisr);
-
if (stat & CLASS1_STORAGE_FAULT_INTR)
__spu_trap_data_map(spu, dar, dsisr);
@@ -363,6 +380,11 @@ spu_irq_class_1(int irq, void *data)
if (stat & CLASS1_LS_COMPARE_SUSPEND_ON_PUT_INTR)
;
+ spu->class_1_dsisr = 0;
+ spu->class_1_dar = 0;
+
+ spin_unlock(&spu->register_lock);
+
return stat ? IRQ_HANDLED : IRQ_NONE;
}
@@ -381,14 +403,12 @@ spu_irq_class_2(int irq, void *data)
mask = spu_int_mask_get(spu, 2);
/* ignore interrupts we're not waiting for */
stat &= mask;
-
/* mailbox interrupts are level triggered. mask them now before
* acknowledging */
if (stat & mailbox_intrs)
spu_int_mask_and(spu, 2, ~(stat & mailbox_intrs));
/* acknowledge all interrupts before the callbacks */
spu_int_stat_clear(spu, 2, stat);
- spin_unlock(&spu->register_lock);
pr_debug("class 2 interrupt %d, %lx, %lx\n", irq, stat, mask);
@@ -396,10 +416,10 @@ spu_irq_class_2(int irq, void *data)
spu->ibox_callback(spu);
if (stat & CLASS2_SPU_STOP_INTR)
- spu->stop_callback(spu);
+ spu->stop_callback(spu, 2);
if (stat & CLASS2_SPU_HALT_INTR)
- spu->stop_callback(spu);
+ spu->stop_callback(spu, 2);
if (stat & CLASS2_SPU_DMA_TAG_GROUP_COMPLETE_INTR)
spu->mfc_callback(spu);
@@ -408,6 +428,9 @@ spu_irq_class_2(int irq, void *data)
spu->wbox_callback(spu);
spu->stats.class2_intr++;
+
+ spin_unlock(&spu->register_lock);
+
return stat ? IRQ_HANDLED : IRQ_NONE;
}
@@ -419,8 +442,7 @@ static int spu_request_irqs(struct spu *spu)
snprintf(spu->irq_c0, sizeof (spu->irq_c0), "spe%02d.0",
spu->number);
ret = request_irq(spu->irqs[0], spu_irq_class_0,
- IRQF_DISABLED,
- spu->irq_c0, spu);
+ 0, spu->irq_c0, spu);
if (ret)
goto bail0;
}
@@ -428,8 +450,7 @@ static int spu_request_irqs(struct spu *spu)
snprintf(spu->irq_c1, sizeof (spu->irq_c1), "spe%02d.1",
spu->number);
ret = request_irq(spu->irqs[1], spu_irq_class_1,
- IRQF_DISABLED,
- spu->irq_c1, spu);
+ 0, spu->irq_c1, spu);
if (ret)
goto bail1;
}
@@ -437,8 +458,7 @@ static int spu_request_irqs(struct spu *spu)
snprintf(spu->irq_c2, sizeof (spu->irq_c2), "spe%02d.2",
spu->number);
ret = request_irq(spu->irqs[2], spu_irq_class_2,
- IRQF_DISABLED,
- spu->irq_c2, spu);
+ 0, spu->irq_c2, spu);
if (ret)
goto bail2;
}
@@ -499,41 +519,32 @@ void spu_init_channels(struct spu *spu)
}
EXPORT_SYMBOL_GPL(spu_init_channels);
-static int spu_shutdown(struct sys_device *sysdev)
-{
- struct spu *spu = container_of(sysdev, struct spu, sysdev);
-
- spu_free_irqs(spu);
- spu_destroy_spu(spu);
- return 0;
-}
-
-static struct sysdev_class spu_sysdev_class = {
+static struct bus_type spu_subsys = {
.name = "spu",
- .shutdown = spu_shutdown,
+ .dev_name = "spu",
};
-int spu_add_sysdev_attr(struct sysdev_attribute *attr)
+int spu_add_dev_attr(struct device_attribute *attr)
{
struct spu *spu;
mutex_lock(&spu_full_list_mutex);
list_for_each_entry(spu, &spu_full_list, full_list)
- sysdev_create_file(&spu->sysdev, attr);
+ device_create_file(&spu->dev, attr);
mutex_unlock(&spu_full_list_mutex);
return 0;
}
-EXPORT_SYMBOL_GPL(spu_add_sysdev_attr);
+EXPORT_SYMBOL_GPL(spu_add_dev_attr);
-int spu_add_sysdev_attr_group(struct attribute_group *attrs)
+int spu_add_dev_attr_group(struct attribute_group *attrs)
{
struct spu *spu;
int rc = 0;
mutex_lock(&spu_full_list_mutex);
list_for_each_entry(spu, &spu_full_list, full_list) {
- rc = sysfs_create_group(&spu->sysdev.kobj, attrs);
+ rc = sysfs_create_group(&spu->dev.kobj, attrs);
/* we're in trouble here, but try unwinding anyway */
if (rc) {
@@ -542,7 +553,7 @@ int spu_add_sysdev_attr_group(struct attribute_group *attrs)
list_for_each_entry_continue_reverse(spu,
&spu_full_list, full_list)
- sysfs_remove_group(&spu->sysdev.kobj, attrs);
+ sysfs_remove_group(&spu->dev.kobj, attrs);
break;
}
}
@@ -551,45 +562,45 @@ int spu_add_sysdev_attr_group(struct attribute_group *attrs)
return rc;
}
-EXPORT_SYMBOL_GPL(spu_add_sysdev_attr_group);
+EXPORT_SYMBOL_GPL(spu_add_dev_attr_group);
-void spu_remove_sysdev_attr(struct sysdev_attribute *attr)
+void spu_remove_dev_attr(struct device_attribute *attr)
{
struct spu *spu;
mutex_lock(&spu_full_list_mutex);
list_for_each_entry(spu, &spu_full_list, full_list)
- sysdev_remove_file(&spu->sysdev, attr);
+ device_remove_file(&spu->dev, attr);
mutex_unlock(&spu_full_list_mutex);
}
-EXPORT_SYMBOL_GPL(spu_remove_sysdev_attr);
+EXPORT_SYMBOL_GPL(spu_remove_dev_attr);
-void spu_remove_sysdev_attr_group(struct attribute_group *attrs)
+void spu_remove_dev_attr_group(struct attribute_group *attrs)
{
struct spu *spu;
mutex_lock(&spu_full_list_mutex);
list_for_each_entry(spu, &spu_full_list, full_list)
- sysfs_remove_group(&spu->sysdev.kobj, attrs);
+ sysfs_remove_group(&spu->dev.kobj, attrs);
mutex_unlock(&spu_full_list_mutex);
}
-EXPORT_SYMBOL_GPL(spu_remove_sysdev_attr_group);
+EXPORT_SYMBOL_GPL(spu_remove_dev_attr_group);
-static int spu_create_sysdev(struct spu *spu)
+static int spu_create_dev(struct spu *spu)
{
int ret;
- spu->sysdev.id = spu->number;
- spu->sysdev.cls = &spu_sysdev_class;
- ret = sysdev_register(&spu->sysdev);
+ spu->dev.id = spu->number;
+ spu->dev.bus = &spu_subsys;
+ ret = device_register(&spu->dev);
if (ret) {
printk(KERN_ERR "Can't register SPU %d with sysfs\n",
spu->number);
return ret;
}
- sysfs_add_device_to_node(&spu->sysdev, spu->node);
+ sysfs_add_device_to_node(&spu->dev, spu->node);
return 0;
}
@@ -625,7 +636,7 @@ static int __init create_spu(void *data)
if (ret)
goto out_destroy;
- ret = spu_create_sysdev(spu);
+ ret = spu_create_dev(spu);
if (ret)
goto out_free_irqs;
@@ -682,9 +693,10 @@ static unsigned long long spu_acct_time(struct spu *spu,
}
-static ssize_t spu_stat_show(struct sys_device *sysdev, char *buf)
+static ssize_t spu_stat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- struct spu *spu = container_of(sysdev, struct spu, sysdev);
+ struct spu *spu = container_of(dev, struct spu, dev);
return sprintf(buf, "%s %llu %llu %llu %llu "
"%llu %llu %llu %llu %llu %llu %llu %llu\n",
@@ -703,7 +715,92 @@ static ssize_t spu_stat_show(struct sys_device *sysdev, char *buf)
spu->stats.libassist);
}
-static SYSDEV_ATTR(stat, 0644, spu_stat_show, NULL);
+static DEVICE_ATTR(stat, 0444, spu_stat_show, NULL);
+
+#ifdef CONFIG_KEXEC
+
+struct crash_spu_info {
+ struct spu *spu;
+ u32 saved_spu_runcntl_RW;
+ u32 saved_spu_status_R;
+ u32 saved_spu_npc_RW;
+ u64 saved_mfc_sr1_RW;
+ u64 saved_mfc_dar;
+ u64 saved_mfc_dsisr;
+};
+
+#define CRASH_NUM_SPUS 16 /* Enough for current hardware */
+static struct crash_spu_info crash_spu_info[CRASH_NUM_SPUS];
+
+static void crash_kexec_stop_spus(void)
+{
+ struct spu *spu;
+ int i;
+ u64 tmp;
+
+ for (i = 0; i < CRASH_NUM_SPUS; i++) {
+ if (!crash_spu_info[i].spu)
+ continue;
+
+ spu = crash_spu_info[i].spu;
+
+ crash_spu_info[i].saved_spu_runcntl_RW =
+ in_be32(&spu->problem->spu_runcntl_RW);
+ crash_spu_info[i].saved_spu_status_R =
+ in_be32(&spu->problem->spu_status_R);
+ crash_spu_info[i].saved_spu_npc_RW =
+ in_be32(&spu->problem->spu_npc_RW);
+
+ crash_spu_info[i].saved_mfc_dar = spu_mfc_dar_get(spu);
+ crash_spu_info[i].saved_mfc_dsisr = spu_mfc_dsisr_get(spu);
+ tmp = spu_mfc_sr1_get(spu);
+ crash_spu_info[i].saved_mfc_sr1_RW = tmp;
+
+ tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
+ spu_mfc_sr1_set(spu, tmp);
+
+ __delay(200);
+ }
+}
+
+static void crash_register_spus(struct list_head *list)
+{
+ struct spu *spu;
+ int ret;
+
+ list_for_each_entry(spu, list, full_list) {
+ if (WARN_ON(spu->number >= CRASH_NUM_SPUS))
+ continue;
+
+ crash_spu_info[spu->number].spu = spu;
+ }
+
+ ret = crash_shutdown_register(&crash_kexec_stop_spus);
+ if (ret)
+ printk(KERN_ERR "Could not register SPU crash handler");
+}
+
+#else
+static inline void crash_register_spus(struct list_head *list)
+{
+}
+#endif
+
+static void spu_shutdown(void)
+{
+ struct spu *spu;
+
+ mutex_lock(&spu_full_list_mutex);
+ list_for_each_entry(spu, &spu_full_list, full_list) {
+ spu_free_irqs(spu);
+ spu_destroy_spu(spu);
+ }
+ mutex_unlock(&spu_full_list_mutex);
+}
+
+static struct syscore_ops spu_syscore_ops = {
+ .shutdown = spu_shutdown,
+};
static int __init init_spu_base(void)
{
@@ -717,8 +814,8 @@ static int __init init_spu_base(void)
if (!spu_management_ops)
goto out;
- /* create sysdev class for spus */
- ret = sysdev_class_register(&spu_sysdev_class);
+ /* create system subsystem for spus */
+ ret = subsys_system_register(&spu_subsys, NULL);
if (ret)
goto out;
@@ -726,34 +823,26 @@ static int __init init_spu_base(void)
if (ret < 0) {
printk(KERN_WARNING "%s: Error initializing spus\n",
- __FUNCTION__);
- goto out_unregister_sysdev_class;
+ __func__);
+ goto out_unregister_subsys;
}
- if (ret > 0) {
- /*
- * We cannot put the forward declaration in
- * <linux/linux_logo.h> because of conflicting session type
- * conflicts for const and __initdata with different compiler
- * versions
- */
- extern const struct linux_logo logo_spe_clut224;
-
+ if (ret > 0)
fb_append_extra_logo(&logo_spe_clut224, ret);
- }
mutex_lock(&spu_full_list_mutex);
xmon_register_spus(&spu_full_list);
crash_register_spus(&spu_full_list);
mutex_unlock(&spu_full_list_mutex);
- spu_add_sysdev_attr(&attr_stat);
+ spu_add_dev_attr(&dev_attr_stat);
+ register_syscore_ops(&spu_syscore_ops);
spu_init_affinity();
return 0;
- out_unregister_sysdev_class:
- sysdev_class_unregister(&spu_sysdev_class);
+ out_unregister_subsys:
+ bus_unregister(&spu_subsys);
out:
return ret;
}
diff --git a/arch/powerpc/platforms/cell/spu_callbacks.c b/arch/powerpc/platforms/cell/spu_callbacks.c
index dceb8b6a938..b0ec78e8ad6 100644
--- a/arch/powerpc/platforms/cell/spu_callbacks.c
+++ b/arch/powerpc/platforms/cell/spu_callbacks.c
@@ -5,7 +5,7 @@
#undef DEBUG
#include <linux/kallsyms.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/syscalls.h>
#include <asm/spu.h>
@@ -54,19 +54,18 @@ long spu_sys_callback(struct spu_syscall_block *s)
long (*syscall)(u64 a1, u64 a2, u64 a3, u64 a4, u64 a5, u64 a6);
if (s->nr_ret >= ARRAY_SIZE(spu_syscall_table)) {
- pr_debug("%s: invalid syscall #%ld", __FUNCTION__, s->nr_ret);
+ pr_debug("%s: invalid syscall #%lld", __func__, s->nr_ret);
return -ENOSYS;
}
syscall = spu_syscall_table[s->nr_ret];
-#ifdef DEBUG
- print_symbol(KERN_DEBUG "SPU-syscall %s:", (unsigned long)syscall);
- printk("syscall%ld(%lx, %lx, %lx, %lx, %lx, %lx)\n",
- s->nr_ret,
- s->parm[0], s->parm[1], s->parm[2],
- s->parm[3], s->parm[4], s->parm[5]);
-#endif
+ pr_debug("SPU-syscall "
+ "%pSR:syscall%lld(%llx, %llx, %llx, %llx, %llx, %llx)\n",
+ syscall,
+ s->nr_ret,
+ s->parm[0], s->parm[1], s->parm[2],
+ s->parm[3], s->parm[4], s->parm[5]);
return syscall(s->parm[0], s->parm[1], s->parm[2],
s->parm[3], s->parm[4], s->parm[5]);
diff --git a/arch/powerpc/platforms/cell/spu_fault.c b/arch/powerpc/platforms/cell/spu_fault.c
index c8b1cd42905..641e7273d75 100644
--- a/arch/powerpc/platforms/cell/spu_fault.c
+++ b/arch/powerpc/platforms/cell/spu_fault.c
@@ -22,7 +22,7 @@
*/
#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
@@ -39,60 +39,56 @@ int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
unsigned long is_write;
int ret;
-#if 0
- if (!IS_VALID_EA(ea)) {
+ if (mm == NULL)
return -EFAULT;
- }
-#endif /* XXX */
- if (mm == NULL) {
- return -EFAULT;
- }
- if (mm->pgd == NULL) {
+
+ if (mm->pgd == NULL)
return -EFAULT;
- }
down_read(&mm->mmap_sem);
+ ret = -EFAULT;
vma = find_vma(mm, ea);
if (!vma)
- goto bad_area;
- if (vma->vm_start <= ea)
- goto good_area;
- if (!(vma->vm_flags & VM_GROWSDOWN))
- goto bad_area;
- if (expand_stack(vma, ea))
- goto bad_area;
-good_area:
+ goto out_unlock;
+
+ if (ea < vma->vm_start) {
+ if (!(vma->vm_flags & VM_GROWSDOWN))
+ goto out_unlock;
+ if (expand_stack(vma, ea))
+ goto out_unlock;
+ }
+
is_write = dsisr & MFC_DSISR_ACCESS_PUT;
if (is_write) {
if (!(vma->vm_flags & VM_WRITE))
- goto bad_area;
+ goto out_unlock;
} else {
if (dsisr & MFC_DSISR_ACCESS_DENIED)
- goto bad_area;
+ goto out_unlock;
if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
- goto bad_area;
+ goto out_unlock;
}
+
ret = 0;
- *flt = handle_mm_fault(mm, vma, ea, is_write);
+ *flt = handle_mm_fault(mm, vma, ea, is_write ? FAULT_FLAG_WRITE : 0);
if (unlikely(*flt & VM_FAULT_ERROR)) {
if (*flt & VM_FAULT_OOM) {
ret = -ENOMEM;
- goto bad_area;
+ goto out_unlock;
} else if (*flt & VM_FAULT_SIGBUS) {
ret = -EFAULT;
- goto bad_area;
+ goto out_unlock;
}
BUG();
}
+
if (*flt & VM_FAULT_MAJOR)
current->maj_flt++;
else
current->min_flt++;
- up_read(&mm->mmap_sem);
- return ret;
-bad_area:
+out_unlock:
up_read(&mm->mmap_sem);
- return -EFAULT;
+ return ret;
}
EXPORT_SYMBOL_GPL(spu_handle_mm_fault);
diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c
index d351bdebf5f..c3327f3d8cf 100644
--- a/arch/powerpc/platforms/cell/spu_manage.c
+++ b/arch/powerpc/platforms/cell/spu_manage.c
@@ -21,9 +21,8 @@
#include <linux/interrupt.h>
#include <linux/list.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/ptrace.h>
-#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/io.h>
@@ -92,7 +91,7 @@ static int __init spu_map_interrupts_old(struct spu *spu,
tmp = of_get_property(np->parent->parent, "node-id", NULL);
if (!tmp) {
- printk(KERN_WARNING "%s: can't find node-id\n", __FUNCTION__);
+ printk(KERN_WARNING "%s: can't find node-id\n", __func__);
nid = spu->node;
} else
nid = tmp[0];
@@ -178,21 +177,20 @@ out:
static int __init spu_map_interrupts(struct spu *spu, struct device_node *np)
{
- struct of_irq oirq;
+ struct of_phandle_args oirq;
int ret;
int i;
for (i=0; i < 3; i++) {
- ret = of_irq_map_one(np, i, &oirq);
+ ret = of_irq_parse_one(np, i, &oirq);
if (ret) {
pr_debug("spu_new: failed to get irq %d\n", i);
goto err;
}
ret = -EINVAL;
- pr_debug(" irq %d no 0x%x on %s\n", i, oirq.specifier[0],
- oirq.controller->full_name);
- spu->irqs[i] = irq_create_of_mapping(oirq.controller,
- oirq.specifier, oirq.size);
+ pr_debug(" irq %d no 0x%x on %s\n", i, oirq.args[0],
+ oirq.np->full_name);
+ spu->irqs[i] = irq_create_of_mapping(&oirq);
if (spu->irqs[i] == NO_IRQ) {
pr_debug("spu_new: failed to map it !\n");
goto err;
@@ -201,7 +199,7 @@ static int __init spu_map_interrupts(struct spu *spu, struct device_node *np)
return 0;
err:
- pr_debug("failed to map irq %x for spu %s\n", *oirq.specifier,
+ pr_debug("failed to map irq %x for spu %s\n", *oirq.args,
spu->name);
for (; i >= 0; i--) {
if (spu->irqs[i] != NO_IRQ)
@@ -223,7 +221,7 @@ static int spu_map_resource(struct spu *spu, int nr,
return ret;
if (phys)
*phys = resource.start;
- len = resource.end - resource.start + 1;
+ len = resource_size(&resource);
*virt = ioremap(resource.start, len);
if (!*virt)
return -EINVAL;
@@ -296,7 +294,7 @@ static int __init of_enumerate_spus(int (*fn)(void *data))
ret = fn(node);
if (ret) {
printk(KERN_WARNING "%s: Error initializing %s\n",
- __FUNCTION__, node->name);
+ __func__, node->name);
break;
}
n++;
@@ -327,7 +325,7 @@ static int __init of_create_spu(struct spu *spu, void *data)
if (!legacy_map) {
legacy_map = 1;
printk(KERN_WARNING "%s: Legacy device tree found, "
- "trying to map old style\n", __FUNCTION__);
+ "trying to map old style\n", __func__);
}
ret = spu_map_device_old(spu);
if (ret) {
@@ -342,7 +340,7 @@ static int __init of_create_spu(struct spu *spu, void *data)
if (!legacy_irq) {
legacy_irq = 1;
printk(KERN_WARNING "%s: Legacy device tree found, "
- "trying old style irq\n", __FUNCTION__);
+ "trying old style irq\n", __func__);
}
ret = spu_map_interrupts_old(spu, spe);
if (ret) {
@@ -457,7 +455,7 @@ neighbour_spu(int cbe, struct device_node *target, struct device_node *avoid)
continue;
vic_handles = of_get_property(spu_dn, "vicinity", &lenp);
for (i=0; i < (lenp / sizeof(phandle)); i++) {
- if (vic_handles[i] == target->linux_phandle)
+ if (vic_handles[i] == target->phandle)
return spu;
}
}
@@ -499,7 +497,7 @@ static void init_affinity_node(int cbe)
if (strcmp(name, "spe") == 0) {
spu = devnode_spu(cbe, vic_dn);
- avoid_ph = last_spu_dn->linux_phandle;
+ avoid_ph = last_spu_dn->phandle;
} else {
/*
* "mic-tm" and "bif0" nodes do not have
@@ -514,7 +512,7 @@ static void init_affinity_node(int cbe)
last_spu->has_mem_affinity = 1;
spu->has_mem_affinity = 1;
}
- avoid_ph = vic_dn->linux_phandle;
+ avoid_ph = vic_dn->phandle;
}
list_add_tail(&spu->aff_list, &last_spu->aff_list);
diff --git a/arch/powerpc/platforms/cell/spu_notify.c b/arch/powerpc/platforms/cell/spu_notify.c
index 34d156959f3..afdf857c318 100644
--- a/arch/powerpc/platforms/cell/spu_notify.c
+++ b/arch/powerpc/platforms/cell/spu_notify.c
@@ -21,7 +21,8 @@
#undef DEBUG
-#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/notifier.h>
#include <asm/spu.h>
#include "spufs/spufs.h"
diff --git a/arch/powerpc/platforms/cell/spu_priv1_mmio.c b/arch/powerpc/platforms/cell/spu_priv1_mmio.c
index 67fa7247b80..66d33724f16 100644
--- a/arch/powerpc/platforms/cell/spu_priv1_mmio.c
+++ b/arch/powerpc/platforms/cell/spu_priv1_mmio.c
@@ -20,14 +20,13 @@
#include <linux/interrupt.h>
#include <linux/list.h>
-#include <linux/module.h>
#include <linux/ptrace.h>
-#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/device.h>
+#include <linux/sched.h>
#include <asm/spu.h>
#include <asm/spu_priv1.h>
@@ -75,8 +74,19 @@ static u64 int_stat_get(struct spu *spu, int class)
static void cpu_affinity_set(struct spu *spu, int cpu)
{
- u64 target = iic_get_target_id(cpu);
- u64 route = target << 48 | target << 32 | target << 16;
+ u64 target;
+ u64 route;
+
+ if (nr_cpus_node(spu->node)) {
+ const struct cpumask *spumask = cpumask_of_node(spu->node),
+ *cpumask = cpumask_of_node(cpu_to_node(cpu));
+
+ if (!cpumask_intersects(spumask, cpumask))
+ return;
+ }
+
+ target = iic_get_target_id(cpu);
+ route = target << 48 | target << 32 | target << 16;
out_be64(&spu->priv1->int_route_RW, route);
}
diff --git a/arch/powerpc/platforms/cell/spu_syscalls.c b/arch/powerpc/platforms/cell/spu_syscalls.c
index 75530d99eda..5e6e0bad6db 100644
--- a/arch/powerpc/platforms/cell/spu_syscalls.c
+++ b/arch/powerpc/platforms/cell/spu_syscalls.c
@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/rcupdate.h>
+#include <linux/binfmts.h>
#include <asm/spu.h>
@@ -65,12 +66,10 @@ static inline void spufs_calls_put(struct spufs_calls *calls) { }
#endif /* CONFIG_SPU_FS_MODULE */
-asmlinkage long sys_spu_create(const char __user *name,
- unsigned int flags, mode_t mode, int neighbor_fd)
+SYSCALL_DEFINE4(spu_create, const char __user *, name, unsigned int, flags,
+ umode_t, mode, int, neighbor_fd)
{
long ret;
- struct file *neighbor;
- int fput_needed;
struct spufs_calls *calls;
calls = spufs_calls_get();
@@ -78,11 +77,11 @@ asmlinkage long sys_spu_create(const char __user *name,
return -ENOSYS;
if (flags & SPU_CREATE_AFFINITY_SPU) {
+ struct fd neighbor = fdget(neighbor_fd);
ret = -EBADF;
- neighbor = fget_light(neighbor_fd, &fput_needed);
- if (neighbor) {
- ret = calls->create_thread(name, flags, mode, neighbor);
- fput_light(neighbor, fput_needed);
+ if (neighbor.file) {
+ ret = calls->create_thread(name, flags, mode, neighbor.file);
+ fdput(neighbor);
}
} else
ret = calls->create_thread(name, flags, mode, NULL);
@@ -94,8 +93,7 @@ asmlinkage long sys_spu_create(const char __user *name,
asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus)
{
long ret;
- struct file *filp;
- int fput_needed;
+ struct fd arg;
struct spufs_calls *calls;
calls = spufs_calls_get();
@@ -103,16 +101,17 @@ asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus)
return -ENOSYS;
ret = -EBADF;
- filp = fget_light(fd, &fput_needed);
- if (filp) {
- ret = calls->spu_run(filp, unpc, ustatus);
- fput_light(filp, fput_needed);
+ arg = fdget(fd);
+ if (arg.file) {
+ ret = calls->spu_run(arg.file, unpc, ustatus);
+ fdput(arg);
}
spufs_calls_put(calls);
return ret;
}
+#ifdef CONFIG_COREDUMP
int elf_coredump_extra_notes_size(void)
{
struct spufs_calls *calls;
@@ -129,7 +128,7 @@ int elf_coredump_extra_notes_size(void)
return ret;
}
-int elf_coredump_extra_notes_write(struct file *file, loff_t *foffset)
+int elf_coredump_extra_notes_write(struct coredump_params *cprm)
{
struct spufs_calls *calls;
int ret;
@@ -138,12 +137,13 @@ int elf_coredump_extra_notes_write(struct file *file, loff_t *foffset)
if (!calls)
return 0;
- ret = calls->coredump_extra_notes_write(file, foffset);
+ ret = calls->coredump_extra_notes_write(cprm);
spufs_calls_put(calls);
return ret;
}
+#endif
void notify_spus_active(void)
{
@@ -172,7 +172,7 @@ EXPORT_SYMBOL_GPL(register_spu_syscalls);
void unregister_spu_syscalls(struct spufs_calls *calls)
{
BUG_ON(spufs_calls->owner != calls->owner);
- rcu_assign_pointer(spufs_calls, NULL);
+ RCU_INIT_POINTER(spufs_calls, NULL);
synchronize_rcu();
}
EXPORT_SYMBOL_GPL(unregister_spu_syscalls);
diff --git a/arch/powerpc/platforms/cell/spufs/.gitignore b/arch/powerpc/platforms/cell/spufs/.gitignore
new file mode 100644
index 00000000000..a09ee8d84d6
--- /dev/null
+++ b/arch/powerpc/platforms/cell/spufs/.gitignore
@@ -0,0 +1,2 @@
+spu_save_dump.h
+spu_restore_dump.h
diff --git a/arch/powerpc/platforms/cell/spufs/Makefile b/arch/powerpc/platforms/cell/spufs/Makefile
index 99610a6361f..52a7d2596d3 100644
--- a/arch/powerpc/platforms/cell/spufs/Makefile
+++ b/arch/powerpc/platforms/cell/spufs/Makefile
@@ -1,10 +1,12 @@
obj-$(CONFIG_SPU_FS) += spufs.o
-spufs-y += inode.o file.o context.o syscalls.o coredump.o
+spufs-y += inode.o file.o context.o syscalls.o
spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o
spufs-y += switch.o fault.o lscsa_alloc.o
+spufs-$(CONFIG_COREDUMP) += coredump.o
-obj-$(CONFIG_SPU_TRACE) += sputrace.o
+# magic for the trace events
+CFLAGS_sched.o := -I$(src)
# Rules to build switch.o with the help of SPU tool chain
SPU_CROSS := spu-
@@ -12,10 +14,8 @@ SPU_CC := $(SPU_CROSS)gcc
SPU_AS := $(SPU_CROSS)gcc
SPU_LD := $(SPU_CROSS)ld
SPU_OBJCOPY := $(SPU_CROSS)objcopy
-SPU_CFLAGS := -O2 -Wall -I$(srctree)/include \
- -I$(objtree)/include2 -D__KERNEL__
-SPU_AFLAGS := -c -D__ASSEMBLY__ -I$(srctree)/include \
- -I$(objtree)/include2 -D__KERNEL__
+SPU_CFLAGS := -O2 -Wall -I$(srctree)/include -D__KERNEL__
+SPU_AFLAGS := -c -D__ASSEMBLY__ -I$(srctree)/include -D__KERNEL__
SPU_LDFLAGS := -N -Ttext=0x0
$(obj)/switch.o: $(obj)/spu_save_dump.h $(obj)/spu_restore_dump.h
diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c
index 64eb15b2204..6e8a9ef8590 100644
--- a/arch/powerpc/platforms/cell/spufs/backing_ops.c
+++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c
@@ -21,7 +21,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c
index 0ad83aeb70b..9c6790d17ed 100644
--- a/arch/powerpc/platforms/cell/spufs/context.c
+++ b/arch/powerpc/platforms/cell/spufs/context.c
@@ -22,12 +22,13 @@
#include <linux/fs.h>
#include <linux/mm.h>
-#include <linux/module.h>
#include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
+#include <linux/sched.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
#include "spufs.h"
+#include "sputrace.h"
atomic_t nr_spu_contexts = ATOMIC_INIT(0);
@@ -35,6 +36,8 @@ atomic_t nr_spu_contexts = ATOMIC_INIT(0);
struct spu_context *alloc_spu_context(struct spu_gang *gang)
{
struct spu_context *ctx;
+ struct timespec ts;
+
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
goto out;
@@ -64,6 +67,8 @@ struct spu_context *alloc_spu_context(struct spu_gang *gang)
__spu_update_sched_info(ctx);
spu_set_timeslice(ctx);
ctx->stats.util_state = SPU_UTIL_IDLE_LOADED;
+ ktime_get_ts(&ts);
+ ctx->stats.tstamp = timespec_to_ns(&ts);
atomic_inc(&nr_spu_contexts);
goto out;
@@ -78,6 +83,7 @@ void destroy_spu_context(struct kref *kref)
{
struct spu_context *ctx;
ctx = container_of(kref, struct spu_context, kref);
+ spu_context_nospu_trace(destroy_spu_context__enter, ctx);
mutex_lock(&ctx->state_mutex);
spu_deactivate(ctx);
mutex_unlock(&ctx->state_mutex);
@@ -88,6 +94,7 @@ void destroy_spu_context(struct kref *kref)
kref_put(ctx->prof_priv_kref, ctx->prof_priv_release);
BUG_ON(!list_empty(&ctx->rq));
atomic_dec(&nr_spu_contexts);
+ kfree(ctx->switch_log);
kfree(ctx);
}
@@ -128,17 +135,17 @@ void spu_unmap_mappings(struct spu_context *ctx)
if (ctx->local_store)
unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1);
if (ctx->mfc)
- unmap_mapping_range(ctx->mfc, 0, 0x1000, 1);
+ unmap_mapping_range(ctx->mfc, 0, SPUFS_MFC_MAP_SIZE, 1);
if (ctx->cntl)
- unmap_mapping_range(ctx->cntl, 0, 0x1000, 1);
+ unmap_mapping_range(ctx->cntl, 0, SPUFS_CNTL_MAP_SIZE, 1);
if (ctx->signal1)
- unmap_mapping_range(ctx->signal1, 0, PAGE_SIZE, 1);
+ unmap_mapping_range(ctx->signal1, 0, SPUFS_SIGNAL_MAP_SIZE, 1);
if (ctx->signal2)
- unmap_mapping_range(ctx->signal2, 0, PAGE_SIZE, 1);
+ unmap_mapping_range(ctx->signal2, 0, SPUFS_SIGNAL_MAP_SIZE, 1);
if (ctx->mss)
- unmap_mapping_range(ctx->mss, 0, 0x1000, 1);
+ unmap_mapping_range(ctx->mss, 0, SPUFS_MSS_MAP_SIZE, 1);
if (ctx->psmap)
- unmap_mapping_range(ctx->psmap, 0, 0x20000, 1);
+ unmap_mapping_range(ctx->psmap, 0, SPUFS_PS_MAP_SIZE, 1);
mutex_unlock(&ctx->mapping_lock);
}
@@ -150,6 +157,8 @@ int spu_acquire_saved(struct spu_context *ctx)
{
int ret;
+ spu_context_nospu_trace(spu_acquire_saved__enter, ctx);
+
ret = spu_acquire(ctx);
if (ret)
return ret;
diff --git a/arch/powerpc/platforms/cell/spufs/coredump.c b/arch/powerpc/platforms/cell/spufs/coredump.c
index 0c6a96b82b2..be6212ddbf0 100644
--- a/arch/powerpc/platforms/cell/spufs/coredump.c
+++ b/arch/powerpc/platforms/cell/spufs/coredump.c
@@ -22,10 +22,13 @@
#include <linux/elf.h>
#include <linux/file.h>
+#include <linux/fdtable.h>
#include <linux/fs.h>
+#include <linux/gfp.h>
#include <linux/list.h>
-#include <linux/module.h>
#include <linux/syscalls.h>
+#include <linux/coredump.h>
+#include <linux/binfmts.h>
#include <asm/uaccess.h>
@@ -41,50 +44,12 @@ static ssize_t do_coredump_read(int num, struct spu_context *ctx, void *buffer,
return spufs_coredump_read[num].read(ctx, buffer, size, off);
data = spufs_coredump_read[num].get(ctx);
- ret = snprintf(buffer, size, "0x%.16lx", data);
+ ret = snprintf(buffer, size, "0x%.16llx", data);
if (ret >= size)
return size;
return ++ret; /* count trailing NULL */
}
-/*
- * These are the only things you should do on a core-file: use only these
- * functions to write out all the necessary info.
- */
-static int spufs_dump_write(struct file *file, const void *addr, int nr, loff_t *foffset)
-{
- unsigned long limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
- ssize_t written;
-
- if (*foffset + nr > limit)
- return -EIO;
-
- written = file->f_op->write(file, addr, nr, &file->f_pos);
- *foffset += written;
-
- if (written != nr)
- return -EIO;
-
- return 0;
-}
-
-static int spufs_dump_align(struct file *file, char *buf, loff_t new_off,
- loff_t *foffset)
-{
- int rc, size;
-
- size = min((loff_t)PAGE_SIZE, new_off - *foffset);
- memset(buf, 0, size);
-
- rc = 0;
- while (rc == 0 && new_off > *foffset) {
- size = min((loff_t)PAGE_SIZE, new_off - *foffset);
- rc = spufs_dump_write(file, buf, size, foffset);
- }
-
- return rc;
-}
-
static int spufs_ctx_note_size(struct spu_context *ctx, int dfd)
{
int i, sz, total = 0;
@@ -105,6 +70,17 @@ static int spufs_ctx_note_size(struct spu_context *ctx, int dfd)
return total;
}
+static int match_context(const void *v, struct file *file, unsigned fd)
+{
+ struct spu_context *ctx;
+ if (file->f_op != &spufs_context_fops)
+ return 0;
+ ctx = SPUFS_I(file_inode(file))->i_ctx;
+ if (ctx->flags & SPU_CREATE_NOSCHED)
+ return 0;
+ return fd + 1;
+}
+
/*
* The additional architecture-specific notes for Cell are various
* context files in the spu context.
@@ -114,31 +90,18 @@ static int spufs_ctx_note_size(struct spu_context *ctx, int dfd)
* internal functionality to dump them without needing to actually
* open the files.
*/
+/*
+ * descriptor table is not shared, so files can't change or go away.
+ */
static struct spu_context *coredump_next_context(int *fd)
{
- struct fdtable *fdt = files_fdtable(current->files);
struct file *file;
- struct spu_context *ctx = NULL;
-
- for (; *fd < fdt->max_fds; (*fd)++) {
- if (!FD_ISSET(*fd, fdt->open_fds))
- continue;
-
- file = fcheck(*fd);
-
- if (!file || file->f_op != &spufs_context_fops)
- continue;
-
- ctx = SPUFS_I(file->f_dentry->d_inode)->i_ctx;
- if (ctx->flags & SPU_CREATE_NOSCHED)
- continue;
-
- /* start searching the next fd next time we're called */
- (*fd)++;
- break;
- }
-
- return ctx;
+ int n = iterate_fd(current->files, *fd, match_context, NULL);
+ if (!n)
+ return NULL;
+ *fd = n - 1;
+ file = fcheck(*fd);
+ return SPUFS_I(file_inode(file))->i_ctx;
}
int spufs_coredump_extra_notes_size(void)
@@ -157,16 +120,19 @@ int spufs_coredump_extra_notes_size(void)
break;
size += rc;
+
+ /* start searching the next fd next time */
+ fd++;
}
return size;
}
static int spufs_arch_write_note(struct spu_context *ctx, int i,
- struct file *file, int dfd, loff_t *foffset)
+ struct coredump_params *cprm, int dfd)
{
loff_t pos = 0;
- int sz, rc, nread, total = 0;
+ int sz, rc, total = 0;
const int bufsz = PAGE_SIZE;
char *name;
char fullname[80], *buf;
@@ -184,42 +150,39 @@ static int spufs_arch_write_note(struct spu_context *ctx, int i,
en.n_descsz = sz;
en.n_type = NT_SPU;
- rc = spufs_dump_write(file, &en, sizeof(en), foffset);
- if (rc)
- goto out;
+ if (!dump_emit(cprm, &en, sizeof(en)))
+ goto Eio;
- rc = spufs_dump_write(file, fullname, en.n_namesz, foffset);
- if (rc)
- goto out;
+ if (!dump_emit(cprm, fullname, en.n_namesz))
+ goto Eio;
- rc = spufs_dump_align(file, buf, roundup(*foffset, 4), foffset);
- if (rc)
- goto out;
+ if (!dump_align(cprm, 4))
+ goto Eio;
do {
- nread = do_coredump_read(i, ctx, buf, bufsz, &pos);
- if (nread > 0) {
- rc = spufs_dump_write(file, buf, nread, foffset);
- if (rc)
- goto out;
- total += nread;
+ rc = do_coredump_read(i, ctx, buf, bufsz, &pos);
+ if (rc > 0) {
+ if (!dump_emit(cprm, buf, rc))
+ goto Eio;
+ total += rc;
}
- } while (nread == bufsz && total < sz);
+ } while (rc == bufsz && total < sz);
- if (nread < 0) {
- rc = nread;
+ if (rc < 0)
goto out;
- }
-
- rc = spufs_dump_align(file, buf, roundup(*foffset - total + sz, 4),
- foffset);
+ if (!dump_skip(cprm,
+ roundup(cprm->written - total + sz, 4) - cprm->written))
+ goto Eio;
out:
free_page((unsigned long)buf);
return rc;
+Eio:
+ free_page((unsigned long)buf);
+ return -EIO;
}
-int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset)
+int spufs_coredump_extra_notes_write(struct coredump_params *cprm)
{
struct spu_context *ctx;
int fd, j, rc;
@@ -231,7 +194,7 @@ int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset)
return rc;
for (j = 0; spufs_coredump_read[j].name != NULL; j++) {
- rc = spufs_arch_write_note(ctx, j, file, fd, foffset);
+ rc = spufs_arch_write_note(ctx, j, cprm, fd);
if (rc) {
spu_release_saved(ctx);
return rc;
@@ -239,6 +202,9 @@ int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset)
}
spu_release_saved(ctx);
+
+ /* start searching the next fd next time */
+ fd++;
}
return 0;
diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c
index e46d300e21a..8cb6260cc80 100644
--- a/arch/powerpc/platforms/cell/spufs/fault.c
+++ b/arch/powerpc/platforms/cell/spufs/fault.c
@@ -21,7 +21,6 @@
*/
#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/module.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
@@ -83,13 +82,18 @@ int spufs_handle_class0(struct spu_context *ctx)
return 0;
if (stat & CLASS0_DMA_ALIGNMENT_INTR)
- spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_DMA_ALIGNMENT);
+ spufs_handle_event(ctx, ctx->csa.class_0_dar,
+ SPE_EVENT_DMA_ALIGNMENT);
if (stat & CLASS0_INVALID_DMA_COMMAND_INTR)
- spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_INVALID_DMA);
+ spufs_handle_event(ctx, ctx->csa.class_0_dar,
+ SPE_EVENT_INVALID_DMA);
if (stat & CLASS0_SPU_ERROR_INTR)
- spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_SPE_ERROR);
+ spufs_handle_event(ctx, ctx->csa.class_0_dar,
+ SPE_EVENT_SPE_ERROR);
+
+ ctx->csa.class_0_pending = 0;
return -EIO;
}
@@ -119,15 +123,15 @@ int spufs_handle_class1(struct spu_context *ctx)
* in time, we can still expect to get the same fault
* the immediately after the context restore.
*/
- ea = ctx->csa.dar;
- dsisr = ctx->csa.dsisr;
+ ea = ctx->csa.class_1_dar;
+ dsisr = ctx->csa.class_1_dsisr;
if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)))
return 0;
spuctx_switch_state(ctx, SPU_UTIL_IOWAIT);
- pr_debug("ctx %p: ea %016lx, dsisr %016lx state %d\n", ctx, ea,
+ pr_debug("ctx %p: ea %016llx, dsisr %016llx state %d\n", ctx, ea,
dsisr, ctx->state);
ctx->stats.hash_flt++;
@@ -158,7 +162,7 @@ int spufs_handle_class1(struct spu_context *ctx)
* time slicing will not preempt the context while the page fault
* handler is running. Context switch code removes mappings.
*/
- ctx->csa.dar = ctx->csa.dsisr = 0;
+ ctx->csa.class_1_dar = ctx->csa.class_1_dsisr = 0;
/*
* If we handled the fault successfully and are in runnable
diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c
index f7a7e8635fb..90986923a53 100644
--- a/arch/powerpc/platforms/cell/spufs/file.c
+++ b/arch/powerpc/platforms/cell/spufs/file.c
@@ -24,20 +24,21 @@
#include <linux/fs.h>
#include <linux/ioctl.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/pagemap.h>
#include <linux/poll.h>
#include <linux/ptrace.h>
#include <linux/seq_file.h>
-#include <linux/marker.h>
+#include <linux/slab.h>
#include <asm/io.h>
-#include <asm/semaphore.h>
+#include <asm/time.h>
#include <asm/spu.h>
#include <asm/spu_info.h>
#include <asm/uaccess.h>
#include "spufs.h"
+#include "sputrace.h"
#define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000)
@@ -147,12 +148,12 @@ static int __fops ## _open(struct inode *inode, struct file *file) \
__simple_attr_check_format(__fmt, 0ull); \
return spufs_attr_open(inode, file, __get, __set, __fmt); \
} \
-static struct file_operations __fops = { \
- .owner = THIS_MODULE, \
+static const struct file_operations __fops = { \
.open = __fops ## _open, \
.release = spufs_attr_release, \
.read = spufs_attr_read, \
.write = spufs_attr_write, \
+ .llseek = generic_file_llseek, \
};
@@ -217,32 +218,27 @@ spufs_mem_write(struct file *file, const char __user *buffer,
loff_t pos = *ppos;
int ret;
- if (pos < 0)
- return -EINVAL;
if (pos > LS_SIZE)
return -EFBIG;
- if (size > LS_SIZE - pos)
- size = LS_SIZE - pos;
ret = spu_acquire(ctx);
if (ret)
return ret;
local_store = ctx->ops->get_ls(ctx);
- ret = copy_from_user(local_store + pos, buffer, size);
+ size = simple_write_to_buffer(local_store, LS_SIZE, ppos, buffer, size);
spu_release(ctx);
- if (ret)
- return -EFAULT;
- *ppos = pos + size;
return size;
}
-static unsigned long spufs_mem_mmap_nopfn(struct vm_area_struct *vma,
- unsigned long address)
+static int
+spufs_mem_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct spu_context *ctx = vma->vm_file->private_data;
- unsigned long pfn, offset, addr0 = address;
+ unsigned long address = (unsigned long)vmf->virtual_address;
+ unsigned long pfn, offset;
+
#ifdef CONFIG_SPU_FS_64K_LS
struct spu_state *csa = &ctx->csa;
int psize;
@@ -260,35 +256,56 @@ static unsigned long spufs_mem_mmap_nopfn(struct vm_area_struct *vma,
}
#endif /* CONFIG_SPU_FS_64K_LS */
- offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
+ offset = vmf->pgoff << PAGE_SHIFT;
if (offset >= LS_SIZE)
- return NOPFN_SIGBUS;
+ return VM_FAULT_SIGBUS;
- pr_debug("spufs_mem_mmap_nopfn address=0x%lx -> 0x%lx, offset=0x%lx\n",
- addr0, address, offset);
+ pr_debug("spufs_mem_mmap_fault address=0x%lx, offset=0x%lx\n",
+ address, offset);
if (spu_acquire(ctx))
- return NOPFN_REFAULT;
+ return VM_FAULT_NOPAGE;
if (ctx->state == SPU_STATE_SAVED) {
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- & ~_PAGE_NO_CACHE);
+ vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset);
} else {
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE);
+ vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot);
pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT;
}
vm_insert_pfn(vma, address, pfn);
spu_release(ctx);
- return NOPFN_REFAULT;
+ return VM_FAULT_NOPAGE;
}
+static int spufs_mem_mmap_access(struct vm_area_struct *vma,
+ unsigned long address,
+ void *buf, int len, int write)
+{
+ struct spu_context *ctx = vma->vm_file->private_data;
+ unsigned long offset = address - vma->vm_start;
+ char *local_store;
+
+ if (write && !(vma->vm_flags & VM_WRITE))
+ return -EACCES;
+ if (spu_acquire(ctx))
+ return -EINTR;
+ if ((offset + len) > vma->vm_end)
+ len = vma->vm_end - offset;
+ local_store = ctx->ops->get_ls(ctx);
+ if (write)
+ memcpy_toio(local_store + offset, buf, len);
+ else
+ memcpy_fromio(buf, local_store + offset, len);
+ spu_release(ctx);
+ return len;
+}
-static struct vm_operations_struct spufs_mem_mmap_vmops = {
- .nopfn = spufs_mem_mmap_nopfn,
+static const struct vm_operations_struct spufs_mem_mmap_vmops = {
+ .fault = spufs_mem_mmap_fault,
+ .access = spufs_mem_mmap_access,
};
static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
@@ -313,8 +330,7 @@ static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE);
+ vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot);
vma->vm_ops = &spufs_mem_mmap_vmops;
return 0;
@@ -335,7 +351,7 @@ static unsigned long spufs_get_unmapped_area(struct file *file,
/* Else, try to obtain a 64K pages slice */
return slice_get_unmapped_area(addr, len, flags,
- MMU_PAGE_64K, 1, 0);
+ MMU_PAGE_64K, 1);
}
#endif /* CONFIG_SPU_FS_64K_LS */
@@ -351,20 +367,22 @@ static const struct file_operations spufs_mem_fops = {
#endif
};
-static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma,
- unsigned long address,
+static int spufs_ps_fault(struct vm_area_struct *vma,
+ struct vm_fault *vmf,
unsigned long ps_offs,
unsigned long ps_size)
{
struct spu_context *ctx = vma->vm_file->private_data;
- unsigned long area, offset = address - vma->vm_start;
+ unsigned long area, offset = vmf->pgoff << PAGE_SHIFT;
int ret = 0;
- spu_context_nospu_trace(spufs_ps_nopfn__enter, ctx);
+ spu_context_nospu_trace(spufs_ps_fault__enter, ctx);
- offset += vma->vm_pgoff << PAGE_SHIFT;
if (offset >= ps_size)
- return NOPFN_SIGBUS;
+ return VM_FAULT_SIGBUS;
+
+ if (fatal_signal_pending(current))
+ return VM_FAULT_SIGBUS;
/*
* Because we release the mmap_sem, the context may be destroyed while
@@ -378,7 +396,7 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma,
* pages to hand out to the user, but we don't want to wait
* with the mmap_sem held.
* It is possible to drop the mmap_sem here, but then we need
- * to return NOPFN_REFAULT because the mappings may have
+ * to return VM_FAULT_NOPAGE because the mappings may have
* hanged.
*/
if (spu_acquire(ctx))
@@ -386,14 +404,15 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma,
if (ctx->state == SPU_STATE_SAVED) {
up_read(&current->mm->mmap_sem);
- spu_context_nospu_trace(spufs_ps_nopfn__sleep, ctx);
+ spu_context_nospu_trace(spufs_ps_fault__sleep, ctx);
ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE);
- spu_context_trace(spufs_ps_nopfn__wake, ctx, ctx->spu);
+ spu_context_trace(spufs_ps_fault__wake, ctx, ctx->spu);
down_read(&current->mm->mmap_sem);
} else {
area = ctx->spu->problem_phys + ps_offs;
- vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT);
- spu_context_trace(spufs_ps_nopfn__insert, ctx, ctx->spu);
+ vm_insert_pfn(vma, (unsigned long)vmf->virtual_address,
+ (area + offset) >> PAGE_SHIFT);
+ spu_context_trace(spufs_ps_fault__insert, ctx, ctx->spu);
}
if (!ret)
@@ -401,18 +420,18 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma,
refault:
put_spu_context(ctx);
- return NOPFN_REFAULT;
+ return VM_FAULT_NOPAGE;
}
#if SPUFS_MMAP_4K
-static unsigned long spufs_cntl_mmap_nopfn(struct vm_area_struct *vma,
- unsigned long address)
+static int spufs_cntl_mmap_fault(struct vm_area_struct *vma,
+ struct vm_fault *vmf)
{
- return spufs_ps_nopfn(vma, address, 0x4000, 0x1000);
+ return spufs_ps_fault(vma, vmf, 0x4000, SPUFS_CNTL_MAP_SIZE);
}
-static struct vm_operations_struct spufs_cntl_mmap_vmops = {
- .nopfn = spufs_cntl_mmap_nopfn,
+static const struct vm_operations_struct spufs_cntl_mmap_vmops = {
+ .fault = spufs_cntl_mmap_fault,
};
/*
@@ -424,8 +443,7 @@ static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_cntl_mmap_vmops;
return 0;
@@ -496,6 +514,7 @@ static const struct file_operations spufs_cntl_fops = {
.release = spufs_cntl_release,
.read = simple_attr_read,
.write = simple_attr_write,
+ .llseek = generic_file_llseek,
.mmap = spufs_cntl_mmap,
};
@@ -523,6 +542,11 @@ spufs_regs_read(struct file *file, char __user *buffer,
int ret;
struct spu_context *ctx = file->private_data;
+ /* pre-check for file position: if we'd return EOF, there's no point
+ * causing a deschedule */
+ if (*pos >= sizeof(ctx->csa.lscsa->gprs))
+ return 0;
+
ret = spu_acquire_saved(ctx);
if (ret)
return ret;
@@ -539,20 +563,18 @@ spufs_regs_write(struct file *file, const char __user *buffer,
struct spu_lscsa *lscsa = ctx->csa.lscsa;
int ret;
- size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size);
- if (size <= 0)
+ if (*pos >= sizeof(lscsa->gprs))
return -EFBIG;
- *pos += size;
ret = spu_acquire_saved(ctx);
if (ret)
return ret;
- ret = copy_from_user(lscsa->gprs + *pos - size,
- buffer, size) ? -EFAULT : size;
+ size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos,
+ buffer, size);
spu_release_saved(ctx);
- return ret;
+ return size;
}
static const struct file_operations spufs_regs_fops = {
@@ -594,20 +616,18 @@ spufs_fpcr_write(struct file *file, const char __user * buffer,
struct spu_lscsa *lscsa = ctx->csa.lscsa;
int ret;
- size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size);
- if (size <= 0)
+ if (*pos >= sizeof(lscsa->fpcr))
return -EFBIG;
ret = spu_acquire_saved(ctx);
if (ret)
return ret;
- *pos += size;
- ret = copy_from_user((char *)&lscsa->fpcr + *pos - size,
- buffer, size) ? -EFAULT : size;
+ size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos,
+ buffer, size);
spu_release_saved(ctx);
- return ret;
+ return size;
}
static const struct file_operations spufs_fpcr_fops = {
@@ -682,6 +702,7 @@ static ssize_t spufs_mbox_read(struct file *file, char __user *buf,
static const struct file_operations spufs_mbox_fops = {
.open = spufs_pipe_open,
.read = spufs_mbox_read,
+ .llseek = no_llseek,
};
static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf,
@@ -711,6 +732,7 @@ static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf,
static const struct file_operations spufs_mbox_stat_fops = {
.open = spufs_pipe_open,
.read = spufs_mbox_stat_read,
+ .llseek = no_llseek,
};
/* low-level ibox access function */
@@ -831,6 +853,7 @@ static const struct file_operations spufs_ibox_fops = {
.read = spufs_ibox_read,
.poll = spufs_ibox_poll,
.fasync = spufs_ibox_fasync,
+ .llseek = no_llseek,
};
static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf,
@@ -858,6 +881,7 @@ static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf,
static const struct file_operations spufs_ibox_stat_fops = {
.open = spufs_pipe_open,
.read = spufs_ibox_stat_read,
+ .llseek = no_llseek,
};
/* low-level mailbox write */
@@ -979,6 +1003,7 @@ static const struct file_operations spufs_wbox_fops = {
.write = spufs_wbox_write,
.poll = spufs_wbox_poll,
.fasync = spufs_wbox_fasync,
+ .llseek = no_llseek,
};
static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf,
@@ -1006,6 +1031,7 @@ static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf,
static const struct file_operations spufs_wbox_stat_fops = {
.open = spufs_pipe_open,
.read = spufs_wbox_stat_read,
+ .llseek = no_llseek,
};
static int spufs_signal1_open(struct inode *inode, struct file *file)
@@ -1097,23 +1123,23 @@ static ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
return 4;
}
-static unsigned long spufs_signal1_mmap_nopfn(struct vm_area_struct *vma,
- unsigned long address)
+static int
+spufs_signal1_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
-#if PAGE_SIZE == 0x1000
- return spufs_ps_nopfn(vma, address, 0x14000, 0x1000);
-#elif PAGE_SIZE == 0x10000
+#if SPUFS_SIGNAL_MAP_SIZE == 0x1000
+ return spufs_ps_fault(vma, vmf, 0x14000, SPUFS_SIGNAL_MAP_SIZE);
+#elif SPUFS_SIGNAL_MAP_SIZE == 0x10000
/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
* signal 1 and 2 area
*/
- return spufs_ps_nopfn(vma, address, 0x10000, 0x10000);
+ return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE);
#else
#error unsupported page size
#endif
}
-static struct vm_operations_struct spufs_signal1_mmap_vmops = {
- .nopfn = spufs_signal1_mmap_nopfn,
+static const struct vm_operations_struct spufs_signal1_mmap_vmops = {
+ .fault = spufs_signal1_mmap_fault,
};
static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
@@ -1122,8 +1148,7 @@ static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_signal1_mmap_vmops;
return 0;
@@ -1135,6 +1160,7 @@ static const struct file_operations spufs_signal1_fops = {
.read = spufs_signal1_read,
.write = spufs_signal1_write,
.mmap = spufs_signal1_mmap,
+ .llseek = no_llseek,
};
static const struct file_operations spufs_signal1_nosched_fops = {
@@ -1142,6 +1168,7 @@ static const struct file_operations spufs_signal1_nosched_fops = {
.release = spufs_signal1_release,
.write = spufs_signal1_write,
.mmap = spufs_signal1_mmap,
+ .llseek = no_llseek,
};
static int spufs_signal2_open(struct inode *inode, struct file *file)
@@ -1234,23 +1261,23 @@ static ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
}
#if SPUFS_MMAP_4K
-static unsigned long spufs_signal2_mmap_nopfn(struct vm_area_struct *vma,
- unsigned long address)
+static int
+spufs_signal2_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
-#if PAGE_SIZE == 0x1000
- return spufs_ps_nopfn(vma, address, 0x1c000, 0x1000);
-#elif PAGE_SIZE == 0x10000
+#if SPUFS_SIGNAL_MAP_SIZE == 0x1000
+ return spufs_ps_fault(vma, vmf, 0x1c000, SPUFS_SIGNAL_MAP_SIZE);
+#elif SPUFS_SIGNAL_MAP_SIZE == 0x10000
/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
* signal 1 and 2 area
*/
- return spufs_ps_nopfn(vma, address, 0x10000, 0x10000);
+ return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE);
#else
#error unsupported page size
#endif
}
-static struct vm_operations_struct spufs_signal2_mmap_vmops = {
- .nopfn = spufs_signal2_mmap_nopfn,
+static const struct vm_operations_struct spufs_signal2_mmap_vmops = {
+ .fault = spufs_signal2_mmap_fault,
};
static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
@@ -1259,8 +1286,7 @@ static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_signal2_mmap_vmops;
return 0;
@@ -1275,6 +1301,7 @@ static const struct file_operations spufs_signal2_fops = {
.read = spufs_signal2_read,
.write = spufs_signal2_write,
.mmap = spufs_signal2_mmap,
+ .llseek = no_llseek,
};
static const struct file_operations spufs_signal2_nosched_fops = {
@@ -1282,6 +1309,7 @@ static const struct file_operations spufs_signal2_nosched_fops = {
.release = spufs_signal2_release,
.write = spufs_signal2_write,
.mmap = spufs_signal2_mmap,
+ .llseek = no_llseek,
};
/*
@@ -1337,7 +1365,7 @@ static u64 spufs_signal1_type_get(struct spu_context *ctx)
return ctx->ops->signal1_type_get(ctx);
}
DEFINE_SPUFS_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get,
- spufs_signal1_type_set, "%llu", SPU_ATTR_ACQUIRE);
+ spufs_signal1_type_set, "%llu\n", SPU_ATTR_ACQUIRE);
static int spufs_signal2_type_set(void *data, u64 val)
@@ -1359,17 +1387,17 @@ static u64 spufs_signal2_type_get(struct spu_context *ctx)
return ctx->ops->signal2_type_get(ctx);
}
DEFINE_SPUFS_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
- spufs_signal2_type_set, "%llu", SPU_ATTR_ACQUIRE);
+ spufs_signal2_type_set, "%llu\n", SPU_ATTR_ACQUIRE);
#if SPUFS_MMAP_4K
-static unsigned long spufs_mss_mmap_nopfn(struct vm_area_struct *vma,
- unsigned long address)
+static int
+spufs_mss_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
- return spufs_ps_nopfn(vma, address, 0x0000, 0x1000);
+ return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_MSS_MAP_SIZE);
}
-static struct vm_operations_struct spufs_mss_mmap_vmops = {
- .nopfn = spufs_mss_mmap_nopfn,
+static const struct vm_operations_struct spufs_mss_mmap_vmops = {
+ .fault = spufs_mss_mmap_fault,
};
/*
@@ -1381,8 +1409,7 @@ static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_mss_mmap_vmops;
return 0;
@@ -1422,16 +1449,17 @@ static const struct file_operations spufs_mss_fops = {
.open = spufs_mss_open,
.release = spufs_mss_release,
.mmap = spufs_mss_mmap,
+ .llseek = no_llseek,
};
-static unsigned long spufs_psmap_mmap_nopfn(struct vm_area_struct *vma,
- unsigned long address)
+static int
+spufs_psmap_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
- return spufs_ps_nopfn(vma, address, 0x0000, 0x20000);
+ return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_PS_MAP_SIZE);
}
-static struct vm_operations_struct spufs_psmap_mmap_vmops = {
- .nopfn = spufs_psmap_mmap_nopfn,
+static const struct vm_operations_struct spufs_psmap_mmap_vmops = {
+ .fault = spufs_psmap_mmap_fault,
};
/*
@@ -1443,8 +1471,7 @@ static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_psmap_mmap_vmops;
return 0;
@@ -1480,18 +1507,19 @@ static const struct file_operations spufs_psmap_fops = {
.open = spufs_psmap_open,
.release = spufs_psmap_release,
.mmap = spufs_psmap_mmap,
+ .llseek = no_llseek,
};
#if SPUFS_MMAP_4K
-static unsigned long spufs_mfc_mmap_nopfn(struct vm_area_struct *vma,
- unsigned long address)
+static int
+spufs_mfc_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
- return spufs_ps_nopfn(vma, address, 0x3000, 0x1000);
+ return spufs_ps_fault(vma, vmf, 0x3000, SPUFS_MFC_MAP_SIZE);
}
-static struct vm_operations_struct spufs_mfc_mmap_vmops = {
- .nopfn = spufs_mfc_mmap_nopfn,
+static const struct vm_operations_struct spufs_mfc_mmap_vmops = {
+ .fault = spufs_mfc_mmap_fault,
};
/*
@@ -1503,8 +1531,7 @@ static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_mfc_mmap_vmops;
return 0;
@@ -1556,7 +1583,7 @@ void spufs_mfc_callback(struct spu *spu)
wake_up_all(&ctx->mfc_wq);
- pr_debug("%s %s\n", __FUNCTION__, spu->name);
+ pr_debug("%s %s\n", __func__, spu->name);
if (ctx->mfc_fasync) {
u32 free_elements, tagstatus;
unsigned int mask;
@@ -1630,7 +1657,7 @@ out:
static int spufs_check_valid_dma(struct mfc_dma_command *cmd)
{
- pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa,
+ pr_debug("queueing DMA %x %llx %x %x %x\n", cmd->lsa,
cmd->ea, cmd->size, cmd->tag, cmd->cmd);
switch (cmd->cmd) {
@@ -1647,7 +1674,7 @@ static int spufs_check_valid_dma(struct mfc_dma_command *cmd)
}
if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) {
- pr_debug("invalid DMA alignment, ea %lx lsa %x\n",
+ pr_debug("invalid DMA alignment, ea %llx lsa %x\n",
cmd->ea, cmd->lsa);
return -EIO;
}
@@ -1790,7 +1817,7 @@ static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait)
if (tagstatus & ctx->tagwait)
mask |= POLLIN | POLLRDNORM;
- pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__,
+ pr_debug("%s: free %d tagstatus %d tagwait %d\n", __func__,
free_elements, tagstatus, ctx->tagwait);
return mask;
@@ -1822,10 +1849,16 @@ out:
return ret;
}
-static int spufs_mfc_fsync(struct file *file, struct dentry *dentry,
- int datasync)
+static int spufs_mfc_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
- return spufs_mfc_flush(file, NULL);
+ struct inode *inode = file_inode(file);
+ int err = filemap_write_and_wait_range(inode->i_mapping, start, end);
+ if (!err) {
+ mutex_lock(&inode->i_mutex);
+ err = spufs_mfc_flush(file, NULL);
+ mutex_unlock(&inode->i_mutex);
+ }
+ return err;
}
static int spufs_mfc_fasync(int fd, struct file *file, int on)
@@ -1845,6 +1878,7 @@ static const struct file_operations spufs_mfc_fops = {
.fsync = spufs_mfc_fsync,
.fasync = spufs_mfc_fasync,
.mmap = spufs_mfc_mmap,
+ .llseek = no_llseek,
};
static int spufs_npc_set(void *data, u64 val)
@@ -2220,6 +2254,7 @@ static ssize_t spufs_dma_info_read(struct file *file, char __user *buf,
static const struct file_operations spufs_dma_info_fops = {
.open = spufs_info_open,
.read = spufs_dma_info_read,
+ .llseek = no_llseek,
};
static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx,
@@ -2273,6 +2308,7 @@ static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf,
static const struct file_operations spufs_proxydma_info_fops = {
.open = spufs_info_open,
.read = spufs_proxydma_info_read,
+ .llseek = no_llseek,
};
static int spufs_show_tid(struct seq_file *s, void *private)
@@ -2387,23 +2423,276 @@ static const struct file_operations spufs_stat_fops = {
.release = single_release,
};
+static inline int spufs_switch_log_used(struct spu_context *ctx)
+{
+ return (ctx->switch_log->head - ctx->switch_log->tail) %
+ SWITCH_LOG_BUFSIZE;
+}
+
+static inline int spufs_switch_log_avail(struct spu_context *ctx)
+{
+ return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx);
+}
+
+static int spufs_switch_log_open(struct inode *inode, struct file *file)
+{
+ struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
+ int rc;
+
+ rc = spu_acquire(ctx);
+ if (rc)
+ return rc;
+
+ if (ctx->switch_log) {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ ctx->switch_log = kmalloc(sizeof(struct switch_log) +
+ SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry),
+ GFP_KERNEL);
+
+ if (!ctx->switch_log) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ ctx->switch_log->head = ctx->switch_log->tail = 0;
+ init_waitqueue_head(&ctx->switch_log->wait);
+ rc = 0;
+
+out:
+ spu_release(ctx);
+ return rc;
+}
+
+static int spufs_switch_log_release(struct inode *inode, struct file *file)
+{
+ struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
+ int rc;
+
+ rc = spu_acquire(ctx);
+ if (rc)
+ return rc;
+
+ kfree(ctx->switch_log);
+ ctx->switch_log = NULL;
+ spu_release(ctx);
+
+ return 0;
+}
+
+static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n)
+{
+ struct switch_log_entry *p;
+
+ p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE;
+
+ return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n",
+ (unsigned int) p->tstamp.tv_sec,
+ (unsigned int) p->tstamp.tv_nsec,
+ p->spu_id,
+ (unsigned int) p->type,
+ (unsigned int) p->val,
+ (unsigned long long) p->timebase);
+}
-struct tree_descr spufs_dir_contents[] = {
+static ssize_t spufs_switch_log_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct inode *inode = file_inode(file);
+ struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
+ int error = 0, cnt = 0;
+
+ if (!buf)
+ return -EINVAL;
+
+ error = spu_acquire(ctx);
+ if (error)
+ return error;
+
+ while (cnt < len) {
+ char tbuf[128];
+ int width;
+
+ if (spufs_switch_log_used(ctx) == 0) {
+ if (cnt > 0) {
+ /* If there's data ready to go, we can
+ * just return straight away */
+ break;
+
+ } else if (file->f_flags & O_NONBLOCK) {
+ error = -EAGAIN;
+ break;
+
+ } else {
+ /* spufs_wait will drop the mutex and
+ * re-acquire, but since we're in read(), the
+ * file cannot be _released (and so
+ * ctx->switch_log is stable).
+ */
+ error = spufs_wait(ctx->switch_log->wait,
+ spufs_switch_log_used(ctx) > 0);
+
+ /* On error, spufs_wait returns without the
+ * state mutex held */
+ if (error)
+ return error;
+
+ /* We may have had entries read from underneath
+ * us while we dropped the mutex in spufs_wait,
+ * so re-check */
+ if (spufs_switch_log_used(ctx) == 0)
+ continue;
+ }
+ }
+
+ width = switch_log_sprint(ctx, tbuf, sizeof(tbuf));
+ if (width < len)
+ ctx->switch_log->tail =
+ (ctx->switch_log->tail + 1) %
+ SWITCH_LOG_BUFSIZE;
+ else
+ /* If the record is greater than space available return
+ * partial buffer (so far) */
+ break;
+
+ error = copy_to_user(buf + cnt, tbuf, width);
+ if (error)
+ break;
+ cnt += width;
+ }
+
+ spu_release(ctx);
+
+ return cnt == 0 ? error : cnt;
+}
+
+static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait)
+{
+ struct inode *inode = file_inode(file);
+ struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
+ unsigned int mask = 0;
+ int rc;
+
+ poll_wait(file, &ctx->switch_log->wait, wait);
+
+ rc = spu_acquire(ctx);
+ if (rc)
+ return rc;
+
+ if (spufs_switch_log_used(ctx) > 0)
+ mask |= POLLIN;
+
+ spu_release(ctx);
+
+ return mask;
+}
+
+static const struct file_operations spufs_switch_log_fops = {
+ .open = spufs_switch_log_open,
+ .read = spufs_switch_log_read,
+ .poll = spufs_switch_log_poll,
+ .release = spufs_switch_log_release,
+ .llseek = no_llseek,
+};
+
+/**
+ * Log a context switch event to a switch log reader.
+ *
+ * Must be called with ctx->state_mutex held.
+ */
+void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
+ u32 type, u32 val)
+{
+ if (!ctx->switch_log)
+ return;
+
+ if (spufs_switch_log_avail(ctx) > 1) {
+ struct switch_log_entry *p;
+
+ p = ctx->switch_log->log + ctx->switch_log->head;
+ ktime_get_ts(&p->tstamp);
+ p->timebase = get_tb();
+ p->spu_id = spu ? spu->number : -1;
+ p->type = type;
+ p->val = val;
+
+ ctx->switch_log->head =
+ (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE;
+ }
+
+ wake_up(&ctx->switch_log->wait);
+}
+
+static int spufs_show_ctx(struct seq_file *s, void *private)
+{
+ struct spu_context *ctx = s->private;
+ u64 mfc_control_RW;
+
+ mutex_lock(&ctx->state_mutex);
+ if (ctx->spu) {
+ struct spu *spu = ctx->spu;
+ struct spu_priv2 __iomem *priv2 = spu->priv2;
+
+ spin_lock_irq(&spu->register_lock);
+ mfc_control_RW = in_be64(&priv2->mfc_control_RW);
+ spin_unlock_irq(&spu->register_lock);
+ } else {
+ struct spu_state *csa = &ctx->csa;
+
+ mfc_control_RW = csa->priv2.mfc_control_RW;
+ }
+
+ seq_printf(s, "%c flgs(%lx) sflgs(%lx) pri(%d) ts(%d) spu(%02d)"
+ " %c %llx %llx %llx %llx %x %x\n",
+ ctx->state == SPU_STATE_SAVED ? 'S' : 'R',
+ ctx->flags,
+ ctx->sched_flags,
+ ctx->prio,
+ ctx->time_slice,
+ ctx->spu ? ctx->spu->number : -1,
+ !list_empty(&ctx->rq) ? 'q' : ' ',
+ ctx->csa.class_0_pending,
+ ctx->csa.class_0_dar,
+ ctx->csa.class_1_dsisr,
+ mfc_control_RW,
+ ctx->ops->runcntl_read(ctx),
+ ctx->ops->status_read(ctx));
+
+ mutex_unlock(&ctx->state_mutex);
+
+ return 0;
+}
+
+static int spufs_ctx_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, spufs_show_ctx, SPUFS_I(inode)->i_ctx);
+}
+
+static const struct file_operations spufs_ctx_fops = {
+ .open = spufs_ctx_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+const struct spufs_tree_descr spufs_dir_contents[] = {
{ "capabilities", &spufs_caps_fops, 0444, },
- { "mem", &spufs_mem_fops, 0666, },
- { "regs", &spufs_regs_fops, 0666, },
+ { "mem", &spufs_mem_fops, 0666, LS_SIZE, },
+ { "regs", &spufs_regs_fops, 0666, sizeof(struct spu_reg128[128]), },
{ "mbox", &spufs_mbox_fops, 0444, },
{ "ibox", &spufs_ibox_fops, 0444, },
{ "wbox", &spufs_wbox_fops, 0222, },
- { "mbox_stat", &spufs_mbox_stat_fops, 0444, },
- { "ibox_stat", &spufs_ibox_stat_fops, 0444, },
- { "wbox_stat", &spufs_wbox_stat_fops, 0444, },
+ { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), },
+ { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), },
+ { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), },
{ "signal1", &spufs_signal1_fops, 0666, },
{ "signal2", &spufs_signal2_fops, 0666, },
{ "signal1_type", &spufs_signal1_type, 0666, },
{ "signal2_type", &spufs_signal2_type, 0666, },
{ "cntl", &spufs_cntl_fops, 0666, },
- { "fpcr", &spufs_fpcr_fops, 0666, },
+ { "fpcr", &spufs_fpcr_fops, 0666, sizeof(struct spu_reg128), },
{ "lslr", &spufs_lslr_ops, 0444, },
{ "mfc", &spufs_mfc_fops, 0666, },
{ "mss", &spufs_mss_fops, 0666, },
@@ -2413,28 +2702,31 @@ struct tree_descr spufs_dir_contents[] = {
{ "decr_status", &spufs_decr_status_ops, 0666, },
{ "event_mask", &spufs_event_mask_ops, 0666, },
{ "event_status", &spufs_event_status_ops, 0444, },
- { "psmap", &spufs_psmap_fops, 0666, },
+ { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, },
{ "phys-id", &spufs_id_ops, 0666, },
{ "object-id", &spufs_object_id_ops, 0666, },
- { "mbox_info", &spufs_mbox_info_fops, 0444, },
- { "ibox_info", &spufs_ibox_info_fops, 0444, },
- { "wbox_info", &spufs_wbox_info_fops, 0444, },
- { "dma_info", &spufs_dma_info_fops, 0444, },
- { "proxydma_info", &spufs_proxydma_info_fops, 0444, },
+ { "mbox_info", &spufs_mbox_info_fops, 0444, sizeof(u32), },
+ { "ibox_info", &spufs_ibox_info_fops, 0444, sizeof(u32), },
+ { "wbox_info", &spufs_wbox_info_fops, 0444, sizeof(u32), },
+ { "dma_info", &spufs_dma_info_fops, 0444,
+ sizeof(struct spu_dma_info), },
+ { "proxydma_info", &spufs_proxydma_info_fops, 0444,
+ sizeof(struct spu_proxydma_info)},
{ "tid", &spufs_tid_fops, 0444, },
{ "stat", &spufs_stat_fops, 0444, },
+ { "switch_log", &spufs_switch_log_fops, 0444 },
{},
};
-struct tree_descr spufs_dir_nosched_contents[] = {
+const struct spufs_tree_descr spufs_dir_nosched_contents[] = {
{ "capabilities", &spufs_caps_fops, 0444, },
- { "mem", &spufs_mem_fops, 0666, },
+ { "mem", &spufs_mem_fops, 0666, LS_SIZE, },
{ "mbox", &spufs_mbox_fops, 0444, },
{ "ibox", &spufs_ibox_fops, 0444, },
{ "wbox", &spufs_wbox_fops, 0222, },
- { "mbox_stat", &spufs_mbox_stat_fops, 0444, },
- { "ibox_stat", &spufs_ibox_stat_fops, 0444, },
- { "wbox_stat", &spufs_wbox_stat_fops, 0444, },
+ { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), },
+ { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), },
+ { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), },
{ "signal1", &spufs_signal1_nosched_fops, 0222, },
{ "signal2", &spufs_signal2_nosched_fops, 0222, },
{ "signal1_type", &spufs_signal1_type, 0666, },
@@ -2443,7 +2735,7 @@ struct tree_descr spufs_dir_nosched_contents[] = {
{ "mfc", &spufs_mfc_fops, 0666, },
{ "cntl", &spufs_cntl_fops, 0666, },
{ "npc", &spufs_npc_ops, 0666, },
- { "psmap", &spufs_psmap_fops, 0666, },
+ { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, },
{ "phys-id", &spufs_id_ops, 0666, },
{ "object-id", &spufs_object_id_ops, 0666, },
{ "tid", &spufs_tid_fops, 0444, },
@@ -2451,7 +2743,12 @@ struct tree_descr spufs_dir_nosched_contents[] = {
{},
};
-struct spufs_coredump_reader spufs_coredump_read[] = {
+const struct spufs_tree_descr spufs_dir_debug_contents[] = {
+ { ".ctx", &spufs_ctx_fops, 0444, },
+ {},
+};
+
+const struct spufs_coredump_reader spufs_coredump_read[] = {
{ "regs", __spufs_regs_read, NULL, sizeof(struct spu_reg128[128])},
{ "fpcr", __spufs_fpcr_read, NULL, sizeof(struct spu_reg128) },
{ "lslr", NULL, spufs_lslr_get, 19 },
diff --git a/arch/powerpc/platforms/cell/spufs/hw_ops.c b/arch/powerpc/platforms/cell/spufs/hw_ops.c
index 64f8540b832..8655c4cbefc 100644
--- a/arch/powerpc/platforms/cell/spufs/hw_ops.c
+++ b/arch/powerpc/platforms/cell/spufs/hw_ops.c
@@ -18,7 +18,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index 6d1228c66c5..87ba7cf99cd 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -23,6 +23,7 @@
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/fsnotify.h>
#include <linux/backing-dev.h>
#include <linux/init.h>
#include <linux/ioctl.h>
@@ -35,17 +36,25 @@
#include <linux/parser.h>
#include <asm/prom.h>
-#include <asm/semaphore.h>
#include <asm/spu.h>
#include <asm/spu_priv1.h>
#include <asm/uaccess.h>
#include "spufs.h"
+struct spufs_sb_info {
+ int debug;
+};
+
static struct kmem_cache *spufs_inode_cache;
char *isolated_loader;
static int isolated_loader_size;
+static struct spufs_sb_info *spufs_get_sb_info(struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+
static struct inode *
spufs_alloc_inode(struct super_block *sb)
{
@@ -62,14 +71,19 @@ spufs_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}
-static void
-spufs_destroy_inode(struct inode *inode)
+static void spufs_i_callback(struct rcu_head *head)
{
+ struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
}
+static void spufs_destroy_inode(struct inode *inode)
+{
+ call_rcu(&inode->i_rcu, spufs_i_callback);
+}
+
static void
-spufs_init_once(struct kmem_cache *cachep, void *p)
+spufs_init_once(void *p)
{
struct spufs_inode_info *ei = p;
@@ -77,7 +91,7 @@ spufs_init_once(struct kmem_cache *cachep, void *p)
}
static struct inode *
-spufs_new_inode(struct super_block *sb, int mode)
+spufs_new_inode(struct super_block *sb, umode_t mode)
{
struct inode *inode;
@@ -85,10 +99,10 @@ spufs_new_inode(struct super_block *sb, int mode)
if (!inode)
goto out;
+ inode->i_ino = get_next_ino();
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
- inode->i_blocks = 0;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
out:
return inode;
@@ -102,16 +116,18 @@ spufs_setattr(struct dentry *dentry, struct iattr *attr)
if ((attr->ia_valid & ATTR_SIZE) &&
(attr->ia_size != inode->i_size))
return -EINVAL;
- return inode_setattr(inode, attr);
+ setattr_copy(inode, attr);
+ mark_inode_dirty(inode);
+ return 0;
}
static int
spufs_new_file(struct super_block *sb, struct dentry *dentry,
- const struct file_operations *fops, int mode,
- struct spu_context *ctx)
+ const struct file_operations *fops, umode_t mode,
+ size_t size, struct spu_context *ctx)
{
- static struct inode_operations spufs_file_iops = {
+ static const struct inode_operations spufs_file_iops = {
.setattr = spufs_setattr,
};
struct inode *inode;
@@ -125,6 +141,7 @@ spufs_new_file(struct super_block *sb, struct dentry *dentry,
ret = 0;
inode->i_op = &spufs_file_iops;
inode->i_fop = fops;
+ inode->i_size = size;
inode->i_private = SPUFS_I(inode)->i_ctx = get_spu_context(ctx);
d_add(dentry, inode);
out:
@@ -132,15 +149,14 @@ out:
}
static void
-spufs_delete_inode(struct inode *inode)
+spufs_evict_inode(struct inode *inode)
{
struct spufs_inode_info *ei = SPUFS_I(inode);
-
+ clear_inode(inode);
if (ei->i_ctx)
put_spu_context(ei->i_ctx);
if (ei->i_gang)
put_spu_gang(ei->i_gang);
- clear_inode(inode);
}
static void spufs_prune_dir(struct dentry *dir)
@@ -149,18 +165,18 @@ static void spufs_prune_dir(struct dentry *dir)
mutex_lock(&dir->d_inode->i_mutex);
list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
- spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (!(d_unhashed(dentry)) && dentry->d_inode) {
- dget_locked(dentry);
+ dget_dlock(dentry);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
simple_unlink(dir->d_inode, dentry);
- spin_unlock(&dcache_lock);
+ /* XXX: what was dcache_lock protecting here? Other
+ * filesystems (IB, configfs) release dcache_lock
+ * before unlink */
dput(dentry);
} else {
spin_unlock(&dentry->d_lock);
- spin_unlock(&dcache_lock);
}
}
shrink_dcache_parent(dir);
@@ -171,46 +187,31 @@ static void spufs_prune_dir(struct dentry *dir)
static int spufs_rmdir(struct inode *parent, struct dentry *dir)
{
/* remove all entries */
+ int res;
spufs_prune_dir(dir);
d_drop(dir);
-
- return simple_rmdir(parent, dir);
+ res = simple_rmdir(parent, dir);
+ /* We have to give up the mm_struct */
+ spu_forget(SPUFS_I(dir->d_inode)->i_ctx);
+ return res;
}
-static int spufs_fill_dir(struct dentry *dir, struct tree_descr *files,
- int mode, struct spu_context *ctx)
+static int spufs_fill_dir(struct dentry *dir,
+ const struct spufs_tree_descr *files, umode_t mode,
+ struct spu_context *ctx)
{
- struct dentry *dentry, *tmp;
- int ret;
-
while (files->name && files->name[0]) {
- ret = -ENOMEM;
- dentry = d_alloc_name(dir, files->name);
+ int ret;
+ struct dentry *dentry = d_alloc_name(dir, files->name);
if (!dentry)
- goto out;
+ return -ENOMEM;
ret = spufs_new_file(dir->d_sb, dentry, files->ops,
- files->mode & mode, ctx);
+ files->mode & mode, files->size, ctx);
if (ret)
- goto out;
+ return ret;
files++;
}
return 0;
-out:
- /*
- * remove all children from dir. dir->inode is not set so don't
- * just simply use spufs_prune_dir() and panic afterwards :)
- * dput() looks like it will do the right thing:
- * - dec parent's ref counter
- * - remove child from parent's child list
- * - free child's inode if possible
- * - free child
- */
- list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
- dput(dentry);
- }
-
- shrink_dcache_parent(dir);
- return ret;
}
static int spufs_dir_close(struct inode *inode, struct file *file)
@@ -224,14 +225,11 @@ static int spufs_dir_close(struct inode *inode, struct file *file)
parent = dir->d_parent->d_inode;
ctx = SPUFS_I(dir->d_inode)->i_ctx;
- mutex_lock(&parent->i_mutex);
+ mutex_lock_nested(&parent->i_mutex, I_MUTEX_PARENT);
ret = spufs_rmdir(parent, dir);
mutex_unlock(&parent->i_mutex);
WARN_ON(ret);
- /* We have to give up the mm_struct */
- spu_forget(ctx);
-
return dcache_dir_close(inode, file);
}
@@ -240,23 +238,22 @@ const struct file_operations spufs_context_fops = {
.release = spufs_dir_close,
.llseek = dcache_dir_lseek,
.read = generic_read_dir,
- .readdir = dcache_readdir,
- .fsync = simple_sync_file,
+ .iterate = dcache_readdir,
+ .fsync = noop_fsync,
};
EXPORT_SYMBOL_GPL(spufs_context_fops);
static int
spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
- int mode)
+ umode_t mode)
{
int ret;
struct inode *inode;
struct spu_context *ctx;
- ret = -ENOSPC;
inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
if (!inode)
- goto out;
+ return -ENOSPC;
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
@@ -264,58 +261,58 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
}
ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */
SPUFS_I(inode)->i_ctx = ctx;
- if (!ctx)
- goto out_iput;
+ if (!ctx) {
+ iput(inode);
+ return -ENOSPC;
+ }
ctx->flags = flags;
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
+
+ mutex_lock(&inode->i_mutex);
+
+ dget(dentry);
+ inc_nlink(dir);
+ inc_nlink(inode);
+
+ d_instantiate(dentry, inode);
+
if (flags & SPU_CREATE_NOSCHED)
ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents,
mode, ctx);
else
ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx);
+ if (!ret && spufs_get_sb_info(dir->i_sb)->debug)
+ ret = spufs_fill_dir(dentry, spufs_dir_debug_contents,
+ mode, ctx);
+
if (ret)
- goto out_free_ctx;
+ spufs_rmdir(dir, dentry);
- d_instantiate(dentry, inode);
- dget(dentry);
- dir->i_nlink++;
- dentry->d_inode->i_nlink++;
- goto out;
+ mutex_unlock(&inode->i_mutex);
-out_free_ctx:
- spu_forget(ctx);
- put_spu_context(ctx);
-out_iput:
- iput(inode);
-out:
return ret;
}
-static int spufs_context_open(struct dentry *dentry, struct vfsmount *mnt)
+static int spufs_context_open(struct path *path)
{
int ret;
struct file *filp;
ret = get_unused_fd();
- if (ret < 0) {
- dput(dentry);
- mntput(mnt);
- goto out;
- }
+ if (ret < 0)
+ return ret;
- filp = dentry_open(dentry, mnt, O_RDONLY);
+ filp = dentry_open(path, O_RDONLY, current_cred());
if (IS_ERR(filp)) {
put_unused_fd(ret);
- ret = PTR_ERR(filp);
- goto out;
+ return PTR_ERR(filp);
}
filp->f_op = &spufs_context_fops;
fd_install(ret, filp);
-out:
return ret;
}
@@ -350,7 +347,7 @@ spufs_assert_affinity(unsigned int flags, struct spu_gang *gang,
return ERR_PTR(-EINVAL);
neighbor = get_spu_context(
- SPUFS_I(filp->f_dentry->d_inode)->i_ctx);
+ SPUFS_I(file_inode(filp))->i_ctx);
if (!list_empty(&neighbor->aff_list) && !(neighbor->aff_head) &&
!list_is_last(&neighbor->aff_list, &gang->aff_list_head) &&
@@ -423,36 +420,33 @@ spufs_set_affinity(unsigned int flags, struct spu_context *ctx,
static int
spufs_create_context(struct inode *inode, struct dentry *dentry,
- struct vfsmount *mnt, int flags, int mode,
+ struct vfsmount *mnt, int flags, umode_t mode,
struct file *aff_filp)
{
int ret;
int affinity;
struct spu_gang *gang;
struct spu_context *neighbor;
+ struct path path = {.mnt = mnt, .dentry = dentry};
- ret = -EPERM;
if ((flags & SPU_CREATE_NOSCHED) &&
!capable(CAP_SYS_NICE))
- goto out_unlock;
+ return -EPERM;
- ret = -EINVAL;
if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE))
== SPU_CREATE_ISOLATE)
- goto out_unlock;
+ return -EINVAL;
- ret = -ENODEV;
if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
- goto out_unlock;
+ return -ENODEV;
gang = NULL;
neighbor = NULL;
affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
if (affinity) {
gang = SPUFS_I(inode)->i_gang;
- ret = -EINVAL;
if (!gang)
- goto out_unlock;
+ return -EINVAL;
mutex_lock(&gang->aff_mutex);
neighbor = spufs_assert_affinity(flags, gang, aff_filp);
if (IS_ERR(neighbor)) {
@@ -472,30 +466,18 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
put_spu_context(neighbor);
}
- /*
- * get references for dget and mntget, will be released
- * in error path of *_open().
- */
- ret = spufs_context_open(dget(dentry), mntget(mnt));
- if (ret < 0) {
+ ret = spufs_context_open(&path);
+ if (ret < 0)
WARN_ON(spufs_rmdir(inode, dentry));
- mutex_unlock(&inode->i_mutex);
- spu_forget(SPUFS_I(dentry->d_inode)->i_ctx);
- goto out;
- }
out_aff_unlock:
if (affinity)
mutex_unlock(&gang->aff_mutex);
-out_unlock:
- mutex_unlock(&inode->i_mutex);
-out:
- dput(dentry);
return ret;
}
static int
-spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode)
+spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
{
int ret;
struct inode *inode;
@@ -521,8 +503,8 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode)
inode->i_fop = &simple_dir_operations;
d_instantiate(dentry, inode);
- dir->i_nlink++;
- dentry->d_inode->i_nlink++;
+ inc_nlink(dir);
+ inc_nlink(dentry->d_inode);
return ret;
out_iput:
@@ -531,123 +513,98 @@ out:
return ret;
}
-static int spufs_gang_open(struct dentry *dentry, struct vfsmount *mnt)
+static int spufs_gang_open(struct path *path)
{
int ret;
struct file *filp;
ret = get_unused_fd();
- if (ret < 0) {
- dput(dentry);
- mntput(mnt);
- goto out;
- }
+ if (ret < 0)
+ return ret;
- filp = dentry_open(dentry, mnt, O_RDONLY);
+ /*
+ * get references for dget and mntget, will be released
+ * in error path of *_open().
+ */
+ filp = dentry_open(path, O_RDONLY, current_cred());
if (IS_ERR(filp)) {
put_unused_fd(ret);
- ret = PTR_ERR(filp);
- goto out;
+ return PTR_ERR(filp);
}
filp->f_op = &simple_dir_operations;
fd_install(ret, filp);
-out:
return ret;
}
static int spufs_create_gang(struct inode *inode,
struct dentry *dentry,
- struct vfsmount *mnt, int mode)
+ struct vfsmount *mnt, umode_t mode)
{
+ struct path path = {.mnt = mnt, .dentry = dentry};
int ret;
ret = spufs_mkgang(inode, dentry, mode & S_IRWXUGO);
- if (ret)
- goto out;
-
- /*
- * get references for dget and mntget, will be released
- * in error path of *_open().
- */
- ret = spufs_gang_open(dget(dentry), mntget(mnt));
- if (ret < 0) {
- int err = simple_rmdir(inode, dentry);
- WARN_ON(err);
+ if (!ret) {
+ ret = spufs_gang_open(&path);
+ if (ret < 0) {
+ int err = simple_rmdir(inode, dentry);
+ WARN_ON(err);
+ }
}
-
-out:
- mutex_unlock(&inode->i_mutex);
- dput(dentry);
return ret;
}
static struct file_system_type spufs_type;
-long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode,
- struct file *filp)
+long spufs_create(struct path *path, struct dentry *dentry,
+ unsigned int flags, umode_t mode, struct file *filp)
{
- struct dentry *dentry;
+ struct inode *dir = path->dentry->d_inode;
int ret;
- ret = -EINVAL;
/* check if we are on spufs */
- if (nd->path.dentry->d_sb->s_type != &spufs_type)
- goto out;
+ if (path->dentry->d_sb->s_type != &spufs_type)
+ return -EINVAL;
/* don't accept undefined flags */
if (flags & (~SPU_CREATE_FLAG_ALL))
- goto out;
+ return -EINVAL;
/* only threads can be underneath a gang */
- if (nd->path.dentry != nd->path.dentry->d_sb->s_root) {
- if ((flags & SPU_CREATE_GANG) ||
- !SPUFS_I(nd->path.dentry->d_inode)->i_gang)
- goto out;
- }
-
- dentry = lookup_create(nd, 1);
- ret = PTR_ERR(dentry);
- if (IS_ERR(dentry))
- goto out_dir;
+ if (path->dentry != path->dentry->d_sb->s_root)
+ if ((flags & SPU_CREATE_GANG) || !SPUFS_I(dir)->i_gang)
+ return -EINVAL;
- ret = -EEXIST;
- if (dentry->d_inode)
- goto out_dput;
-
- mode &= ~current->fs->umask;
+ mode &= ~current_umask();
if (flags & SPU_CREATE_GANG)
- return spufs_create_gang(nd->path.dentry->d_inode,
- dentry, nd->path.mnt, mode);
+ ret = spufs_create_gang(dir, dentry, path->mnt, mode);
else
- return spufs_create_context(nd->path.dentry->d_inode,
- dentry, nd->path.mnt, flags, mode,
+ ret = spufs_create_context(dir, dentry, path->mnt, flags, mode,
filp);
+ if (ret >= 0)
+ fsnotify_mkdir(dir, dentry);
-out_dput:
- dput(dentry);
-out_dir:
- mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
-out:
return ret;
}
/* File system initialization */
enum {
- Opt_uid, Opt_gid, Opt_mode, Opt_err,
+ Opt_uid, Opt_gid, Opt_mode, Opt_debug, Opt_err,
};
-static match_table_t spufs_tokens = {
- { Opt_uid, "uid=%d" },
- { Opt_gid, "gid=%d" },
- { Opt_mode, "mode=%o" },
- { Opt_err, NULL },
+static const match_table_t spufs_tokens = {
+ { Opt_uid, "uid=%d" },
+ { Opt_gid, "gid=%d" },
+ { Opt_mode, "mode=%o" },
+ { Opt_debug, "debug" },
+ { Opt_err, NULL },
};
static int
-spufs_parse_options(char *options, struct inode *root)
+spufs_parse_options(struct super_block *sb, char *options, struct inode *root)
{
char *p;
substring_t args[MAX_OPT_ARGS];
@@ -663,18 +620,25 @@ spufs_parse_options(char *options, struct inode *root)
case Opt_uid:
if (match_int(&args[0], &option))
return 0;
- root->i_uid = option;
+ root->i_uid = make_kuid(current_user_ns(), option);
+ if (!uid_valid(root->i_uid))
+ return 0;
break;
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
- root->i_gid = option;
+ root->i_gid = make_kgid(current_user_ns(), option);
+ if (!gid_valid(root->i_gid))
+ return 0;
break;
case Opt_mode:
if (match_octal(&args[0], &option))
return 0;
root->i_mode = option | S_IFDIR;
break;
+ case Opt_debug:
+ spufs_get_sb_info(sb)->debug = 1;
+ break;
default:
return 0;
}
@@ -731,15 +695,16 @@ spufs_create_root(struct super_block *sb, void *data)
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
SPUFS_I(inode)->i_ctx = NULL;
+ inc_nlink(inode);
ret = -EINVAL;
- if (!spufs_parse_options(data, inode))
+ if (!spufs_parse_options(sb, data, inode))
goto out_iput;
ret = -ENOMEM;
- sb->s_root = d_alloc_root(inode);
+ sb->s_root = d_make_root(inode);
if (!sb->s_root)
- goto out_iput;
+ goto out;
return 0;
out_iput:
@@ -751,39 +716,45 @@ out:
static int
spufs_fill_super(struct super_block *sb, void *data, int silent)
{
- static struct super_operations s_ops = {
+ struct spufs_sb_info *info;
+ static const struct super_operations s_ops = {
.alloc_inode = spufs_alloc_inode,
.destroy_inode = spufs_destroy_inode,
.statfs = simple_statfs,
- .delete_inode = spufs_delete_inode,
- .drop_inode = generic_delete_inode,
+ .evict_inode = spufs_evict_inode,
.show_options = generic_show_options,
};
save_mount_options(sb, data);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = SPUFS_MAGIC;
sb->s_op = &s_ops;
+ sb->s_fs_info = info;
return spufs_create_root(sb, data);
}
-static int
-spufs_get_sb(struct file_system_type *fstype, int flags,
- const char *name, void *data, struct vfsmount *mnt)
+static struct dentry *
+spufs_mount(struct file_system_type *fstype, int flags,
+ const char *name, void *data)
{
- return get_sb_single(fstype, flags, data, spufs_fill_super, mnt);
+ return mount_single(fstype, flags, data, spufs_fill_super);
}
static struct file_system_type spufs_type = {
.owner = THIS_MODULE,
.name = "spufs",
- .get_sb = spufs_get_sb,
+ .mount = spufs_mount,
.kill_sb = kill_litter_super,
};
+MODULE_ALIAS_FS("spufs");
static int __init spufs_init(void)
{
@@ -803,19 +774,19 @@ static int __init spufs_init(void)
ret = spu_sched_init();
if (ret)
goto out_cache;
- ret = register_filesystem(&spufs_type);
+ ret = register_spu_syscalls(&spufs_calls);
if (ret)
goto out_sched;
- ret = register_spu_syscalls(&spufs_calls);
+ ret = register_filesystem(&spufs_type);
if (ret)
- goto out_fs;
+ goto out_syscalls;
spufs_init_isolated_loader();
return 0;
-out_fs:
- unregister_filesystem(&spufs_type);
+out_syscalls:
+ unregister_spu_syscalls(&spufs_calls);
out_sched:
spu_sched_exit();
out_cache:
diff --git a/arch/powerpc/platforms/cell/spufs/lscsa_alloc.c b/arch/powerpc/platforms/cell/spufs/lscsa_alloc.c
index 0e9f325c9ff..147069938cf 100644
--- a/arch/powerpc/platforms/cell/spufs/lscsa_alloc.c
+++ b/arch/powerpc/platforms/cell/spufs/lscsa_alloc.c
@@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <asm/spu.h>
@@ -35,10 +36,9 @@ static int spu_alloc_lscsa_std(struct spu_state *csa)
struct spu_lscsa *lscsa;
unsigned char *p;
- lscsa = vmalloc(sizeof(struct spu_lscsa));
+ lscsa = vzalloc(sizeof(struct spu_lscsa));
if (!lscsa)
return -ENOMEM;
- memset(lscsa, 0, sizeof(struct spu_lscsa));
csa->lscsa = lscsa;
/* Set LS pages reserved to allow for user-space mapping. */
@@ -90,7 +90,7 @@ int spu_alloc_lscsa(struct spu_state *csa)
*/
for (i = 0; i < SPU_LSCSA_NUM_BIG_PAGES; i++) {
/* XXX This is likely to fail, we should use a special pool
- * similiar to what hugetlbfs does.
+ * similar to what hugetlbfs does.
*/
csa->lscsa_pages[i] = alloc_pages(GFP_KERNEL,
SPU_64K_PAGE_ORDER);
diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c
index cac69e11677..4ddf769a64e 100644
--- a/arch/powerpc/platforms/cell/spufs/run.c
+++ b/arch/powerpc/platforms/cell/spufs/run.c
@@ -11,7 +11,7 @@
#include "spufs.h"
/* interrupt-level stop callback function. */
-void spufs_stop_callback(struct spu *spu)
+void spufs_stop_callback(struct spu *spu, int irq)
{
struct spu_context *ctx = spu->ctx;
@@ -24,9 +24,18 @@ void spufs_stop_callback(struct spu *spu)
*/
if (ctx) {
/* Copy exception arguments into module specific structure */
- ctx->csa.class_0_pending = spu->class_0_pending;
- ctx->csa.dsisr = spu->dsisr;
- ctx->csa.dar = spu->dar;
+ switch(irq) {
+ case 0 :
+ ctx->csa.class_0_pending = spu->class_0_pending;
+ ctx->csa.class_0_dar = spu->class_0_dar;
+ break;
+ case 1 :
+ ctx->csa.class_1_dsisr = spu->class_1_dsisr;
+ ctx->csa.class_1_dar = spu->class_1_dar;
+ break;
+ case 2 :
+ break;
+ }
/* ensure that the exception status has hit memory before a
* thread waiting on the context's stop queue is woken */
@@ -34,11 +43,6 @@ void spufs_stop_callback(struct spu *spu)
wake_up_all(&ctx->stop_wq);
}
-
- /* Clear callback arguments from spu structure */
- spu->class_0_pending = 0;
- spu->dsisr = 0;
- spu->dar = 0;
}
int spu_stopped(struct spu_context *ctx, u32 *stat)
@@ -46,17 +50,25 @@ int spu_stopped(struct spu_context *ctx, u32 *stat)
u64 dsisr;
u32 stopped;
- *stat = ctx->ops->status_read(ctx);
+ stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP |
+ SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP;
- if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))
+top:
+ *stat = ctx->ops->status_read(ctx);
+ if (*stat & stopped) {
+ /*
+ * If the spu hasn't finished stopping, we need to
+ * re-read the register to get the stopped value.
+ */
+ if (*stat & SPU_STATUS_RUNNING)
+ goto top;
return 1;
+ }
- stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP |
- SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP;
- if (!(*stat & SPU_STATUS_RUNNING) && (*stat & stopped))
+ if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))
return 1;
- dsisr = ctx->csa.dsisr;
+ dsisr = ctx->csa.class_1_dsisr;
if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))
return 1;
@@ -98,13 +110,16 @@ static int spu_setup_isolated(struct spu_context *ctx)
!= MFC_CNTL_PURGE_DMA_COMPLETE) {
if (time_after(jiffies, timeout)) {
printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n",
- __FUNCTION__);
+ __func__);
ret = -EIO;
goto out;
}
cond_resched();
}
+ /* clear purge status */
+ out_be64(mfc_cntl, 0);
+
/* put the SPE in kernel mode to allow access to the loader */
sr1 = spu_mfc_sr1_get(ctx->spu);
sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK;
@@ -124,7 +139,7 @@ static int spu_setup_isolated(struct spu_context *ctx)
status_loading) {
if (time_after(jiffies, timeout)) {
printk(KERN_ERR "%s: timeout waiting for loader\n",
- __FUNCTION__);
+ __func__);
ret = -EIO;
goto out_drop_priv;
}
@@ -134,7 +149,7 @@ static int spu_setup_isolated(struct spu_context *ctx)
if (!(status & SPU_STATUS_RUNNING)) {
/* If isolated LOAD has failed: run SPU, we will get a stop-and
* signal later. */
- pr_debug("%s: isolated LOAD failed\n", __FUNCTION__);
+ pr_debug("%s: isolated LOAD failed\n", __func__);
ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
ret = -EACCES;
goto out_drop_priv;
@@ -142,7 +157,7 @@ static int spu_setup_isolated(struct spu_context *ctx)
if (!(status & SPU_STATUS_ISOLATED_STATE)) {
/* This isn't allowed by the CBEA, but check anyway */
- pr_debug("%s: SPU fell out of isolated mode?\n", __FUNCTION__);
+ pr_debug("%s: SPU fell out of isolated mode?\n", __func__);
ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP);
ret = -EINVAL;
goto out_drop_priv;
@@ -194,11 +209,6 @@ static int spu_run_init(struct spu_context *ctx, u32 *npc)
(SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE);
if (runcntl == 0)
runcntl = SPU_RUNCNTL_RUNNABLE;
- }
-
- if (ctx->flags & SPU_CREATE_NOSCHED) {
- spuctx_switch_state(ctx, SPU_UTIL_USER);
- ctx->ops->runcntl_write(ctx, runcntl);
} else {
unsigned long privcntl;
@@ -207,9 +217,15 @@ static int spu_run_init(struct spu_context *ctx, u32 *npc)
else
privcntl = SPU_PRIVCNTL_MODE_NORMAL;
- ctx->ops->npc_write(ctx, *npc);
ctx->ops->privcntl_write(ctx, privcntl);
- ctx->ops->runcntl_write(ctx, runcntl);
+ ctx->ops->npc_write(ctx, *npc);
+ }
+
+ ctx->ops->runcntl_write(ctx, runcntl);
+
+ if (ctx->flags & SPU_CREATE_NOSCHED) {
+ spuctx_switch_state(ctx, SPU_UTIL_USER);
+ } else {
if (ctx->state == SPU_STATE_SAVED) {
ret = spu_activate(ctx, 0);
@@ -236,6 +252,7 @@ static int spu_run_fini(struct spu_context *ctx, u32 *npc,
spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED);
clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags);
+ spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status);
spu_release(ctx);
if (signal_pending(current))
@@ -282,7 +299,7 @@ static int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret,
break;
default:
printk(KERN_WARNING "%s: unexpected return code %ld\n",
- __FUNCTION__, *spu_ret);
+ __func__, *spu_ret);
ret = 0;
}
return ret;
@@ -294,7 +311,7 @@ static int spu_process_callback(struct spu_context *ctx)
u32 ls_pointer, npc;
void __iomem *ls;
long spu_ret;
- int ret, ret2;
+ int ret;
/* get syscall block from local store */
npc = ctx->ops->npc_read(ctx) & ~3;
@@ -316,13 +333,15 @@ static int spu_process_callback(struct spu_context *ctx)
if (spu_ret <= -ERESTARTSYS) {
ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
}
- ret2 = spu_acquire(ctx);
+ mutex_lock(&ctx->state_mutex);
if (ret == -ERESTARTSYS)
return ret;
- if (ret2)
- return -EINTR;
}
+ /* need to re-get the ls, as it may have changed when we released the
+ * spu */
+ ls = (void __iomem *)ctx->ops->get_ls(ctx);
+
/* write result, jump over indirect pointer */
memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret));
ctx->ops->npc_write(ctx, npc);
@@ -339,13 +358,14 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event)
if (mutex_lock_interruptible(&ctx->run_mutex))
return -ERESTARTSYS;
- spu_enable_spu(ctx);
ctx->event_return = 0;
ret = spu_acquire(ctx);
if (ret)
goto out_unlock;
+ spu_enable_spu(ctx);
+
spu_update_sched_info(ctx);
ret = spu_run_init(ctx, npc);
diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c
index 00528ef84ad..4a0a64fe25d 100644
--- a/arch/powerpc/platforms/cell/spufs/sched.c
+++ b/arch/powerpc/platforms/cell/spufs/sched.c
@@ -22,11 +22,12 @@
#undef DEBUG
-#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
+#include <linux/sched/rt.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/vmalloc.h>
#include <linux/smp.h>
@@ -39,7 +40,6 @@
#include <linux/pid_namespace.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
-#include <linux/marker.h>
#include <asm/io.h>
#include <asm/mmu_context.h>
@@ -47,6 +47,8 @@
#include <asm/spu_csa.h>
#include <asm/spu_priv1.h>
#include "spufs.h"
+#define CREATE_TRACE_POINTS
+#include "sputrace.h"
struct spu_prio_array {
DECLARE_BITMAP(bitmap, MAX_PRIO);
@@ -81,7 +83,6 @@ static struct timer_list spuloadavg_timer;
#define MIN_SPU_TIMESLICE max(5 * HZ / (1000 * SPUSCHED_TICK), 1)
#define DEF_SPU_TIMESLICE (100 * HZ / (1000 * SPUSCHED_TICK))
-#define MAX_USER_PRIO (MAX_PRIO - MAX_RT_PRIO)
#define SCALE_PRIO(x, prio) \
max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO / 2), MIN_SPU_TIMESLICE)
@@ -139,7 +140,10 @@ void __spu_update_sched_info(struct spu_context *ctx)
* runqueue. The context will be rescheduled on the proper node
* if it is timesliced or preempted.
*/
- ctx->cpus_allowed = current->cpus_allowed;
+ cpumask_copy(&ctx->cpus_allowed, tsk_cpus_allowed(current));
+
+ /* Save the current cpu id for spu interrupt routing. */
+ ctx->last_ran = raw_smp_processor_id();
}
void spu_update_sched_info(struct spu_context *ctx)
@@ -163,9 +167,9 @@ void spu_update_sched_info(struct spu_context *ctx)
static int __node_allowed(struct spu_context *ctx, int node)
{
if (nr_cpus_node(node)) {
- cpumask_t mask = node_to_cpumask(node);
+ const struct cpumask *mask = cpumask_of_node(node);
- if (cpus_intersects(mask, ctx->cpus_allowed))
+ if (cpumask_intersects(mask, &ctx->cpus_allowed))
return 1;
}
@@ -227,22 +231,26 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx)
ctx->stats.slb_flt_base = spu->stats.slb_flt;
ctx->stats.class2_intr_base = spu->stats.class2_intr;
+ spu_associate_mm(spu, ctx->owner);
+
+ spin_lock_irq(&spu->register_lock);
spu->ctx = ctx;
spu->flags = 0;
ctx->spu = spu;
ctx->ops = &spu_hw_ops;
spu->pid = current->pid;
spu->tgid = current->tgid;
- spu_associate_mm(spu, ctx->owner);
spu->ibox_callback = spufs_ibox_callback;
spu->wbox_callback = spufs_wbox_callback;
spu->stop_callback = spufs_stop_callback;
spu->mfc_callback = spufs_mfc_callback;
- mb();
+ spin_unlock_irq(&spu->register_lock);
+
spu_unmap_mappings(ctx);
+
+ spu_switch_log_notify(spu, ctx, SWITCH_LOG_START, 0);
spu_restore(&ctx->csa, spu);
spu->timestamp = jiffies;
- spu_cpu_affinity_set(spu, raw_smp_processor_id());
spu_switch_notify(spu, ctx);
ctx->state = SPU_STATE_RUNNABLE;
@@ -305,11 +313,35 @@ static struct spu *aff_ref_location(struct spu_context *ctx, int mem_aff,
*/
node = cpu_to_node(raw_smp_processor_id());
for (n = 0; n < MAX_NUMNODES; n++, node++) {
+ /*
+ * "available_spus" counts how many spus are not potentially
+ * going to be used by other affinity gangs whose reference
+ * context is already in place. Although this code seeks to
+ * avoid having affinity gangs with a summed amount of
+ * contexts bigger than the amount of spus in the node,
+ * this may happen sporadically. In this case, available_spus
+ * becomes negative, which is harmless.
+ */
+ int available_spus;
+
node = (node < MAX_NUMNODES) ? node : 0;
if (!node_allowed(ctx, node))
continue;
+
+ available_spus = 0;
mutex_lock(&cbe_spu_info[node].list_mutex);
list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) {
+ if (spu->ctx && spu->ctx->gang && !spu->ctx->aff_offset
+ && spu->ctx->gang->aff_ref_spu)
+ available_spus -= spu->ctx->gang->contexts;
+ available_spus++;
+ }
+ if (available_spus < ctx->gang->contexts) {
+ mutex_unlock(&cbe_spu_info[node].list_mutex);
+ continue;
+ }
+
+ list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) {
if ((!mem_aff || spu->has_mem_affinity) &&
sched_spu(spu)) {
mutex_unlock(&cbe_spu_info[node].list_mutex);
@@ -382,6 +414,9 @@ static int has_affinity(struct spu_context *ctx)
if (list_empty(&ctx->aff_list))
return 0;
+ if (atomic_read(&ctx->gang->aff_sched_count) == 0)
+ ctx->gang->aff_ref_spu = NULL;
+
if (!gang->aff_ref_spu) {
if (!(gang->aff_flags & AFF_MERGED))
aff_merge_remaining_ctxs(gang);
@@ -400,6 +435,8 @@ static int has_affinity(struct spu_context *ctx)
*/
static void spu_unbind_context(struct spu *spu, struct spu_context *ctx)
{
+ u32 status;
+
spu_context_trace(spu_unbind_context__enter, ctx, spu);
spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
@@ -407,30 +444,34 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx)
if (spu->ctx->flags & SPU_CREATE_NOSCHED)
atomic_dec(&cbe_spu_info[spu->node].reserved_spus);
- if (ctx->gang){
- mutex_lock(&ctx->gang->aff_mutex);
- if (has_affinity(ctx)) {
- if (atomic_dec_and_test(&ctx->gang->aff_sched_count))
- ctx->gang->aff_ref_spu = NULL;
- }
- mutex_unlock(&ctx->gang->aff_mutex);
- }
+ if (ctx->gang)
+ /*
+ * If ctx->gang->aff_sched_count is positive, SPU affinity is
+ * being considered in this gang. Using atomic_dec_if_positive
+ * allow us to skip an explicit check for affinity in this gang
+ */
+ atomic_dec_if_positive(&ctx->gang->aff_sched_count);
spu_switch_notify(spu, NULL);
spu_unmap_mappings(ctx);
spu_save(&ctx->csa, spu);
+ spu_switch_log_notify(spu, ctx, SWITCH_LOG_STOP, 0);
+
+ spin_lock_irq(&spu->register_lock);
spu->timestamp = jiffies;
ctx->state = SPU_STATE_SAVED;
spu->ibox_callback = NULL;
spu->wbox_callback = NULL;
spu->stop_callback = NULL;
spu->mfc_callback = NULL;
- spu_associate_mm(spu, NULL);
spu->pid = 0;
spu->tgid = 0;
ctx->ops = &spu_backing_ops;
spu->flags = 0;
spu->ctx = NULL;
+ spin_unlock_irq(&spu->register_lock);
+
+ spu_associate_mm(spu, NULL);
ctx->stats.slb_flt +=
(spu->stats.slb_flt - ctx->stats.slb_flt_base);
@@ -440,6 +481,9 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx)
/* This maps the underlying spu state to idle */
spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED);
ctx->spu = NULL;
+
+ if (spu_stopped(ctx, &status))
+ wake_up_all(&ctx->stop_wq);
}
/**
@@ -465,7 +509,7 @@ static void __spu_add_to_rq(struct spu_context *ctx)
list_add_tail(&ctx->rq, &spu_prio->runq[ctx->prio]);
set_bit(ctx->prio, spu_prio->bitmap);
if (!spu_prio->nr_waiting++)
- __mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK);
+ mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK);
}
}
@@ -545,10 +589,7 @@ static struct spu *spu_get_idle(struct spu_context *ctx)
goto found;
mutex_unlock(&cbe_spu_info[node].list_mutex);
- mutex_lock(&ctx->gang->aff_mutex);
- if (atomic_dec_and_test(&ctx->gang->aff_sched_count))
- ctx->gang->aff_ref_spu = NULL;
- mutex_unlock(&ctx->gang->aff_mutex);
+ atomic_dec(&ctx->gang->aff_sched_count);
goto not_found;
}
mutex_unlock(&ctx->gang->aff_mutex);
@@ -591,7 +632,7 @@ static struct spu *find_victim(struct spu_context *ctx)
struct spu *spu;
int node, n;
- spu_context_nospu_trace(spu_find_vitim__enter, ctx);
+ spu_context_nospu_trace(spu_find_victim__enter, ctx);
/*
* Look for a possible preemption candidate on the local node first.
@@ -613,9 +654,12 @@ static struct spu *find_victim(struct spu_context *ctx)
if (tmp && tmp->prio > ctx->prio &&
!(tmp->flags & SPU_CREATE_NOSCHED) &&
- (!victim || tmp->prio > victim->prio))
+ (!victim || tmp->prio > victim->prio)) {
victim = spu->ctx;
+ }
}
+ if (victim)
+ get_spu_context(victim);
mutex_unlock(&cbe_spu_info[node].list_mutex);
if (victim) {
@@ -630,6 +674,7 @@ static struct spu *find_victim(struct spu_context *ctx)
* look at another context or give up after X retries.
*/
if (!mutex_trylock(&victim->state_mutex)) {
+ put_spu_context(victim);
victim = NULL;
goto restart;
}
@@ -642,6 +687,7 @@ static struct spu *find_victim(struct spu_context *ctx)
* restart the search.
*/
mutex_unlock(&victim->state_mutex);
+ put_spu_context(victim);
victim = NULL;
goto restart;
}
@@ -655,9 +701,11 @@ static struct spu *find_victim(struct spu_context *ctx)
victim->stats.invol_ctx_switch++;
spu->stats.invol_ctx_switch++;
- spu_add_to_rq(victim);
+ if (test_bit(SPU_SCHED_SPU_RUN, &victim->sched_flags))
+ spu_add_to_rq(victim);
mutex_unlock(&victim->state_mutex);
+ put_spu_context(victim);
return spu;
}
@@ -693,17 +741,33 @@ static void spu_schedule(struct spu *spu, struct spu_context *ctx)
/* not a candidate for interruptible because it's called either
from the scheduler thread or from spu_deactivate */
mutex_lock(&ctx->state_mutex);
- __spu_schedule(spu, ctx);
+ if (ctx->state == SPU_STATE_SAVED)
+ __spu_schedule(spu, ctx);
spu_release(ctx);
}
-static void spu_unschedule(struct spu *spu, struct spu_context *ctx)
+/**
+ * spu_unschedule - remove a context from a spu, and possibly release it.
+ * @spu: The SPU to unschedule from
+ * @ctx: The context currently scheduled on the SPU
+ * @free_spu Whether to free the SPU for other contexts
+ *
+ * Unbinds the context @ctx from the SPU @spu. If @free_spu is non-zero, the
+ * SPU is made available for other contexts (ie, may be returned by
+ * spu_get_idle). If this is zero, the caller is expected to schedule another
+ * context to this spu.
+ *
+ * Should be called with ctx->state_mutex held.
+ */
+static void spu_unschedule(struct spu *spu, struct spu_context *ctx,
+ int free_spu)
{
int node = spu->node;
mutex_lock(&cbe_spu_info[node].list_mutex);
cbe_spu_info[node].nr_active--;
- spu->alloc_state = SPU_FREE;
+ if (free_spu)
+ spu->alloc_state = SPU_FREE;
spu_unbind_context(spu, ctx);
ctx->stats.invol_ctx_switch++;
spu->stats.invol_ctx_switch++;
@@ -781,7 +845,7 @@ static struct spu_context *grab_runnable_context(int prio, int node)
struct list_head *rq = &spu_prio->runq[best];
list_for_each_entry(ctx, rq, rq) {
- /* XXX(hch): check for affinity here aswell */
+ /* XXX(hch): check for affinity here as well */
if (__node_allowed(ctx, node)) {
__spu_del_from_rq(ctx);
goto found;
@@ -803,7 +867,7 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio)
if (spu) {
new = grab_runnable_context(max_prio, spu->node);
if (new || force) {
- spu_unschedule(spu, ctx);
+ spu_unschedule(spu, ctx, new == NULL);
if (new) {
if (new->flags & SPU_CREATE_NOSCHED)
wake_up(&new->stop_wq);
@@ -876,12 +940,13 @@ static noinline void spusched_tick(struct spu_context *ctx)
new = grab_runnable_context(ctx->prio + 1, spu->node);
if (new) {
- spu_unschedule(spu, ctx);
+ spu_unschedule(spu, ctx, 0);
if (test_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags))
spu_add_to_rq(ctx);
} else {
spu_context_nospu_trace(spusched_tick__newslice, ctx);
- ctx->time_slice++;
+ if (!ctx->time_slice)
+ ctx->time_slice++;
}
out:
spu_release(ctx);
@@ -955,9 +1020,11 @@ static int spusched_thread(void *unused)
struct spu_context *ctx = spu->ctx;
if (ctx) {
+ get_spu_context(ctx);
mutex_unlock(mtx);
spusched_tick(ctx);
mutex_lock(mtx);
+ put_spu_context(ctx);
}
}
mutex_unlock(mtx);
@@ -975,6 +1042,7 @@ void spuctx_switch_state(struct spu_context *ctx,
struct timespec ts;
struct spu *spu;
enum spu_utilization_state old_state;
+ int node;
ktime_get_ts(&ts);
curtime = timespec_to_ns(&ts);
@@ -996,6 +1064,11 @@ void spuctx_switch_state(struct spu_context *ctx,
spu->stats.times[old_state] += delta;
spu->stats.util_state = new_state;
spu->stats.tstamp = curtime;
+ node = spu->node;
+ if (old_state == SPU_UTIL_USER)
+ atomic_dec(&cbe_spu_info[node].busy_spus);
+ if (new_state == SPU_UTIL_USER)
+ atomic_inc(&cbe_spu_info[node].busy_spus);
}
}
@@ -1021,7 +1094,7 @@ static int show_spu_loadavg(struct seq_file *s, void *private)
LOAD_INT(c), LOAD_FRAC(c),
count_active_contexts(),
atomic_read(&nr_spu_contexts),
- current->nsproxy->pid_ns->last_pid);
+ task_active_pid_ns(current)->last_pid);
return 0;
}
@@ -1063,10 +1136,9 @@ int __init spu_sched_init(void)
mod_timer(&spuloadavg_timer, 0);
- entry = create_proc_entry("spu_loadavg", 0, NULL);
+ entry = proc_create("spu_loadavg", 0, NULL, &spu_loadavg_fops);
if (!entry)
goto out_stop_kthread;
- entry->proc_fops = &spu_loadavg_fops;
pr_debug("spusched: tick: %d, min ticks: %d, default ticks: %d\n",
SPUSCHED_TICK, MIN_SPU_TIMESLICE, DEF_SPU_TIMESLICE);
diff --git a/arch/powerpc/platforms/cell/spufs/spu_restore.c b/arch/powerpc/platforms/cell/spufs/spu_restore.c
index 21a9c952d88..72c905f1ee7 100644
--- a/arch/powerpc/platforms/cell/spufs/spu_restore.c
+++ b/arch/powerpc/platforms/cell/spufs/spu_restore.c
@@ -284,7 +284,7 @@ static inline void restore_complete(void)
exit_instrs[3] = BR_INSTR;
break;
default:
- /* SPU_Status[R]=1. No additonal instructions. */
+ /* SPU_Status[R]=1. No additional instructions. */
break;
}
spu_sync();
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h
index cdc515182f8..bcfd6f063ef 100644
--- a/arch/powerpc/platforms/cell/spufs/spufs.h
+++ b/arch/powerpc/platforms/cell/spufs/spufs.h
@@ -32,6 +32,12 @@
#include <asm/spu_csa.h>
#include <asm/spu_info.h>
+#define SPUFS_PS_MAP_SIZE 0x20000
+#define SPUFS_MFC_MAP_SIZE 0x1000
+#define SPUFS_CNTL_MAP_SIZE 0x1000
+#define SPUFS_SIGNAL_MAP_SIZE PAGE_SIZE
+#define SPUFS_MSS_MAP_SIZE 0x1000
+
/* The magic number for our file system */
enum {
SPUFS_MAGIC = 0x23c9b64e,
@@ -47,6 +53,29 @@ enum {
SPU_SCHED_SPU_RUN, /* context is within spu_run */
};
+enum {
+ SWITCH_LOG_BUFSIZE = 4096,
+};
+
+enum {
+ SWITCH_LOG_START,
+ SWITCH_LOG_STOP,
+ SWITCH_LOG_EXIT,
+};
+
+struct switch_log {
+ wait_queue_head_t wait;
+ unsigned long head;
+ unsigned long tail;
+ struct switch_log_entry {
+ struct timespec tstamp;
+ s32 spu_id;
+ u32 type;
+ u32 val;
+ u64 timebase;
+ } log[];
+};
+
struct spu_context {
struct spu *spu; /* pointer to a physical SPU */
struct spu_state csa; /* SPU context save area. */
@@ -97,6 +126,7 @@ struct spu_context {
cpumask_t cpus_allowed;
int policy;
int prio;
+ int last_ran;
/* statistics */
struct {
@@ -116,6 +146,9 @@ struct spu_context {
unsigned long long libassist;
} stats;
+ /* context switch log */
+ struct switch_log *switch_log;
+
struct list_head aff_list;
int aff_head;
int aff_offset;
@@ -200,17 +233,26 @@ struct spufs_inode_info {
#define SPUFS_I(inode) \
container_of(inode, struct spufs_inode_info, vfs_inode)
-extern struct tree_descr spufs_dir_contents[];
-extern struct tree_descr spufs_dir_nosched_contents[];
+struct spufs_tree_descr {
+ const char *name;
+ const struct file_operations *ops;
+ umode_t mode;
+ size_t size;
+};
+
+extern const struct spufs_tree_descr spufs_dir_contents[];
+extern const struct spufs_tree_descr spufs_dir_nosched_contents[];
+extern const struct spufs_tree_descr spufs_dir_debug_contents[];
/* system call implementation */
extern struct spufs_calls spufs_calls;
+struct coredump_params;
long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *status);
-long spufs_create(struct nameidata *nd, unsigned int flags,
- mode_t mode, struct file *filp);
+long spufs_create(struct path *nd, struct dentry *dentry, unsigned int flags,
+ umode_t mode, struct file *filp);
/* ELF coredump callbacks for writing SPU ELF notes */
extern int spufs_coredump_extra_notes_size(void);
-extern int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset);
+extern int spufs_coredump_extra_notes_write(struct coredump_params *cprm);
extern const struct file_operations spufs_context_fops;
@@ -256,6 +298,8 @@ int spu_activate(struct spu_context *ctx, unsigned long flags);
void spu_deactivate(struct spu_context *ctx);
void spu_yield(struct spu_context *ctx);
void spu_switch_notify(struct spu *spu, struct spu_context *ctx);
+void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
+ u32 type, u32 val);
void spu_set_timeslice(struct spu_context *ctx);
void spu_update_sched_info(struct spu_context *ctx);
void __spu_update_sched_info(struct spu_context *ctx);
@@ -270,7 +314,7 @@ extern char *isolated_loader;
* we need to call spu_release(ctx) before sleeping, and
* then spu_acquire(ctx) when awoken.
*
- * Returns with state_mutex re-acquired when successfull or
+ * Returns with state_mutex re-acquired when successful or
* with -ERESTARTSYS and the state_mutex dropped when interrupted.
*/
@@ -302,7 +346,7 @@ size_t spu_ibox_read(struct spu_context *ctx, u32 *data);
/* irq callback funcs. */
void spufs_ibox_callback(struct spu *spu);
void spufs_wbox_callback(struct spu *spu);
-void spufs_stop_callback(struct spu *spu);
+void spufs_stop_callback(struct spu *spu, int irq);
void spufs_mfc_callback(struct spu *spu);
void spufs_dma_callback(struct spu *spu, int type);
@@ -314,7 +358,7 @@ struct spufs_coredump_reader {
u64 (*get)(struct spu_context *ctx);
size_t size;
};
-extern struct spufs_coredump_reader spufs_coredump_read[];
+extern const struct spufs_coredump_reader spufs_coredump_read[];
extern int spufs_coredump_num_notes;
extern int spu_init_csa(struct spu_state *csa);
@@ -329,9 +373,4 @@ extern void spu_free_lscsa(struct spu_state *csa);
extern void spuctx_switch_state(struct spu_context *ctx,
enum spu_utilization_state new_state);
-#define spu_context_trace(name, ctx, spu) \
- trace_mark(name, "%p %p", ctx, spu);
-#define spu_context_nospu_trace(name, ctx) \
- trace_mark(name, "%p", ctx);
-
#endif
diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.c b/arch/powerpc/platforms/cell/spufs/sputrace.c
deleted file mode 100644
index 79aa773f3c9..00000000000
--- a/arch/powerpc/platforms/cell/spufs/sputrace.c
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2007 IBM Deutschland Entwicklung GmbH
- * Released under GPL v2.
- *
- * Partially based on net/ipv4/tcp_probe.c.
- *
- * Simple tracing facility for spu contexts.
- */
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/marker.h>
-#include <linux/proc_fs.h>
-#include <linux/wait.h>
-#include <asm/atomic.h>
-#include <asm/uaccess.h>
-#include "spufs.h"
-
-struct spu_probe {
- const char *name;
- const char *format;
- marker_probe_func *probe_func;
-};
-
-struct sputrace {
- ktime_t tstamp;
- int owner_tid; /* owner */
- int curr_tid;
- const char *name;
- int number;
-};
-
-static int bufsize __read_mostly = 16384;
-MODULE_PARM_DESC(bufsize, "Log buffer size (number of records)");
-module_param(bufsize, int, 0);
-
-
-static DEFINE_SPINLOCK(sputrace_lock);
-static DECLARE_WAIT_QUEUE_HEAD(sputrace_wait);
-static ktime_t sputrace_start;
-static unsigned long sputrace_head, sputrace_tail;
-static struct sputrace *sputrace_log;
-
-static int sputrace_used(void)
-{
- return (sputrace_head - sputrace_tail) % bufsize;
-}
-
-static inline int sputrace_avail(void)
-{
- return bufsize - sputrace_used();
-}
-
-static int sputrace_sprint(char *tbuf, int n)
-{
- const struct sputrace *t = sputrace_log + sputrace_tail % bufsize;
- struct timespec tv =
- ktime_to_timespec(ktime_sub(t->tstamp, sputrace_start));
-
- return snprintf(tbuf, n,
- "[%lu.%09lu] %d: %s (ctxthread = %d, spu = %d)\n",
- (unsigned long) tv.tv_sec,
- (unsigned long) tv.tv_nsec,
- t->curr_tid,
- t->name,
- t->owner_tid,
- t->number);
-}
-
-static ssize_t sputrace_read(struct file *file, char __user *buf,
- size_t len, loff_t *ppos)
-{
- int error = 0, cnt = 0;
-
- if (!buf || len < 0)
- return -EINVAL;
-
- while (cnt < len) {
- char tbuf[128];
- int width;
-
- error = wait_event_interruptible(sputrace_wait,
- sputrace_used() > 0);
- if (error)
- break;
-
- spin_lock(&sputrace_lock);
- if (sputrace_head == sputrace_tail) {
- spin_unlock(&sputrace_lock);
- continue;
- }
-
- width = sputrace_sprint(tbuf, sizeof(tbuf));
- if (width < len)
- sputrace_tail = (sputrace_tail + 1) % bufsize;
- spin_unlock(&sputrace_lock);
-
- if (width >= len)
- break;
-
- error = copy_to_user(buf + cnt, tbuf, width);
- if (error)
- break;
- cnt += width;
- }
-
- return cnt == 0 ? error : cnt;
-}
-
-static int sputrace_open(struct inode *inode, struct file *file)
-{
- spin_lock(&sputrace_lock);
- sputrace_head = sputrace_tail = 0;
- sputrace_start = ktime_get();
- spin_unlock(&sputrace_lock);
-
- return 0;
-}
-
-static const struct file_operations sputrace_fops = {
- .owner = THIS_MODULE,
- .open = sputrace_open,
- .read = sputrace_read,
-};
-
-static void sputrace_log_item(const char *name, struct spu_context *ctx,
- struct spu *spu)
-{
- spin_lock(&sputrace_lock);
- if (sputrace_avail() > 1) {
- struct sputrace *t = sputrace_log + sputrace_head;
-
- t->tstamp = ktime_get();
- t->owner_tid = ctx->tid;
- t->name = name;
- t->curr_tid = current->pid;
- t->number = spu ? spu->number : -1;
-
- sputrace_head = (sputrace_head + 1) % bufsize;
- } else {
- printk(KERN_WARNING
- "sputrace: lost samples due to full buffer.\n");
- }
- spin_unlock(&sputrace_lock);
-
- wake_up(&sputrace_wait);
-}
-
-static void spu_context_event(void *probe_private, void *call_data,
- const char *format, va_list *args)
-{
- struct spu_probe *p = probe_private;
- struct spu_context *ctx;
- struct spu *spu;
-
- ctx = va_arg(*args, struct spu_context *);
- spu = va_arg(*args, struct spu *);
-
- sputrace_log_item(p->name, ctx, spu);
-}
-
-static void spu_context_nospu_event(void *probe_private, void *call_data,
- const char *format, va_list *args)
-{
- struct spu_probe *p = probe_private;
- struct spu_context *ctx;
-
- ctx = va_arg(*args, struct spu_context *);
-
- sputrace_log_item(p->name, ctx, NULL);
-}
-
-struct spu_probe spu_probes[] = {
- { "spu_bind_context__enter", "%p %p", spu_context_event },
- { "spu_unbind_context__enter", "%p %p", spu_context_event },
- { "spu_get_idle__enter", "%p", spu_context_nospu_event },
- { "spu_get_idle__found", "%p %p", spu_context_event },
- { "spu_get_idle__not_found", "%p", spu_context_nospu_event },
- { "spu_find_victim__enter", "%p", spu_context_nospu_event },
- { "spusched_tick__preempt", "%p %p", spu_context_event },
- { "spusched_tick__newslice", "%p", spu_context_nospu_event },
- { "spu_yield__enter", "%p", spu_context_nospu_event },
- { "spu_deactivate__enter", "%p", spu_context_nospu_event },
- { "__spu_deactivate__unload", "%p %p", spu_context_event },
- { "spufs_ps_nopfn__enter", "%p", spu_context_nospu_event },
- { "spufs_ps_nopfn__sleep", "%p", spu_context_nospu_event },
- { "spufs_ps_nopfn__wake", "%p %p", spu_context_event },
- { "spufs_ps_nopfn__insert", "%p %p", spu_context_event },
- { "spu_acquire_saved__enter", "%p", spu_context_nospu_event },
- { "destroy_spu_context__enter", "%p", spu_context_nospu_event },
- { "spufs_stop_callback__enter", "%p %p", spu_context_event },
-};
-
-static int __init sputrace_init(void)
-{
- struct proc_dir_entry *entry;
- int i, error = -ENOMEM;
-
- sputrace_log = kcalloc(sizeof(struct sputrace),
- bufsize, GFP_KERNEL);
- if (!sputrace_log)
- goto out;
-
- entry = create_proc_entry("sputrace", S_IRUSR, NULL);
- if (!entry)
- goto out_free_log;
- entry->proc_fops = &sputrace_fops;
-
- for (i = 0; i < ARRAY_SIZE(spu_probes); i++) {
- struct spu_probe *p = &spu_probes[i];
-
- error = marker_probe_register(p->name, p->format,
- p->probe_func, p);
- if (error)
- printk(KERN_INFO "Unable to register probe %s\n",
- p->name);
- }
-
- return 0;
-
-out_free_log:
- kfree(sputrace_log);
-out:
- return -ENOMEM;
-}
-
-static void __exit sputrace_exit(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(spu_probes); i++)
- marker_probe_unregister(spu_probes[i].name,
- spu_probes[i].probe_func, &spu_probes[i]);
-
- remove_proc_entry("sputrace", NULL);
- kfree(sputrace_log);
-}
-
-module_init(sputrace_init);
-module_exit(sputrace_exit);
-
-MODULE_LICENSE("GPL");
diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.h b/arch/powerpc/platforms/cell/spufs/sputrace.h
new file mode 100644
index 00000000000..db2656aa410
--- /dev/null
+++ b/arch/powerpc/platforms/cell/spufs/sputrace.h
@@ -0,0 +1,39 @@
+#if !defined(_TRACE_SPUFS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_SPUFS_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM spufs
+
+TRACE_EVENT(spufs_context,
+ TP_PROTO(struct spu_context *ctx, struct spu *spu, const char *name),
+ TP_ARGS(ctx, spu, name),
+
+ TP_STRUCT__entry(
+ __field(const char *, name)
+ __field(int, owner_tid)
+ __field(int, number)
+ ),
+
+ TP_fast_assign(
+ __entry->name = name;
+ __entry->owner_tid = ctx->tid;
+ __entry->number = spu ? spu->number : -1;
+ ),
+
+ TP_printk("%s (ctxthread = %d, spu = %d)",
+ __entry->name, __entry->owner_tid, __entry->number)
+);
+
+#define spu_context_trace(name, ctx, spu) \
+ trace_spufs_context(ctx, spu, __stringify(name))
+#define spu_context_nospu_trace(name, ctx) \
+ trace_spufs_context(ctx, NULL, __stringify(name))
+
+#endif /* _TRACE_SPUFS_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE sputrace
+#include <trace/define_trace.h>
diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c
index e9dc7a55d1b..dde35551e74 100644
--- a/arch/powerpc/platforms/cell/spufs/switch.c
+++ b/arch/powerpc/platforms/cell/spufs/switch.c
@@ -32,7 +32,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/errno.h>
#include <linux/hardirq.h>
#include <linux/sched.h>
@@ -132,6 +132,14 @@ static inline void disable_interrupts(struct spu_state *csa, struct spu *spu)
spu_int_mask_set(spu, 2, 0ul);
eieio();
spin_unlock_irq(&spu->register_lock);
+
+ /*
+ * This flag needs to be set before calling synchronize_irq so
+ * that the update will be visible to the relevant handlers
+ * via a simple load.
+ */
+ set_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags);
+ clear_bit(SPU_CONTEXT_FAULT_PENDING, &spu->flags);
synchronize_irq(spu->irqs[0]);
synchronize_irq(spu->irqs[1]);
synchronize_irq(spu->irqs[2]);
@@ -166,9 +174,8 @@ static inline void set_switch_pending(struct spu_state *csa, struct spu *spu)
/* Save, Step 7:
* Restore, Step 5:
* Set a software context switch pending flag.
+ * Done above in Step 3 - disable_interrupts().
*/
- set_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags);
- mb();
}
static inline void save_mfc_cntl(struct spu_state *csa, struct spu *spu)
@@ -186,20 +193,21 @@ static inline void save_mfc_cntl(struct spu_state *csa, struct spu *spu)
MFC_CNTL_SUSPEND_COMPLETE);
/* fall through */
case MFC_CNTL_SUSPEND_COMPLETE:
- if (csa) {
+ if (csa)
csa->priv2.mfc_control_RW =
- MFC_CNTL_SUSPEND_MASK |
+ in_be64(&priv2->mfc_control_RW) |
MFC_CNTL_SUSPEND_DMA_QUEUE;
- }
break;
case MFC_CNTL_NORMAL_DMA_QUEUE_OPERATION:
out_be64(&priv2->mfc_control_RW, MFC_CNTL_SUSPEND_DMA_QUEUE);
POLL_WHILE_FALSE((in_be64(&priv2->mfc_control_RW) &
MFC_CNTL_SUSPEND_DMA_STATUS_MASK) ==
MFC_CNTL_SUSPEND_COMPLETE);
- if (csa) {
- csa->priv2.mfc_control_RW = 0;
- }
+ if (csa)
+ csa->priv2.mfc_control_RW =
+ in_be64(&priv2->mfc_control_RW) &
+ ~MFC_CNTL_SUSPEND_DMA_QUEUE &
+ ~MFC_CNTL_SUSPEND_MASK;
break;
}
}
@@ -249,16 +257,21 @@ static inline void save_spu_status(struct spu_state *csa, struct spu *spu)
}
}
-static inline void save_mfc_decr(struct spu_state *csa, struct spu *spu)
+static inline void save_mfc_stopped_status(struct spu_state *csa,
+ struct spu *spu)
{
struct spu_priv2 __iomem *priv2 = spu->priv2;
+ const u64 mask = MFC_CNTL_DECREMENTER_RUNNING |
+ MFC_CNTL_DMA_QUEUES_EMPTY;
/* Save, Step 12:
* Read MFC_CNTL[Ds]. Update saved copy of
* CSA.MFC_CNTL[Ds].
+ *
+ * update: do the same with MFC_CNTL[Q].
*/
- csa->priv2.mfc_control_RW |=
- in_be64(&priv2->mfc_control_RW) & MFC_CNTL_DECREMENTER_RUNNING;
+ csa->priv2.mfc_control_RW &= ~mask;
+ csa->priv2.mfc_control_RW |= in_be64(&priv2->mfc_control_RW) & mask;
}
static inline void halt_mfc_decr(struct spu_state *csa, struct spu *spu)
@@ -462,7 +475,9 @@ static inline void purge_mfc_queue(struct spu_state *csa, struct spu *spu)
* Restore, Step 14.
* Write MFC_CNTL[Pc]=1 (purge queue).
*/
- out_be64(&priv2->mfc_control_RW, MFC_CNTL_PURGE_DMA_REQUEST);
+ out_be64(&priv2->mfc_control_RW,
+ MFC_CNTL_PURGE_DMA_REQUEST |
+ MFC_CNTL_SUSPEND_MASK);
eieio();
}
@@ -725,10 +740,14 @@ static inline void set_switch_active(struct spu_state *csa, struct spu *spu)
/* Save, Step 48:
* Restore, Step 23.
* Change the software context switch pending flag
- * to context switch active.
+ * to context switch active. This implementation does
+ * not uses a switch active flag.
*
- * This implementation does not uses a switch active flag.
+ * Now that we have saved the mfc in the csa, we can add in the
+ * restart command if an exception occurred.
*/
+ if (test_bit(SPU_CONTEXT_FAULT_PENDING, &spu->flags))
+ csa->priv2.mfc_control_RW |= MFC_CNTL_RESTART_DMA_COMMAND;
clear_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags);
mb();
}
@@ -1690,6 +1709,13 @@ static inline void restore_mfc_sr1(struct spu_state *csa, struct spu *spu)
eieio();
}
+static inline void set_int_route(struct spu_state *csa, struct spu *spu)
+{
+ struct spu_context *ctx = spu->ctx;
+
+ spu_cpu_affinity_set(spu, ctx->last_ran);
+}
+
static inline void restore_other_spu_access(struct spu_state *csa,
struct spu *spu)
{
@@ -1721,15 +1747,15 @@ static inline void restore_mfc_cntl(struct spu_state *csa, struct spu *spu)
*/
out_be64(&priv2->mfc_control_RW, csa->priv2.mfc_control_RW);
eieio();
+
/*
- * FIXME: this is to restart a DMA that we were processing
- * before the save. better remember the fault information
- * in the csa instead.
+ * The queue is put back into the same state that was evident prior to
+ * the context switch. The suspend flag is added to the saved state in
+ * the csa, if the operational state was suspending or suspended. In
+ * this case, the code that suspended the mfc is responsible for
+ * continuing it. Note that SPE faults do not change the operational
+ * state of the spu.
*/
- if ((csa->priv2.mfc_control_RW & MFC_CNTL_SUSPEND_DMA_QUEUE_MASK)) {
- out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND);
- eieio();
- }
}
static inline void enable_user_access(struct spu_state *csa, struct spu *spu)
@@ -1788,7 +1814,7 @@ static int quiece_spu(struct spu_state *prev, struct spu *spu)
save_spu_runcntl(prev, spu); /* Step 9. */
save_mfc_sr1(prev, spu); /* Step 10. */
save_spu_status(prev, spu); /* Step 11. */
- save_mfc_decr(prev, spu); /* Step 12. */
+ save_mfc_stopped_status(prev, spu); /* Step 12. */
halt_mfc_decr(prev, spu); /* Step 13. */
save_timebase(prev, spu); /* Step 14. */
remove_other_spu_access(prev, spu); /* Step 15. */
@@ -1815,6 +1841,7 @@ static void save_csa(struct spu_state *prev, struct spu *spu)
save_mfc_csr_ato(prev, spu); /* Step 24. */
save_mfc_tclass_id(prev, spu); /* Step 25. */
set_mfc_tclass_id(prev, spu); /* Step 26. */
+ save_mfc_cmd(prev, spu); /* Step 26a - moved from 44. */
purge_mfc_queue(prev, spu); /* Step 27. */
wait_purge_complete(prev, spu); /* Step 28. */
setup_mfc_sr1(prev, spu); /* Step 30. */
@@ -1831,7 +1858,6 @@ static void save_csa(struct spu_state *prev, struct spu *spu)
save_ppuint_mb(prev, spu); /* Step 41. */
save_ch_part1(prev, spu); /* Step 42. */
save_spu_mb(prev, spu); /* Step 43. */
- save_mfc_cmd(prev, spu); /* Step 44. */
reset_ch(prev, spu); /* Step 45. */
}
@@ -2000,6 +2026,7 @@ static void restore_csa(struct spu_state *next, struct spu *spu)
check_ppuint_mb_stat(next, spu); /* Step 67. */
spu_invalidate_slbs(spu); /* Modified Step 68. */
restore_mfc_sr1(next, spu); /* Step 69. */
+ set_int_route(next, spu); /* NEW */
restore_other_spu_access(next, spu); /* Step 70. */
restore_spu_runcntl(next, spu); /* Step 71. */
restore_mfc_cntl(next, spu); /* Step 72. */
diff --git a/arch/powerpc/platforms/cell/spufs/syscalls.c b/arch/powerpc/platforms/cell/spufs/syscalls.c
index 49c87769b1f..a87200a535f 100644
--- a/arch/powerpc/platforms/cell/spufs/syscalls.c
+++ b/arch/powerpc/platforms/cell/spufs/syscalls.c
@@ -1,8 +1,9 @@
#include <linux/file.h>
#include <linux/fs.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/mount.h>
#include <linux/namei.h>
+#include <linux/slab.h>
#include <asm/uaccess.h>
@@ -46,7 +47,7 @@ static long do_spu_run(struct file *filp,
if (filp->f_op != &spufs_context_fops)
goto out;
- i = SPUFS_I(filp->f_path.dentry->d_inode);
+ i = SPUFS_I(file_inode(filp));
ret = spufs_run_spu(i->i_ctx, &npc, &status);
if (put_user(npc, unpc))
@@ -59,23 +60,17 @@ out:
}
static long do_spu_create(const char __user *pathname, unsigned int flags,
- mode_t mode, struct file *neighbor)
+ umode_t mode, struct file *neighbor)
{
- char *tmp;
+ struct path path;
+ struct dentry *dentry;
int ret;
- tmp = getname(pathname);
- ret = PTR_ERR(tmp);
- if (!IS_ERR(tmp)) {
- struct nameidata nd;
-
- ret = path_lookup(tmp, LOOKUP_PARENT|
- LOOKUP_OPEN|LOOKUP_CREATE, &nd);
- if (!ret) {
- ret = spufs_create(&nd, flags, mode, neighbor);
- path_put(&nd.path);
- }
- putname(tmp);
+ dentry = user_path_create(AT_FDCWD, pathname, &path, LOOKUP_DIRECTORY);
+ ret = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ ret = spufs_create(&path, dentry, flags, mode, neighbor);
+ done_path_create(&path, dentry);
}
return ret;
@@ -84,8 +79,10 @@ static long do_spu_create(const char __user *pathname, unsigned int flags,
struct spufs_calls spufs_calls = {
.create_thread = do_spu_create,
.spu_run = do_spu_run,
- .coredump_extra_notes_size = spufs_coredump_extra_notes_size,
- .coredump_extra_notes_write = spufs_coredump_extra_notes_write,
.notify_spus_active = do_notify_spus_active,
.owner = THIS_MODULE,
+#ifdef CONFIG_COREDUMP
+ .coredump_extra_notes_size = spufs_coredump_extra_notes_size,
+ .coredump_extra_notes_write = spufs_coredump_extra_notes_write,
+#endif
};