aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/sysdev
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r--arch/powerpc/sysdev/6xx-suspend.S52
-rw-r--r--arch/powerpc/sysdev/Kconfig34
-rw-r--r--arch/powerpc/sysdev/Makefile53
-rw-r--r--arch/powerpc/sysdev/axonram.c106
-rw-r--r--arch/powerpc/sysdev/bestcomm/Kconfig39
-rw-r--r--arch/powerpc/sysdev/bestcomm/Makefile14
-rw-r--r--arch/powerpc/sysdev/bestcomm/ata.c154
-rw-r--r--arch/powerpc/sysdev/bestcomm/ata.h37
-rw-r--r--arch/powerpc/sysdev/bestcomm/bcom_ata_task.c67
-rw-r--r--arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c78
-rw-r--r--arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c91
-rw-r--r--arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c63
-rw-r--r--arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c69
-rw-r--r--arch/powerpc/sysdev/bestcomm/bestcomm.c539
-rw-r--r--arch/powerpc/sysdev/bestcomm/bestcomm.h190
-rw-r--r--arch/powerpc/sysdev/bestcomm/bestcomm_priv.h334
-rw-r--r--arch/powerpc/sysdev/bestcomm/fec.c270
-rw-r--r--arch/powerpc/sysdev/bestcomm/fec.h61
-rw-r--r--arch/powerpc/sysdev/bestcomm/gen_bd.c260
-rw-r--r--arch/powerpc/sysdev/bestcomm/gen_bd.h48
-rw-r--r--arch/powerpc/sysdev/bestcomm/sram.c177
-rw-r--r--arch/powerpc/sysdev/bestcomm/sram.h54
-rw-r--r--arch/powerpc/sysdev/cpm1.c461
-rw-r--r--arch/powerpc/sysdev/cpm2.c165
-rw-r--r--arch/powerpc/sysdev/cpm2_pic.c134
-rw-r--r--arch/powerpc/sysdev/cpm2_pic.h2
-rw-r--r--arch/powerpc/sysdev/cpm_common.c171
-rw-r--r--arch/powerpc/sysdev/dart_iommu.c122
-rw-r--r--arch/powerpc/sysdev/dcr-low.S8
-rw-r--r--arch/powerpc/sysdev/dcr.c168
-rw-r--r--arch/powerpc/sysdev/ehv_pic.c296
-rw-r--r--arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h101
-rw-r--r--arch/powerpc/sysdev/fsl_85xx_cache_sram.c160
-rw-r--r--arch/powerpc/sysdev/fsl_85xx_l2ctlr.c234
-rw-r--r--arch/powerpc/sysdev/fsl_gtm.c438
-rw-r--r--arch/powerpc/sysdev/fsl_lbc.c410
-rw-r--r--arch/powerpc/sysdev/fsl_mpic_err.c149
-rw-r--r--arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c161
-rw-r--r--arch/powerpc/sysdev/fsl_msi.c594
-rw-r--r--arch/powerpc/sysdev/fsl_msi.h50
-rw-r--r--arch/powerpc/sysdev/fsl_pci.c1261
-rw-r--r--arch/powerpc/sysdev/fsl_pci.h81
-rw-r--r--arch/powerpc/sysdev/fsl_pmc.c93
-rw-r--r--arch/powerpc/sysdev/fsl_rio.c1293
-rw-r--r--arch/powerpc/sysdev/fsl_rio.h127
-rw-r--r--arch/powerpc/sysdev/fsl_rmu.c1107
-rw-r--r--arch/powerpc/sysdev/fsl_soc.c1348
-rw-r--r--arch/powerpc/sysdev/fsl_soc.h39
-rw-r--r--arch/powerpc/sysdev/ge/Makefile1
-rw-r--r--arch/powerpc/sysdev/ge/ge_pic.c251
-rw-r--r--arch/powerpc/sysdev/ge/ge_pic.h10
-rw-r--r--arch/powerpc/sysdev/grackle.c6
-rw-r--r--arch/powerpc/sysdev/i8259.c101
-rw-r--r--arch/powerpc/sysdev/indirect_pci.c28
-rw-r--r--arch/powerpc/sysdev/ipic.c253
-rw-r--r--arch/powerpc/sysdev/ipic.h2
-rw-r--r--arch/powerpc/sysdev/micropatch.c30
-rw-r--r--arch/powerpc/sysdev/mmio_nvram.c36
-rw-r--r--arch/powerpc/sysdev/mpc5xxx_clocks.c34
-rw-r--r--arch/powerpc/sysdev/mpc8xx_pic.c109
-rw-r--r--arch/powerpc/sysdev/mpic.c1208
-rw-r--r--arch/powerpc/sysdev/mpic.h30
-rw-r--r--arch/powerpc/sysdev/mpic_msgr.c283
-rw-r--r--arch/powerpc/sysdev/mpic_msi.c145
-rw-r--r--arch/powerpc/sysdev/mpic_pasemi_msi.c58
-rw-r--r--arch/powerpc/sysdev/mpic_timer.c601
-rw-r--r--arch/powerpc/sysdev/mpic_u3msi.c96
-rw-r--r--arch/powerpc/sysdev/msi_bitmap.c249
-rw-r--r--arch/powerpc/sysdev/mv64x60_dev.c161
-rw-r--r--arch/powerpc/sysdev/mv64x60_pci.c14
-rw-r--r--arch/powerpc/sysdev/mv64x60_pic.c66
-rw-r--r--arch/powerpc/sysdev/mv64x60_udbg.c8
-rw-r--r--arch/powerpc/sysdev/of_rtc.c2
-rw-r--r--arch/powerpc/sysdev/pmi.c37
-rw-r--r--arch/powerpc/sysdev/ppc4xx_cpm.c346
-rw-r--r--arch/powerpc/sysdev/ppc4xx_gpio.c215
-rw-r--r--arch/powerpc/sysdev/ppc4xx_hsta_msi.c215
-rw-r--r--arch/powerpc/sysdev/ppc4xx_msi.c291
-rw-r--r--arch/powerpc/sysdev/ppc4xx_ocm.c416
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.c1201
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.h136
-rw-r--r--arch/powerpc/sysdev/ppc4xx_soc.c222
-rw-r--r--arch/powerpc/sysdev/qe_lib/Kconfig5
-rw-r--r--arch/powerpc/sysdev/qe_lib/Makefile2
-rw-r--r--arch/powerpc/sysdev/qe_lib/gpio.c317
-rw-r--r--arch/powerpc/sysdev/qe_lib/qe.c277
-rw-r--r--arch/powerpc/sysdev/qe_lib/qe_ic.c110
-rw-r--r--arch/powerpc/sysdev/qe_lib/qe_ic.h4
-rw-r--r--arch/powerpc/sysdev/qe_lib/qe_io.c112
-rw-r--r--arch/powerpc/sysdev/qe_lib/ucc.c19
-rw-r--r--arch/powerpc/sysdev/qe_lib/ucc_fast.c57
-rw-r--r--arch/powerpc/sysdev/qe_lib/ucc_slow.c31
-rw-r--r--arch/powerpc/sysdev/qe_lib/usb.c56
-rw-r--r--arch/powerpc/sysdev/rtc_cmos_setup.c31
-rw-r--r--arch/powerpc/sysdev/scom.c236
-rw-r--r--arch/powerpc/sysdev/simple_gpio.c152
-rw-r--r--arch/powerpc/sysdev/simple_gpio.h12
-rw-r--r--arch/powerpc/sysdev/tsi108_dev.c14
-rw-r--r--arch/powerpc/sysdev/tsi108_pci.c72
-rw-r--r--arch/powerpc/sysdev/udbg_memcons.c104
-rw-r--r--arch/powerpc/sysdev/uic.c152
-rw-r--r--arch/powerpc/sysdev/xics/Kconfig13
-rw-r--r--arch/powerpc/sysdev/xics/Makefile7
-rw-r--r--arch/powerpc/sysdev/xics/icp-hv.c184
-rw-r--r--arch/powerpc/sysdev/xics/icp-native.c317
-rw-r--r--arch/powerpc/sysdev/xics/ics-opal.c245
-rw-r--r--arch/powerpc/sysdev/xics/ics-rtas.c240
-rw-r--r--arch/powerpc/sysdev/xics/xics-common.c433
-rw-r--r--arch/powerpc/sysdev/xilinx_intc.c226
-rw-r--r--arch/powerpc/sysdev/xilinx_pci.c132
110 files changed, 15462 insertions, 6922 deletions
diff --git a/arch/powerpc/sysdev/6xx-suspend.S b/arch/powerpc/sysdev/6xx-suspend.S
new file mode 100644
index 00000000000..cf48e9cb257
--- /dev/null
+++ b/arch/powerpc/sysdev/6xx-suspend.S
@@ -0,0 +1,52 @@
+/*
+ * Enter and leave sleep state on chips with 6xx-style HID0
+ * power management bits, which don't leave sleep state via reset.
+ *
+ * Author: Scott Wood <scottwood@freescale.com>
+ *
+ * Copyright (c) 2006-2007 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+
+#include <asm/ppc_asm.h>
+#include <asm/reg.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+
+_GLOBAL(mpc6xx_enter_standby)
+ mflr r4
+
+ mfspr r5, SPRN_HID0
+ rlwinm r5, r5, 0, ~(HID0_DOZE | HID0_NAP)
+ oris r5, r5, HID0_SLEEP@h
+ mtspr SPRN_HID0, r5
+ isync
+
+ lis r5, ret_from_standby@h
+ ori r5, r5, ret_from_standby@l
+ mtlr r5
+
+ CURRENT_THREAD_INFO(r5, r1)
+ lwz r6, TI_LOCAL_FLAGS(r5)
+ ori r6, r6, _TLF_SLEEPING
+ stw r6, TI_LOCAL_FLAGS(r5)
+
+ mfmsr r5
+ ori r5, r5, MSR_EE
+ oris r5, r5, MSR_POW@h
+ sync
+ mtmsr r5
+ isync
+
+1: b 1b
+
+ret_from_standby:
+ mfspr r5, SPRN_HID0
+ rlwinm r5, r5, 0, ~HID0_SLEEP
+ mtspr SPRN_HID0, r5
+
+ mtlr r4
+ blr
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index 72fb35b9ebc..a19332a3871 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -6,3 +6,37 @@ config PPC4xx_PCI_EXPRESS
bool
depends on PCI && 4xx
default n
+
+config PPC4xx_HSTA_MSI
+ bool
+ depends on PCI_MSI
+ depends on PCI && 4xx
+ default n
+
+config PPC4xx_MSI
+ bool
+ depends on PCI_MSI
+ depends on PCI && 4xx
+ default n
+
+config PPC_MSI_BITMAP
+ bool
+ depends on PCI_MSI
+ default y if MPIC
+ default y if FSL_PCI
+ default y if PPC4xx_MSI
+ default y if PPC_POWERNV
+
+source "arch/powerpc/sysdev/xics/Kconfig"
+
+config PPC_SCOM
+ bool
+
+config SCOM_DEBUGFS
+ bool "Expose SCOM controllers via debugfs"
+ depends on PPC_SCOM && DEBUG_FS
+ default n
+
+config GE_FPGA
+ bool
+ default n
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 15f3e8527d7..f7cb2a1b01f 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -1,44 +1,75 @@
-ifeq ($(CONFIG_PPC64),y)
-EXTRA_CFLAGS += -mno-minimal-toc
-endif
+subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
+
+ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o
obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y)
+obj-$(CONFIG_MPIC_TIMER) += mpic_timer.o
+obj-$(CONFIG_FSL_MPIC_TIMER_WAKEUP) += fsl_mpic_timer_wakeup.o
+mpic-msgr-obj-$(CONFIG_MPIC_MSGR) += mpic_msgr.o
+obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) $(mpic-msgr-obj-y)
+obj-$(CONFIG_PPC_EPAPR_HV_PIC) += ehv_pic.o
+fsl-msi-obj-$(CONFIG_PCI_MSI) += fsl_msi.o
+obj-$(CONFIG_PPC_MSI_BITMAP) += msi_bitmap.o
obj-$(CONFIG_PPC_MPC106) += grackle.o
obj-$(CONFIG_PPC_DCR_NATIVE) += dcr-low.o
obj-$(CONFIG_PPC_PMI) += pmi.o
obj-$(CONFIG_U3_DART) += dart_iommu.o
obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
-obj-$(CONFIG_FSL_SOC) += fsl_soc.o
-obj-$(CONFIG_FSL_PCI) += fsl_pci.o
-obj-$(CONFIG_RAPIDIO) += fsl_rio.o
+obj-$(CONFIG_FSL_SOC) += fsl_soc.o fsl_mpic_err.o
+obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y)
+obj-$(CONFIG_FSL_PMC) += fsl_pmc.o
+obj-$(CONFIG_FSL_LBC) += fsl_lbc.o
+obj-$(CONFIG_FSL_GTM) += fsl_gtm.o
+obj-$(CONFIG_FSL_85XX_CACHE_SRAM) += fsl_85xx_l2ctlr.o fsl_85xx_cache_sram.o
+obj-$(CONFIG_SIMPLE_GPIO) += simple_gpio.o
+obj-$(CONFIG_FSL_RIO) += fsl_rio.o fsl_rmu.o
obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o
obj-$(CONFIG_QUICC_ENGINE) += qe_lib/
-obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
mv64x60-$(CONFIG_PCI) += mv64x60_pci.o
obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o \
mv64x60_udbg.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc_cmos_setup.o
obj-$(CONFIG_AXON_RAM) += axonram.o
-ifeq ($(CONFIG_PPC_MERGE),y)
obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o
obj-$(CONFIG_PPC_I8259) += i8259.o
obj-$(CONFIG_IPIC) += ipic.o
obj-$(CONFIG_4xx) += uic.o
+obj-$(CONFIG_PPC4xx_OCM) += ppc4xx_ocm.o
+obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o
obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o
+obj-$(CONFIG_XILINX_PCI) += xilinx_pci.o
obj-$(CONFIG_OF_RTC) += of_rtc.o
ifeq ($(CONFIG_PCI),y)
obj-$(CONFIG_4xx) += ppc4xx_pci.o
endif
-endif
+obj-$(CONFIG_PPC4xx_HSTA_MSI) += ppc4xx_hsta_msi.o
+obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o
+obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o
+obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o
-# Temporary hack until we have migrated to asm-powerpc
-ifeq ($(ARCH),powerpc)
obj-$(CONFIG_CPM) += cpm_common.o
obj-$(CONFIG_CPM2) += cpm2.o cpm2_pic.o
+obj-$(CONFIG_QUICC_ENGINE) += cpm_common.o
obj-$(CONFIG_PPC_DCR) += dcr.o
obj-$(CONFIG_8xx) += mpc8xx_pic.o cpm1.o
obj-$(CONFIG_UCODE_PATCH) += micropatch.o
+
+obj-$(CONFIG_PPC_MPC512x) += mpc5xxx_clocks.o
+obj-$(CONFIG_PPC_MPC52xx) += mpc5xxx_clocks.o
+
+ifeq ($(CONFIG_SUSPEND),y)
+obj-$(CONFIG_6xx) += 6xx-suspend.o
endif
+
+obj-$(CONFIG_PPC_SCOM) += scom.o
+
+obj-$(CONFIG_PPC_EARLY_DEBUG_MEMCONS) += udbg_memcons.o
+
+subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
+
+obj-$(CONFIG_PPC_XICS) += xics/
+
+obj-$(CONFIG_GE_FPGA) += ge/
diff --git a/arch/powerpc/sysdev/axonram.c b/arch/powerpc/sysdev/axonram.c
index d359d6e9297..47b6b9f81d4 100644
--- a/arch/powerpc/sysdev/axonram.c
+++ b/arch/powerpc/sysdev/axonram.c
@@ -25,7 +25,6 @@
#include <linux/bio.h>
#include <linux/blkdev.h>
-#include <linux/buffer_head.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -57,8 +56,10 @@
#define AXON_RAM_SECTOR_SIZE 1 << AXON_RAM_SECTOR_SHIFT
#define AXON_RAM_IRQ_FLAGS IRQF_SHARED | IRQF_TRIGGER_RISING
+static int azfs_major, azfs_minor;
+
struct axon_ram_bank {
- struct of_device *device;
+ struct platform_device *device;
struct gendisk *disk;
unsigned int irq_id;
unsigned long ph_addr;
@@ -70,7 +71,7 @@ struct axon_ram_bank {
static ssize_t
axon_ram_sysfs_ecc(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct of_device *device = to_of_device(dev);
+ struct platform_device *device = to_platform_device(dev);
struct axon_ram_bank *bank = device->dev.platform_data;
BUG_ON(!bank);
@@ -88,12 +89,12 @@ static DEVICE_ATTR(ecc, S_IRUGO, axon_ram_sysfs_ecc, NULL);
static irqreturn_t
axon_ram_irq_handler(int irq, void *dev)
{
- struct of_device *device = dev;
+ struct platform_device *device = dev;
struct axon_ram_bank *bank = device->dev.platform_data;
BUG_ON(!bank);
- dev_err(&device->dev, "Correctable memory error occured\n");
+ dev_err(&device->dev, "Correctable memory error occurred\n");
bank->ecc_counter++;
return IRQ_HANDLED;
}
@@ -102,39 +103,36 @@ axon_ram_irq_handler(int irq, void *dev)
* axon_ram_make_request - make_request() method for block device
* @queue, @bio: see blk_queue_make_request()
*/
-static int
+static void
axon_ram_make_request(struct request_queue *queue, struct bio *bio)
{
struct axon_ram_bank *bank = bio->bi_bdev->bd_disk->private_data;
unsigned long phys_mem, phys_end;
void *user_mem;
- struct bio_vec *vec;
+ struct bio_vec vec;
unsigned int transfered;
- unsigned short idx;
- int rc = 0;
+ struct bvec_iter iter;
- phys_mem = bank->io_addr + (bio->bi_sector << AXON_RAM_SECTOR_SHIFT);
+ phys_mem = bank->io_addr + (bio->bi_iter.bi_sector <<
+ AXON_RAM_SECTOR_SHIFT);
phys_end = bank->io_addr + bank->size;
transfered = 0;
- bio_for_each_segment(vec, bio, idx) {
- if (unlikely(phys_mem + vec->bv_len > phys_end)) {
+ bio_for_each_segment(vec, bio, iter) {
+ if (unlikely(phys_mem + vec.bv_len > phys_end)) {
bio_io_error(bio);
- rc = -ERANGE;
- break;
+ return;
}
- user_mem = page_address(vec->bv_page) + vec->bv_offset;
+ user_mem = page_address(vec.bv_page) + vec.bv_offset;
if (bio_data_dir(bio) == READ)
- memcpy(user_mem, (void *) phys_mem, vec->bv_len);
+ memcpy(user_mem, (void *) phys_mem, vec.bv_len);
else
- memcpy((void *) phys_mem, user_mem, vec->bv_len);
+ memcpy((void *) phys_mem, user_mem, vec.bv_len);
- phys_mem += vec->bv_len;
- transfered += vec->bv_len;
+ phys_mem += vec.bv_len;
+ transfered += vec.bv_len;
}
bio_endio(bio, 0);
-
- return rc;
}
/**
@@ -143,33 +141,36 @@ axon_ram_make_request(struct request_queue *queue, struct bio *bio)
*/
static int
axon_ram_direct_access(struct block_device *device, sector_t sector,
- unsigned long *data)
+ void **kaddr, unsigned long *pfn)
{
struct axon_ram_bank *bank = device->bd_disk->private_data;
loff_t offset;
- offset = sector << AXON_RAM_SECTOR_SHIFT;
+ offset = sector;
+ if (device->bd_part != NULL)
+ offset += device->bd_part->start_sect;
+ offset <<= AXON_RAM_SECTOR_SHIFT;
if (offset >= bank->size) {
dev_err(&bank->device->dev, "Access outside of address space\n");
return -ERANGE;
}
- *data = bank->ph_addr + offset;
+ *kaddr = (void *)(bank->ph_addr + offset);
+ *pfn = virt_to_phys(kaddr) >> PAGE_SHIFT;
return 0;
}
-static struct block_device_operations axon_ram_devops = {
+static const struct block_device_operations axon_ram_devops = {
.owner = THIS_MODULE,
.direct_access = axon_ram_direct_access
};
/**
* axon_ram_probe - probe() method for platform driver
- * @device, @device_id: see of_platform_driver method
+ * @device: see platform_driver method
*/
-static int
-axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
+static int axon_ram_probe(struct platform_device *device)
{
static int axon_ram_bank_id = -1;
struct axon_ram_bank *bank;
@@ -179,7 +180,7 @@ axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
axon_ram_bank_id++;
dev_info(&device->dev, "Found memory controller on %s\n",
- device->node->full_name);
+ device->dev.of_node->full_name);
bank = kzalloc(sizeof(struct axon_ram_bank), GFP_KERNEL);
if (bank == NULL) {
@@ -192,13 +193,13 @@ axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
bank->device = device;
- if (of_address_to_resource(device->node, 0, &resource) != 0) {
+ if (of_address_to_resource(device->dev.of_node, 0, &resource) != 0) {
dev_err(&device->dev, "Cannot access device tree\n");
rc = -EFAULT;
goto failed;
}
- bank->size = resource.end - resource.start + 1;
+ bank->size = resource_size(&resource);
if (bank->size == 0) {
dev_err(&device->dev, "No DDR2 memory found for %s%d\n",
@@ -211,7 +212,7 @@ axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
AXON_RAM_DEVICE_NAME, axon_ram_bank_id, bank->size >> 20);
bank->ph_addr = resource.start;
- bank->io_addr = (unsigned long) ioremap_flags(
+ bank->io_addr = (unsigned long) ioremap_prot(
bank->ph_addr, bank->size, _PAGE_NO_CACHE);
if (bank->io_addr == 0) {
dev_err(&device->dev, "ioremap() failed\n");
@@ -226,19 +227,14 @@ axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
goto failed;
}
- bank->disk->first_minor = 0;
+ bank->disk->major = azfs_major;
+ bank->disk->first_minor = azfs_minor;
bank->disk->fops = &axon_ram_devops;
bank->disk->private_data = bank;
bank->disk->driverfs_dev = &device->dev;
sprintf(bank->disk->disk_name, "%s%d",
AXON_RAM_DEVICE_NAME, axon_ram_bank_id);
- bank->disk->major = register_blkdev(0, bank->disk->disk_name);
- if (bank->disk->major < 0) {
- dev_err(&device->dev, "Cannot register block device\n");
- rc = -EFAULT;
- goto failed;
- }
bank->disk->queue = blk_alloc_queue(GFP_KERNEL);
if (bank->disk->queue == NULL) {
@@ -249,10 +245,10 @@ axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
set_capacity(bank->disk, bank->size >> AXON_RAM_SECTOR_SHIFT);
blk_queue_make_request(bank->disk->queue, axon_ram_make_request);
- blk_queue_hardsect_size(bank->disk->queue, AXON_RAM_SECTOR_SIZE);
+ blk_queue_logical_block_size(bank->disk->queue, AXON_RAM_SECTOR_SIZE);
add_disk(bank->disk);
- bank->irq_id = irq_of_parse_and_map(device->node, 0);
+ bank->irq_id = irq_of_parse_and_map(device->dev.of_node, 0);
if (bank->irq_id == NO_IRQ) {
dev_err(&device->dev, "Cannot access ECC interrupt ID\n");
rc = -EFAULT;
@@ -275,6 +271,8 @@ axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
goto failed;
}
+ azfs_minor += bank->disk->minors;
+
return 0;
failed:
@@ -301,7 +299,7 @@ failed:
* @device: see of_platform_driver method
*/
static int
-axon_ram_remove(struct of_device *device)
+axon_ram_remove(struct platform_device *device)
{
struct axon_ram_bank *bank = device->dev.platform_data;
@@ -309,7 +307,6 @@ axon_ram_remove(struct of_device *device)
device_remove_file(&device->dev, &dev_attr_ecc);
free_irq(bank->irq_id, device);
- unregister_blkdev(bank->disk->major, bank->disk->disk_name);
del_gendisk(bank->disk);
iounmap((void __iomem *) bank->io_addr);
kfree(bank);
@@ -324,13 +321,13 @@ static struct of_device_id axon_ram_device_id[] = {
{}
};
-static struct of_platform_driver axon_ram_driver = {
- .match_table = axon_ram_device_id,
+static struct platform_driver axon_ram_driver = {
.probe = axon_ram_probe,
.remove = axon_ram_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = AXON_RAM_MODULE_NAME,
+ .driver = {
+ .name = AXON_RAM_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = axon_ram_device_id,
},
};
@@ -340,7 +337,15 @@ static struct of_platform_driver axon_ram_driver = {
static int __init
axon_ram_init(void)
{
- return of_register_platform_driver(&axon_ram_driver);
+ azfs_major = register_blkdev(azfs_major, AXON_RAM_DEVICE_NAME);
+ if (azfs_major < 0) {
+ printk(KERN_ERR "%s cannot become block device major number\n",
+ AXON_RAM_MODULE_NAME);
+ return -EFAULT;
+ }
+ azfs_minor = 0;
+
+ return platform_driver_register(&axon_ram_driver);
}
/**
@@ -349,7 +354,8 @@ axon_ram_init(void)
static void __exit
axon_ram_exit(void)
{
- of_unregister_platform_driver(&axon_ram_driver);
+ platform_driver_unregister(&axon_ram_driver);
+ unregister_blkdev(azfs_major, AXON_RAM_DEVICE_NAME);
}
module_init(axon_ram_init);
diff --git a/arch/powerpc/sysdev/bestcomm/Kconfig b/arch/powerpc/sysdev/bestcomm/Kconfig
deleted file mode 100644
index 57cc5656256..00000000000
--- a/arch/powerpc/sysdev/bestcomm/Kconfig
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Kconfig options for Bestcomm
-#
-
-config PPC_BESTCOMM
- tristate "Bestcomm DMA engine support"
- depends on PPC_MPC52xx
- default n
- select PPC_LIB_RHEAP
- help
- BestComm is the name of the communication coprocessor found
- on the Freescale MPC5200 family of processor. It's usage is
- optionnal for some drivers (like ATA), but required for
- others (like FEC).
-
- If you want to use drivers that require DMA operations,
- answer Y or M. Otherwise say N.
-
-config PPC_BESTCOMM_ATA
- tristate "Bestcomm ATA task support"
- depends on PPC_BESTCOMM
- default n
- help
- This option enables the support for the ATA task.
-
-config PPC_BESTCOMM_FEC
- tristate "Bestcomm FEC tasks support"
- depends on PPC_BESTCOMM
- default n
- help
- This option enables the support for the FEC tasks.
-
-config PPC_BESTCOMM_GEN_BD
- tristate "Bestcomm GenBD tasks support"
- depends on PPC_BESTCOMM
- default n
- help
- This option enables the support for the GenBD tasks.
-
diff --git a/arch/powerpc/sysdev/bestcomm/Makefile b/arch/powerpc/sysdev/bestcomm/Makefile
deleted file mode 100644
index aed2df2a658..00000000000
--- a/arch/powerpc/sysdev/bestcomm/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Makefile for BestComm & co
-#
-
-bestcomm-core-objs := bestcomm.o sram.o
-bestcomm-ata-objs := ata.o bcom_ata_task.o
-bestcomm-fec-objs := fec.o bcom_fec_rx_task.o bcom_fec_tx_task.o
-bestcomm-gen-bd-objs := gen_bd.o bcom_gen_bd_rx_task.o bcom_gen_bd_tx_task.o
-
-obj-$(CONFIG_PPC_BESTCOMM) += bestcomm-core.o
-obj-$(CONFIG_PPC_BESTCOMM_ATA) += bestcomm-ata.o
-obj-$(CONFIG_PPC_BESTCOMM_FEC) += bestcomm-fec.o
-obj-$(CONFIG_PPC_BESTCOMM_GEN_BD) += bestcomm-gen-bd.o
-
diff --git a/arch/powerpc/sysdev/bestcomm/ata.c b/arch/powerpc/sysdev/bestcomm/ata.c
deleted file mode 100644
index 1f5258fb38c..00000000000
--- a/arch/powerpc/sysdev/bestcomm/ata.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Bestcomm ATA task driver
- *
- *
- * Patterned after bestcomm/fec.c by Dale Farnsworth <dfarnsworth@mvista.com>
- * 2003-2004 (c) MontaVista, Software, Inc.
- *
- * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2006 Freescale - John Rigby
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <asm/io.h>
-
-#include "bestcomm.h"
-#include "bestcomm_priv.h"
-#include "ata.h"
-
-
-/* ======================================================================== */
-/* Task image/var/inc */
-/* ======================================================================== */
-
-/* ata task image */
-extern u32 bcom_ata_task[];
-
-/* ata task vars that need to be set before enabling the task */
-struct bcom_ata_var {
- u32 enable; /* (u16*) address of task's control register */
- u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */
- u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
- u32 bd_start; /* (struct bcom_bd*) current bd */
- u32 buffer_size; /* size of receive buffer */
-};
-
-/* ata task incs that need to be set before enabling the task */
-struct bcom_ata_inc {
- u16 pad0;
- s16 incr_bytes;
- u16 pad1;
- s16 incr_dst;
- u16 pad2;
- s16 incr_src;
-};
-
-
-/* ======================================================================== */
-/* Task support code */
-/* ======================================================================== */
-
-struct bcom_task *
-bcom_ata_init(int queue_len, int maxbufsize)
-{
- struct bcom_task *tsk;
- struct bcom_ata_var *var;
- struct bcom_ata_inc *inc;
-
- tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_ata_bd), 0);
- if (!tsk)
- return NULL;
-
- tsk->flags = BCOM_FLAGS_NONE;
-
- bcom_ata_reset_bd(tsk);
-
- var = (struct bcom_ata_var *) bcom_task_var(tsk->tasknum);
- inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum);
-
- if (bcom_load_image(tsk->tasknum, bcom_ata_task)) {
- bcom_task_free(tsk);
- return NULL;
- }
-
- var->enable = bcom_eng->regs_base +
- offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
- var->bd_base = tsk->bd_pa;
- var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
- var->bd_start = tsk->bd_pa;
- var->buffer_size = maxbufsize;
-
- /* Configure some stuff */
- bcom_set_task_pragma(tsk->tasknum, BCOM_ATA_PRAGMA);
- bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
-
- out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ATA_RX], BCOM_IPR_ATA_RX);
- out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ATA_TX], BCOM_IPR_ATA_TX);
-
- out_be32(&bcom_eng->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
-
- return tsk;
-}
-EXPORT_SYMBOL_GPL(bcom_ata_init);
-
-void bcom_ata_rx_prepare(struct bcom_task *tsk)
-{
- struct bcom_ata_inc *inc;
-
- inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum);
-
- inc->incr_bytes = -(s16)sizeof(u32);
- inc->incr_src = 0;
- inc->incr_dst = sizeof(u32);
-
- bcom_set_initiator(tsk->tasknum, BCOM_INITIATOR_ATA_RX);
-}
-EXPORT_SYMBOL_GPL(bcom_ata_rx_prepare);
-
-void bcom_ata_tx_prepare(struct bcom_task *tsk)
-{
- struct bcom_ata_inc *inc;
-
- inc = (struct bcom_ata_inc *) bcom_task_inc(tsk->tasknum);
-
- inc->incr_bytes = -(s16)sizeof(u32);
- inc->incr_src = sizeof(u32);
- inc->incr_dst = 0;
-
- bcom_set_initiator(tsk->tasknum, BCOM_INITIATOR_ATA_TX);
-}
-EXPORT_SYMBOL_GPL(bcom_ata_tx_prepare);
-
-void bcom_ata_reset_bd(struct bcom_task *tsk)
-{
- struct bcom_ata_var *var;
-
- /* Reset all BD */
- memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
-
- tsk->index = 0;
- tsk->outdex = 0;
-
- var = (struct bcom_ata_var *) bcom_task_var(tsk->tasknum);
- var->bd_start = var->bd_base;
-}
-EXPORT_SYMBOL_GPL(bcom_ata_reset_bd);
-
-void bcom_ata_release(struct bcom_task *tsk)
-{
- /* Nothing special for the ATA tasks */
- bcom_task_free(tsk);
-}
-EXPORT_SYMBOL_GPL(bcom_ata_release);
-
-
-MODULE_DESCRIPTION("BestComm ATA task driver");
-MODULE_AUTHOR("John Rigby");
-MODULE_LICENSE("GPL v2");
-
diff --git a/arch/powerpc/sysdev/bestcomm/ata.h b/arch/powerpc/sysdev/bestcomm/ata.h
deleted file mode 100644
index 10982769c46..00000000000
--- a/arch/powerpc/sysdev/bestcomm/ata.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Header for Bestcomm ATA task driver
- *
- *
- * Copyright (C) 2006 Freescale - John Rigby
- * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#ifndef __BESTCOMM_ATA_H__
-#define __BESTCOMM_ATA_H__
-
-
-struct bcom_ata_bd {
- u32 status;
- u32 dst_pa;
- u32 src_pa;
-};
-
-extern struct bcom_task *
-bcom_ata_init(int queue_len, int maxbufsize);
-
-extern void
-bcom_ata_rx_prepare(struct bcom_task *tsk);
-
-extern void
-bcom_ata_tx_prepare(struct bcom_task *tsk);
-
-extern void
-bcom_ata_reset_bd(struct bcom_task *tsk);
-
-
-#endif /* __BESTCOMM_ATA_H__ */
-
diff --git a/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c b/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c
deleted file mode 100644
index cc6049a4e46..00000000000
--- a/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Bestcomm ATA task microcode
- *
- * Copyright (c) 2004 Freescale Semiconductor, Inc.
- *
- * 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.
- *
- * Created based on bestcom/code_dma/image_rtos1/dma_image.hex
- */
-
-#include <asm/types.h>
-
-/*
- * The header consists of the following fields:
- * u32 magic;
- * u8 desc_size;
- * u8 var_size;
- * u8 inc_size;
- * u8 first_var;
- * u8 reserved[8];
- *
- * The size fields contain the number of 32-bit words.
- */
-
-u32 bcom_ata_task[] = {
- /* header */
- 0x4243544b,
- 0x0e060709,
- 0x00000000,
- 0x00000000,
-
- /* Task descriptors */
- 0x8198009b, /* LCD: idx0 = var3; idx0 <= var2; idx0 += inc3 */
- 0x13e00c08, /* DRD1A: var3 = var1; FN=0 MORE init=31 WS=0 RS=0 */
- 0xb8000264, /* LCD: idx1 = *idx0, idx2 = var0; idx1 < var9; idx1 += inc4, idx2 += inc4 */
- 0x10000f00, /* DRD1A: var3 = idx0; FN=0 MORE init=0 WS=0 RS=0 */
- 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */
- 0x0c8cfc8a, /* DRD2B1: *idx2 = EU3(); EU3(*idx2,var10) */
- 0xd8988240, /* LCDEXT: idx1 = idx1; idx1 > var9; idx1 += inc0 */
- 0xf845e011, /* LCDEXT: idx2 = *(idx0 + var00000015); ; idx2 += inc2 */
- 0xb845e00a, /* LCD: idx3 = *(idx0 + var00000019); ; idx3 += inc1 */
- 0x0bfecf90, /* DRD1A: *idx3 = *idx2; FN=0 TFD init=31 WS=3 RS=3 */
- 0x9898802d, /* LCD: idx1 = idx1; idx1 once var0; idx1 += inc5 */
- 0x64000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 INT EXT init=0 WS=0 RS=0 */
- 0x0c0cf849, /* DRD2B1: *idx0 = EU3(); EU3(idx1,var9) */
- 0x000001f8, /* NOP */
-
- /* VAR[9]-VAR[14] */
- 0x40000000,
- 0x7fff7fff,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
-
- /* INC[0]-INC[6] */
- 0x40000000,
- 0xe0000000,
- 0xe0000000,
- 0xa000000c,
- 0x20000000,
- 0x00000000,
- 0x00000000,
-};
-
diff --git a/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c
deleted file mode 100644
index a1ad6a02fce..00000000000
--- a/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Bestcomm FEC RX task microcode
- *
- * Copyright (c) 2004 Freescale Semiconductor, Inc.
- *
- * 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.
- *
- * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
- * on Tue Mar 22 11:19:38 2005 GMT
- */
-
-#include <asm/types.h>
-
-/*
- * The header consists of the following fields:
- * u32 magic;
- * u8 desc_size;
- * u8 var_size;
- * u8 inc_size;
- * u8 first_var;
- * u8 reserved[8];
- *
- * The size fields contain the number of 32-bit words.
- */
-
-u32 bcom_fec_rx_task[] = {
- /* header */
- 0x4243544b,
- 0x18060709,
- 0x00000000,
- 0x00000000,
-
- /* Task descriptors */
- 0x808220e3, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */
- 0x10601010, /* DRD1A: var4 = var2; FN=0 MORE init=3 WS=0 RS=0 */
- 0xb8800264, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc4, idx3 += inc4 */
- 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */
- 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */
- 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */
- 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */
- 0xb8c58029, /* LCD: idx3 = *(idx1 + var00000015); idx3 once var0; idx3 += inc5 */
- 0x60000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=0 RS=0 */
- 0x088cf8cc, /* DRD2B1: idx2 = EU3(); EU3(idx3,var12) */
- 0x991982f2, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var11; idx2 += inc6, idx3 += inc2 */
- 0x006acf80, /* DRD1A: *idx3 = *idx0; FN=0 init=3 WS=1 RS=1 */
- 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */
- 0x9999802d, /* LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */
- 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */
- 0x034cfc4e, /* DRD2B1: var13 = EU3(); EU3(*idx1,var14) */
- 0x00008868, /* DRD1A: idx2 = var13; FN=0 init=0 WS=0 RS=0 */
- 0x99198341, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var13; idx2 += inc0, idx3 += inc1 */
- 0x007ecf80, /* DRD1A: *idx3 = *idx0; FN=0 init=3 WS=3 RS=3 */
- 0x99198272, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc6, idx3 += inc2 */
- 0x046acf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=3 WS=1 RS=1 */
- 0x9819002d, /* LCD: idx2 = idx0; idx2 once var0; idx2 += inc5 */
- 0x0060c790, /* DRD1A: *idx1 = *idx2; FN=0 init=3 WS=0 RS=0 */
- 0x000001f8, /* NOP */
-
- /* VAR[9]-VAR[14] */
- 0x40000000,
- 0x7fff7fff,
- 0x00000000,
- 0x00000003,
- 0x40000008,
- 0x43ffffff,
-
- /* INC[0]-INC[6] */
- 0x40000000,
- 0xe0000000,
- 0xe0000000,
- 0xa0000008,
- 0x20000000,
- 0x00000000,
- 0x4000ffff,
-};
-
diff --git a/arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c
deleted file mode 100644
index b1c495c3a65..00000000000
--- a/arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Bestcomm FEC TX task microcode
- *
- * Copyright (c) 2004 Freescale Semiconductor, Inc.
- *
- * 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.
- *
- * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
- * on Tue Mar 22 11:19:29 2005 GMT
- */
-
-#include <asm/types.h>
-
-/*
- * The header consists of the following fields:
- * u32 magic;
- * u8 desc_size;
- * u8 var_size;
- * u8 inc_size;
- * u8 first_var;
- * u8 reserved[8];
- *
- * The size fields contain the number of 32-bit words.
- */
-
-u32 bcom_fec_tx_task[] = {
- /* header */
- 0x4243544b,
- 0x2407070d,
- 0x00000000,
- 0x00000000,
-
- /* Task descriptors */
- 0x8018001b, /* LCD: idx0 = var0; idx0 <= var0; idx0 += inc3 */
- 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */
- 0x01ccfc0d, /* DRD2B1: var7 = EU3(); EU3(*idx0,var13) */
- 0x8082a123, /* LCD: idx0 = var1, idx1 = var5; idx1 <= var4; idx0 += inc4, idx1 += inc3 */
- 0x10801418, /* DRD1A: var5 = var3; FN=0 MORE init=4 WS=0 RS=0 */
- 0xf88103a4, /* LCDEXT: idx2 = *idx1, idx3 = var2; idx2 < var14; idx2 += inc4, idx3 += inc4 */
- 0x801a6024, /* LCD: idx4 = var0; ; idx4 += inc4 */
- 0x10001708, /* DRD1A: var5 = idx1; FN=0 MORE init=0 WS=0 RS=0 */
- 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */
- 0x0cccfccf, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var15) */
- 0x991a002c, /* LCD: idx2 = idx2, idx3 = idx4; idx2 once var0; idx2 += inc5, idx3 += inc4 */
- 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */
- 0x024cfc4d, /* DRD2B1: var9 = EU3(); EU3(*idx1,var13) */
- 0x60000003, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=3 EXT init=0 WS=0 RS=0 */
- 0x0cccf247, /* DRD2B1: *idx3 = EU3(); EU3(var9,var7) */
- 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */
- 0xb8c80029, /* LCD: idx3 = *(idx1 + var0000001a); idx3 once var0; idx3 += inc5 */
- 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */
- 0x088cf8d1, /* DRD2B1: idx2 = EU3(); EU3(idx3,var17) */
- 0x00002f10, /* DRD1A: var11 = idx2; FN=0 init=0 WS=0 RS=0 */
- 0x99198432, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var16; idx2 += inc6, idx3 += inc2 */
- 0x008ac398, /* DRD1A: *idx0 = *idx3; FN=0 init=4 WS=1 RS=1 */
- 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */
- 0x9999802d, /* LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */
- 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */
- 0x048cfc53, /* DRD2B1: var18 = EU3(); EU3(*idx1,var19) */
- 0x60000008, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=8 EXT init=0 WS=0 RS=0 */
- 0x088cf48b, /* DRD2B1: idx2 = EU3(); EU3(var18,var11) */
- 0x99198481, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var18; idx2 += inc0, idx3 += inc1 */
- 0x009ec398, /* DRD1A: *idx0 = *idx3; FN=0 init=4 WS=3 RS=3 */
- 0x991983b2, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var14; idx2 += inc6, idx3 += inc2 */
- 0x088ac398, /* DRD1A: *idx0 = *idx3; FN=0 TFD init=4 WS=1 RS=1 */
- 0x9919002d, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc5 */
- 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */
- 0x0c4cf88e, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var14) */
- 0x000001f8, /* NOP */
-
- /* VAR[13]-VAR[19] */
- 0x0c000000,
- 0x40000000,
- 0x7fff7fff,
- 0x00000000,
- 0x00000003,
- 0x40000004,
- 0x43ffffff,
-
- /* INC[0]-INC[6] */
- 0x40000000,
- 0xe0000000,
- 0xe0000000,
- 0xa0000008,
- 0x20000000,
- 0x00000000,
- 0x4000ffff,
-};
-
diff --git a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c
deleted file mode 100644
index efee022b025..00000000000
--- a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Bestcomm GenBD RX task microcode
- *
- * Copyright (C) 2006 AppSpec Computer Technologies Corp.
- * Jeff Gibbons <jeff.gibbons@appspec.com>
- * Copyright (c) 2004 Freescale Semiconductor, Inc.
- *
- * 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.
- *
- * Based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
- * on Tue Mar 4 10:14:12 2006 GMT
- *
- */
-
-#include <asm/types.h>
-
-/*
- * The header consists of the following fields:
- * u32 magic;
- * u8 desc_size;
- * u8 var_size;
- * u8 inc_size;
- * u8 first_var;
- * u8 reserved[8];
- *
- * The size fields contain the number of 32-bit words.
- */
-
-u32 bcom_gen_bd_rx_task[] = {
- /* header */
- 0x4243544b,
- 0x0d020409,
- 0x00000000,
- 0x00000000,
-
- /* Task descriptors */
- 0x808220da, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc3, idx1 += inc2 */
- 0x13e01010, /* DRD1A: var4 = var2; FN=0 MORE init=31 WS=0 RS=0 */
- 0xb880025b, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc3, idx3 += inc3 */
- 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */
- 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */
- 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */
- 0xd9190240, /* LCDEXT: idx2 = idx2; idx2 > var9; idx2 += inc0 */
- 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */
- 0x07fecf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=31 WS=3 RS=3 */
- 0x99190024, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc4 */
- 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */
- 0x0c4cf889, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var9) */
- 0x000001f8, /* NOP */
-
- /* VAR[9]-VAR[10] */
- 0x40000000,
- 0x7fff7fff,
-
- /* INC[0]-INC[3] */
- 0x40000000,
- 0xe0000000,
- 0xa0000008,
- 0x20000000,
-};
-
diff --git a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c b/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c
deleted file mode 100644
index c605aa42ecb..00000000000
--- a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Bestcomm GenBD TX task microcode
- *
- * Copyright (C) 2006 AppSpec Computer Technologies Corp.
- * Jeff Gibbons <jeff.gibbons@appspec.com>
- * Copyright (c) 2004 Freescale Semiconductor, Inc.
- *
- * 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.
- *
- * Based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex
- * on Tue Mar 4 10:14:12 2006 GMT
- *
- */
-
-#include <asm/types.h>
-
-/*
- * The header consists of the following fields:
- * u32 magic;
- * u8 desc_size;
- * u8 var_size;
- * u8 inc_size;
- * u8 first_var;
- * u8 reserved[8];
- *
- * The size fields contain the number of 32-bit words.
- */
-
-u32 bcom_gen_bd_tx_task[] = {
- /* header */
- 0x4243544b,
- 0x0f040609,
- 0x00000000,
- 0x00000000,
-
- /* Task descriptors */
- 0x800220e3, /* LCD: idx0 = var0, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */
- 0x13e01010, /* DRD1A: var4 = var2; FN=0 MORE init=31 WS=0 RS=0 */
- 0xb8808264, /* LCD: idx2 = *idx1, idx3 = var1; idx2 < var9; idx2 += inc4, idx3 += inc4 */
- 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */
- 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */
- 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */
- 0xd9190300, /* LCDEXT: idx2 = idx2; idx2 > var12; idx2 += inc0 */
- 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */
- 0x03fec398, /* DRD1A: *idx0 = *idx3; FN=0 init=31 WS=3 RS=3 */
- 0x9919826a, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc5, idx3 += inc2 */
- 0x0feac398, /* DRD1A: *idx0 = *idx3; FN=0 TFD INT init=31 WS=1 RS=1 */
- 0x99190036, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc6 */
- 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */
- 0x0c4cf889, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var9) */
- 0x000001f8, /* NOP */
-
- /* VAR[9]-VAR[12] */
- 0x40000000,
- 0x7fff7fff,
- 0x00000000,
- 0x40000004,
-
- /* INC[0]-INC[5] */
- 0x40000000,
- 0xe0000000,
- 0xe0000000,
- 0xa0000008,
- 0x20000000,
- 0x4000ffff,
-};
-
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.c b/arch/powerpc/sysdev/bestcomm/bestcomm.c
deleted file mode 100644
index 64ec7d62936..00000000000
--- a/arch/powerpc/sysdev/bestcomm/bestcomm.c
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * Driver for MPC52xx processor BestComm peripheral controller
- *
- *
- * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2005 Varma Electronics Oy,
- * ( by Andrey Volkov <avolkov@varma-el.com> )
- * Copyright (C) 2003-2004 MontaVista, Software, Inc.
- * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/mpc52xx.h>
-
-#include "sram.h"
-#include "bestcomm_priv.h"
-#include "bestcomm.h"
-
-#define DRIVER_NAME "bestcomm-core"
-
-/* MPC5200 device tree match tables */
-static struct of_device_id mpc52xx_sram_ids[] __devinitdata = {
- { .compatible = "fsl,mpc5200-sram", },
- { .compatible = "mpc5200-sram", },
- {}
-};
-
-
-struct bcom_engine *bcom_eng = NULL;
-EXPORT_SYMBOL_GPL(bcom_eng); /* needed for inline functions */
-
-/* ======================================================================== */
-/* Public and private API */
-/* ======================================================================== */
-
-/* Private API */
-
-struct bcom_task *
-bcom_task_alloc(int bd_count, int bd_size, int priv_size)
-{
- int i, tasknum = -1;
- struct bcom_task *tsk;
-
- /* Don't try to do anything if bestcomm init failed */
- if (!bcom_eng)
- return NULL;
-
- /* Get and reserve a task num */
- spin_lock(&bcom_eng->lock);
-
- for (i=0; i<BCOM_MAX_TASKS; i++)
- if (!bcom_eng->tdt[i].stop) { /* we use stop as a marker */
- bcom_eng->tdt[i].stop = 0xfffffffful; /* dummy addr */
- tasknum = i;
- break;
- }
-
- spin_unlock(&bcom_eng->lock);
-
- if (tasknum < 0)
- return NULL;
-
- /* Allocate our structure */
- tsk = kzalloc(sizeof(struct bcom_task) + priv_size, GFP_KERNEL);
- if (!tsk)
- goto error;
-
- tsk->tasknum = tasknum;
- if (priv_size)
- tsk->priv = (void*)tsk + sizeof(struct bcom_task);
-
- /* Get IRQ of that task */
- tsk->irq = irq_of_parse_and_map(bcom_eng->ofnode, tsk->tasknum);
- if (tsk->irq == NO_IRQ)
- goto error;
-
- /* Init the BDs, if needed */
- if (bd_count) {
- tsk->cookie = kmalloc(sizeof(void*) * bd_count, GFP_KERNEL);
- if (!tsk->cookie)
- goto error;
-
- tsk->bd = bcom_sram_alloc(bd_count * bd_size, 4, &tsk->bd_pa);
- if (!tsk->bd)
- goto error;
- memset(tsk->bd, 0x00, bd_count * bd_size);
-
- tsk->num_bd = bd_count;
- tsk->bd_size = bd_size;
- }
-
- return tsk;
-
-error:
- if (tsk) {
- if (tsk->irq != NO_IRQ)
- irq_dispose_mapping(tsk->irq);
- bcom_sram_free(tsk->bd);
- kfree(tsk->cookie);
- kfree(tsk);
- }
-
- bcom_eng->tdt[tasknum].stop = 0;
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(bcom_task_alloc);
-
-void
-bcom_task_free(struct bcom_task *tsk)
-{
- /* Stop the task */
- bcom_disable_task(tsk->tasknum);
-
- /* Clear TDT */
- bcom_eng->tdt[tsk->tasknum].start = 0;
- bcom_eng->tdt[tsk->tasknum].stop = 0;
-
- /* Free everything */
- irq_dispose_mapping(tsk->irq);
- bcom_sram_free(tsk->bd);
- kfree(tsk->cookie);
- kfree(tsk);
-}
-EXPORT_SYMBOL_GPL(bcom_task_free);
-
-int
-bcom_load_image(int task, u32 *task_image)
-{
- struct bcom_task_header *hdr = (struct bcom_task_header *)task_image;
- struct bcom_tdt *tdt;
- u32 *desc, *var, *inc;
- u32 *desc_src, *var_src, *inc_src;
-
- /* Safety checks */
- if (hdr->magic != BCOM_TASK_MAGIC) {
- printk(KERN_ERR DRIVER_NAME
- ": Trying to load invalid microcode\n");
- return -EINVAL;
- }
-
- if ((task < 0) || (task >= BCOM_MAX_TASKS)) {
- printk(KERN_ERR DRIVER_NAME
- ": Trying to load invalid task %d\n", task);
- return -EINVAL;
- }
-
- /* Initial load or reload */
- tdt = &bcom_eng->tdt[task];
-
- if (tdt->start) {
- desc = bcom_task_desc(task);
- if (hdr->desc_size != bcom_task_num_descs(task)) {
- printk(KERN_ERR DRIVER_NAME
- ": Trying to reload wrong task image "
- "(%d size %d/%d)!\n",
- task,
- hdr->desc_size,
- bcom_task_num_descs(task));
- return -EINVAL;
- }
- } else {
- phys_addr_t start_pa;
-
- desc = bcom_sram_alloc(hdr->desc_size * sizeof(u32), 4, &start_pa);
- if (!desc)
- return -ENOMEM;
-
- tdt->start = start_pa;
- tdt->stop = start_pa + ((hdr->desc_size-1) * sizeof(u32));
- }
-
- var = bcom_task_var(task);
- inc = bcom_task_inc(task);
-
- /* Clear & copy */
- memset(var, 0x00, BCOM_VAR_SIZE);
- memset(inc, 0x00, BCOM_INC_SIZE);
-
- desc_src = (u32 *)(hdr + 1);
- var_src = desc_src + hdr->desc_size;
- inc_src = var_src + hdr->var_size;
-
- memcpy(desc, desc_src, hdr->desc_size * sizeof(u32));
- memcpy(var + hdr->first_var, var_src, hdr->var_size * sizeof(u32));
- memcpy(inc, inc_src, hdr->inc_size * sizeof(u32));
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(bcom_load_image);
-
-void
-bcom_set_initiator(int task, int initiator)
-{
- int i;
- int num_descs;
- u32 *desc;
- int next_drd_has_initiator;
-
- bcom_set_tcr_initiator(task, initiator);
-
- /* Just setting tcr is apparently not enough due to some problem */
- /* with it. So we just go thru all the microcode and replace in */
- /* the DRD directly */
-
- desc = bcom_task_desc(task);
- next_drd_has_initiator = 1;
- num_descs = bcom_task_num_descs(task);
-
- for (i=0; i<num_descs; i++, desc++) {
- if (!bcom_desc_is_drd(*desc))
- continue;
- if (next_drd_has_initiator)
- if (bcom_desc_initiator(*desc) != BCOM_INITIATOR_ALWAYS)
- bcom_set_desc_initiator(desc, initiator);
- next_drd_has_initiator = !bcom_drd_is_extended(*desc);
- }
-}
-EXPORT_SYMBOL_GPL(bcom_set_initiator);
-
-
-/* Public API */
-
-void
-bcom_enable(struct bcom_task *tsk)
-{
- bcom_enable_task(tsk->tasknum);
-}
-EXPORT_SYMBOL_GPL(bcom_enable);
-
-void
-bcom_disable(struct bcom_task *tsk)
-{
- bcom_disable_task(tsk->tasknum);
-}
-EXPORT_SYMBOL_GPL(bcom_disable);
-
-
-/* ======================================================================== */
-/* Engine init/cleanup */
-/* ======================================================================== */
-
-/* Function Descriptor table */
-/* this will need to be updated if Freescale changes their task code FDT */
-static u32 fdt_ops[] = {
- 0xa0045670, /* FDT[48] - load_acc() */
- 0x80045670, /* FDT[49] - unload_acc() */
- 0x21800000, /* FDT[50] - and() */
- 0x21e00000, /* FDT[51] - or() */
- 0x21500000, /* FDT[52] - xor() */
- 0x21400000, /* FDT[53] - andn() */
- 0x21500000, /* FDT[54] - not() */
- 0x20400000, /* FDT[55] - add() */
- 0x20500000, /* FDT[56] - sub() */
- 0x20800000, /* FDT[57] - lsh() */
- 0x20a00000, /* FDT[58] - rsh() */
- 0xc0170000, /* FDT[59] - crc8() */
- 0xc0145670, /* FDT[60] - crc16() */
- 0xc0345670, /* FDT[61] - crc32() */
- 0xa0076540, /* FDT[62] - endian32() */
- 0xa0000760, /* FDT[63] - endian16() */
-};
-
-
-static int __devinit
-bcom_engine_init(void)
-{
- int task;
- phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;
- unsigned int tdt_size, ctx_size, var_size, fdt_size;
- u16 regval;
-
- /* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */
- tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt);
- ctx_size = BCOM_MAX_TASKS * BCOM_CTX_SIZE;
- var_size = BCOM_MAX_TASKS * (BCOM_VAR_SIZE + BCOM_INC_SIZE);
- fdt_size = BCOM_FDT_SIZE;
-
- bcom_eng->tdt = bcom_sram_alloc(tdt_size, sizeof(u32), &tdt_pa);
- bcom_eng->ctx = bcom_sram_alloc(ctx_size, BCOM_CTX_ALIGN, &ctx_pa);
- bcom_eng->var = bcom_sram_alloc(var_size, BCOM_VAR_ALIGN, &var_pa);
- bcom_eng->fdt = bcom_sram_alloc(fdt_size, BCOM_FDT_ALIGN, &fdt_pa);
-
- if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) {
- printk(KERN_ERR "DMA: SRAM alloc failed in engine init !\n");
-
- bcom_sram_free(bcom_eng->tdt);
- bcom_sram_free(bcom_eng->ctx);
- bcom_sram_free(bcom_eng->var);
- bcom_sram_free(bcom_eng->fdt);
-
- return -ENOMEM;
- }
-
- memset(bcom_eng->tdt, 0x00, tdt_size);
- memset(bcom_eng->ctx, 0x00, ctx_size);
- memset(bcom_eng->var, 0x00, var_size);
- memset(bcom_eng->fdt, 0x00, fdt_size);
-
- /* Copy the FDT for the EU#3 */
- memcpy(&bcom_eng->fdt[48], fdt_ops, sizeof(fdt_ops));
-
- /* Initialize Task base structure */
- for (task=0; task<BCOM_MAX_TASKS; task++)
- {
- out_be16(&bcom_eng->regs->tcr[task], 0);
- out_8(&bcom_eng->regs->ipr[task], 0);
-
- bcom_eng->tdt[task].context = ctx_pa;
- bcom_eng->tdt[task].var = var_pa;
- bcom_eng->tdt[task].fdt = fdt_pa;
-
- var_pa += BCOM_VAR_SIZE + BCOM_INC_SIZE;
- ctx_pa += BCOM_CTX_SIZE;
- }
-
- out_be32(&bcom_eng->regs->taskBar, tdt_pa);
-
- /* Init 'always' initiator */
- out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
-
- /* Disable COMM Bus Prefetch on the original 5200; it's broken */
- if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) {
- regval = in_be16(&bcom_eng->regs->PtdCntrl);
- out_be16(&bcom_eng->regs->PtdCntrl, regval | 1);
- }
-
- /* Init lock */
- spin_lock_init(&bcom_eng->lock);
-
- return 0;
-}
-
-static void
-bcom_engine_cleanup(void)
-{
- int task;
-
- /* Stop all tasks */
- for (task=0; task<BCOM_MAX_TASKS; task++)
- {
- out_be16(&bcom_eng->regs->tcr[task], 0);
- out_8(&bcom_eng->regs->ipr[task], 0);
- }
-
- out_be32(&bcom_eng->regs->taskBar, 0ul);
-
- /* Release the SRAM zones */
- bcom_sram_free(bcom_eng->tdt);
- bcom_sram_free(bcom_eng->ctx);
- bcom_sram_free(bcom_eng->var);
- bcom_sram_free(bcom_eng->fdt);
-}
-
-
-/* ======================================================================== */
-/* OF platform driver */
-/* ======================================================================== */
-
-static int __devinit
-mpc52xx_bcom_probe(struct of_device *op, const struct of_device_id *match)
-{
- struct device_node *ofn_sram;
- struct resource res_bcom;
-
- int rv;
-
- /* Inform user we're ok so far */
- printk(KERN_INFO "DMA: MPC52xx BestComm driver\n");
-
- /* Get the bestcomm node */
- of_node_get(op->node);
-
- /* Prepare SRAM */
- ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids);
- if (!ofn_sram) {
- printk(KERN_ERR DRIVER_NAME ": "
- "No SRAM found in device tree\n");
- rv = -ENODEV;
- goto error_ofput;
- }
- rv = bcom_sram_init(ofn_sram, DRIVER_NAME);
- of_node_put(ofn_sram);
-
- if (rv) {
- printk(KERN_ERR DRIVER_NAME ": "
- "Error in SRAM init\n");
- goto error_ofput;
- }
-
- /* Get a clean struct */
- bcom_eng = kzalloc(sizeof(struct bcom_engine), GFP_KERNEL);
- if (!bcom_eng) {
- printk(KERN_ERR DRIVER_NAME ": "
- "Can't allocate state structure\n");
- rv = -ENOMEM;
- goto error_sramclean;
- }
-
- /* Save the node */
- bcom_eng->ofnode = op->node;
-
- /* Get, reserve & map io */
- if (of_address_to_resource(op->node, 0, &res_bcom)) {
- printk(KERN_ERR DRIVER_NAME ": "
- "Can't get resource\n");
- rv = -EINVAL;
- goto error_sramclean;
- }
-
- if (!request_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma),
- DRIVER_NAME)) {
- printk(KERN_ERR DRIVER_NAME ": "
- "Can't request registers region\n");
- rv = -EBUSY;
- goto error_sramclean;
- }
-
- bcom_eng->regs_base = res_bcom.start;
- bcom_eng->regs = ioremap(res_bcom.start, sizeof(struct mpc52xx_sdma));
- if (!bcom_eng->regs) {
- printk(KERN_ERR DRIVER_NAME ": "
- "Can't map registers\n");
- rv = -ENOMEM;
- goto error_release;
- }
-
- /* Now, do the real init */
- rv = bcom_engine_init();
- if (rv)
- goto error_unmap;
-
- /* Done ! */
- printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n",
- bcom_eng->regs_base);
-
- return 0;
-
- /* Error path */
-error_unmap:
- iounmap(bcom_eng->regs);
-error_release:
- release_mem_region(res_bcom.start, sizeof(struct mpc52xx_sdma));
-error_sramclean:
- kfree(bcom_eng);
- bcom_sram_cleanup();
-error_ofput:
- of_node_put(op->node);
-
- printk(KERN_ERR "DMA: MPC52xx BestComm init failed !\n");
-
- return rv;
-}
-
-
-static int
-mpc52xx_bcom_remove(struct of_device *op)
-{
- /* Clean up the engine */
- bcom_engine_cleanup();
-
- /* Cleanup SRAM */
- bcom_sram_cleanup();
-
- /* Release regs */
- iounmap(bcom_eng->regs);
- release_mem_region(bcom_eng->regs_base, sizeof(struct mpc52xx_sdma));
-
- /* Release the node */
- of_node_put(bcom_eng->ofnode);
-
- /* Release memory */
- kfree(bcom_eng);
- bcom_eng = NULL;
-
- return 0;
-}
-
-static struct of_device_id mpc52xx_bcom_of_match[] = {
- { .compatible = "fsl,mpc5200-bestcomm", },
- { .compatible = "mpc5200-bestcomm", },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match);
-
-
-static struct of_platform_driver mpc52xx_bcom_of_platform_driver = {
- .owner = THIS_MODULE,
- .name = DRIVER_NAME,
- .match_table = mpc52xx_bcom_of_match,
- .probe = mpc52xx_bcom_probe,
- .remove = mpc52xx_bcom_remove,
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-
-/* ======================================================================== */
-/* Module */
-/* ======================================================================== */
-
-static int __init
-mpc52xx_bcom_init(void)
-{
- return of_register_platform_driver(&mpc52xx_bcom_of_platform_driver);
-}
-
-static void __exit
-mpc52xx_bcom_exit(void)
-{
- of_unregister_platform_driver(&mpc52xx_bcom_of_platform_driver);
-}
-
-/* If we're not a module, we must make sure everything is setup before */
-/* anyone tries to use us ... that's why we use subsys_initcall instead */
-/* of module_init. */
-subsys_initcall(mpc52xx_bcom_init);
-module_exit(mpc52xx_bcom_exit);
-
-MODULE_DESCRIPTION("Freescale MPC52xx BestComm DMA");
-MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
-MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
-MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
-MODULE_LICENSE("GPL v2");
-
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.h b/arch/powerpc/sysdev/bestcomm/bestcomm.h
deleted file mode 100644
index c960a8b4965..00000000000
--- a/arch/powerpc/sysdev/bestcomm/bestcomm.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Public header for the MPC52xx processor BestComm driver
- *
- *
- * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2005 Varma Electronics Oy,
- * ( by Andrey Volkov <avolkov@varma-el.com> )
- * Copyright (C) 2003-2004 MontaVista, Software, Inc.
- * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#ifndef __BESTCOMM_H__
-#define __BESTCOMM_H__
-
-struct bcom_bd; /* defined later on ... */
-
-
-/* ======================================================================== */
-/* Generic task management */
-/* ======================================================================== */
-
-/**
- * struct bcom_task - Structure describing a loaded BestComm task
- *
- * This structure is never built by the driver it self. It's built and
- * filled the intermediate layer of the BestComm API, the task dependent
- * support code.
- *
- * Most likely you don't need to poke around inside this structure. The
- * fields are exposed in the header just for the sake of inline functions
- */
-struct bcom_task {
- unsigned int tasknum;
- unsigned int flags;
- int irq;
-
- struct bcom_bd *bd;
- phys_addr_t bd_pa;
- void **cookie;
- unsigned short index;
- unsigned short outdex;
- unsigned int num_bd;
- unsigned int bd_size;
-
- void* priv;
-};
-
-#define BCOM_FLAGS_NONE 0x00000000ul
-#define BCOM_FLAGS_ENABLE_TASK (1ul << 0)
-
-/**
- * bcom_enable - Enable a BestComm task
- * @tsk: The BestComm task structure
- *
- * This function makes sure the given task is enabled and can be run
- * by the BestComm engine as needed
- */
-extern void bcom_enable(struct bcom_task *tsk);
-
-/**
- * bcom_disable - Disable a BestComm task
- * @tsk: The BestComm task structure
- *
- * This function disable a given task, making sure it's not executed
- * by the BestComm engine.
- */
-extern void bcom_disable(struct bcom_task *tsk);
-
-
-/**
- * bcom_get_task_irq - Returns the irq number of a BestComm task
- * @tsk: The BestComm task structure
- */
-static inline int
-bcom_get_task_irq(struct bcom_task *tsk) {
- return tsk->irq;
-}
-
-/* ======================================================================== */
-/* BD based tasks helpers */
-/* ======================================================================== */
-
-/**
- * struct bcom_bd - Structure describing a generic BestComm buffer descriptor
- * @status: The current status of this buffer. Exact meaning depends on the
- * task type
- * @data: An array of u32 whose meaning depends on the task type.
- */
-struct bcom_bd {
- u32 status;
- u32 data[1]; /* variable, but at least 1 */
-};
-
-#define BCOM_BD_READY 0x40000000ul
-
-/** _bcom_next_index - Get next input index.
- * @tsk: pointer to task structure
- *
- * Support function; Device drivers should not call this
- */
-static inline int
-_bcom_next_index(struct bcom_task *tsk)
-{
- return ((tsk->index + 1) == tsk->num_bd) ? 0 : tsk->index + 1;
-}
-
-/** _bcom_next_outdex - Get next output index.
- * @tsk: pointer to task structure
- *
- * Support function; Device drivers should not call this
- */
-static inline int
-_bcom_next_outdex(struct bcom_task *tsk)
-{
- return ((tsk->outdex + 1) == tsk->num_bd) ? 0 : tsk->outdex + 1;
-}
-
-/**
- * bcom_queue_empty - Checks if a BestComm task BD queue is empty
- * @tsk: The BestComm task structure
- */
-static inline int
-bcom_queue_empty(struct bcom_task *tsk)
-{
- return tsk->index == tsk->outdex;
-}
-
-/**
- * bcom_queue_full - Checks if a BestComm task BD queue is full
- * @tsk: The BestComm task structure
- */
-static inline int
-bcom_queue_full(struct bcom_task *tsk)
-{
- return tsk->outdex == _bcom_next_index(tsk);
-}
-
-/**
- * bcom_buffer_done - Checks if a BestComm
- * @tsk: The BestComm task structure
- */
-static inline int
-bcom_buffer_done(struct bcom_task *tsk)
-{
- if (bcom_queue_empty(tsk))
- return 0;
- return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
-}
-
-/**
- * bcom_prepare_next_buffer - clear status of next available buffer.
- * @tsk: The BestComm task structure
- *
- * Returns pointer to next buffer descriptor
- */
-static inline struct bcom_bd *
-bcom_prepare_next_buffer(struct bcom_task *tsk)
-{
- tsk->bd[tsk->index].status = 0; /* cleanup last status */
- return &tsk->bd[tsk->index];
-}
-
-static inline void
-bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie)
-{
- tsk->cookie[tsk->index] = cookie;
- mb(); /* ensure the bd is really up-to-date */
- tsk->bd[tsk->index].status |= BCOM_BD_READY;
- tsk->index = _bcom_next_index(tsk);
- if (tsk->flags & BCOM_FLAGS_ENABLE_TASK)
- bcom_enable(tsk);
-}
-
-static inline void *
-bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd)
-{
- void *cookie = tsk->cookie[tsk->outdex];
- if (p_status)
- *p_status = tsk->bd[tsk->outdex].status;
- if (p_bd)
- *p_bd = &tsk->bd[tsk->outdex];
- tsk->outdex = _bcom_next_outdex(tsk);
- return cookie;
-}
-
-#endif /* __BESTCOMM_H__ */
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
deleted file mode 100644
index 866a2915ef2..00000000000
--- a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Private header for the MPC52xx processor BestComm driver
- *
- * By private, we mean that driver should not use it directly. It's meant
- * to be used by the BestComm engine driver itself and by the intermediate
- * layer between the core and the drivers.
- *
- * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2005 Varma Electronics Oy,
- * ( by Andrey Volkov <avolkov@varma-el.com> )
- * Copyright (C) 2003-2004 MontaVista, Software, Inc.
- * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#ifndef __BESTCOMM_PRIV_H__
-#define __BESTCOMM_PRIV_H__
-
-#include <linux/spinlock.h>
-#include <linux/of.h>
-#include <asm/io.h>
-#include <asm/mpc52xx.h>
-
-#include "sram.h"
-
-
-/* ======================================================================== */
-/* Engine related stuff */
-/* ======================================================================== */
-
-/* Zones sizes and needed alignments */
-#define BCOM_MAX_TASKS 16
-#define BCOM_MAX_VAR 24
-#define BCOM_MAX_INC 8
-#define BCOM_MAX_FDT 64
-#define BCOM_MAX_CTX 20
-#define BCOM_CTX_SIZE (BCOM_MAX_CTX * sizeof(u32))
-#define BCOM_CTX_ALIGN 0x100
-#define BCOM_VAR_SIZE (BCOM_MAX_VAR * sizeof(u32))
-#define BCOM_INC_SIZE (BCOM_MAX_INC * sizeof(u32))
-#define BCOM_VAR_ALIGN 0x80
-#define BCOM_FDT_SIZE (BCOM_MAX_FDT * sizeof(u32))
-#define BCOM_FDT_ALIGN 0x100
-
-/**
- * struct bcom_tdt - Task Descriptor Table Entry
- *
- */
-struct bcom_tdt {
- u32 start;
- u32 stop;
- u32 var;
- u32 fdt;
- u32 exec_status; /* used internally by BestComm engine */
- u32 mvtp; /* used internally by BestComm engine */
- u32 context;
- u32 litbase;
-};
-
-/**
- * struct bcom_engine
- *
- * This holds all info needed globaly to handle the engine
- */
-struct bcom_engine {
- struct device_node *ofnode;
- struct mpc52xx_sdma __iomem *regs;
- phys_addr_t regs_base;
-
- struct bcom_tdt *tdt;
- u32 *ctx;
- u32 *var;
- u32 *fdt;
-
- spinlock_t lock;
-};
-
-extern struct bcom_engine *bcom_eng;
-
-
-/* ======================================================================== */
-/* Tasks related stuff */
-/* ======================================================================== */
-
-/* Tasks image header */
-#define BCOM_TASK_MAGIC 0x4243544B /* 'BCTK' */
-
-struct bcom_task_header {
- u32 magic;
- u8 desc_size; /* the size fields */
- u8 var_size; /* are given in number */
- u8 inc_size; /* of 32-bits words */
- u8 first_var;
- u8 reserved[8];
-};
-
-/* Descriptors stucture & co */
-#define BCOM_DESC_NOP 0x000001f8
-#define BCOM_LCD_MASK 0x80000000
-#define BCOM_DRD_EXTENDED 0x40000000
-#define BCOM_DRD_INITIATOR_SHIFT 21
-
-/* Tasks pragma */
-#define BCOM_PRAGMA_BIT_RSV 7 /* reserved pragma bit */
-#define BCOM_PRAGMA_BIT_PRECISE_INC 6 /* increment 0=when possible, */
- /* 1=iter end */
-#define BCOM_PRAGMA_BIT_RST_ERROR_NO 5 /* don't reset errors on */
- /* task enable */
-#define BCOM_PRAGMA_BIT_PACK 4 /* pack data enable */
-#define BCOM_PRAGMA_BIT_INTEGER 3 /* data alignment */
- /* 0=frac(msb), 1=int(lsb) */
-#define BCOM_PRAGMA_BIT_SPECREAD 2 /* XLB speculative read */
-#define BCOM_PRAGMA_BIT_CW 1 /* write line buffer enable */
-#define BCOM_PRAGMA_BIT_RL 0 /* read line buffer enable */
-
- /* Looks like XLB speculative read generates XLB errors when a buffer
- * is at the end of the physical memory. i.e. when accessing the
- * lasts words, the engine tries to prefetch the next but there is no
- * next ...
- */
-#define BCOM_STD_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \
- (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \
- (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \
- (0 << BCOM_PRAGMA_BIT_PACK) | \
- (0 << BCOM_PRAGMA_BIT_INTEGER) | \
- (0 << BCOM_PRAGMA_BIT_SPECREAD) | \
- (1 << BCOM_PRAGMA_BIT_CW) | \
- (1 << BCOM_PRAGMA_BIT_RL))
-
-#define BCOM_PCI_PRAGMA ((0 << BCOM_PRAGMA_BIT_RSV) | \
- (0 << BCOM_PRAGMA_BIT_PRECISE_INC) | \
- (0 << BCOM_PRAGMA_BIT_RST_ERROR_NO) | \
- (0 << BCOM_PRAGMA_BIT_PACK) | \
- (1 << BCOM_PRAGMA_BIT_INTEGER) | \
- (0 << BCOM_PRAGMA_BIT_SPECREAD) | \
- (1 << BCOM_PRAGMA_BIT_CW) | \
- (1 << BCOM_PRAGMA_BIT_RL))
-
-#define BCOM_ATA_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_CRC16_DP_0_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_CRC16_DP_1_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_FEC_RX_BD_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_FEC_TX_BD_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_GEN_DP_0_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_GEN_DP_1_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_GEN_DP_2_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_GEN_DP_3_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_GEN_DP_BD_0_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_GEN_DP_BD_1_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_GEN_RX_BD_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_GEN_TX_BD_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_GEN_LPC_PRAGMA BCOM_STD_PRAGMA
-#define BCOM_PCI_RX_PRAGMA BCOM_PCI_PRAGMA
-#define BCOM_PCI_TX_PRAGMA BCOM_PCI_PRAGMA
-
-/* Initiators number */
-#define BCOM_INITIATOR_ALWAYS 0
-#define BCOM_INITIATOR_SCTMR_0 1
-#define BCOM_INITIATOR_SCTMR_1 2
-#define BCOM_INITIATOR_FEC_RX 3
-#define BCOM_INITIATOR_FEC_TX 4
-#define BCOM_INITIATOR_ATA_RX 5
-#define BCOM_INITIATOR_ATA_TX 6
-#define BCOM_INITIATOR_SCPCI_RX 7
-#define BCOM_INITIATOR_SCPCI_TX 8
-#define BCOM_INITIATOR_PSC3_RX 9
-#define BCOM_INITIATOR_PSC3_TX 10
-#define BCOM_INITIATOR_PSC2_RX 11
-#define BCOM_INITIATOR_PSC2_TX 12
-#define BCOM_INITIATOR_PSC1_RX 13
-#define BCOM_INITIATOR_PSC1_TX 14
-#define BCOM_INITIATOR_SCTMR_2 15
-#define BCOM_INITIATOR_SCLPC 16
-#define BCOM_INITIATOR_PSC5_RX 17
-#define BCOM_INITIATOR_PSC5_TX 18
-#define BCOM_INITIATOR_PSC4_RX 19
-#define BCOM_INITIATOR_PSC4_TX 20
-#define BCOM_INITIATOR_I2C2_RX 21
-#define BCOM_INITIATOR_I2C2_TX 22
-#define BCOM_INITIATOR_I2C1_RX 23
-#define BCOM_INITIATOR_I2C1_TX 24
-#define BCOM_INITIATOR_PSC6_RX 25
-#define BCOM_INITIATOR_PSC6_TX 26
-#define BCOM_INITIATOR_IRDA_RX 25
-#define BCOM_INITIATOR_IRDA_TX 26
-#define BCOM_INITIATOR_SCTMR_3 27
-#define BCOM_INITIATOR_SCTMR_4 28
-#define BCOM_INITIATOR_SCTMR_5 29
-#define BCOM_INITIATOR_SCTMR_6 30
-#define BCOM_INITIATOR_SCTMR_7 31
-
-/* Initiators priorities */
-#define BCOM_IPR_ALWAYS 7
-#define BCOM_IPR_SCTMR_0 2
-#define BCOM_IPR_SCTMR_1 2
-#define BCOM_IPR_FEC_RX 6
-#define BCOM_IPR_FEC_TX 5
-#define BCOM_IPR_ATA_RX 4
-#define BCOM_IPR_ATA_TX 3
-#define BCOM_IPR_SCPCI_RX 2
-#define BCOM_IPR_SCPCI_TX 2
-#define BCOM_IPR_PSC3_RX 2
-#define BCOM_IPR_PSC3_TX 2
-#define BCOM_IPR_PSC2_RX 2
-#define BCOM_IPR_PSC2_TX 2
-#define BCOM_IPR_PSC1_RX 2
-#define BCOM_IPR_PSC1_TX 2
-#define BCOM_IPR_SCTMR_2 2
-#define BCOM_IPR_SCLPC 2
-#define BCOM_IPR_PSC5_RX 2
-#define BCOM_IPR_PSC5_TX 2
-#define BCOM_IPR_PSC4_RX 2
-#define BCOM_IPR_PSC4_TX 2
-#define BCOM_IPR_I2C2_RX 2
-#define BCOM_IPR_I2C2_TX 2
-#define BCOM_IPR_I2C1_RX 2
-#define BCOM_IPR_I2C1_TX 2
-#define BCOM_IPR_PSC6_RX 2
-#define BCOM_IPR_PSC6_TX 2
-#define BCOM_IPR_IRDA_RX 2
-#define BCOM_IPR_IRDA_TX 2
-#define BCOM_IPR_SCTMR_3 2
-#define BCOM_IPR_SCTMR_4 2
-#define BCOM_IPR_SCTMR_5 2
-#define BCOM_IPR_SCTMR_6 2
-#define BCOM_IPR_SCTMR_7 2
-
-
-/* ======================================================================== */
-/* API */
-/* ======================================================================== */
-
-extern struct bcom_task *bcom_task_alloc(int bd_count, int bd_size, int priv_size);
-extern void bcom_task_free(struct bcom_task *tsk);
-extern int bcom_load_image(int task, u32 *task_image);
-extern void bcom_set_initiator(int task, int initiator);
-
-
-#define TASK_ENABLE 0x8000
-
-static inline void
-bcom_enable_task(int task)
-{
- u16 reg;
- reg = in_be16(&bcom_eng->regs->tcr[task]);
- out_be16(&bcom_eng->regs->tcr[task], reg | TASK_ENABLE);
-}
-
-static inline void
-bcom_disable_task(int task)
-{
- u16 reg = in_be16(&bcom_eng->regs->tcr[task]);
- out_be16(&bcom_eng->regs->tcr[task], reg & ~TASK_ENABLE);
-}
-
-
-static inline u32 *
-bcom_task_desc(int task)
-{
- return bcom_sram_pa2va(bcom_eng->tdt[task].start);
-}
-
-static inline int
-bcom_task_num_descs(int task)
-{
- return (bcom_eng->tdt[task].stop - bcom_eng->tdt[task].start)/sizeof(u32) + 1;
-}
-
-static inline u32 *
-bcom_task_var(int task)
-{
- return bcom_sram_pa2va(bcom_eng->tdt[task].var);
-}
-
-static inline u32 *
-bcom_task_inc(int task)
-{
- return &bcom_task_var(task)[BCOM_MAX_VAR];
-}
-
-
-static inline int
-bcom_drd_is_extended(u32 desc)
-{
- return (desc) & BCOM_DRD_EXTENDED;
-}
-
-static inline int
-bcom_desc_is_drd(u32 desc)
-{
- return !(desc & BCOM_LCD_MASK) && desc != BCOM_DESC_NOP;
-}
-
-static inline int
-bcom_desc_initiator(u32 desc)
-{
- return (desc >> BCOM_DRD_INITIATOR_SHIFT) & 0x1f;
-}
-
-static inline void
-bcom_set_desc_initiator(u32 *desc, int initiator)
-{
- *desc = (*desc & ~(0x1f << BCOM_DRD_INITIATOR_SHIFT)) |
- ((initiator & 0x1f) << BCOM_DRD_INITIATOR_SHIFT);
-}
-
-
-static inline void
-bcom_set_task_pragma(int task, int pragma)
-{
- u32 *fdt = &bcom_eng->tdt[task].fdt;
- *fdt = (*fdt & ~0xff) | pragma;
-}
-
-static inline void
-bcom_set_task_auto_start(int task, int next_task)
-{
- u16 __iomem *tcr = &bcom_eng->regs->tcr[task];
- out_be16(tcr, (in_be16(tcr) & ~0xff) | 0x00c0 | next_task);
-}
-
-static inline void
-bcom_set_tcr_initiator(int task, int initiator)
-{
- u16 __iomem *tcr = &bcom_eng->regs->tcr[task];
- out_be16(tcr, (in_be16(tcr) & ~0x1f00) | ((initiator & 0x1f) << 8));
-}
-
-
-#endif /* __BESTCOMM_PRIV_H__ */
-
diff --git a/arch/powerpc/sysdev/bestcomm/fec.c b/arch/powerpc/sysdev/bestcomm/fec.c
deleted file mode 100644
index 957a988d23e..00000000000
--- a/arch/powerpc/sysdev/bestcomm/fec.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Bestcomm FEC tasks driver
- *
- *
- * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2003-2004 MontaVista, Software, Inc.
- * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <asm/io.h>
-
-#include "bestcomm.h"
-#include "bestcomm_priv.h"
-#include "fec.h"
-
-
-/* ======================================================================== */
-/* Task image/var/inc */
-/* ======================================================================== */
-
-/* fec tasks images */
-extern u32 bcom_fec_rx_task[];
-extern u32 bcom_fec_tx_task[];
-
-/* rx task vars that need to be set before enabling the task */
-struct bcom_fec_rx_var {
- u32 enable; /* (u16*) address of task's control register */
- u32 fifo; /* (u32*) address of fec's fifo */
- u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */
- u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
- u32 bd_start; /* (struct bcom_bd*) current bd */
- u32 buffer_size; /* size of receive buffer */
-};
-
-/* rx task incs that need to be set before enabling the task */
-struct bcom_fec_rx_inc {
- u16 pad0;
- s16 incr_bytes;
- u16 pad1;
- s16 incr_dst;
- u16 pad2;
- s16 incr_dst_ma;
-};
-
-/* tx task vars that need to be set before enabling the task */
-struct bcom_fec_tx_var {
- u32 DRD; /* (u32*) address of self-modified DRD */
- u32 fifo; /* (u32*) address of fec's fifo */
- u32 enable; /* (u16*) address of task's control register */
- u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */
- u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
- u32 bd_start; /* (struct bcom_bd*) current bd */
- u32 buffer_size; /* set by uCode for each packet */
-};
-
-/* tx task incs that need to be set before enabling the task */
-struct bcom_fec_tx_inc {
- u16 pad0;
- s16 incr_bytes;
- u16 pad1;
- s16 incr_src;
- u16 pad2;
- s16 incr_src_ma;
-};
-
-/* private structure in the task */
-struct bcom_fec_priv {
- phys_addr_t fifo;
- int maxbufsize;
-};
-
-
-/* ======================================================================== */
-/* Task support code */
-/* ======================================================================== */
-
-struct bcom_task *
-bcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize)
-{
- struct bcom_task *tsk;
- struct bcom_fec_priv *priv;
-
- tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd),
- sizeof(struct bcom_fec_priv));
- if (!tsk)
- return NULL;
-
- tsk->flags = BCOM_FLAGS_NONE;
-
- priv = tsk->priv;
- priv->fifo = fifo;
- priv->maxbufsize = maxbufsize;
-
- if (bcom_fec_rx_reset(tsk)) {
- bcom_task_free(tsk);
- return NULL;
- }
-
- return tsk;
-}
-EXPORT_SYMBOL_GPL(bcom_fec_rx_init);
-
-int
-bcom_fec_rx_reset(struct bcom_task *tsk)
-{
- struct bcom_fec_priv *priv = tsk->priv;
- struct bcom_fec_rx_var *var;
- struct bcom_fec_rx_inc *inc;
-
- /* Shutdown the task */
- bcom_disable_task(tsk->tasknum);
-
- /* Reset the microcode */
- var = (struct bcom_fec_rx_var *) bcom_task_var(tsk->tasknum);
- inc = (struct bcom_fec_rx_inc *) bcom_task_inc(tsk->tasknum);
-
- if (bcom_load_image(tsk->tasknum, bcom_fec_rx_task))
- return -1;
-
- var->enable = bcom_eng->regs_base +
- offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
- var->fifo = (u32) priv->fifo;
- var->bd_base = tsk->bd_pa;
- var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
- var->bd_start = tsk->bd_pa;
- var->buffer_size = priv->maxbufsize;
-
- inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */
- inc->incr_dst = sizeof(u32); /* task image, but we stick */
- inc->incr_dst_ma= sizeof(u8); /* to the official ones */
-
- /* Reset the BDs */
- tsk->index = 0;
- tsk->outdex = 0;
-
- memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
-
- /* Configure some stuff */
- bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_RX_BD_PRAGMA);
- bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
-
- out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_RX], BCOM_IPR_FEC_RX);
-
- out_be32(&bcom_eng->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(bcom_fec_rx_reset);
-
-void
-bcom_fec_rx_release(struct bcom_task *tsk)
-{
- /* Nothing special for the FEC tasks */
- bcom_task_free(tsk);
-}
-EXPORT_SYMBOL_GPL(bcom_fec_rx_release);
-
-
-
- /* Return 2nd to last DRD */
- /* This is an ugly hack, but at least it's only done
- once at initialization */
-static u32 *self_modified_drd(int tasknum)
-{
- u32 *desc;
- int num_descs;
- int drd_count;
- int i;
-
- num_descs = bcom_task_num_descs(tasknum);
- desc = bcom_task_desc(tasknum) + num_descs - 1;
- drd_count = 0;
- for (i=0; i<num_descs; i++, desc--)
- if (bcom_desc_is_drd(*desc) && ++drd_count == 3)
- break;
- return desc;
-}
-
-struct bcom_task *
-bcom_fec_tx_init(int queue_len, phys_addr_t fifo)
-{
- struct bcom_task *tsk;
- struct bcom_fec_priv *priv;
-
- tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd),
- sizeof(struct bcom_fec_priv));
- if (!tsk)
- return NULL;
-
- tsk->flags = BCOM_FLAGS_ENABLE_TASK;
-
- priv = tsk->priv;
- priv->fifo = fifo;
-
- if (bcom_fec_tx_reset(tsk)) {
- bcom_task_free(tsk);
- return NULL;
- }
-
- return tsk;
-}
-EXPORT_SYMBOL_GPL(bcom_fec_tx_init);
-
-int
-bcom_fec_tx_reset(struct bcom_task *tsk)
-{
- struct bcom_fec_priv *priv = tsk->priv;
- struct bcom_fec_tx_var *var;
- struct bcom_fec_tx_inc *inc;
-
- /* Shutdown the task */
- bcom_disable_task(tsk->tasknum);
-
- /* Reset the microcode */
- var = (struct bcom_fec_tx_var *) bcom_task_var(tsk->tasknum);
- inc = (struct bcom_fec_tx_inc *) bcom_task_inc(tsk->tasknum);
-
- if (bcom_load_image(tsk->tasknum, bcom_fec_tx_task))
- return -1;
-
- var->enable = bcom_eng->regs_base +
- offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
- var->fifo = (u32) priv->fifo;
- var->DRD = bcom_sram_va2pa(self_modified_drd(tsk->tasknum));
- var->bd_base = tsk->bd_pa;
- var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
- var->bd_start = tsk->bd_pa;
-
- inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */
- inc->incr_src = sizeof(u32); /* task image, but we stick */
- inc->incr_src_ma= sizeof(u8); /* to the official ones */
-
- /* Reset the BDs */
- tsk->index = 0;
- tsk->outdex = 0;
-
- memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
-
- /* Configure some stuff */
- bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_TX_BD_PRAGMA);
- bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
-
- out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_TX], BCOM_IPR_FEC_TX);
-
- out_be32(&bcom_eng->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(bcom_fec_tx_reset);
-
-void
-bcom_fec_tx_release(struct bcom_task *tsk)
-{
- /* Nothing special for the FEC tasks */
- bcom_task_free(tsk);
-}
-EXPORT_SYMBOL_GPL(bcom_fec_tx_release);
-
-
-MODULE_DESCRIPTION("BestComm FEC tasks driver");
-MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>");
-MODULE_LICENSE("GPL v2");
-
diff --git a/arch/powerpc/sysdev/bestcomm/fec.h b/arch/powerpc/sysdev/bestcomm/fec.h
deleted file mode 100644
index ee565d94d50..00000000000
--- a/arch/powerpc/sysdev/bestcomm/fec.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Header for Bestcomm FEC tasks driver
- *
- *
- * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2003-2004 MontaVista, Software, Inc.
- * ( by Dale Farnsworth <dfarnsworth@mvista.com> )
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#ifndef __BESTCOMM_FEC_H__
-#define __BESTCOMM_FEC_H__
-
-
-struct bcom_fec_bd {
- u32 status;
- u32 skb_pa;
-};
-
-#define BCOM_FEC_TX_BD_TFD 0x08000000ul /* transmit frame done */
-#define BCOM_FEC_TX_BD_TC 0x04000000ul /* transmit CRC */
-#define BCOM_FEC_TX_BD_ABC 0x02000000ul /* append bad CRC */
-
-#define BCOM_FEC_RX_BD_L 0x08000000ul /* buffer is last in frame */
-#define BCOM_FEC_RX_BD_BC 0x00800000ul /* DA is broadcast */
-#define BCOM_FEC_RX_BD_MC 0x00400000ul /* DA is multicast and not broadcast */
-#define BCOM_FEC_RX_BD_LG 0x00200000ul /* Rx frame length violation */
-#define BCOM_FEC_RX_BD_NO 0x00100000ul /* Rx non-octet aligned frame */
-#define BCOM_FEC_RX_BD_CR 0x00040000ul /* Rx CRC error */
-#define BCOM_FEC_RX_BD_OV 0x00020000ul /* overrun */
-#define BCOM_FEC_RX_BD_TR 0x00010000ul /* Rx frame truncated */
-#define BCOM_FEC_RX_BD_LEN_MASK 0x000007fful /* mask for length of received frame */
-#define BCOM_FEC_RX_BD_ERRORS (BCOM_FEC_RX_BD_LG | BCOM_FEC_RX_BD_NO | \
- BCOM_FEC_RX_BD_CR | BCOM_FEC_RX_BD_OV | BCOM_FEC_RX_BD_TR)
-
-
-extern struct bcom_task *
-bcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize);
-
-extern int
-bcom_fec_rx_reset(struct bcom_task *tsk);
-
-extern void
-bcom_fec_rx_release(struct bcom_task *tsk);
-
-
-extern struct bcom_task *
-bcom_fec_tx_init(int queue_len, phys_addr_t fifo);
-
-extern int
-bcom_fec_tx_reset(struct bcom_task *tsk);
-
-extern void
-bcom_fec_tx_release(struct bcom_task *tsk);
-
-
-#endif /* __BESTCOMM_FEC_H__ */
-
diff --git a/arch/powerpc/sysdev/bestcomm/gen_bd.c b/arch/powerpc/sysdev/bestcomm/gen_bd.c
deleted file mode 100644
index 8d33eafbb3f..00000000000
--- a/arch/powerpc/sysdev/bestcomm/gen_bd.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Driver for MPC52xx processor BestComm General Buffer Descriptor
- *
- * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2006 AppSpec Computer Technologies Corp.
- * Jeff Gibbons <jeff.gibbons@appspec.com>
- *
- * 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.
- *
- */
-
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <asm/errno.h>
-#include <asm/io.h>
-
-#include <asm/mpc52xx.h>
-
-#include "bestcomm.h"
-#include "bestcomm_priv.h"
-#include "gen_bd.h"
-
-
-/* ======================================================================== */
-/* Task image/var/inc */
-/* ======================================================================== */
-
-/* gen_bd tasks images */
-extern u32 bcom_gen_bd_rx_task[];
-extern u32 bcom_gen_bd_tx_task[];
-
-/* rx task vars that need to be set before enabling the task */
-struct bcom_gen_bd_rx_var {
- u32 enable; /* (u16*) address of task's control register */
- u32 fifo; /* (u32*) address of gen_bd's fifo */
- u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */
- u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
- u32 bd_start; /* (struct bcom_bd*) current bd */
- u32 buffer_size; /* size of receive buffer */
-};
-
-/* rx task incs that need to be set before enabling the task */
-struct bcom_gen_bd_rx_inc {
- u16 pad0;
- s16 incr_bytes;
- u16 pad1;
- s16 incr_dst;
-};
-
-/* tx task vars that need to be set before enabling the task */
-struct bcom_gen_bd_tx_var {
- u32 fifo; /* (u32*) address of gen_bd's fifo */
- u32 enable; /* (u16*) address of task's control register */
- u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */
- u32 bd_last; /* (struct bcom_bd*) end of ring buffer */
- u32 bd_start; /* (struct bcom_bd*) current bd */
- u32 buffer_size; /* set by uCode for each packet */
-};
-
-/* tx task incs that need to be set before enabling the task */
-struct bcom_gen_bd_tx_inc {
- u16 pad0;
- s16 incr_bytes;
- u16 pad1;
- s16 incr_src;
- u16 pad2;
- s16 incr_src_ma;
-};
-
-/* private structure */
-struct bcom_gen_bd_priv {
- phys_addr_t fifo;
- int initiator;
- int ipr;
- int maxbufsize;
-};
-
-
-/* ======================================================================== */
-/* Task support code */
-/* ======================================================================== */
-
-struct bcom_task *
-bcom_gen_bd_rx_init(int queue_len, phys_addr_t fifo,
- int initiator, int ipr, int maxbufsize)
-{
- struct bcom_task *tsk;
- struct bcom_gen_bd_priv *priv;
-
- tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_gen_bd),
- sizeof(struct bcom_gen_bd_priv));
- if (!tsk)
- return NULL;
-
- tsk->flags = BCOM_FLAGS_NONE;
-
- priv = tsk->priv;
- priv->fifo = fifo;
- priv->initiator = initiator;
- priv->ipr = ipr;
- priv->maxbufsize = maxbufsize;
-
- if (bcom_gen_bd_rx_reset(tsk)) {
- bcom_task_free(tsk);
- return NULL;
- }
-
- return tsk;
-}
-EXPORT_SYMBOL_GPL(bcom_gen_bd_rx_init);
-
-int
-bcom_gen_bd_rx_reset(struct bcom_task *tsk)
-{
- struct bcom_gen_bd_priv *priv = tsk->priv;
- struct bcom_gen_bd_rx_var *var;
- struct bcom_gen_bd_rx_inc *inc;
-
- /* Shutdown the task */
- bcom_disable_task(tsk->tasknum);
-
- /* Reset the microcode */
- var = (struct bcom_gen_bd_rx_var *) bcom_task_var(tsk->tasknum);
- inc = (struct bcom_gen_bd_rx_inc *) bcom_task_inc(tsk->tasknum);
-
- if (bcom_load_image(tsk->tasknum, bcom_gen_bd_rx_task))
- return -1;
-
- var->enable = bcom_eng->regs_base +
- offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
- var->fifo = (u32) priv->fifo;
- var->bd_base = tsk->bd_pa;
- var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
- var->bd_start = tsk->bd_pa;
- var->buffer_size = priv->maxbufsize;
-
- inc->incr_bytes = -(s16)sizeof(u32);
- inc->incr_dst = sizeof(u32);
-
- /* Reset the BDs */
- tsk->index = 0;
- tsk->outdex = 0;
-
- memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
-
- /* Configure some stuff */
- bcom_set_task_pragma(tsk->tasknum, BCOM_GEN_RX_BD_PRAGMA);
- bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
-
- out_8(&bcom_eng->regs->ipr[priv->initiator], priv->ipr);
- bcom_set_initiator(tsk->tasknum, priv->initiator);
-
- out_be32(&bcom_eng->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(bcom_gen_bd_rx_reset);
-
-void
-bcom_gen_bd_rx_release(struct bcom_task *tsk)
-{
- /* Nothing special for the GenBD tasks */
- bcom_task_free(tsk);
-}
-EXPORT_SYMBOL_GPL(bcom_gen_bd_rx_release);
-
-
-extern struct bcom_task *
-bcom_gen_bd_tx_init(int queue_len, phys_addr_t fifo,
- int initiator, int ipr)
-{
- struct bcom_task *tsk;
- struct bcom_gen_bd_priv *priv;
-
- tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_gen_bd),
- sizeof(struct bcom_gen_bd_priv));
- if (!tsk)
- return NULL;
-
- tsk->flags = BCOM_FLAGS_NONE;
-
- priv = tsk->priv;
- priv->fifo = fifo;
- priv->initiator = initiator;
- priv->ipr = ipr;
-
- if (bcom_gen_bd_tx_reset(tsk)) {
- bcom_task_free(tsk);
- return NULL;
- }
-
- return tsk;
-}
-EXPORT_SYMBOL_GPL(bcom_gen_bd_tx_init);
-
-int
-bcom_gen_bd_tx_reset(struct bcom_task *tsk)
-{
- struct bcom_gen_bd_priv *priv = tsk->priv;
- struct bcom_gen_bd_tx_var *var;
- struct bcom_gen_bd_tx_inc *inc;
-
- /* Shutdown the task */
- bcom_disable_task(tsk->tasknum);
-
- /* Reset the microcode */
- var = (struct bcom_gen_bd_tx_var *) bcom_task_var(tsk->tasknum);
- inc = (struct bcom_gen_bd_tx_inc *) bcom_task_inc(tsk->tasknum);
-
- if (bcom_load_image(tsk->tasknum, bcom_gen_bd_tx_task))
- return -1;
-
- var->enable = bcom_eng->regs_base +
- offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]);
- var->fifo = (u32) priv->fifo;
- var->bd_base = tsk->bd_pa;
- var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size);
- var->bd_start = tsk->bd_pa;
-
- inc->incr_bytes = -(s16)sizeof(u32);
- inc->incr_src = sizeof(u32);
- inc->incr_src_ma = sizeof(u8);
-
- /* Reset the BDs */
- tsk->index = 0;
- tsk->outdex = 0;
-
- memset(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size);
-
- /* Configure some stuff */
- bcom_set_task_pragma(tsk->tasknum, BCOM_GEN_TX_BD_PRAGMA);
- bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum);
-
- out_8(&bcom_eng->regs->ipr[priv->initiator], priv->ipr);
- bcom_set_initiator(tsk->tasknum, priv->initiator);
-
- out_be32(&bcom_eng->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(bcom_gen_bd_tx_reset);
-
-void
-bcom_gen_bd_tx_release(struct bcom_task *tsk)
-{
- /* Nothing special for the GenBD tasks */
- bcom_task_free(tsk);
-}
-EXPORT_SYMBOL_GPL(bcom_gen_bd_tx_release);
-
-
-MODULE_DESCRIPTION("BestComm General Buffer Descriptor tasks driver");
-MODULE_AUTHOR("Jeff Gibbons <jeff.gibbons@appspec.com>");
-MODULE_LICENSE("GPL v2");
-
diff --git a/arch/powerpc/sysdev/bestcomm/gen_bd.h b/arch/powerpc/sysdev/bestcomm/gen_bd.h
deleted file mode 100644
index 5b6fa803c6a..00000000000
--- a/arch/powerpc/sysdev/bestcomm/gen_bd.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Header for Bestcomm General Buffer Descriptor tasks driver
- *
- *
- * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2006 AppSpec Computer Technologies Corp.
- * Jeff Gibbons <jeff.gibbons@appspec.com>
- *
- * 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.
- *
- *
- */
-
-#ifndef __BESTCOMM_GEN_BD_H__
-#define __BESTCOMM_GEN_BD_H__
-
-struct bcom_gen_bd {
- u32 status;
- u32 buf_pa;
-};
-
-
-extern struct bcom_task *
-bcom_gen_bd_rx_init(int queue_len, phys_addr_t fifo,
- int initiator, int ipr, int maxbufsize);
-
-extern int
-bcom_gen_bd_rx_reset(struct bcom_task *tsk);
-
-extern void
-bcom_gen_bd_rx_release(struct bcom_task *tsk);
-
-
-extern struct bcom_task *
-bcom_gen_bd_tx_init(int queue_len, phys_addr_t fifo,
- int initiator, int ipr);
-
-extern int
-bcom_gen_bd_tx_reset(struct bcom_task *tsk);
-
-extern void
-bcom_gen_bd_tx_release(struct bcom_task *tsk);
-
-
-#endif /* __BESTCOMM_GEN_BD_H__ */
-
diff --git a/arch/powerpc/sysdev/bestcomm/sram.c b/arch/powerpc/sysdev/bestcomm/sram.c
deleted file mode 100644
index 99784383a84..00000000000
--- a/arch/powerpc/sysdev/bestcomm/sram.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Simple memory allocator for on-board SRAM
- *
- *
- * Maintainer : Sylvain Munaut <tnt@246tNt.com>
- *
- * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/of.h>
-
-#include <asm/io.h>
-#include <asm/mmu.h>
-
-#include "sram.h"
-
-
-/* Struct keeping our 'state' */
-struct bcom_sram *bcom_sram = NULL;
-EXPORT_SYMBOL_GPL(bcom_sram); /* needed for inline functions */
-
-
-/* ======================================================================== */
-/* Public API */
-/* ======================================================================== */
-/* DO NOT USE in interrupts, if needed in irq handler, we should use the
- _irqsave version of the spin_locks */
-
-int bcom_sram_init(struct device_node *sram_node, char *owner)
-{
- int rv;
- const u32 *regaddr_p;
- u64 regaddr64, size64;
- unsigned int psize;
-
- /* Create our state struct */
- if (bcom_sram) {
- printk(KERN_ERR "%s: bcom_sram_init: "
- "Already initialized !\n", owner);
- return -EBUSY;
- }
-
- bcom_sram = kmalloc(sizeof(struct bcom_sram), GFP_KERNEL);
- if (!bcom_sram) {
- printk(KERN_ERR "%s: bcom_sram_init: "
- "Couldn't allocate internal state !\n", owner);
- return -ENOMEM;
- }
-
- /* Get address and size of the sram */
- regaddr_p = of_get_address(sram_node, 0, &size64, NULL);
- if (!regaddr_p) {
- printk(KERN_ERR "%s: bcom_sram_init: "
- "Invalid device node !\n", owner);
- rv = -EINVAL;
- goto error_free;
- }
-
- regaddr64 = of_translate_address(sram_node, regaddr_p);
-
- bcom_sram->base_phys = (phys_addr_t) regaddr64;
- bcom_sram->size = (unsigned int) size64;
-
- /* Request region */
- if (!request_mem_region(bcom_sram->base_phys, bcom_sram->size, owner)) {
- printk(KERN_ERR "%s: bcom_sram_init: "
- "Couldn't request region !\n", owner);
- rv = -EBUSY;
- goto error_free;
- }
-
- /* Map SRAM */
- /* sram is not really __iomem */
- bcom_sram->base_virt = (void*) ioremap(bcom_sram->base_phys, bcom_sram->size);
-
- if (!bcom_sram->base_virt) {
- printk(KERN_ERR "%s: bcom_sram_init: "
- "Map error SRAM zone 0x%08lx (0x%0x)!\n",
- owner, bcom_sram->base_phys, bcom_sram->size );
- rv = -ENOMEM;
- goto error_release;
- }
-
- /* Create an rheap (defaults to 32 bits word alignment) */
- bcom_sram->rh = rh_create(4);
-
- /* Attach the free zones */
-#if 0
- /* Currently disabled ... for future use only */
- reg_addr_p = of_get_property(sram_node, "available", &psize);
-#else
- regaddr_p = NULL;
- psize = 0;
-#endif
-
- if (!regaddr_p || !psize) {
- /* Attach the whole zone */
- rh_attach_region(bcom_sram->rh, 0, bcom_sram->size);
- } else {
- /* Attach each zone independently */
- while (psize >= 2 * sizeof(u32)) {
- phys_addr_t zbase = of_translate_address(sram_node, regaddr_p);
- rh_attach_region(bcom_sram->rh, zbase - bcom_sram->base_phys, regaddr_p[1]);
- regaddr_p += 2;
- psize -= 2 * sizeof(u32);
- }
- }
-
- /* Init our spinlock */
- spin_lock_init(&bcom_sram->lock);
-
- return 0;
-
-error_release:
- release_mem_region(bcom_sram->base_phys, bcom_sram->size);
-error_free:
- kfree(bcom_sram);
- bcom_sram = NULL;
-
- return rv;
-}
-EXPORT_SYMBOL_GPL(bcom_sram_init);
-
-void bcom_sram_cleanup(void)
-{
- /* Free resources */
- if (bcom_sram) {
- rh_destroy(bcom_sram->rh);
- iounmap((void __iomem *)bcom_sram->base_virt);
- release_mem_region(bcom_sram->base_phys, bcom_sram->size);
- kfree(bcom_sram);
- bcom_sram = NULL;
- }
-}
-EXPORT_SYMBOL_GPL(bcom_sram_cleanup);
-
-void* bcom_sram_alloc(int size, int align, phys_addr_t *phys)
-{
- unsigned long offset;
-
- spin_lock(&bcom_sram->lock);
- offset = rh_alloc_align(bcom_sram->rh, size, align, NULL);
- spin_unlock(&bcom_sram->lock);
-
- if (IS_ERR_VALUE(offset))
- return NULL;
-
- *phys = bcom_sram->base_phys + offset;
- return bcom_sram->base_virt + offset;
-}
-EXPORT_SYMBOL_GPL(bcom_sram_alloc);
-
-void bcom_sram_free(void *ptr)
-{
- unsigned long offset;
-
- if (!ptr)
- return;
-
- offset = ptr - bcom_sram->base_virt;
-
- spin_lock(&bcom_sram->lock);
- rh_free(bcom_sram->rh, offset);
- spin_unlock(&bcom_sram->lock);
-}
-EXPORT_SYMBOL_GPL(bcom_sram_free);
-
diff --git a/arch/powerpc/sysdev/bestcomm/sram.h b/arch/powerpc/sysdev/bestcomm/sram.h
deleted file mode 100644
index b6d668963cc..00000000000
--- a/arch/powerpc/sysdev/bestcomm/sram.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Handling of a sram zone for bestcomm
- *
- *
- * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#ifndef __BESTCOMM_SRAM_H__
-#define __BESTCOMM_SRAM_H__
-
-#include <asm/rheap.h>
-#include <asm/mmu.h>
-#include <linux/spinlock.h>
-
-
-/* Structure used internally */
- /* The internals are here for the inline functions
- * sake, certainly not for the user to mess with !
- */
-struct bcom_sram {
- phys_addr_t base_phys;
- void *base_virt;
- unsigned int size;
- rh_info_t *rh;
- spinlock_t lock;
-};
-
-extern struct bcom_sram *bcom_sram;
-
-
-/* Public API */
-extern int bcom_sram_init(struct device_node *sram_node, char *owner);
-extern void bcom_sram_cleanup(void);
-
-extern void* bcom_sram_alloc(int size, int align, phys_addr_t *phys);
-extern void bcom_sram_free(void *ptr);
-
-static inline phys_addr_t bcom_sram_va2pa(void *va) {
- return bcom_sram->base_phys +
- (unsigned long)(va - bcom_sram->base_virt);
-}
-
-static inline void *bcom_sram_pa2va(phys_addr_t pa) {
- return bcom_sram->base_virt +
- (unsigned long)(pa - bcom_sram->base_phys);
-}
-
-
-#endif /* __BESTCOMM_SRAM_H__ */
-
diff --git a/arch/powerpc/sysdev/cpm1.c b/arch/powerpc/sysdev/cpm1.c
index df8bd2b6479..5e6ff38ea69 100644
--- a/arch/powerpc/sysdev/cpm1.c
+++ b/arch/powerpc/sysdev/cpm1.c
@@ -30,6 +30,8 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/8xx_immap.h>
@@ -42,43 +44,44 @@
#include <asm/fs_pd.h>
+#ifdef CONFIG_8xx_GPIO
+#include <linux/of_gpio.h>
+#endif
+
#define CPM_MAP_SIZE (0x4000)
-#ifndef CONFIG_PPC_CPM_NEW_BINDING
-static void m8xx_cpm_dpinit(void);
-#endif
cpm8xx_t __iomem *cpmp; /* Pointer to comm processor space */
immap_t __iomem *mpc8xx_immr;
static cpic8xx_t __iomem *cpic_reg;
-static struct irq_host *cpm_pic_host;
+static struct irq_domain *cpm_pic_host;
-static void cpm_mask_irq(unsigned int irq)
+static void cpm_mask_irq(struct irq_data *d)
{
- unsigned int cpm_vec = (unsigned int)irq_map[irq].hwirq;
+ unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
clrbits32(&cpic_reg->cpic_cimr, (1 << cpm_vec));
}
-static void cpm_unmask_irq(unsigned int irq)
+static void cpm_unmask_irq(struct irq_data *d)
{
- unsigned int cpm_vec = (unsigned int)irq_map[irq].hwirq;
+ unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
setbits32(&cpic_reg->cpic_cimr, (1 << cpm_vec));
}
-static void cpm_end_irq(unsigned int irq)
+static void cpm_end_irq(struct irq_data *d)
{
- unsigned int cpm_vec = (unsigned int)irq_map[irq].hwirq;
+ unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
out_be32(&cpic_reg->cpic_cisr, (1 << cpm_vec));
}
static struct irq_chip cpm_pic = {
- .typename = " CPM PIC ",
- .mask = cpm_mask_irq,
- .unmask = cpm_unmask_irq,
- .eoi = cpm_end_irq,
+ .name = "CPM PIC",
+ .irq_mask = cpm_mask_irq,
+ .irq_unmask = cpm_unmask_irq,
+ .irq_eoi = cpm_end_irq,
};
int cpm_get_irq(void)
@@ -95,13 +98,13 @@ int cpm_get_irq(void)
return irq_linear_revmap(cpm_pic_host, cpm_vec);
}
-static int cpm_pic_host_map(struct irq_host *h, unsigned int virq,
+static int cpm_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
pr_debug("cpm_pic_host_map(%d, 0x%lx)\n", virq, hw);
- get_irq_desc(virq)->status |= IRQ_LEVEL;
- set_irq_chip_and_handler(virq, &cpm_pic, handle_fasteoi_irq);
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ irq_set_chip_and_handler(virq, &cpm_pic, handle_fasteoi_irq);
return 0;
}
@@ -117,11 +120,11 @@ static irqreturn_t cpm_error_interrupt(int irq, void *dev)
static struct irqaction cpm_error_irqaction = {
.handler = cpm_error_interrupt,
- .mask = CPU_MASK_NONE,
+ .flags = IRQF_NO_THREAD,
.name = "error",
};
-static struct irq_host_ops cpm_pic_host_ops = {
+static const struct irq_domain_ops cpm_pic_host_ops = {
.map = cpm_pic_host_map,
};
@@ -146,7 +149,7 @@ unsigned int cpm_pic_init(void)
if (ret)
goto end;
- cpic_reg = ioremap(res.start, res.end - res.start + 1);
+ cpic_reg = ioremap(res.start, resource_size(&res));
if (cpic_reg == NULL)
goto end;
@@ -155,15 +158,14 @@ unsigned int cpm_pic_init(void)
goto end;
/* Initialize the CPM interrupt controller. */
- hwirq = (unsigned int)irq_map[sirq].hwirq;
+ hwirq = (unsigned int)virq_to_hw(sirq);
out_be32(&cpic_reg->cpic_cicr,
(CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
((hwirq/2) << 13) | CICR_HP_MASK);
out_be32(&cpic_reg->cpic_cimr, 0);
- cpm_pic_host = irq_alloc_host(of_node_get(np), IRQ_HOST_MAP_LINEAR,
- 64, &cpm_pic_host_ops, 64);
+ cpm_pic_host = irq_domain_add_linear(np, 64, &cpm_pic_host_ops, NULL);
if (cpm_pic_host == NULL) {
printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
sirq = NO_IRQ;
@@ -221,7 +223,7 @@ void __init cpm_reset(void)
/* Set SDMA Bus Request priority 5.
* On 860T, this also enables FEC priority 6. I am not sure
- * this is what we realy want for some applications, but the
+ * this is what we really want for some applications, but the
* manual recommends it.
* Bit 25, FAM can also be set to use FEC aggressive mode (860T).
*/
@@ -229,12 +231,7 @@ void __init cpm_reset(void)
out_be32(&siu_conf->sc_sdcr, 1);
immr_unmap(siu_conf);
-#ifdef CONFIG_PPC_CPM_NEW_BINDING
cpm_muram_init();
-#else
- /* Reclaim the DP memory for our use. */
- m8xx_cpm_dpinit();
-#endif
}
static DEFINE_SPINLOCK(cmd_lock);
@@ -257,7 +254,7 @@ int cpm_command(u32 command, u8 opcode)
if ((in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG) == 0)
goto out;
- printk(KERN_ERR "%s(): Not able to issue CPM command\n", __FUNCTION__);
+ printk(KERN_ERR "%s(): Not able to issue CPM command\n", __func__);
ret = -EIO;
out:
spin_unlock_irqrestore(&cmd_lock, flags);
@@ -293,129 +290,29 @@ cpm_setbrg(uint brg, uint rate)
CPM_BRG_EN | CPM_BRG_DIV16);
}
-#ifndef CONFIG_PPC_CPM_NEW_BINDING
-/*
- * dpalloc / dpfree bits.
- */
-static spinlock_t cpm_dpmem_lock;
-/*
- * 16 blocks should be enough to satisfy all requests
- * until the memory subsystem goes up...
- */
-static rh_block_t cpm_boot_dpmem_rh_block[16];
-static rh_info_t cpm_dpmem_info;
-
-#define CPM_DPMEM_ALIGNMENT 8
-static u8 __iomem *dpram_vbase;
-static phys_addr_t dpram_pbase;
-
-static void m8xx_cpm_dpinit(void)
-{
- spin_lock_init(&cpm_dpmem_lock);
-
- dpram_vbase = cpmp->cp_dpmem;
- dpram_pbase = get_immrbase() + offsetof(immap_t, im_cpm.cp_dpmem);
-
- /* Initialize the info header */
- rh_init(&cpm_dpmem_info, CPM_DPMEM_ALIGNMENT,
- sizeof(cpm_boot_dpmem_rh_block) /
- sizeof(cpm_boot_dpmem_rh_block[0]),
- cpm_boot_dpmem_rh_block);
-
- /*
- * Attach the usable dpmem area.
- * XXX: This is actually crap. CPM_DATAONLY_BASE and
- * CPM_DATAONLY_SIZE are a subset of the available dparm. It varies
- * with the processor and the microcode patches applied / activated.
- * But the following should be at least safe.
- */
- rh_attach_region(&cpm_dpmem_info, CPM_DATAONLY_BASE, CPM_DATAONLY_SIZE);
-}
-
-/*
- * Allocate the requested size worth of DP memory.
- * This function returns an offset into the DPRAM area.
- * Use cpm_dpram_addr() to get the virtual address of the area.
- */
-unsigned long cpm_dpalloc(uint size, uint align)
-{
- unsigned long start;
- unsigned long flags;
-
- spin_lock_irqsave(&cpm_dpmem_lock, flags);
- cpm_dpmem_info.alignment = align;
- start = rh_alloc(&cpm_dpmem_info, size, "commproc");
- spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
-
- return (uint)start;
-}
-EXPORT_SYMBOL(cpm_dpalloc);
-
-int cpm_dpfree(unsigned long offset)
-{
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&cpm_dpmem_lock, flags);
- ret = rh_free(&cpm_dpmem_info, offset);
- spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(cpm_dpfree);
-
-unsigned long cpm_dpalloc_fixed(unsigned long offset, uint size, uint align)
-{
- unsigned long start;
- unsigned long flags;
-
- spin_lock_irqsave(&cpm_dpmem_lock, flags);
- cpm_dpmem_info.alignment = align;
- start = rh_alloc_fixed(&cpm_dpmem_info, offset, size, "commproc");
- spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
-
- return start;
-}
-EXPORT_SYMBOL(cpm_dpalloc_fixed);
-
-void cpm_dpdump(void)
-{
- rh_dump(&cpm_dpmem_info);
-}
-EXPORT_SYMBOL(cpm_dpdump);
-
-void *cpm_dpram_addr(unsigned long offset)
-{
- return (void *)(dpram_vbase + offset);
-}
-EXPORT_SYMBOL(cpm_dpram_addr);
-
-uint cpm_dpram_phys(u8 *addr)
-{
- return (dpram_pbase + (uint)(addr - dpram_vbase));
-}
-EXPORT_SYMBOL(cpm_dpram_phys);
-#endif /* !CONFIG_PPC_CPM_NEW_BINDING */
-
struct cpm_ioport16 {
__be16 dir, par, odr_sor, dat, intr;
__be16 res[3];
};
-struct cpm_ioport32 {
- __be32 dir, par, sor;
+struct cpm_ioport32b {
+ __be32 dir, par, odr, dat;
+};
+
+struct cpm_ioport32e {
+ __be32 dir, par, sor, odr, dat;
};
static void cpm1_set_pin32(int port, int pin, int flags)
{
- struct cpm_ioport32 __iomem *iop;
+ struct cpm_ioport32e __iomem *iop;
pin = 1 << (31 - pin);
if (port == CPM_PORTB)
- iop = (struct cpm_ioport32 __iomem *)
+ iop = (struct cpm_ioport32e __iomem *)
&mpc8xx_immr->im_cpm.cp_pbdir;
else
- iop = (struct cpm_ioport32 __iomem *)
+ iop = (struct cpm_ioport32e __iomem *)
&mpc8xx_immr->im_cpm.cp_pedir;
if (flags & CPM_PIN_OUTPUT)
@@ -589,9 +486,6 @@ int cpm1_clk_setup(enum cpm_clk_target target, int clock, int mode)
return -EINVAL;
}
- if (reg == &mpc8xx_immr->im_cpm.cp_sicr && mode == CPM_CLK_RX)
- shift += 3;
-
for (i = 0; i < ARRAY_SIZE(clk_map); i++) {
if (clk_map[i][0] == target && clk_map[i][1] == clock) {
bits = clk_map[i][2];
@@ -606,7 +500,290 @@ int cpm1_clk_setup(enum cpm_clk_target target, int clock, int mode)
bits <<= shift;
mask <<= shift;
+
+ if (reg == &mpc8xx_immr->im_cpm.cp_sicr) {
+ if (mode == CPM_CLK_RTX) {
+ bits |= bits << 3;
+ mask |= mask << 3;
+ } else if (mode == CPM_CLK_RX) {
+ bits <<= 3;
+ mask <<= 3;
+ }
+ }
+
out_be32(reg, (in_be32(reg) & ~mask) | bits);
return 0;
}
+
+/*
+ * GPIO LIB API implementation
+ */
+#ifdef CONFIG_8xx_GPIO
+
+struct cpm1_gpio16_chip {
+ struct of_mm_gpio_chip mm_gc;
+ spinlock_t lock;
+
+ /* shadowed data register to clear/set bits safely */
+ u16 cpdata;
+};
+
+static inline struct cpm1_gpio16_chip *
+to_cpm1_gpio16_chip(struct of_mm_gpio_chip *mm_gc)
+{
+ return container_of(mm_gc, struct cpm1_gpio16_chip, mm_gc);
+}
+
+static void cpm1_gpio16_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+ struct cpm1_gpio16_chip *cpm1_gc = to_cpm1_gpio16_chip(mm_gc);
+ struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+
+ cpm1_gc->cpdata = in_be16(&iop->dat);
+}
+
+static int cpm1_gpio16_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+ u16 pin_mask;
+
+ pin_mask = 1 << (15 - gpio);
+
+ return !!(in_be16(&iop->dat) & pin_mask);
+}
+
+static void __cpm1_gpio16_set(struct of_mm_gpio_chip *mm_gc, u16 pin_mask,
+ int value)
+{
+ struct cpm1_gpio16_chip *cpm1_gc = to_cpm1_gpio16_chip(mm_gc);
+ struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+
+ if (value)
+ cpm1_gc->cpdata |= pin_mask;
+ else
+ cpm1_gc->cpdata &= ~pin_mask;
+
+ out_be16(&iop->dat, cpm1_gc->cpdata);
+}
+
+static void cpm1_gpio16_set(struct gpio_chip *gc, unsigned int gpio, int value)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm1_gpio16_chip *cpm1_gc = to_cpm1_gpio16_chip(mm_gc);
+ unsigned long flags;
+ u16 pin_mask = 1 << (15 - gpio);
+
+ spin_lock_irqsave(&cpm1_gc->lock, flags);
+
+ __cpm1_gpio16_set(mm_gc, pin_mask, value);
+
+ spin_unlock_irqrestore(&cpm1_gc->lock, flags);
+}
+
+static int cpm1_gpio16_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm1_gpio16_chip *cpm1_gc = to_cpm1_gpio16_chip(mm_gc);
+ struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+ unsigned long flags;
+ u16 pin_mask = 1 << (15 - gpio);
+
+ spin_lock_irqsave(&cpm1_gc->lock, flags);
+
+ setbits16(&iop->dir, pin_mask);
+ __cpm1_gpio16_set(mm_gc, pin_mask, val);
+
+ spin_unlock_irqrestore(&cpm1_gc->lock, flags);
+
+ return 0;
+}
+
+static int cpm1_gpio16_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm1_gpio16_chip *cpm1_gc = to_cpm1_gpio16_chip(mm_gc);
+ struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+ unsigned long flags;
+ u16 pin_mask = 1 << (15 - gpio);
+
+ spin_lock_irqsave(&cpm1_gc->lock, flags);
+
+ clrbits16(&iop->dir, pin_mask);
+
+ spin_unlock_irqrestore(&cpm1_gc->lock, flags);
+
+ return 0;
+}
+
+int cpm1_gpiochip_add16(struct device_node *np)
+{
+ struct cpm1_gpio16_chip *cpm1_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ struct gpio_chip *gc;
+
+ cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL);
+ if (!cpm1_gc)
+ return -ENOMEM;
+
+ spin_lock_init(&cpm1_gc->lock);
+
+ mm_gc = &cpm1_gc->mm_gc;
+ gc = &mm_gc->gc;
+
+ mm_gc->save_regs = cpm1_gpio16_save_regs;
+ gc->ngpio = 16;
+ gc->direction_input = cpm1_gpio16_dir_in;
+ gc->direction_output = cpm1_gpio16_dir_out;
+ gc->get = cpm1_gpio16_get;
+ gc->set = cpm1_gpio16_set;
+
+ return of_mm_gpiochip_add(np, mm_gc);
+}
+
+struct cpm1_gpio32_chip {
+ struct of_mm_gpio_chip mm_gc;
+ spinlock_t lock;
+
+ /* shadowed data register to clear/set bits safely */
+ u32 cpdata;
+};
+
+static inline struct cpm1_gpio32_chip *
+to_cpm1_gpio32_chip(struct of_mm_gpio_chip *mm_gc)
+{
+ return container_of(mm_gc, struct cpm1_gpio32_chip, mm_gc);
+}
+
+static void cpm1_gpio32_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+ struct cpm1_gpio32_chip *cpm1_gc = to_cpm1_gpio32_chip(mm_gc);
+ struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+
+ cpm1_gc->cpdata = in_be32(&iop->dat);
+}
+
+static int cpm1_gpio32_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+ u32 pin_mask;
+
+ pin_mask = 1 << (31 - gpio);
+
+ return !!(in_be32(&iop->dat) & pin_mask);
+}
+
+static void __cpm1_gpio32_set(struct of_mm_gpio_chip *mm_gc, u32 pin_mask,
+ int value)
+{
+ struct cpm1_gpio32_chip *cpm1_gc = to_cpm1_gpio32_chip(mm_gc);
+ struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+
+ if (value)
+ cpm1_gc->cpdata |= pin_mask;
+ else
+ cpm1_gc->cpdata &= ~pin_mask;
+
+ out_be32(&iop->dat, cpm1_gc->cpdata);
+}
+
+static void cpm1_gpio32_set(struct gpio_chip *gc, unsigned int gpio, int value)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm1_gpio32_chip *cpm1_gc = to_cpm1_gpio32_chip(mm_gc);
+ unsigned long flags;
+ u32 pin_mask = 1 << (31 - gpio);
+
+ spin_lock_irqsave(&cpm1_gc->lock, flags);
+
+ __cpm1_gpio32_set(mm_gc, pin_mask, value);
+
+ spin_unlock_irqrestore(&cpm1_gc->lock, flags);
+}
+
+static int cpm1_gpio32_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm1_gpio32_chip *cpm1_gc = to_cpm1_gpio32_chip(mm_gc);
+ struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+ unsigned long flags;
+ u32 pin_mask = 1 << (31 - gpio);
+
+ spin_lock_irqsave(&cpm1_gc->lock, flags);
+
+ setbits32(&iop->dir, pin_mask);
+ __cpm1_gpio32_set(mm_gc, pin_mask, val);
+
+ spin_unlock_irqrestore(&cpm1_gc->lock, flags);
+
+ return 0;
+}
+
+static int cpm1_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm1_gpio32_chip *cpm1_gc = to_cpm1_gpio32_chip(mm_gc);
+ struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+ unsigned long flags;
+ u32 pin_mask = 1 << (31 - gpio);
+
+ spin_lock_irqsave(&cpm1_gc->lock, flags);
+
+ clrbits32(&iop->dir, pin_mask);
+
+ spin_unlock_irqrestore(&cpm1_gc->lock, flags);
+
+ return 0;
+}
+
+int cpm1_gpiochip_add32(struct device_node *np)
+{
+ struct cpm1_gpio32_chip *cpm1_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ struct gpio_chip *gc;
+
+ cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL);
+ if (!cpm1_gc)
+ return -ENOMEM;
+
+ spin_lock_init(&cpm1_gc->lock);
+
+ mm_gc = &cpm1_gc->mm_gc;
+ gc = &mm_gc->gc;
+
+ mm_gc->save_regs = cpm1_gpio32_save_regs;
+ gc->ngpio = 32;
+ gc->direction_input = cpm1_gpio32_dir_in;
+ gc->direction_output = cpm1_gpio32_dir_out;
+ gc->get = cpm1_gpio32_get;
+ gc->set = cpm1_gpio32_set;
+
+ return of_mm_gpiochip_add(np, mm_gc);
+}
+
+static int cpm_init_par_io(void)
+{
+ struct device_node *np;
+
+ for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-a")
+ cpm1_gpiochip_add16(np);
+
+ for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-b")
+ cpm1_gpiochip_add32(np);
+
+ for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-c")
+ cpm1_gpiochip_add16(np);
+
+ for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-d")
+ cpm1_gpiochip_add16(np);
+
+ /* Port E uses CPM2 layout */
+ for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-e")
+ cpm2_gpiochip_add32(np);
+ return 0;
+}
+arch_initcall(cpm_init_par_io);
+
+#endif /* CONFIG_8xx_GPIO */
diff --git a/arch/powerpc/sysdev/cpm2.c b/arch/powerpc/sysdev/cpm2.c
index dd066bb1d56..8dc1e24f3c2 100644
--- a/arch/powerpc/sysdev/cpm2.c
+++ b/arch/powerpc/sysdev/cpm2.c
@@ -46,16 +46,13 @@
#include <sysdev/fsl_soc.h>
-#ifndef CONFIG_PPC_CPM_NEW_BINDING
-static void cpm2_dpinit(void);
-#endif
-
cpm_cpm2_t __iomem *cpmp; /* Pointer to comm processor space */
/* We allocate this here because it is used almost exclusively for
* the communication processor devices.
*/
cpm2_map_t __iomem *cpm2_immr;
+EXPORT_SYMBOL(cpm2_immr);
#define CPM_MAP_SIZE (0x40000) /* 256k - the PQ3 reserve this amount
of space for CPM as it is larger
@@ -64,22 +61,24 @@ cpm2_map_t __iomem *cpm2_immr;
void __init cpm2_reset(void)
{
#ifdef CONFIG_PPC_85xx
- cpm2_immr = ioremap(CPM_MAP_ADDR, CPM_MAP_SIZE);
+ cpm2_immr = ioremap(get_immrbase() + 0x80000, CPM_MAP_SIZE);
#else
cpm2_immr = ioremap(get_immrbase(), CPM_MAP_SIZE);
#endif
/* Reclaim the DP memory for our use.
*/
-#ifdef CONFIG_PPC_CPM_NEW_BINDING
cpm_muram_init();
-#else
- cpm2_dpinit();
-#endif
/* Tell everyone where the comm processor resides.
*/
cpmp = &cpm2_immr->im_cpm;
+
+#ifndef CONFIG_PPC_EARLY_DEBUG_CPM
+ /* Reset the CPM.
+ */
+ cpm_command(CPM_CR_RST, 0);
+#endif
}
static DEFINE_SPINLOCK(cmd_lock);
@@ -99,7 +98,7 @@ int cpm_command(u32 command, u8 opcode)
if ((in_be32(&cpmp->cp_cpcr) & CPM_CR_FLG) == 0)
goto out;
- printk(KERN_ERR "%s(): Not able to issue CPM command\n", __FUNCTION__);
+ printk(KERN_ERR "%s(): Not able to issue CPM command\n", __func__);
ret = -EIO;
out:
spin_unlock_irqrestore(&cmd_lock, flags);
@@ -117,16 +116,10 @@ EXPORT_SYMBOL(cpm_command);
* Baud rate clocks are zero-based in the driver code (as that maps
* to port numbers). Documentation uses 1-based numbering.
*/
-#define BRG_INT_CLK (get_brgfreq())
-#define BRG_UART_CLK (BRG_INT_CLK/16)
-
-/* This function is used by UARTS, or anything else that uses a 16x
- * oversampled clock.
- */
-void
-cpm_setbrg(uint brg, uint rate)
+void __cpm2_setbrg(uint brg, uint rate, uint clk, int div16, int src)
{
u32 __iomem *bp;
+ u32 val;
/* This is good enough to get SMCs running.....
*/
@@ -137,34 +130,15 @@ cpm_setbrg(uint brg, uint rate)
brg -= 4;
}
bp += brg;
- out_be32(bp, (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN);
-
- cpm2_unmap(bp);
-}
-
-/* This function is used to set high speed synchronous baud rate
- * clocks.
- */
-void
-cpm2_fastbrg(uint brg, uint rate, int div16)
-{
- u32 __iomem *bp;
- u32 val;
-
- if (brg < 4) {
- bp = cpm2_map_size(im_brgc1, 16);
- } else {
- bp = cpm2_map_size(im_brgc5, 16);
- brg -= 4;
- }
- bp += brg;
- val = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN;
+ /* Round the clock divider to the nearest integer. */
+ val = (((clk * 2 / rate) - 1) & ~1) | CPM_BRG_EN | src;
if (div16)
val |= CPM_BRG_DIV16;
out_be32(bp, val);
cpm2_unmap(bp);
}
+EXPORT_SYMBOL(__cpm2_setbrg);
int cpm2_clk_setup(enum cpm_clk_target target, int clock, int mode)
{
@@ -270,9 +244,6 @@ int cpm2_clk_setup(enum cpm_clk_target target, int clock, int mode)
return -EINVAL;
}
- if (mode == CPM_CLK_RX)
- shift += 3;
-
for (i = 0; i < ARRAY_SIZE(clk_map); i++) {
if (clk_map[i][0] == target && clk_map[i][1] == clock) {
bits = clk_map[i][2];
@@ -285,6 +256,14 @@ int cpm2_clk_setup(enum cpm_clk_target target, int clock, int mode)
bits <<= shift;
mask <<= shift;
+ if (mode == CPM_CLK_RTX) {
+ bits |= bits << 3;
+ mask |= mask << 3;
+ } else if (mode == CPM_CLK_RX) {
+ bits <<= 3;
+ mask <<= 3;
+ }
+
out_be32(reg, (in_be32(reg) & ~mask) | bits);
cpm2_unmap(im_cpmux);
@@ -347,95 +326,6 @@ int cpm2_smc_clk_setup(enum cpm_clk_target target, int clock)
return ret;
}
-#ifndef CONFIG_PPC_CPM_NEW_BINDING
-/*
- * dpalloc / dpfree bits.
- */
-static spinlock_t cpm_dpmem_lock;
-/* 16 blocks should be enough to satisfy all requests
- * until the memory subsystem goes up... */
-static rh_block_t cpm_boot_dpmem_rh_block[16];
-static rh_info_t cpm_dpmem_info;
-static u8 __iomem *im_dprambase;
-
-static void cpm2_dpinit(void)
-{
- spin_lock_init(&cpm_dpmem_lock);
-
- /* initialize the info header */
- rh_init(&cpm_dpmem_info, 1,
- sizeof(cpm_boot_dpmem_rh_block) /
- sizeof(cpm_boot_dpmem_rh_block[0]),
- cpm_boot_dpmem_rh_block);
-
- im_dprambase = cpm2_immr;
-
- /* Attach the usable dpmem area */
- /* XXX: This is actually crap. CPM_DATAONLY_BASE and
- * CPM_DATAONLY_SIZE is only a subset of the available dpram. It
- * varies with the processor and the microcode patches activated.
- * But the following should be at least safe.
- */
- rh_attach_region(&cpm_dpmem_info, CPM_DATAONLY_BASE, CPM_DATAONLY_SIZE);
-}
-
-/* This function returns an index into the DPRAM area.
- */
-unsigned long cpm_dpalloc(uint size, uint align)
-{
- unsigned long start;
- unsigned long flags;
-
- spin_lock_irqsave(&cpm_dpmem_lock, flags);
- cpm_dpmem_info.alignment = align;
- start = rh_alloc(&cpm_dpmem_info, size, "commproc");
- spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
-
- return (uint)start;
-}
-EXPORT_SYMBOL(cpm_dpalloc);
-
-int cpm_dpfree(unsigned long offset)
-{
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&cpm_dpmem_lock, flags);
- ret = rh_free(&cpm_dpmem_info, offset);
- spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(cpm_dpfree);
-
-/* not sure if this is ever needed */
-unsigned long cpm_dpalloc_fixed(unsigned long offset, uint size, uint align)
-{
- unsigned long start;
- unsigned long flags;
-
- spin_lock_irqsave(&cpm_dpmem_lock, flags);
- cpm_dpmem_info.alignment = align;
- start = rh_alloc_fixed(&cpm_dpmem_info, offset, size, "commproc");
- spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
-
- return start;
-}
-EXPORT_SYMBOL(cpm_dpalloc_fixed);
-
-void cpm_dpdump(void)
-{
- rh_dump(&cpm_dpmem_info);
-}
-EXPORT_SYMBOL(cpm_dpdump);
-
-void *cpm_dpram_addr(unsigned long offset)
-{
- return (void *)(im_dprambase + offset);
-}
-EXPORT_SYMBOL(cpm_dpram_addr);
-#endif /* !CONFIG_PPC_CPM_NEW_BINDING */
-
struct cpm2_ioports {
u32 dir, par, sor, odr, dat;
u32 res[3];
@@ -468,3 +358,14 @@ void cpm2_set_pin(int port, int pin, int flags)
else
clrbits32(&iop[port].odr, pin);
}
+
+static int cpm_init_par_io(void)
+{
+ struct device_node *np;
+
+ for_each_compatible_node(np, NULL, "fsl,cpm2-pario-bank")
+ cpm2_gpiochip_add32(np);
+ return 0;
+}
+arch_initcall(cpm_init_par_io);
+
diff --git a/arch/powerpc/sysdev/cpm2_pic.c b/arch/powerpc/sysdev/cpm2_pic.c
index 5fe65b2f8f3..a11bd1d433a 100644
--- a/arch/powerpc/sysdev/cpm2_pic.c
+++ b/arch/powerpc/sysdev/cpm2_pic.c
@@ -27,7 +27,6 @@
*/
#include <linux/stddef.h>
-#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/irq.h>
@@ -50,9 +49,8 @@
static intctl_cpm2_t __iomem *cpm2_intctl;
-static struct irq_host *cpm2_pic_host;
-#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
-static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
+static struct irq_domain *cpm2_pic_host;
+static unsigned long ppc_cached_irq_mask[2]; /* 2 32-bit registers */
static const u_char irq_to_siureg[] = {
1, 1, 1, 1, 1, 1, 1, 1,
@@ -78,10 +76,10 @@ static const u_char irq_to_siubit[] = {
24, 25, 26, 27, 28, 29, 30, 31,
};
-static void cpm2_mask_irq(unsigned int virq)
+static void cpm2_mask_irq(struct irq_data *d)
{
int bit, word;
- unsigned int irq_nr = virq_to_hw(virq);
+ unsigned int irq_nr = irqd_to_hwirq(d);
bit = irq_to_siubit[irq_nr];
word = irq_to_siureg[irq_nr];
@@ -90,10 +88,10 @@ static void cpm2_mask_irq(unsigned int virq)
out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
}
-static void cpm2_unmask_irq(unsigned int virq)
+static void cpm2_unmask_irq(struct irq_data *d)
{
int bit, word;
- unsigned int irq_nr = virq_to_hw(virq);
+ unsigned int irq_nr = irqd_to_hwirq(d);
bit = irq_to_siubit[irq_nr];
word = irq_to_siureg[irq_nr];
@@ -102,10 +100,10 @@ static void cpm2_unmask_irq(unsigned int virq)
out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
}
-static void cpm2_ack(unsigned int virq)
+static void cpm2_ack(struct irq_data *d)
{
int bit, word;
- unsigned int irq_nr = virq_to_hw(virq);
+ unsigned int irq_nr = irqd_to_hwirq(d);
bit = irq_to_siubit[irq_nr];
word = irq_to_siureg[irq_nr];
@@ -113,50 +111,53 @@ static void cpm2_ack(unsigned int virq)
out_be32(&cpm2_intctl->ic_sipnrh + word, 1 << bit);
}
-static void cpm2_end_irq(unsigned int virq)
+static void cpm2_end_irq(struct irq_data *d)
{
int bit, word;
- unsigned int irq_nr = virq_to_hw(virq);
+ unsigned int irq_nr = irqd_to_hwirq(d);
- if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
- && irq_desc[irq_nr].action) {
-
- bit = irq_to_siubit[irq_nr];
- word = irq_to_siureg[irq_nr];
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
- ppc_cached_irq_mask[word] |= 1 << bit;
- out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
+ ppc_cached_irq_mask[word] |= 1 << bit;
+ out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
- /*
- * Work around large numbers of spurious IRQs on PowerPC 82xx
- * systems.
- */
- mb();
- }
+ /*
+ * Work around large numbers of spurious IRQs on PowerPC 82xx
+ * systems.
+ */
+ mb();
}
-static int cpm2_set_irq_type(unsigned int virq, unsigned int flow_type)
+static int cpm2_set_irq_type(struct irq_data *d, unsigned int flow_type)
{
- unsigned int src = virq_to_hw(virq);
- struct irq_desc *desc = get_irq_desc(virq);
+ unsigned int src = irqd_to_hwirq(d);
unsigned int vold, vnew, edibit;
- if (flow_type == IRQ_TYPE_NONE)
- flow_type = IRQ_TYPE_LEVEL_LOW;
-
- if (flow_type & IRQ_TYPE_EDGE_RISING) {
- printk(KERN_ERR "CPM2 PIC: sense type 0x%x not supported\n",
- flow_type);
- return -EINVAL;
+ /* Port C interrupts are either IRQ_TYPE_EDGE_FALLING or
+ * IRQ_TYPE_EDGE_BOTH (default). All others are IRQ_TYPE_EDGE_FALLING
+ * or IRQ_TYPE_LEVEL_LOW (default)
+ */
+ if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0) {
+ if (flow_type == IRQ_TYPE_NONE)
+ flow_type = IRQ_TYPE_EDGE_BOTH;
+
+ if (flow_type != IRQ_TYPE_EDGE_BOTH &&
+ flow_type != IRQ_TYPE_EDGE_FALLING)
+ goto err_sense;
+ } else {
+ if (flow_type == IRQ_TYPE_NONE)
+ flow_type = IRQ_TYPE_LEVEL_LOW;
+
+ if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
+ goto err_sense;
}
- desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
- desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
- if (flow_type & IRQ_TYPE_LEVEL_LOW) {
- desc->status |= IRQ_LEVEL;
- desc->handle_irq = handle_level_irq;
- } else
- desc->handle_irq = handle_edge_irq;
+ irqd_set_trigger_type(d, flow_type);
+ if (flow_type & IRQ_TYPE_LEVEL_LOW)
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ else
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
/* internal IRQ senses are LEVEL_LOW
* EXT IRQ and Port C IRQ senses are programmable
@@ -165,9 +166,10 @@ static int cpm2_set_irq_type(unsigned int virq, unsigned int flow_type)
edibit = (14 - (src - CPM2_IRQ_EXT1));
else
if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0)
- edibit = (31 - (src - CPM2_IRQ_PORTC15));
+ edibit = (31 - (CPM2_IRQ_PORTC0 - src));
else
- return (flow_type & IRQ_TYPE_LEVEL_LOW) ? 0 : -EINVAL;
+ return (flow_type & IRQ_TYPE_LEVEL_LOW) ?
+ IRQ_SET_MASK_OK_NOCOPY : -EINVAL;
vold = in_be32(&cpm2_intctl->ic_siexr);
@@ -178,16 +180,21 @@ static int cpm2_set_irq_type(unsigned int virq, unsigned int flow_type)
if (vold != vnew)
out_be32(&cpm2_intctl->ic_siexr, vnew);
- return 0;
+ return IRQ_SET_MASK_OK_NOCOPY;
+
+err_sense:
+ pr_err("CPM2 PIC: sense type 0x%x not supported\n", flow_type);
+ return -EINVAL;
}
static struct irq_chip cpm2_pic = {
- .typename = " CPM2 SIU ",
- .mask = cpm2_mask_irq,
- .unmask = cpm2_unmask_irq,
- .ack = cpm2_ack,
- .eoi = cpm2_end_irq,
- .set_type = cpm2_set_irq_type,
+ .name = "CPM2 SIU",
+ .irq_mask = cpm2_mask_irq,
+ .irq_unmask = cpm2_unmask_irq,
+ .irq_ack = cpm2_ack,
+ .irq_eoi = cpm2_end_irq,
+ .irq_set_type = cpm2_set_irq_type,
+ .flags = IRQCHIP_EOI_IF_HANDLED,
};
unsigned int cpm2_get_irq(void)
@@ -205,31 +212,19 @@ unsigned int cpm2_get_irq(void)
return irq_linear_revmap(cpm2_pic_host, irq);
}
-static int cpm2_pic_host_map(struct irq_host *h, unsigned int virq,
+static int cpm2_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
pr_debug("cpm2_pic_host_map(%d, 0x%lx)\n", virq, hw);
- get_irq_desc(virq)->status |= IRQ_LEVEL;
- set_irq_chip_and_handler(virq, &cpm2_pic, handle_level_irq);
- return 0;
-}
-
-static int cpm2_pic_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
- irq_hw_number_t *out_hwirq, unsigned int *out_flags)
-{
- *out_hwirq = intspec[0];
- if (intsize > 1)
- *out_flags = intspec[1];
- else
- *out_flags = IRQ_TYPE_NONE;
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ irq_set_chip_and_handler(virq, &cpm2_pic, handle_level_irq);
return 0;
}
-static struct irq_host_ops cpm2_pic_host_ops = {
+static const struct irq_domain_ops cpm2_pic_host_ops = {
.map = cpm2_pic_host_map,
- .xlate = cpm2_pic_host_xlate,
+ .xlate = irq_domain_xlate_onetwocell,
};
void cpm2_pic_init(struct device_node *node)
@@ -266,8 +261,7 @@ void cpm2_pic_init(struct device_node *node)
out_be32(&cpm2_intctl->ic_scprrl, 0x05309770);
/* create a legacy host */
- cpm2_pic_host = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LINEAR,
- 64, &cpm2_pic_host_ops, 64);
+ cpm2_pic_host = irq_domain_add_linear(node, 64, &cpm2_pic_host_ops, NULL);
if (cpm2_pic_host == NULL) {
printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
return;
diff --git a/arch/powerpc/sysdev/cpm2_pic.h b/arch/powerpc/sysdev/cpm2_pic.h
index 30e5828a278..2c5f70c2448 100644
--- a/arch/powerpc/sysdev/cpm2_pic.h
+++ b/arch/powerpc/sysdev/cpm2_pic.h
@@ -3,6 +3,6 @@
extern unsigned int cpm2_get_irq(void);
-extern void cpm2_pic_init(struct device_node*);
+extern void cpm2_pic_init(struct device_node *);
#endif /* _PPC_KERNEL_CPM2_H */
diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c
index 165981c8778..4f786957129 100644
--- a/arch/powerpc/sysdev/cpm_common.c
+++ b/arch/powerpc/sysdev/cpm_common.c
@@ -3,7 +3,7 @@
*
* Author: Scott Wood <scottwood@freescale.com>
*
- * Copyright 2007 Freescale Semiconductor, Inc.
+ * Copyright 2007-2008,2010 Freescale Semiconductor, Inc.
*
* Some parts derived from commproc.c/cpm2_common.c, which is:
* Copyright (c) 1997 Dan error_act (dmalek@jlc.net)
@@ -19,15 +19,23 @@
#include <linux/init.h>
#include <linux/of_device.h>
+#include <linux/spinlock.h>
+#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
#include <asm/udbg.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/rheap.h>
#include <asm/cpm.h>
#include <mm/mmu_decl.h>
+#if defined(CONFIG_CPM2) || defined(CONFIG_8xx_GPIO)
+#include <linux/of_gpio.h>
+#endif
+
#ifdef CONFIG_PPC_EARLY_DEBUG_CPM
static u32 __iomem *cpm_udbg_txdesc =
(u32 __iomem __force *)CONFIG_PPC_EARLY_DEBUG_CPM_ADDR;
@@ -37,7 +45,7 @@ static void udbg_putc_cpm(char c)
u8 __iomem *txbuf = (u8 __iomem __force *)in_be32(&cpm_udbg_txdesc[1]);
if (c == '\n')
- udbg_putc('\r');
+ udbg_putc_cpm('\r');
while (in_be32(&cpm_udbg_txdesc[0]) & 0x80000000)
;
@@ -50,15 +58,13 @@ void __init udbg_init_cpm(void)
{
if (cpm_udbg_txdesc) {
#ifdef CONFIG_CPM2
- setbat(1, 0xf0000000, 0xf0000000, 1024*1024, _PAGE_IO);
+ setbat(1, 0xf0000000, 0xf0000000, 1024*1024, PAGE_KERNEL_NCG);
#endif
udbg_putc = udbg_putc_cpm;
- udbg_putc('X');
}
}
#endif
-#ifdef CONFIG_PPC_CPM_NEW_BINDING
static spinlock_t cpm_muram_lock;
static rh_block_t cpm_boot_muram_rh_block[16];
static rh_info_t cpm_muram_info;
@@ -68,7 +74,7 @@ static phys_addr_t muram_pbase;
/* Max address size we deal with */
#define OF_MAX_ADDR_CELLS 4
-int __init cpm_muram_init(void)
+int cpm_muram_init(void)
{
struct device_node *np;
struct resource r;
@@ -77,6 +83,9 @@ int __init cpm_muram_init(void)
int i = 0;
int ret = 0;
+ if (muram_pbase)
+ return 0;
+
spin_lock_init(&cpm_muram_lock);
/* initialize the info header */
rh_init(&cpm_muram_info, 1,
@@ -86,9 +95,13 @@ int __init cpm_muram_init(void)
np = of_find_compatible_node(NULL, NULL, "fsl,cpm-muram-data");
if (!np) {
- printk(KERN_ERR "Cannot find CPM muram data node");
- ret = -ENODEV;
- goto out;
+ /* try legacy bindings */
+ np = of_find_node_by_name(NULL, "data-only");
+ if (!np) {
+ printk(KERN_ERR "Cannot find CPM muram data node");
+ ret = -ENODEV;
+ goto out;
+ }
}
muram_pbase = of_translate_address(np, zero);
@@ -103,7 +116,7 @@ int __init cpm_muram_init(void)
max = r.end;
rh_attach_region(&cpm_muram_info, r.start - muram_pbase,
- r.end - r.start + 1);
+ resource_size(&r));
}
muram_vbase = ioremap(muram_pbase, max - muram_pbase + 1);
@@ -134,6 +147,7 @@ unsigned long cpm_muram_alloc(unsigned long size, unsigned long align)
spin_lock_irqsave(&cpm_muram_lock, flags);
cpm_muram_info.alignment = align;
start = rh_alloc(&cpm_muram_info, size, "commproc");
+ memset(cpm_muram_addr(start), 0, size);
spin_unlock_irqrestore(&cpm_muram_lock, flags);
return start;
@@ -190,6 +204,12 @@ void __iomem *cpm_muram_addr(unsigned long offset)
}
EXPORT_SYMBOL(cpm_muram_addr);
+unsigned long cpm_muram_offset(void __iomem *addr)
+{
+ return addr - (void __iomem *)muram_vbase;
+}
+EXPORT_SYMBOL(cpm_muram_offset);
+
/**
* cpm_muram_dma - turn a muram virtual address into a DMA address
* @offset: virtual address from cpm_muram_addr() to convert
@@ -200,4 +220,131 @@ dma_addr_t cpm_muram_dma(void __iomem *addr)
}
EXPORT_SYMBOL(cpm_muram_dma);
-#endif /* CONFIG_PPC_CPM_NEW_BINDING */
+#if defined(CONFIG_CPM2) || defined(CONFIG_8xx_GPIO)
+
+struct cpm2_ioports {
+ u32 dir, par, sor, odr, dat;
+ u32 res[3];
+};
+
+struct cpm2_gpio32_chip {
+ struct of_mm_gpio_chip mm_gc;
+ spinlock_t lock;
+
+ /* shadowed data register to clear/set bits safely */
+ u32 cpdata;
+};
+
+static inline struct cpm2_gpio32_chip *
+to_cpm2_gpio32_chip(struct of_mm_gpio_chip *mm_gc)
+{
+ return container_of(mm_gc, struct cpm2_gpio32_chip, mm_gc);
+}
+
+static void cpm2_gpio32_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+ struct cpm2_gpio32_chip *cpm2_gc = to_cpm2_gpio32_chip(mm_gc);
+ struct cpm2_ioports __iomem *iop = mm_gc->regs;
+
+ cpm2_gc->cpdata = in_be32(&iop->dat);
+}
+
+static int cpm2_gpio32_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm2_ioports __iomem *iop = mm_gc->regs;
+ u32 pin_mask;
+
+ pin_mask = 1 << (31 - gpio);
+
+ return !!(in_be32(&iop->dat) & pin_mask);
+}
+
+static void __cpm2_gpio32_set(struct of_mm_gpio_chip *mm_gc, u32 pin_mask,
+ int value)
+{
+ struct cpm2_gpio32_chip *cpm2_gc = to_cpm2_gpio32_chip(mm_gc);
+ struct cpm2_ioports __iomem *iop = mm_gc->regs;
+
+ if (value)
+ cpm2_gc->cpdata |= pin_mask;
+ else
+ cpm2_gc->cpdata &= ~pin_mask;
+
+ out_be32(&iop->dat, cpm2_gc->cpdata);
+}
+
+static void cpm2_gpio32_set(struct gpio_chip *gc, unsigned int gpio, int value)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm2_gpio32_chip *cpm2_gc = to_cpm2_gpio32_chip(mm_gc);
+ unsigned long flags;
+ u32 pin_mask = 1 << (31 - gpio);
+
+ spin_lock_irqsave(&cpm2_gc->lock, flags);
+
+ __cpm2_gpio32_set(mm_gc, pin_mask, value);
+
+ spin_unlock_irqrestore(&cpm2_gc->lock, flags);
+}
+
+static int cpm2_gpio32_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm2_gpio32_chip *cpm2_gc = to_cpm2_gpio32_chip(mm_gc);
+ struct cpm2_ioports __iomem *iop = mm_gc->regs;
+ unsigned long flags;
+ u32 pin_mask = 1 << (31 - gpio);
+
+ spin_lock_irqsave(&cpm2_gc->lock, flags);
+
+ setbits32(&iop->dir, pin_mask);
+ __cpm2_gpio32_set(mm_gc, pin_mask, val);
+
+ spin_unlock_irqrestore(&cpm2_gc->lock, flags);
+
+ return 0;
+}
+
+static int cpm2_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct cpm2_gpio32_chip *cpm2_gc = to_cpm2_gpio32_chip(mm_gc);
+ struct cpm2_ioports __iomem *iop = mm_gc->regs;
+ unsigned long flags;
+ u32 pin_mask = 1 << (31 - gpio);
+
+ spin_lock_irqsave(&cpm2_gc->lock, flags);
+
+ clrbits32(&iop->dir, pin_mask);
+
+ spin_unlock_irqrestore(&cpm2_gc->lock, flags);
+
+ return 0;
+}
+
+int cpm2_gpiochip_add32(struct device_node *np)
+{
+ struct cpm2_gpio32_chip *cpm2_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ struct gpio_chip *gc;
+
+ cpm2_gc = kzalloc(sizeof(*cpm2_gc), GFP_KERNEL);
+ if (!cpm2_gc)
+ return -ENOMEM;
+
+ spin_lock_init(&cpm2_gc->lock);
+
+ mm_gc = &cpm2_gc->mm_gc;
+ gc = &mm_gc->gc;
+
+ mm_gc->save_regs = cpm2_gpio32_save_regs;
+ gc->ngpio = 32;
+ gc->direction_input = cpm2_gpio32_dir_in;
+ gc->direction_output = cpm2_gpio32_dir_out;
+ gc->get = cpm2_gpio32_get;
+ gc->set = cpm2_gpio32_set;
+
+ return of_mm_gpiochip_add(np, mm_gc);
+}
+#endif /* CONFIG_CPM2 || CONFIG_8xx_GPIO */
diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c
index e0e24b01e3a..9e5353ff6d1 100644
--- a/arch/powerpc/sysdev/dart_iommu.c
+++ b/arch/powerpc/sysdev/dart_iommu.c
@@ -29,7 +29,6 @@
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/string.h>
@@ -37,14 +36,14 @@
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/suspend.h>
+#include <linux/memblock.h>
+#include <linux/gfp.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/iommu.h>
#include <asm/pci-bridge.h>
#include <asm/machdep.h>
-#include <asm/abs_addr.h>
#include <asm/cacheflush.h>
-#include <asm/lmb.h>
#include <asm/ppc-pci.h>
#include "dart.h"
@@ -70,13 +69,20 @@ static int iommu_table_dart_inited;
static int dart_dirty;
static int dart_is_u4;
+#define DART_U4_BYPASS_BASE 0x8000000000ull
+
#define DBG(...)
+static DEFINE_SPINLOCK(invalidate_lock);
+
static inline void dart_tlb_invalidate_all(void)
{
unsigned long l = 0;
unsigned int reg, inv_bit;
unsigned long limit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&invalidate_lock, flags);
DBG("dart: flush\n");
@@ -109,12 +115,17 @@ retry:
panic("DART: TLB did not flush after waiting a long "
"time. Buggy U3 ?");
}
+
+ spin_unlock_irqrestore(&invalidate_lock, flags);
}
static inline void dart_tlb_invalidate_one(unsigned long bus_rpn)
{
unsigned int reg;
unsigned int l, limit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&invalidate_lock, flags);
reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE |
(bus_rpn & DART_CNTL_U4_IONE_MASK);
@@ -136,6 +147,8 @@ wait_more:
panic("DART: TLB did not flush after waiting a long "
"time. Buggy U4 ?");
}
+
+ spin_unlock_irqrestore(&invalidate_lock, flags);
}
static void dart_flush(struct iommu_table *tbl)
@@ -147,9 +160,10 @@ static void dart_flush(struct iommu_table *tbl)
}
}
-static void dart_build(struct iommu_table *tbl, long index,
+static int dart_build(struct iommu_table *tbl, long index,
long npages, unsigned long uaddr,
- enum dma_data_direction direction)
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs)
{
unsigned int *dp;
unsigned int rpn;
@@ -159,12 +173,12 @@ static void dart_build(struct iommu_table *tbl, long index,
dp = ((unsigned int*)tbl->it_base) + index;
- /* On U3, all memory is contigous, so we can move this
+ /* On U3, all memory is contiguous, so we can move this
* out of the loop.
*/
l = npages;
while (l--) {
- rpn = virt_to_abs(uaddr) >> DART_PAGE_SHIFT;
+ rpn = __pa(uaddr) >> DART_PAGE_SHIFT;
*(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK);
@@ -183,6 +197,7 @@ static void dart_build(struct iommu_table *tbl, long index,
} else {
dart_dirty = 1;
}
+ return 0;
}
@@ -230,17 +245,17 @@ static int __init dart_init(struct device_node *dart_node)
* that to work around what looks like a problem with the HT bridge
* prefetching into invalid pages and corrupting data
*/
- tmp = lmb_alloc(DART_PAGE_SIZE, DART_PAGE_SIZE);
+ tmp = memblock_alloc(DART_PAGE_SIZE, DART_PAGE_SIZE);
dart_emptyval = DARTMAP_VALID | ((tmp >> DART_PAGE_SHIFT) &
DARTMAP_RPNMASK);
/* Map in DART registers */
- dart = ioremap(r.start, r.end - r.start + 1);
+ dart = ioremap(r.start, resource_size(&r));
if (dart == NULL)
panic("DART: Cannot map registers!");
/* Map in DART table */
- dart_vbase = ioremap(virt_to_abs(dart_tablebase), dart_tablesize);
+ dart_vbase = ioremap(__pa(dart_tablebase), dart_tablesize);
/* Fill initial table */
for (i = 0; i < dart_tablesize/4; i++)
@@ -277,6 +292,7 @@ static void iommu_table_dart_setup(void)
iommu_table_dart.it_offset = 0;
/* it_size is in number of entries */
iommu_table_dart.it_size = dart_tablesize / sizeof(u32);
+ iommu_table_dart.it_page_shift = IOMMU_PAGE_SHIFT_4K;
/* Initialize the common IOMMU code */
iommu_table_dart.it_base = (unsigned long)dart_vbase;
@@ -290,27 +306,67 @@ static void iommu_table_dart_setup(void)
set_bit(iommu_table_dart.it_size - 1, iommu_table_dart.it_map);
}
-static void pci_dma_dev_setup_dart(struct pci_dev *dev)
+static void dma_dev_setup_dart(struct device *dev)
{
/* We only have one iommu table on the mac for now, which makes
* things simple. Setup all PCI devices to point to this table
*/
- dev->dev.archdata.dma_data = &iommu_table_dart;
+ if (get_dma_ops(dev) == &dma_direct_ops)
+ set_dma_offset(dev, DART_U4_BYPASS_BASE);
+ else
+ set_iommu_table_base(dev, &iommu_table_dart);
}
-static void pci_dma_bus_setup_dart(struct pci_bus *bus)
+static void pci_dma_dev_setup_dart(struct pci_dev *dev)
{
- struct device_node *dn;
+ dma_dev_setup_dart(&dev->dev);
+}
+static void pci_dma_bus_setup_dart(struct pci_bus *bus)
+{
if (!iommu_table_dart_inited) {
iommu_table_dart_inited = 1;
iommu_table_dart_setup();
}
+}
+
+static bool dart_device_on_pcie(struct device *dev)
+{
+ struct device_node *np = of_node_get(dev->of_node);
+
+ while(np) {
+ if (of_device_is_compatible(np, "U4-pcie") ||
+ of_device_is_compatible(np, "u4-pcie")) {
+ of_node_put(np);
+ return true;
+ }
+ np = of_get_next_parent(np);
+ }
+ return false;
+}
- dn = pci_bus_to_OF_node(bus);
+static int dart_dma_set_mask(struct device *dev, u64 dma_mask)
+{
+ if (!dev->dma_mask || !dma_supported(dev, dma_mask))
+ return -EIO;
+
+ /* U4 supports a DART bypass, we use it for 64-bit capable
+ * devices to improve performances. However, that only works
+ * for devices connected to U4 own PCIe interface, not bridged
+ * through hypertransport. We need the device to support at
+ * least 40 bits of addresses.
+ */
+ if (dart_device_on_pcie(dev) && dma_mask >= DMA_BIT_MASK(40)) {
+ dev_info(dev, "Using 64-bit DMA iommu bypass\n");
+ set_dma_ops(dev, &dma_direct_ops);
+ } else {
+ dev_info(dev, "Using 32-bit DMA via iommu\n");
+ set_dma_ops(dev, &dma_iommu_ops);
+ }
+ dma_dev_setup_dart(dev);
- if (dn)
- PCI_DN(dn)->iommu_table = &iommu_table_dart;
+ *dev->dma_mask = dma_mask;
+ return 0;
}
void __init iommu_init_early_dart(void)
@@ -322,24 +378,29 @@ void __init iommu_init_early_dart(void)
if (dn == NULL) {
dn = of_find_compatible_node(NULL, "dart", "u4-dart");
if (dn == NULL)
- goto bail;
+ return; /* use default direct_dma_ops */
dart_is_u4 = 1;
}
+ /* Initialize the DART HW */
+ if (dart_init(dn) != 0)
+ goto bail;
+
/* Setup low level TCE operations for the core IOMMU code */
ppc_md.tce_build = dart_build;
ppc_md.tce_free = dart_free;
ppc_md.tce_flush = dart_flush;
- /* Initialize the DART HW */
- if (dart_init(dn) == 0) {
- ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_dart;
- ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_dart;
+ /* Setup bypass if supported */
+ if (dart_is_u4)
+ ppc_md.dma_set_mask = dart_dma_set_mask;
- /* Setup pci_dma ops */
- set_pci_dma_ops(&dma_iommu_ops);
- return;
- }
+ ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_dart;
+ ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_dart;
+
+ /* Setup pci_dma ops */
+ set_pci_dma_ops(&dma_iommu_ops);
+ return;
bail:
/* If init failed, use direct iommu and null setup functions */
@@ -405,7 +466,7 @@ void __init alloc_dart_table(void)
if (iommu_is_off)
return;
- if (!iommu_force_on && lmb_end_of_DRAM() <= 0x40000000ull)
+ if (!iommu_force_on && memblock_end_of_DRAM() <= 0x40000000ull)
return;
/* 512 pages (2MB) is max DART tablesize. */
@@ -414,7 +475,12 @@ void __init alloc_dart_table(void)
* will blow up an entire large page anyway in the kernel mapping
*/
dart_tablebase = (unsigned long)
- abs_to_virt(lmb_alloc_base(1UL<<24, 1UL<<24, 0x80000000L));
+ __va(memblock_alloc_base(1UL<<24, 1UL<<24, 0x80000000L));
+ /*
+ * The DART space is later unmapped from the kernel linear mapping and
+ * accessing dart_tablebase during kmemleak scanning will fault.
+ */
+ kmemleak_no_scan((void *)dart_tablebase);
printk(KERN_INFO "DART table allocated at: %lx\n", dart_tablebase);
}
diff --git a/arch/powerpc/sysdev/dcr-low.S b/arch/powerpc/sysdev/dcr-low.S
index 2078f39e2f1..d3098ef1404 100644
--- a/arch/powerpc/sysdev/dcr-low.S
+++ b/arch/powerpc/sysdev/dcr-low.S
@@ -11,14 +11,20 @@
#include <asm/ppc_asm.h>
#include <asm/processor.h>
+#include <asm/bug.h>
#define DCR_ACCESS_PROLOG(table) \
+ cmpli cr0,r3,1024; \
rlwinm r3,r3,4,18,27; \
lis r5,table@h; \
ori r5,r5,table@l; \
add r3,r3,r5; \
+ bge- 1f; \
mtctr r3; \
- bctr
+ bctr; \
+1: trap; \
+ EMIT_BUG_ENTRY 1b,__FILE__,__LINE__,0; \
+ blr
_GLOBAL(__mfdcr)
DCR_ACCESS_PROLOG(__mfdcr_table)
diff --git a/arch/powerpc/sysdev/dcr.c b/arch/powerpc/sysdev/dcr.c
index 437e48d3ae3..e9056e43857 100644
--- a/arch/powerpc/sysdev/dcr.c
+++ b/arch/powerpc/sysdev/dcr.c
@@ -20,10 +20,113 @@
#undef DEBUG
#include <linux/kernel.h>
+#include <linux/export.h>
#include <asm/prom.h>
#include <asm/dcr.h>
-unsigned int dcr_resource_start(struct device_node *np, unsigned int index)
+#ifdef CONFIG_PPC_DCR_MMIO
+static struct device_node *find_dcr_parent(struct device_node *node)
+{
+ struct device_node *par, *tmp;
+ const u32 *p;
+
+ for (par = of_node_get(node); par;) {
+ if (of_get_property(par, "dcr-controller", NULL))
+ break;
+ p = of_get_property(par, "dcr-parent", NULL);
+ tmp = par;
+ if (p == NULL)
+ par = of_get_parent(par);
+ else
+ par = of_find_node_by_phandle(*p);
+ of_node_put(tmp);
+ }
+ return par;
+}
+#endif
+
+#if defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO)
+
+bool dcr_map_ok_generic(dcr_host_t host)
+{
+ if (host.type == DCR_HOST_NATIVE)
+ return dcr_map_ok_native(host.host.native);
+ else if (host.type == DCR_HOST_MMIO)
+ return dcr_map_ok_mmio(host.host.mmio);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dcr_map_ok_generic);
+
+dcr_host_t dcr_map_generic(struct device_node *dev,
+ unsigned int dcr_n,
+ unsigned int dcr_c)
+{
+ dcr_host_t host;
+ struct device_node *dp;
+ const char *prop;
+
+ host.type = DCR_HOST_INVALID;
+
+ dp = find_dcr_parent(dev);
+ if (dp == NULL)
+ return host;
+
+ prop = of_get_property(dp, "dcr-access-method", NULL);
+
+ pr_debug("dcr_map_generic(dcr-access-method = %s)\n", prop);
+
+ if (!strcmp(prop, "native")) {
+ host.type = DCR_HOST_NATIVE;
+ host.host.native = dcr_map_native(dev, dcr_n, dcr_c);
+ } else if (!strcmp(prop, "mmio")) {
+ host.type = DCR_HOST_MMIO;
+ host.host.mmio = dcr_map_mmio(dev, dcr_n, dcr_c);
+ }
+
+ of_node_put(dp);
+ return host;
+}
+EXPORT_SYMBOL_GPL(dcr_map_generic);
+
+void dcr_unmap_generic(dcr_host_t host, unsigned int dcr_c)
+{
+ if (host.type == DCR_HOST_NATIVE)
+ dcr_unmap_native(host.host.native, dcr_c);
+ else if (host.type == DCR_HOST_MMIO)
+ dcr_unmap_mmio(host.host.mmio, dcr_c);
+ else /* host.type == DCR_HOST_INVALID */
+ WARN_ON(true);
+}
+EXPORT_SYMBOL_GPL(dcr_unmap_generic);
+
+u32 dcr_read_generic(dcr_host_t host, unsigned int dcr_n)
+{
+ if (host.type == DCR_HOST_NATIVE)
+ return dcr_read_native(host.host.native, dcr_n);
+ else if (host.type == DCR_HOST_MMIO)
+ return dcr_read_mmio(host.host.mmio, dcr_n);
+ else /* host.type == DCR_HOST_INVALID */
+ WARN_ON(true);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dcr_read_generic);
+
+void dcr_write_generic(dcr_host_t host, unsigned int dcr_n, u32 value)
+{
+ if (host.type == DCR_HOST_NATIVE)
+ dcr_write_native(host.host.native, dcr_n, value);
+ else if (host.type == DCR_HOST_MMIO)
+ dcr_write_mmio(host.host.mmio, dcr_n, value);
+ else /* host.type == DCR_HOST_INVALID */
+ WARN_ON(true);
+}
+EXPORT_SYMBOL_GPL(dcr_write_generic);
+
+#endif /* defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO) */
+
+unsigned int dcr_resource_start(const struct device_node *np,
+ unsigned int index)
{
unsigned int ds;
const u32 *dr = of_get_property(np, "dcr-reg", &ds);
@@ -35,7 +138,7 @@ unsigned int dcr_resource_start(struct device_node *np, unsigned int index)
}
EXPORT_SYMBOL_GPL(dcr_resource_start);
-unsigned int dcr_resource_len(struct device_node *np, unsigned int index)
+unsigned int dcr_resource_len(const struct device_node *np, unsigned int index)
{
unsigned int ds;
const u32 *dr = of_get_property(np, "dcr-reg", &ds);
@@ -47,35 +150,16 @@ unsigned int dcr_resource_len(struct device_node *np, unsigned int index)
}
EXPORT_SYMBOL_GPL(dcr_resource_len);
-#ifndef CONFIG_PPC_DCR_NATIVE
+#ifdef CONFIG_PPC_DCR_MMIO
-static struct device_node * find_dcr_parent(struct device_node * node)
-{
- struct device_node *par, *tmp;
- const u32 *p;
-
- for (par = of_node_get(node); par;) {
- if (of_get_property(par, "dcr-controller", NULL))
- break;
- p = of_get_property(par, "dcr-parent", NULL);
- tmp = par;
- if (p == NULL)
- par = of_get_parent(par);
- else
- par = of_find_node_by_phandle(*p);
- of_node_put(tmp);
- }
- return par;
-}
-
-u64 of_translate_dcr_address(struct device_node *dev,
- unsigned int dcr_n,
- unsigned int *out_stride)
+static u64 of_translate_dcr_address(struct device_node *dev,
+ unsigned int dcr_n,
+ unsigned int *out_stride)
{
struct device_node *dp;
const u32 *p;
unsigned int stride;
- u64 ret;
+ u64 ret = OF_BAD_ADDR;
dp = find_dcr_parent(dev);
if (dp == NULL)
@@ -90,7 +174,7 @@ u64 of_translate_dcr_address(struct device_node *dev,
if (p == NULL)
p = of_get_property(dp, "dcr-mmio-space", NULL);
if (p == NULL)
- return OF_BAD_ADDR;
+ goto done;
/* Maybe could do some better range checking here */
ret = of_translate_address(dp, p);
@@ -98,21 +182,25 @@ u64 of_translate_dcr_address(struct device_node *dev,
ret += (u64)(stride) * (u64)dcr_n;
if (out_stride)
*out_stride = stride;
+
+ done:
+ of_node_put(dp);
return ret;
}
-dcr_host_t dcr_map(struct device_node *dev, unsigned int dcr_n,
- unsigned int dcr_c)
+dcr_host_mmio_t dcr_map_mmio(struct device_node *dev,
+ unsigned int dcr_n,
+ unsigned int dcr_c)
{
- dcr_host_t ret = { .token = NULL, .stride = 0, .base = dcr_n };
+ dcr_host_mmio_t ret = { .token = NULL, .stride = 0, .base = dcr_n };
u64 addr;
pr_debug("dcr_map(%s, 0x%x, 0x%x)\n",
dev->full_name, dcr_n, dcr_c);
addr = of_translate_dcr_address(dev, dcr_n, &ret.stride);
- pr_debug("translates to addr: 0x%lx, stride: 0x%x\n",
- addr, ret.stride);
+ pr_debug("translates to addr: 0x%llx, stride: 0x%x\n",
+ (unsigned long long) addr, ret.stride);
if (addr == OF_BAD_ADDR)
return ret;
pr_debug("mapping 0x%x bytes\n", dcr_c * ret.stride);
@@ -124,11 +212,11 @@ dcr_host_t dcr_map(struct device_node *dev, unsigned int dcr_n,
ret.token -= dcr_n * ret.stride;
return ret;
}
-EXPORT_SYMBOL_GPL(dcr_map);
+EXPORT_SYMBOL_GPL(dcr_map_mmio);
-void dcr_unmap(dcr_host_t host, unsigned int dcr_c)
+void dcr_unmap_mmio(dcr_host_mmio_t host, unsigned int dcr_c)
{
- dcr_host_t h = host;
+ dcr_host_mmio_t h = host;
if (h.token == NULL)
return;
@@ -136,7 +224,11 @@ void dcr_unmap(dcr_host_t host, unsigned int dcr_c)
iounmap(h.token);
h.token = NULL;
}
-EXPORT_SYMBOL_GPL(dcr_unmap);
-#else /* defined(CONFIG_PPC_DCR_NATIVE) */
+EXPORT_SYMBOL_GPL(dcr_unmap_mmio);
+
+#endif /* defined(CONFIG_PPC_DCR_MMIO) */
+
+#ifdef CONFIG_PPC_DCR_NATIVE
DEFINE_SPINLOCK(dcr_ind_lock);
-#endif /* !defined(CONFIG_PPC_DCR_NATIVE) */
+#endif /* defined(CONFIG_PPC_DCR_NATIVE) */
+
diff --git a/arch/powerpc/sysdev/ehv_pic.c b/arch/powerpc/sysdev/ehv_pic.c
new file mode 100644
index 00000000000..2d20f10a420
--- /dev/null
+++ b/arch/powerpc/sysdev/ehv_pic.c
@@ -0,0 +1,296 @@
+/*
+ * Driver for ePAPR Embedded Hypervisor PIC
+ *
+ * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ *
+ * Author: Ashish Kalra <ashish.kalra@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/smp.h>
+#include <asm/machdep.h>
+#include <asm/ehv_pic.h>
+#include <asm/fsl_hcalls.h>
+
+static struct ehv_pic *global_ehv_pic;
+static DEFINE_SPINLOCK(ehv_pic_lock);
+
+static u32 hwirq_intspec[NR_EHV_PIC_INTS];
+static u32 __iomem *mpic_percpu_base_vaddr;
+
+#define IRQ_TYPE_MPIC_DIRECT 4
+#define MPIC_EOI 0x00B0
+
+/*
+ * Linux descriptor level callbacks
+ */
+
+void ehv_pic_unmask_irq(struct irq_data *d)
+{
+ unsigned int src = virq_to_hw(d->irq);
+
+ ev_int_set_mask(src, 0);
+}
+
+void ehv_pic_mask_irq(struct irq_data *d)
+{
+ unsigned int src = virq_to_hw(d->irq);
+
+ ev_int_set_mask(src, 1);
+}
+
+void ehv_pic_end_irq(struct irq_data *d)
+{
+ unsigned int src = virq_to_hw(d->irq);
+
+ ev_int_eoi(src);
+}
+
+void ehv_pic_direct_end_irq(struct irq_data *d)
+{
+ out_be32(mpic_percpu_base_vaddr + MPIC_EOI / 4, 0);
+}
+
+int ehv_pic_set_affinity(struct irq_data *d, const struct cpumask *dest,
+ bool force)
+{
+ unsigned int src = virq_to_hw(d->irq);
+ unsigned int config, prio, cpu_dest;
+ int cpuid = irq_choose_cpu(dest);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ehv_pic_lock, flags);
+ ev_int_get_config(src, &config, &prio, &cpu_dest);
+ ev_int_set_config(src, config, prio, cpuid);
+ spin_unlock_irqrestore(&ehv_pic_lock, flags);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static unsigned int ehv_pic_type_to_vecpri(unsigned int type)
+{
+ /* Now convert sense value */
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ return EHV_PIC_INFO(VECPRI_SENSE_EDGE) |
+ EHV_PIC_INFO(VECPRI_POLARITY_POSITIVE);
+
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_EDGE_BOTH:
+ return EHV_PIC_INFO(VECPRI_SENSE_EDGE) |
+ EHV_PIC_INFO(VECPRI_POLARITY_NEGATIVE);
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ return EHV_PIC_INFO(VECPRI_SENSE_LEVEL) |
+ EHV_PIC_INFO(VECPRI_POLARITY_POSITIVE);
+
+ case IRQ_TYPE_LEVEL_LOW:
+ default:
+ return EHV_PIC_INFO(VECPRI_SENSE_LEVEL) |
+ EHV_PIC_INFO(VECPRI_POLARITY_NEGATIVE);
+ }
+}
+
+int ehv_pic_set_irq_type(struct irq_data *d, unsigned int flow_type)
+{
+ unsigned int src = virq_to_hw(d->irq);
+ unsigned int vecpri, vold, vnew, prio, cpu_dest;
+ unsigned long flags;
+
+ if (flow_type == IRQ_TYPE_NONE)
+ flow_type = IRQ_TYPE_LEVEL_LOW;
+
+ irqd_set_trigger_type(d, flow_type);
+
+ vecpri = ehv_pic_type_to_vecpri(flow_type);
+
+ spin_lock_irqsave(&ehv_pic_lock, flags);
+ ev_int_get_config(src, &vold, &prio, &cpu_dest);
+ vnew = vold & ~(EHV_PIC_INFO(VECPRI_POLARITY_MASK) |
+ EHV_PIC_INFO(VECPRI_SENSE_MASK));
+ vnew |= vecpri;
+
+ /*
+ * TODO : Add specific interface call for platform to set
+ * individual interrupt priorities.
+ * platform currently using static/default priority for all ints
+ */
+
+ prio = 8;
+
+ ev_int_set_config(src, vecpri, prio, cpu_dest);
+
+ spin_unlock_irqrestore(&ehv_pic_lock, flags);
+ return IRQ_SET_MASK_OK_NOCOPY;
+}
+
+static struct irq_chip ehv_pic_irq_chip = {
+ .irq_mask = ehv_pic_mask_irq,
+ .irq_unmask = ehv_pic_unmask_irq,
+ .irq_eoi = ehv_pic_end_irq,
+ .irq_set_type = ehv_pic_set_irq_type,
+};
+
+static struct irq_chip ehv_pic_direct_eoi_irq_chip = {
+ .irq_mask = ehv_pic_mask_irq,
+ .irq_unmask = ehv_pic_unmask_irq,
+ .irq_eoi = ehv_pic_direct_end_irq,
+ .irq_set_type = ehv_pic_set_irq_type,
+};
+
+/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */
+unsigned int ehv_pic_get_irq(void)
+{
+ int irq;
+
+ BUG_ON(global_ehv_pic == NULL);
+
+ if (global_ehv_pic->coreint_flag)
+ irq = mfspr(SPRN_EPR); /* if core int mode */
+ else
+ ev_int_iack(0, &irq); /* legacy mode */
+
+ if (irq == 0xFFFF) /* 0xFFFF --> no irq is pending */
+ return NO_IRQ;
+
+ /*
+ * this will also setup revmap[] in the slow path for the first
+ * time, next calls will always use fast path by indexing revmap
+ */
+ return irq_linear_revmap(global_ehv_pic->irqhost, irq);
+}
+
+static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node)
+{
+ /* Exact match, unless ehv_pic node is NULL */
+ return h->of_node == NULL || h->of_node == node;
+}
+
+static int ehv_pic_host_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct ehv_pic *ehv_pic = h->host_data;
+ struct irq_chip *chip;
+
+ /* Default chip */
+ chip = &ehv_pic->hc_irq;
+
+ if (mpic_percpu_base_vaddr)
+ if (hwirq_intspec[hw] & IRQ_TYPE_MPIC_DIRECT)
+ chip = &ehv_pic_direct_eoi_irq_chip;
+
+ irq_set_chip_data(virq, chip);
+ /*
+ * using handle_fasteoi_irq as our irq handler, this will
+ * only call the eoi callback and suitable for the MPIC
+ * controller which set ISR/IPR automatically and clear the
+ * highest priority active interrupt in ISR/IPR when we do
+ * a specific eoi
+ */
+ irq_set_chip_and_handler(virq, chip, handle_fasteoi_irq);
+
+ /* Set default irq type */
+ irq_set_irq_type(virq, IRQ_TYPE_NONE);
+
+ return 0;
+}
+
+static int ehv_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)
+
+{
+ /*
+ * interrupt sense values coming from the guest device tree
+ * interrupt specifiers can have four possible sense and
+ * level encoding information and they need to
+ * be translated between firmware type & linux type.
+ */
+
+ static unsigned char map_of_senses_to_linux_irqtype[4] = {
+ IRQ_TYPE_EDGE_FALLING,
+ IRQ_TYPE_EDGE_RISING,
+ IRQ_TYPE_LEVEL_LOW,
+ IRQ_TYPE_LEVEL_HIGH,
+ };
+
+ *out_hwirq = intspec[0];
+ if (intsize > 1) {
+ hwirq_intspec[intspec[0]] = intspec[1];
+ *out_flags = map_of_senses_to_linux_irqtype[intspec[1] &
+ ~IRQ_TYPE_MPIC_DIRECT];
+ } else {
+ *out_flags = IRQ_TYPE_NONE;
+ }
+
+ return 0;
+}
+
+static const struct irq_domain_ops ehv_pic_host_ops = {
+ .match = ehv_pic_host_match,
+ .map = ehv_pic_host_map,
+ .xlate = ehv_pic_host_xlate,
+};
+
+void __init ehv_pic_init(void)
+{
+ struct device_node *np, *np2;
+ struct ehv_pic *ehv_pic;
+ int coreint_flag = 1;
+
+ np = of_find_compatible_node(NULL, NULL, "epapr,hv-pic");
+ if (!np) {
+ pr_err("ehv_pic_init: could not find epapr,hv-pic node\n");
+ return;
+ }
+
+ if (!of_find_property(np, "has-external-proxy", NULL))
+ coreint_flag = 0;
+
+ ehv_pic = kzalloc(sizeof(struct ehv_pic), GFP_KERNEL);
+ if (!ehv_pic) {
+ of_node_put(np);
+ return;
+ }
+
+ ehv_pic->irqhost = irq_domain_add_linear(np, NR_EHV_PIC_INTS,
+ &ehv_pic_host_ops, ehv_pic);
+ if (!ehv_pic->irqhost) {
+ of_node_put(np);
+ kfree(ehv_pic);
+ return;
+ }
+
+ np2 = of_find_compatible_node(NULL, NULL, "fsl,hv-mpic-per-cpu");
+ if (np2) {
+ mpic_percpu_base_vaddr = of_iomap(np2, 0);
+ if (!mpic_percpu_base_vaddr)
+ pr_err("ehv_pic_init: of_iomap failed\n");
+
+ of_node_put(np2);
+ }
+
+ ehv_pic->hc_irq = ehv_pic_irq_chip;
+ ehv_pic->hc_irq.irq_set_affinity = ehv_pic_set_affinity;
+ ehv_pic->coreint_flag = coreint_flag;
+
+ global_ehv_pic = ehv_pic;
+ irq_set_default_host(global_ehv_pic->irqhost);
+}
diff --git a/arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h b/arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h
new file mode 100644
index 00000000000..2aa97ddb7b7
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2009-2010, 2012 Freescale Semiconductor, Inc
+ *
+ * QorIQ based Cache Controller Memory Mapped Registers
+ *
+ * Author: Vivek Mahajan <vivek.mahajan@freescale.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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __FSL_85XX_CACHE_CTLR_H__
+#define __FSL_85XX_CACHE_CTLR_H__
+
+#define L2CR_L2FI 0x40000000 /* L2 flash invalidate */
+#define L2CR_L2IO 0x00200000 /* L2 instruction only */
+#define L2CR_SRAM_ZERO 0x00000000 /* L2SRAM zero size */
+#define L2CR_SRAM_FULL 0x00010000 /* L2SRAM full size */
+#define L2CR_SRAM_HALF 0x00020000 /* L2SRAM half size */
+#define L2CR_SRAM_TWO_HALFS 0x00030000 /* L2SRAM two half sizes */
+#define L2CR_SRAM_QUART 0x00040000 /* L2SRAM one quarter size */
+#define L2CR_SRAM_TWO_QUARTS 0x00050000 /* L2SRAM two quarter size */
+#define L2CR_SRAM_EIGHTH 0x00060000 /* L2SRAM one eighth size */
+#define L2CR_SRAM_TWO_EIGHTH 0x00070000 /* L2SRAM two eighth size */
+
+#define L2SRAM_OPTIMAL_SZ_SHIFT 0x00000003 /* Optimum size for L2SRAM */
+
+#define L2SRAM_BAR_MSK_LO18 0xFFFFC000 /* Lower 18 bits */
+#define L2SRAM_BARE_MSK_HI4 0x0000000F /* Upper 4 bits */
+
+enum cache_sram_lock_ways {
+ LOCK_WAYS_ZERO,
+ LOCK_WAYS_EIGHTH,
+ LOCK_WAYS_TWO_EIGHTH,
+ LOCK_WAYS_HALF = 4,
+ LOCK_WAYS_FULL = 8,
+};
+
+struct mpc85xx_l2ctlr {
+ u32 ctl; /* 0x000 - L2 control */
+ u8 res1[0xC];
+ u32 ewar0; /* 0x010 - External write address 0 */
+ u32 ewarea0; /* 0x014 - External write address extended 0 */
+ u32 ewcr0; /* 0x018 - External write ctrl */
+ u8 res2[4];
+ u32 ewar1; /* 0x020 - External write address 1 */
+ u32 ewarea1; /* 0x024 - External write address extended 1 */
+ u32 ewcr1; /* 0x028 - External write ctrl 1 */
+ u8 res3[4];
+ u32 ewar2; /* 0x030 - External write address 2 */
+ u32 ewarea2; /* 0x034 - External write address extended 2 */
+ u32 ewcr2; /* 0x038 - External write ctrl 2 */
+ u8 res4[4];
+ u32 ewar3; /* 0x040 - External write address 3 */
+ u32 ewarea3; /* 0x044 - External write address extended 3 */
+ u32 ewcr3; /* 0x048 - External write ctrl 3 */
+ u8 res5[0xB4];
+ u32 srbar0; /* 0x100 - SRAM base address 0 */
+ u32 srbarea0; /* 0x104 - SRAM base addr reg ext address 0 */
+ u32 srbar1; /* 0x108 - SRAM base address 1 */
+ u32 srbarea1; /* 0x10C - SRAM base addr reg ext address 1 */
+ u8 res6[0xCF0];
+ u32 errinjhi; /* 0xE00 - Error injection mask high */
+ u32 errinjlo; /* 0xE04 - Error injection mask low */
+ u32 errinjctl; /* 0xE08 - Error injection tag/ecc control */
+ u8 res7[0x14];
+ u32 captdatahi; /* 0xE20 - Error data high capture */
+ u32 captdatalo; /* 0xE24 - Error data low capture */
+ u32 captecc; /* 0xE28 - Error syndrome */
+ u8 res8[0x14];
+ u32 errdet; /* 0xE40 - Error detect */
+ u32 errdis; /* 0xE44 - Error disable */
+ u32 errinten; /* 0xE48 - Error interrupt enable */
+ u32 errattr; /* 0xE4c - Error attribute capture */
+ u32 erradrrl; /* 0xE50 - Error address capture low */
+ u32 erradrrh; /* 0xE54 - Error address capture high */
+ u32 errctl; /* 0xE58 - Error control */
+ u8 res9[0x1A4];
+};
+
+struct sram_parameters {
+ unsigned int sram_size;
+ phys_addr_t sram_offset;
+};
+
+extern int instantiate_cache_sram(struct platform_device *dev,
+ struct sram_parameters sram_params);
+extern void remove_cache_sram(struct platform_device *dev);
+
+#endif /* __FSL_85XX_CACHE_CTLR_H__ */
diff --git a/arch/powerpc/sysdev/fsl_85xx_cache_sram.c b/arch/powerpc/sysdev/fsl_85xx_cache_sram.c
new file mode 100644
index 00000000000..37a69097e02
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_85xx_cache_sram.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2009-2010 Freescale Semiconductor, Inc.
+ *
+ * Simple memory allocator abstraction for QorIQ (P1/P2) based Cache-SRAM
+ *
+ * Author: Vivek Mahajan <vivek.mahajan@freescale.com>
+ *
+ * This file is derived from the original work done
+ * by Sylvain Munaut for the Bestcomm SRAM allocator.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of_platform.h>
+#include <asm/pgtable.h>
+#include <asm/fsl_85xx_cache_sram.h>
+
+#include "fsl_85xx_cache_ctlr.h"
+
+struct mpc85xx_cache_sram *cache_sram;
+
+void *mpc85xx_cache_sram_alloc(unsigned int size,
+ phys_addr_t *phys, unsigned int align)
+{
+ unsigned long offset;
+ unsigned long flags;
+
+ if (unlikely(cache_sram == NULL))
+ return NULL;
+
+ if (!size || (size > cache_sram->size) || (align > cache_sram->size)) {
+ pr_err("%s(): size(=%x) or align(=%x) zero or too big\n",
+ __func__, size, align);
+ return NULL;
+ }
+
+ if ((align & (align - 1)) || align <= 1) {
+ pr_err("%s(): align(=%x) must be power of two and >1\n",
+ __func__, align);
+ return NULL;
+ }
+
+ spin_lock_irqsave(&cache_sram->lock, flags);
+ offset = rh_alloc_align(cache_sram->rh, size, align, NULL);
+ spin_unlock_irqrestore(&cache_sram->lock, flags);
+
+ if (IS_ERR_VALUE(offset))
+ return NULL;
+
+ *phys = cache_sram->base_phys + offset;
+
+ return (unsigned char *)cache_sram->base_virt + offset;
+}
+EXPORT_SYMBOL(mpc85xx_cache_sram_alloc);
+
+void mpc85xx_cache_sram_free(void *ptr)
+{
+ unsigned long flags;
+ BUG_ON(!ptr);
+
+ spin_lock_irqsave(&cache_sram->lock, flags);
+ rh_free(cache_sram->rh, ptr - cache_sram->base_virt);
+ spin_unlock_irqrestore(&cache_sram->lock, flags);
+}
+EXPORT_SYMBOL(mpc85xx_cache_sram_free);
+
+int __init instantiate_cache_sram(struct platform_device *dev,
+ struct sram_parameters sram_params)
+{
+ int ret = 0;
+
+ if (cache_sram) {
+ dev_err(&dev->dev, "Already initialized cache-sram\n");
+ return -EBUSY;
+ }
+
+ cache_sram = kzalloc(sizeof(struct mpc85xx_cache_sram), GFP_KERNEL);
+ if (!cache_sram) {
+ dev_err(&dev->dev, "Out of memory for cache_sram structure\n");
+ return -ENOMEM;
+ }
+
+ cache_sram->base_phys = sram_params.sram_offset;
+ cache_sram->size = sram_params.sram_size;
+
+ if (!request_mem_region(cache_sram->base_phys, cache_sram->size,
+ "fsl_85xx_cache_sram")) {
+ dev_err(&dev->dev, "%s: request memory failed\n",
+ dev->dev.of_node->full_name);
+ ret = -ENXIO;
+ goto out_free;
+ }
+
+ cache_sram->base_virt = ioremap_prot(cache_sram->base_phys,
+ cache_sram->size, _PAGE_COHERENT | PAGE_KERNEL);
+ if (!cache_sram->base_virt) {
+ dev_err(&dev->dev, "%s: ioremap_prot failed\n",
+ dev->dev.of_node->full_name);
+ ret = -ENOMEM;
+ goto out_release;
+ }
+
+ cache_sram->rh = rh_create(sizeof(unsigned int));
+ if (IS_ERR(cache_sram->rh)) {
+ dev_err(&dev->dev, "%s: Unable to create remote heap\n",
+ dev->dev.of_node->full_name);
+ ret = PTR_ERR(cache_sram->rh);
+ goto out_unmap;
+ }
+
+ rh_attach_region(cache_sram->rh, 0, cache_sram->size);
+ spin_lock_init(&cache_sram->lock);
+
+ dev_info(&dev->dev, "[base:0x%llx, size:0x%x] configured and loaded\n",
+ (unsigned long long)cache_sram->base_phys, cache_sram->size);
+
+ return 0;
+
+out_unmap:
+ iounmap(cache_sram->base_virt);
+
+out_release:
+ release_mem_region(cache_sram->base_phys, cache_sram->size);
+
+out_free:
+ kfree(cache_sram);
+ return ret;
+}
+
+void remove_cache_sram(struct platform_device *dev)
+{
+ BUG_ON(!cache_sram);
+
+ rh_detach_region(cache_sram->rh, 0, cache_sram->size);
+ rh_destroy(cache_sram->rh);
+
+ iounmap(cache_sram->base_virt);
+ release_mem_region(cache_sram->base_phys, cache_sram->size);
+
+ kfree(cache_sram);
+ cache_sram = NULL;
+
+ dev_info(&dev->dev, "MPC85xx Cache-SRAM driver unloaded\n");
+}
diff --git a/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c b/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c
new file mode 100644
index 00000000000..afc2dbf3701
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2009-2010, 2012 Freescale Semiconductor, Inc.
+ *
+ * QorIQ (P1/P2) L2 controller init for Cache-SRAM instantiation
+ *
+ * Author: Vivek Mahajan <vivek.mahajan@freescale.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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <asm/io.h>
+
+#include "fsl_85xx_cache_ctlr.h"
+
+static char *sram_size;
+static char *sram_offset;
+struct mpc85xx_l2ctlr __iomem *l2ctlr;
+
+static int get_cache_sram_params(struct sram_parameters *sram_params)
+{
+ unsigned long long addr;
+ unsigned int size;
+
+ if (!sram_size || (kstrtouint(sram_size, 0, &size) < 0))
+ return -EINVAL;
+
+ if (!sram_offset || (kstrtoull(sram_offset, 0, &addr) < 0))
+ return -EINVAL;
+
+ sram_params->sram_offset = addr;
+ sram_params->sram_size = size;
+
+ return 0;
+}
+
+static int __init get_size_from_cmdline(char *str)
+{
+ if (!str)
+ return 0;
+
+ sram_size = str;
+ return 1;
+}
+
+static int __init get_offset_from_cmdline(char *str)
+{
+ if (!str)
+ return 0;
+
+ sram_offset = str;
+ return 1;
+}
+
+__setup("cache-sram-size=", get_size_from_cmdline);
+__setup("cache-sram-offset=", get_offset_from_cmdline);
+
+static int mpc85xx_l2ctlr_of_probe(struct platform_device *dev)
+{
+ long rval;
+ unsigned int rem;
+ unsigned char ways;
+ const unsigned int *prop;
+ unsigned int l2cache_size;
+ struct sram_parameters sram_params;
+
+ if (!dev->dev.of_node) {
+ dev_err(&dev->dev, "Device's OF-node is NULL\n");
+ return -EINVAL;
+ }
+
+ prop = of_get_property(dev->dev.of_node, "cache-size", NULL);
+ if (!prop) {
+ dev_err(&dev->dev, "Missing L2 cache-size\n");
+ return -EINVAL;
+ }
+ l2cache_size = *prop;
+
+ if (get_cache_sram_params(&sram_params)) {
+ dev_err(&dev->dev,
+ "Entire L2 as cache, provide valid sram offset and size\n");
+ return -EINVAL;
+ }
+
+
+ rem = l2cache_size % sram_params.sram_size;
+ ways = LOCK_WAYS_FULL * sram_params.sram_size / l2cache_size;
+ if (rem || (ways & (ways - 1))) {
+ dev_err(&dev->dev, "Illegal cache-sram-size in command line\n");
+ return -EINVAL;
+ }
+
+ l2ctlr = of_iomap(dev->dev.of_node, 0);
+ if (!l2ctlr) {
+ dev_err(&dev->dev, "Can't map L2 controller\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Write bits[0-17] to srbar0
+ */
+ out_be32(&l2ctlr->srbar0,
+ lower_32_bits(sram_params.sram_offset) & L2SRAM_BAR_MSK_LO18);
+
+ /*
+ * Write bits[18-21] to srbare0
+ */
+#ifdef CONFIG_PHYS_64BIT
+ out_be32(&l2ctlr->srbarea0,
+ upper_32_bits(sram_params.sram_offset) & L2SRAM_BARE_MSK_HI4);
+#endif
+
+ clrsetbits_be32(&l2ctlr->ctl, L2CR_L2E, L2CR_L2FI);
+
+ switch (ways) {
+ case LOCK_WAYS_EIGHTH:
+ setbits32(&l2ctlr->ctl,
+ L2CR_L2E | L2CR_L2FI | L2CR_SRAM_EIGHTH);
+ break;
+
+ case LOCK_WAYS_TWO_EIGHTH:
+ setbits32(&l2ctlr->ctl,
+ L2CR_L2E | L2CR_L2FI | L2CR_SRAM_QUART);
+ break;
+
+ case LOCK_WAYS_HALF:
+ setbits32(&l2ctlr->ctl,
+ L2CR_L2E | L2CR_L2FI | L2CR_SRAM_HALF);
+ break;
+
+ case LOCK_WAYS_FULL:
+ default:
+ setbits32(&l2ctlr->ctl,
+ L2CR_L2E | L2CR_L2FI | L2CR_SRAM_FULL);
+ break;
+ }
+ eieio();
+
+ rval = instantiate_cache_sram(dev, sram_params);
+ if (rval < 0) {
+ dev_err(&dev->dev, "Can't instantiate Cache-SRAM\n");
+ iounmap(l2ctlr);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mpc85xx_l2ctlr_of_remove(struct platform_device *dev)
+{
+ BUG_ON(!l2ctlr);
+
+ iounmap(l2ctlr);
+ remove_cache_sram(dev);
+ dev_info(&dev->dev, "MPC85xx L2 controller unloaded\n");
+
+ return 0;
+}
+
+static struct of_device_id mpc85xx_l2ctlr_of_match[] = {
+ {
+ .compatible = "fsl,p2020-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,p2010-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,p1020-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,p1011-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,p1013-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,p1022-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,mpc8548-l2-cache-controller",
+ },
+ { .compatible = "fsl,mpc8544-l2-cache-controller",},
+ { .compatible = "fsl,mpc8572-l2-cache-controller",},
+ { .compatible = "fsl,mpc8536-l2-cache-controller",},
+ { .compatible = "fsl,p1021-l2-cache-controller",},
+ { .compatible = "fsl,p1012-l2-cache-controller",},
+ { .compatible = "fsl,p1025-l2-cache-controller",},
+ { .compatible = "fsl,p1016-l2-cache-controller",},
+ { .compatible = "fsl,p1024-l2-cache-controller",},
+ { .compatible = "fsl,p1015-l2-cache-controller",},
+ { .compatible = "fsl,p1010-l2-cache-controller",},
+ { .compatible = "fsl,bsc9131-l2-cache-controller",},
+ {},
+};
+
+static struct platform_driver mpc85xx_l2ctlr_of_platform_driver = {
+ .driver = {
+ .name = "fsl-l2ctlr",
+ .owner = THIS_MODULE,
+ .of_match_table = mpc85xx_l2ctlr_of_match,
+ },
+ .probe = mpc85xx_l2ctlr_of_probe,
+ .remove = mpc85xx_l2ctlr_of_remove,
+};
+
+static __init int mpc85xx_l2ctlr_of_init(void)
+{
+ return platform_driver_register(&mpc85xx_l2ctlr_of_platform_driver);
+}
+
+static void __exit mpc85xx_l2ctlr_of_exit(void)
+{
+ platform_driver_unregister(&mpc85xx_l2ctlr_of_platform_driver);
+}
+
+subsys_initcall(mpc85xx_l2ctlr_of_init);
+module_exit(mpc85xx_l2ctlr_of_exit);
+
+MODULE_DESCRIPTION("Freescale MPC85xx L2 controller init");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/powerpc/sysdev/fsl_gtm.c b/arch/powerpc/sysdev/fsl_gtm.c
new file mode 100644
index 00000000000..06ac3c61b3d
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_gtm.c
@@ -0,0 +1,438 @@
+/*
+ * Freescale General-purpose Timers Module
+ *
+ * Copyright (c) Freescale Semiconductor, Inc. 2006.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <asm/fsl_gtm.h>
+
+#define GTCFR_STP(x) ((x) & 1 ? 1 << 5 : 1 << 1)
+#define GTCFR_RST(x) ((x) & 1 ? 1 << 4 : 1 << 0)
+
+#define GTMDR_ICLK_MASK (3 << 1)
+#define GTMDR_ICLK_ICAS (0 << 1)
+#define GTMDR_ICLK_ICLK (1 << 1)
+#define GTMDR_ICLK_SLGO (2 << 1)
+#define GTMDR_FRR (1 << 3)
+#define GTMDR_ORI (1 << 4)
+#define GTMDR_SPS(x) ((x) << 8)
+
+struct gtm_timers_regs {
+ u8 gtcfr1; /* Timer 1, Timer 2 global config register */
+ u8 res0[0x3];
+ u8 gtcfr2; /* Timer 3, timer 4 global config register */
+ u8 res1[0xB];
+ __be16 gtmdr1; /* Timer 1 mode register */
+ __be16 gtmdr2; /* Timer 2 mode register */
+ __be16 gtrfr1; /* Timer 1 reference register */
+ __be16 gtrfr2; /* Timer 2 reference register */
+ __be16 gtcpr1; /* Timer 1 capture register */
+ __be16 gtcpr2; /* Timer 2 capture register */
+ __be16 gtcnr1; /* Timer 1 counter */
+ __be16 gtcnr2; /* Timer 2 counter */
+ __be16 gtmdr3; /* Timer 3 mode register */
+ __be16 gtmdr4; /* Timer 4 mode register */
+ __be16 gtrfr3; /* Timer 3 reference register */
+ __be16 gtrfr4; /* Timer 4 reference register */
+ __be16 gtcpr3; /* Timer 3 capture register */
+ __be16 gtcpr4; /* Timer 4 capture register */
+ __be16 gtcnr3; /* Timer 3 counter */
+ __be16 gtcnr4; /* Timer 4 counter */
+ __be16 gtevr1; /* Timer 1 event register */
+ __be16 gtevr2; /* Timer 2 event register */
+ __be16 gtevr3; /* Timer 3 event register */
+ __be16 gtevr4; /* Timer 4 event register */
+ __be16 gtpsr1; /* Timer 1 prescale register */
+ __be16 gtpsr2; /* Timer 2 prescale register */
+ __be16 gtpsr3; /* Timer 3 prescale register */
+ __be16 gtpsr4; /* Timer 4 prescale register */
+ u8 res2[0x40];
+} __attribute__ ((packed));
+
+struct gtm {
+ unsigned int clock;
+ struct gtm_timers_regs __iomem *regs;
+ struct gtm_timer timers[4];
+ spinlock_t lock;
+ struct list_head list_node;
+};
+
+static LIST_HEAD(gtms);
+
+/**
+ * gtm_get_timer - request GTM timer to use it with the rest of GTM API
+ * Context: non-IRQ
+ *
+ * This function reserves GTM timer for later use. It returns gtm_timer
+ * structure to use with the rest of GTM API, you should use timer->irq
+ * to manage timer interrupt.
+ */
+struct gtm_timer *gtm_get_timer16(void)
+{
+ struct gtm *gtm = NULL;
+ int i;
+
+ list_for_each_entry(gtm, &gtms, list_node) {
+ spin_lock_irq(&gtm->lock);
+
+ for (i = 0; i < ARRAY_SIZE(gtm->timers); i++) {
+ if (!gtm->timers[i].requested) {
+ gtm->timers[i].requested = true;
+ spin_unlock_irq(&gtm->lock);
+ return &gtm->timers[i];
+ }
+ }
+
+ spin_unlock_irq(&gtm->lock);
+ }
+
+ if (gtm)
+ return ERR_PTR(-EBUSY);
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL(gtm_get_timer16);
+
+/**
+ * gtm_get_specific_timer - request specific GTM timer
+ * @gtm: specific GTM, pass here GTM's device_node->data
+ * @timer: specific timer number, Timer1 is 0.
+ * Context: non-IRQ
+ *
+ * This function reserves GTM timer for later use. It returns gtm_timer
+ * structure to use with the rest of GTM API, you should use timer->irq
+ * to manage timer interrupt.
+ */
+struct gtm_timer *gtm_get_specific_timer16(struct gtm *gtm,
+ unsigned int timer)
+{
+ struct gtm_timer *ret = ERR_PTR(-EBUSY);
+
+ if (timer > 3)
+ return ERR_PTR(-EINVAL);
+
+ spin_lock_irq(&gtm->lock);
+
+ if (gtm->timers[timer].requested)
+ goto out;
+
+ ret = &gtm->timers[timer];
+ ret->requested = true;
+
+out:
+ spin_unlock_irq(&gtm->lock);
+ return ret;
+}
+EXPORT_SYMBOL(gtm_get_specific_timer16);
+
+/**
+ * gtm_put_timer16 - release 16 bits GTM timer
+ * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer
+ * Context: any
+ *
+ * This function releases GTM timer so others may request it.
+ */
+void gtm_put_timer16(struct gtm_timer *tmr)
+{
+ gtm_stop_timer16(tmr);
+
+ spin_lock_irq(&tmr->gtm->lock);
+ tmr->requested = false;
+ spin_unlock_irq(&tmr->gtm->lock);
+}
+EXPORT_SYMBOL(gtm_put_timer16);
+
+/*
+ * This is back-end for the exported functions, it's used to reset single
+ * timer in reference mode.
+ */
+static int gtm_set_ref_timer16(struct gtm_timer *tmr, int frequency,
+ int reference_value, bool free_run)
+{
+ struct gtm *gtm = tmr->gtm;
+ int num = tmr - &gtm->timers[0];
+ unsigned int prescaler;
+ u8 iclk = GTMDR_ICLK_ICLK;
+ u8 psr;
+ u8 sps;
+ unsigned long flags;
+ int max_prescaler = 256 * 256 * 16;
+
+ /* CPM2 doesn't have primary prescaler */
+ if (!tmr->gtpsr)
+ max_prescaler /= 256;
+
+ prescaler = gtm->clock / frequency;
+ /*
+ * We have two 8 bit prescalers -- primary and secondary (psr, sps),
+ * plus "slow go" mode (clk / 16). So, total prescale value is
+ * 16 * (psr + 1) * (sps + 1). Though, for CPM2 GTMs we losing psr.
+ */
+ if (prescaler > max_prescaler)
+ return -EINVAL;
+
+ if (prescaler > max_prescaler / 16) {
+ iclk = GTMDR_ICLK_SLGO;
+ prescaler /= 16;
+ }
+
+ if (prescaler <= 256) {
+ psr = 0;
+ sps = prescaler - 1;
+ } else {
+ psr = 256 - 1;
+ sps = prescaler / 256 - 1;
+ }
+
+ spin_lock_irqsave(&gtm->lock, flags);
+
+ /*
+ * Properly reset timers: stop, reset, set up prescalers, reference
+ * value and clear event register.
+ */
+ clrsetbits_8(tmr->gtcfr, ~(GTCFR_STP(num) | GTCFR_RST(num)),
+ GTCFR_STP(num) | GTCFR_RST(num));
+
+ setbits8(tmr->gtcfr, GTCFR_STP(num));
+
+ if (tmr->gtpsr)
+ out_be16(tmr->gtpsr, psr);
+ clrsetbits_be16(tmr->gtmdr, 0xFFFF, iclk | GTMDR_SPS(sps) |
+ GTMDR_ORI | (free_run ? GTMDR_FRR : 0));
+ out_be16(tmr->gtcnr, 0);
+ out_be16(tmr->gtrfr, reference_value);
+ out_be16(tmr->gtevr, 0xFFFF);
+
+ /* Let it be. */
+ clrbits8(tmr->gtcfr, GTCFR_STP(num));
+
+ spin_unlock_irqrestore(&gtm->lock, flags);
+
+ return 0;
+}
+
+/**
+ * gtm_set_timer16 - (re)set 16 bit timer with arbitrary precision
+ * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer
+ * @usec: timer interval in microseconds
+ * @reload: if set, the timer will reset upon expiry rather than
+ * continue running free.
+ * Context: any
+ *
+ * This function (re)sets the GTM timer so that it counts up to the requested
+ * interval value, and fires the interrupt when the value is reached. This
+ * function will reduce the precision of the timer as needed in order for the
+ * requested timeout to fit in a 16-bit register.
+ */
+int gtm_set_timer16(struct gtm_timer *tmr, unsigned long usec, bool reload)
+{
+ /* quite obvious, frequency which is enough for µSec precision */
+ int freq = 1000000;
+ unsigned int bit;
+
+ bit = fls_long(usec);
+ if (bit > 15) {
+ freq >>= bit - 15;
+ usec >>= bit - 15;
+ }
+
+ if (!freq)
+ return -EINVAL;
+
+ return gtm_set_ref_timer16(tmr, freq, usec, reload);
+}
+EXPORT_SYMBOL(gtm_set_timer16);
+
+/**
+ * gtm_set_exact_utimer16 - (re)set 16 bits timer
+ * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer
+ * @usec: timer interval in microseconds
+ * @reload: if set, the timer will reset upon expiry rather than
+ * continue running free.
+ * Context: any
+ *
+ * This function (re)sets GTM timer so that it counts up to the requested
+ * interval value, and fires the interrupt when the value is reached. If reload
+ * flag was set, timer will also reset itself upon reference value, otherwise
+ * it continues to increment.
+ *
+ * The _exact_ bit in the function name states that this function will not
+ * crop precision of the "usec" argument, thus usec is limited to 16 bits
+ * (single timer width).
+ */
+int gtm_set_exact_timer16(struct gtm_timer *tmr, u16 usec, bool reload)
+{
+ /* quite obvious, frequency which is enough for µSec precision */
+ const int freq = 1000000;
+
+ /*
+ * We can lower the frequency (and probably power consumption) by
+ * dividing both frequency and usec by 2 until there is no remainder.
+ * But we won't bother with this unless savings are measured, so just
+ * run the timer as is.
+ */
+
+ return gtm_set_ref_timer16(tmr, freq, usec, reload);
+}
+EXPORT_SYMBOL(gtm_set_exact_timer16);
+
+/**
+ * gtm_stop_timer16 - stop single timer
+ * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer
+ * Context: any
+ *
+ * This function simply stops the GTM timer.
+ */
+void gtm_stop_timer16(struct gtm_timer *tmr)
+{
+ struct gtm *gtm = tmr->gtm;
+ int num = tmr - &gtm->timers[0];
+ unsigned long flags;
+
+ spin_lock_irqsave(&gtm->lock, flags);
+
+ setbits8(tmr->gtcfr, GTCFR_STP(num));
+ out_be16(tmr->gtevr, 0xFFFF);
+
+ spin_unlock_irqrestore(&gtm->lock, flags);
+}
+EXPORT_SYMBOL(gtm_stop_timer16);
+
+/**
+ * gtm_ack_timer16 - acknowledge timer event (free-run timers only)
+ * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer
+ * @events: events mask to ack
+ * Context: any
+ *
+ * Thus function used to acknowledge timer interrupt event, use it inside the
+ * interrupt handler.
+ */
+void gtm_ack_timer16(struct gtm_timer *tmr, u16 events)
+{
+ out_be16(tmr->gtevr, events);
+}
+EXPORT_SYMBOL(gtm_ack_timer16);
+
+static void __init gtm_set_shortcuts(struct device_node *np,
+ struct gtm_timer *timers,
+ struct gtm_timers_regs __iomem *regs)
+{
+ /*
+ * Yeah, I don't like this either, but timers' registers a bit messed,
+ * so we have to provide shortcuts to write timer independent code.
+ * Alternative option is to create gt*() accessors, but that will be
+ * even uglier and cryptic.
+ */
+ timers[0].gtcfr = &regs->gtcfr1;
+ timers[0].gtmdr = &regs->gtmdr1;
+ timers[0].gtcnr = &regs->gtcnr1;
+ timers[0].gtrfr = &regs->gtrfr1;
+ timers[0].gtevr = &regs->gtevr1;
+
+ timers[1].gtcfr = &regs->gtcfr1;
+ timers[1].gtmdr = &regs->gtmdr2;
+ timers[1].gtcnr = &regs->gtcnr2;
+ timers[1].gtrfr = &regs->gtrfr2;
+ timers[1].gtevr = &regs->gtevr2;
+
+ timers[2].gtcfr = &regs->gtcfr2;
+ timers[2].gtmdr = &regs->gtmdr3;
+ timers[2].gtcnr = &regs->gtcnr3;
+ timers[2].gtrfr = &regs->gtrfr3;
+ timers[2].gtevr = &regs->gtevr3;
+
+ timers[3].gtcfr = &regs->gtcfr2;
+ timers[3].gtmdr = &regs->gtmdr4;
+ timers[3].gtcnr = &regs->gtcnr4;
+ timers[3].gtrfr = &regs->gtrfr4;
+ timers[3].gtevr = &regs->gtevr4;
+
+ /* CPM2 doesn't have primary prescaler */
+ if (!of_device_is_compatible(np, "fsl,cpm2-gtm")) {
+ timers[0].gtpsr = &regs->gtpsr1;
+ timers[1].gtpsr = &regs->gtpsr2;
+ timers[2].gtpsr = &regs->gtpsr3;
+ timers[3].gtpsr = &regs->gtpsr4;
+ }
+}
+
+static int __init fsl_gtm_init(void)
+{
+ struct device_node *np;
+
+ for_each_compatible_node(np, NULL, "fsl,gtm") {
+ int i;
+ struct gtm *gtm;
+ const u32 *clock;
+ int size;
+
+ gtm = kzalloc(sizeof(*gtm), GFP_KERNEL);
+ if (!gtm) {
+ pr_err("%s: unable to allocate memory\n",
+ np->full_name);
+ continue;
+ }
+
+ spin_lock_init(&gtm->lock);
+
+ clock = of_get_property(np, "clock-frequency", &size);
+ if (!clock || size != sizeof(*clock)) {
+ pr_err("%s: no clock-frequency\n", np->full_name);
+ goto err;
+ }
+ gtm->clock = *clock;
+
+ for (i = 0; i < ARRAY_SIZE(gtm->timers); i++) {
+ unsigned int irq;
+
+ irq = irq_of_parse_and_map(np, i);
+ if (irq == NO_IRQ) {
+ pr_err("%s: not enough interrupts specified\n",
+ np->full_name);
+ goto err;
+ }
+ gtm->timers[i].irq = irq;
+ gtm->timers[i].gtm = gtm;
+ }
+
+ gtm->regs = of_iomap(np, 0);
+ if (!gtm->regs) {
+ pr_err("%s: unable to iomap registers\n",
+ np->full_name);
+ goto err;
+ }
+
+ gtm_set_shortcuts(np, gtm->timers, gtm->regs);
+ list_add(&gtm->list_node, &gtms);
+
+ /* We don't want to lose the node and its ->data */
+ np->data = gtm;
+ of_node_get(np);
+
+ continue;
+err:
+ kfree(gtm);
+ }
+ return 0;
+}
+arch_initcall(fsl_gtm_init);
diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c
new file mode 100644
index 00000000000..d631022ffb4
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_lbc.c
@@ -0,0 +1,410 @@
+/*
+ * Freescale LBC and UPM routines.
+ *
+ * Copyright © 2007-2008 MontaVista Software, Inc.
+ * Copyright © 2010 Freescale Semiconductor
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ * Author: Jack Lan <Jack.Lan@freescale.com>
+ * Author: Roy Zang <tie-fei.zang@freescale.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 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
+#include <asm/prom.h>
+#include <asm/fsl_lbc.h>
+
+static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
+struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
+EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
+
+/**
+ * fsl_lbc_addr - convert the base address
+ * @addr_base: base address of the memory bank
+ *
+ * This function converts a base address of lbc into the right format for the
+ * BR register. If the SOC has eLBC then it returns 32bit physical address
+ * else it convers a 34bit local bus physical address to correct format of
+ * 32bit address for BR register (Example: MPC8641).
+ */
+u32 fsl_lbc_addr(phys_addr_t addr_base)
+{
+ struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node;
+ u32 addr = addr_base & 0xffff8000;
+
+ if (of_device_is_compatible(np, "fsl,elbc"))
+ return addr;
+
+ return addr | ((addr_base & 0x300000000ull) >> 19);
+}
+EXPORT_SYMBOL(fsl_lbc_addr);
+
+/**
+ * fsl_lbc_find - find Localbus bank
+ * @addr_base: base address of the memory bank
+ *
+ * This function walks LBC banks comparing "Base address" field of the BR
+ * registers with the supplied addr_base argument. When bases match this
+ * function returns bank number (starting with 0), otherwise it returns
+ * appropriate errno value.
+ */
+int fsl_lbc_find(phys_addr_t addr_base)
+{
+ int i;
+ struct fsl_lbc_regs __iomem *lbc;
+
+ if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+ return -ENODEV;
+
+ lbc = fsl_lbc_ctrl_dev->regs;
+ for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
+ u32 br = in_be32(&lbc->bank[i].br);
+ u32 or = in_be32(&lbc->bank[i].or);
+
+ if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base))
+ return i;
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(fsl_lbc_find);
+
+/**
+ * fsl_upm_find - find pre-programmed UPM via base address
+ * @addr_base: base address of the memory bank controlled by the UPM
+ * @upm: pointer to the allocated fsl_upm structure
+ *
+ * This function fills fsl_upm structure so you can use it with the rest of
+ * UPM API. On success this function returns 0, otherwise it returns
+ * appropriate errno value.
+ */
+int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm)
+{
+ int bank;
+ u32 br;
+ struct fsl_lbc_regs __iomem *lbc;
+
+ bank = fsl_lbc_find(addr_base);
+ if (bank < 0)
+ return bank;
+
+ if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+ return -ENODEV;
+
+ lbc = fsl_lbc_ctrl_dev->regs;
+ br = in_be32(&lbc->bank[bank].br);
+
+ switch (br & BR_MSEL) {
+ case BR_MS_UPMA:
+ upm->mxmr = &lbc->mamr;
+ break;
+ case BR_MS_UPMB:
+ upm->mxmr = &lbc->mbmr;
+ break;
+ case BR_MS_UPMC:
+ upm->mxmr = &lbc->mcmr;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (br & BR_PS) {
+ case BR_PS_8:
+ upm->width = 8;
+ break;
+ case BR_PS_16:
+ upm->width = 16;
+ break;
+ case BR_PS_32:
+ upm->width = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(fsl_upm_find);
+
+/**
+ * fsl_upm_run_pattern - actually run an UPM pattern
+ * @upm: pointer to the fsl_upm structure obtained via fsl_upm_find
+ * @io_base: remapped pointer to where memory access should happen
+ * @mar: MAR register content during pattern execution
+ *
+ * This function triggers dummy write to the memory specified by the io_base,
+ * thus UPM pattern actually executed. Note that mar usage depends on the
+ * pre-programmed AMX bits in the UPM RAM.
+ */
+int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+ return -ENODEV;
+
+ spin_lock_irqsave(&fsl_lbc_lock, flags);
+
+ out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
+
+ switch (upm->width) {
+ case 8:
+ out_8(io_base, 0x0);
+ break;
+ case 16:
+ out_be16(io_base, 0x0);
+ break;
+ case 32:
+ out_be32(io_base, 0x0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ spin_unlock_irqrestore(&fsl_lbc_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(fsl_upm_run_pattern);
+
+static int fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl,
+ struct device_node *node)
+{
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+
+ /* clear event registers */
+ setbits32(&lbc->ltesr, LTESR_CLEAR);
+ out_be32(&lbc->lteatr, 0);
+ out_be32(&lbc->ltear, 0);
+ out_be32(&lbc->lteccr, LTECCR_CLEAR);
+ out_be32(&lbc->ltedr, LTEDR_ENABLE);
+
+ /* Set the monitor timeout value to the maximum for erratum A001 */
+ if (of_device_is_compatible(node, "fsl,elbc"))
+ clrsetbits_be32(&lbc->lbcr, LBCR_BMT, LBCR_BMTPS);
+
+ return 0;
+}
+
+/*
+ * NOTE: This interrupt is used to report localbus events of various kinds,
+ * such as transaction errors on the chipselects.
+ */
+
+static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
+{
+ struct fsl_lbc_ctrl *ctrl = data;
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+ u32 status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fsl_lbc_lock, flags);
+ status = in_be32(&lbc->ltesr);
+ if (!status) {
+ spin_unlock_irqrestore(&fsl_lbc_lock, flags);
+ return IRQ_NONE;
+ }
+
+ out_be32(&lbc->ltesr, LTESR_CLEAR);
+ out_be32(&lbc->lteatr, 0);
+ out_be32(&lbc->ltear, 0);
+ ctrl->irq_status = status;
+
+ if (status & LTESR_BM)
+ dev_err(ctrl->dev, "Local bus monitor time-out: "
+ "LTESR 0x%08X\n", status);
+ if (status & LTESR_WP)
+ dev_err(ctrl->dev, "Write protect error: "
+ "LTESR 0x%08X\n", status);
+ if (status & LTESR_ATMW)
+ dev_err(ctrl->dev, "Atomic write error: "
+ "LTESR 0x%08X\n", status);
+ if (status & LTESR_ATMR)
+ dev_err(ctrl->dev, "Atomic read error: "
+ "LTESR 0x%08X\n", status);
+ if (status & LTESR_CS)
+ dev_err(ctrl->dev, "Chip select error: "
+ "LTESR 0x%08X\n", status);
+ if (status & LTESR_UPM)
+ ;
+ if (status & LTESR_FCT) {
+ dev_err(ctrl->dev, "FCM command time-out: "
+ "LTESR 0x%08X\n", status);
+ smp_wmb();
+ wake_up(&ctrl->irq_wait);
+ }
+ if (status & LTESR_PAR) {
+ dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
+ "LTESR 0x%08X\n", status);
+ smp_wmb();
+ wake_up(&ctrl->irq_wait);
+ }
+ if (status & LTESR_CC) {
+ smp_wmb();
+ wake_up(&ctrl->irq_wait);
+ }
+ if (status & ~LTESR_MASK)
+ dev_err(ctrl->dev, "Unknown error: "
+ "LTESR 0x%08X\n", status);
+ spin_unlock_irqrestore(&fsl_lbc_lock, flags);
+ return IRQ_HANDLED;
+}
+
+/*
+ * fsl_lbc_ctrl_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code allocates all of
+ * the resources needed for the controller only. The
+ * resources for the NAND banks themselves are allocated
+ * in the chip probe function.
+*/
+
+static int fsl_lbc_ctrl_probe(struct platform_device *dev)
+{
+ int ret;
+
+ if (!dev->dev.of_node) {
+ dev_err(&dev->dev, "Device OF-Node is NULL");
+ return -EFAULT;
+ }
+
+ fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
+ if (!fsl_lbc_ctrl_dev)
+ return -ENOMEM;
+
+ dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
+
+ spin_lock_init(&fsl_lbc_ctrl_dev->lock);
+ init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
+
+ fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
+ if (!fsl_lbc_ctrl_dev->regs) {
+ dev_err(&dev->dev, "failed to get memory region\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ fsl_lbc_ctrl_dev->irq[0] = irq_of_parse_and_map(dev->dev.of_node, 0);
+ if (!fsl_lbc_ctrl_dev->irq[0]) {
+ dev_err(&dev->dev, "failed to get irq resource\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ fsl_lbc_ctrl_dev->dev = &dev->dev;
+
+ ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev, dev->dev.of_node);
+ if (ret < 0)
+ goto err;
+
+ ret = request_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_irq, 0,
+ "fsl-lbc", fsl_lbc_ctrl_dev);
+ if (ret != 0) {
+ dev_err(&dev->dev, "failed to install irq (%d)\n",
+ fsl_lbc_ctrl_dev->irq[0]);
+ ret = fsl_lbc_ctrl_dev->irq[0];
+ goto err;
+ }
+
+ fsl_lbc_ctrl_dev->irq[1] = irq_of_parse_and_map(dev->dev.of_node, 1);
+ if (fsl_lbc_ctrl_dev->irq[1]) {
+ ret = request_irq(fsl_lbc_ctrl_dev->irq[1], fsl_lbc_ctrl_irq,
+ IRQF_SHARED, "fsl-lbc-err", fsl_lbc_ctrl_dev);
+ if (ret) {
+ dev_err(&dev->dev, "failed to install irq (%d)\n",
+ fsl_lbc_ctrl_dev->irq[1]);
+ ret = fsl_lbc_ctrl_dev->irq[1];
+ goto err1;
+ }
+ }
+
+ /* Enable interrupts for any detected events */
+ out_be32(&fsl_lbc_ctrl_dev->regs->lteir, LTEIR_ENABLE);
+
+ return 0;
+
+err1:
+ free_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_dev);
+err:
+ iounmap(fsl_lbc_ctrl_dev->regs);
+ kfree(fsl_lbc_ctrl_dev);
+ fsl_lbc_ctrl_dev = NULL;
+ return ret;
+}
+
+#ifdef CONFIG_SUSPEND
+
+/* save lbc registers */
+static int fsl_lbc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct fsl_lbc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+
+ ctrl->saved_regs = kmalloc(sizeof(struct fsl_lbc_regs), GFP_KERNEL);
+ if (!ctrl->saved_regs)
+ return -ENOMEM;
+
+ _memcpy_fromio(ctrl->saved_regs, lbc, sizeof(struct fsl_lbc_regs));
+ return 0;
+}
+
+/* restore lbc registers */
+static int fsl_lbc_resume(struct platform_device *pdev)
+{
+ struct fsl_lbc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+
+ if (ctrl->saved_regs) {
+ _memcpy_toio(lbc, ctrl->saved_regs,
+ sizeof(struct fsl_lbc_regs));
+ kfree(ctrl->saved_regs);
+ ctrl->saved_regs = NULL;
+ }
+ return 0;
+}
+#endif /* CONFIG_SUSPEND */
+
+static const struct of_device_id fsl_lbc_match[] = {
+ { .compatible = "fsl,elbc", },
+ { .compatible = "fsl,pq3-localbus", },
+ { .compatible = "fsl,pq2-localbus", },
+ { .compatible = "fsl,pq2pro-localbus", },
+ {},
+};
+
+static struct platform_driver fsl_lbc_ctrl_driver = {
+ .driver = {
+ .name = "fsl-lbc",
+ .of_match_table = fsl_lbc_match,
+ },
+ .probe = fsl_lbc_ctrl_probe,
+#ifdef CONFIG_SUSPEND
+ .suspend = fsl_lbc_suspend,
+ .resume = fsl_lbc_resume,
+#endif
+};
+
+static int __init fsl_lbc_init(void)
+{
+ return platform_driver_register(&fsl_lbc_ctrl_driver);
+}
+module_init(fsl_lbc_init);
diff --git a/arch/powerpc/sysdev/fsl_mpic_err.c b/arch/powerpc/sysdev/fsl_mpic_err.c
new file mode 100644
index 00000000000..b83f32562a3
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_mpic_err.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *
+ * Author: Varun Sethi <varun.sethi@freescale.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; version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mpic.h>
+
+#include "mpic.h"
+
+#define MPIC_ERR_INT_BASE 0x3900
+#define MPIC_ERR_INT_EISR 0x0000
+#define MPIC_ERR_INT_EIMR 0x0010
+
+static inline u32 mpic_fsl_err_read(u32 __iomem *base, unsigned int err_reg)
+{
+ return in_be32(base + (err_reg >> 2));
+}
+
+static inline void mpic_fsl_err_write(u32 __iomem *base, u32 value)
+{
+ out_be32(base + (MPIC_ERR_INT_EIMR >> 2), value);
+}
+
+static void fsl_mpic_mask_err(struct irq_data *d)
+{
+ u32 eimr;
+ struct mpic *mpic = irq_data_get_irq_chip_data(d);
+ unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0];
+
+ eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR);
+ eimr |= (1 << (31 - src));
+ mpic_fsl_err_write(mpic->err_regs, eimr);
+}
+
+static void fsl_mpic_unmask_err(struct irq_data *d)
+{
+ u32 eimr;
+ struct mpic *mpic = irq_data_get_irq_chip_data(d);
+ unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0];
+
+ eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR);
+ eimr &= ~(1 << (31 - src));
+ mpic_fsl_err_write(mpic->err_regs, eimr);
+}
+
+static struct irq_chip fsl_mpic_err_chip = {
+ .irq_disable = fsl_mpic_mask_err,
+ .irq_mask = fsl_mpic_mask_err,
+ .irq_unmask = fsl_mpic_unmask_err,
+};
+
+int mpic_setup_error_int(struct mpic *mpic, int intvec)
+{
+ int i;
+
+ mpic->err_regs = ioremap(mpic->paddr + MPIC_ERR_INT_BASE, 0x1000);
+ if (!mpic->err_regs) {
+ pr_err("could not map mpic error registers\n");
+ return -ENOMEM;
+ }
+ mpic->hc_err = fsl_mpic_err_chip;
+ mpic->hc_err.name = mpic->name;
+ mpic->flags |= MPIC_FSL_HAS_EIMR;
+ /* allocate interrupt vectors for error interrupts */
+ for (i = MPIC_MAX_ERR - 1; i >= 0; i--)
+ mpic->err_int_vecs[i] = --intvec;
+
+ return 0;
+}
+
+int mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw)
+{
+ if ((mpic->flags & MPIC_FSL_HAS_EIMR) &&
+ (hw >= mpic->err_int_vecs[0] &&
+ hw <= mpic->err_int_vecs[MPIC_MAX_ERR - 1])) {
+ WARN_ON(mpic->flags & MPIC_SECONDARY);
+
+ pr_debug("mpic: mapping as Error Interrupt\n");
+ irq_set_chip_data(virq, mpic);
+ irq_set_chip_and_handler(virq, &mpic->hc_err,
+ handle_level_irq);
+ return 1;
+ }
+
+ return 0;
+}
+
+static irqreturn_t fsl_error_int_handler(int irq, void *data)
+{
+ struct mpic *mpic = (struct mpic *) data;
+ u32 eisr, eimr;
+ int errint;
+ unsigned int cascade_irq;
+
+ eisr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EISR);
+ eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR);
+
+ if (!(eisr & ~eimr))
+ return IRQ_NONE;
+
+ while (eisr) {
+ errint = __builtin_clz(eisr);
+ cascade_irq = irq_linear_revmap(mpic->irqhost,
+ mpic->err_int_vecs[errint]);
+ WARN_ON(cascade_irq == NO_IRQ);
+ if (cascade_irq != NO_IRQ) {
+ generic_handle_irq(cascade_irq);
+ } else {
+ eimr |= 1 << (31 - errint);
+ mpic_fsl_err_write(mpic->err_regs, eimr);
+ }
+ eisr &= ~(1 << (31 - errint));
+ }
+
+ return IRQ_HANDLED;
+}
+
+void mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum)
+{
+ unsigned int virq;
+ int ret;
+
+ virq = irq_create_mapping(mpic->irqhost, irqnum);
+ if (virq == NO_IRQ) {
+ pr_err("Error interrupt setup failed\n");
+ return;
+ }
+
+ /* Mask all error interrupts */
+ mpic_fsl_err_write(mpic->err_regs, ~0);
+
+ ret = request_irq(virq, fsl_error_int_handler, IRQF_NO_THREAD,
+ "mpic-error-int", mpic);
+ if (ret)
+ pr_err("Failed to register error interrupt handler\n");
+}
diff --git a/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c
new file mode 100644
index 00000000000..1707bf04dec
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c
@@ -0,0 +1,161 @@
+/*
+ * MPIC timer wakeup driver
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ *
+ * 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <asm/mpic_timer.h>
+#include <asm/mpic.h>
+
+struct fsl_mpic_timer_wakeup {
+ struct mpic_timer *timer;
+ struct work_struct free_work;
+};
+
+static struct fsl_mpic_timer_wakeup *fsl_wakeup;
+static DEFINE_MUTEX(sysfs_lock);
+
+static void fsl_free_resource(struct work_struct *ws)
+{
+ struct fsl_mpic_timer_wakeup *wakeup =
+ container_of(ws, struct fsl_mpic_timer_wakeup, free_work);
+
+ mutex_lock(&sysfs_lock);
+
+ if (wakeup->timer) {
+ disable_irq_wake(wakeup->timer->irq);
+ mpic_free_timer(wakeup->timer);
+ }
+
+ wakeup->timer = NULL;
+ mutex_unlock(&sysfs_lock);
+}
+
+static irqreturn_t fsl_mpic_timer_irq(int irq, void *dev_id)
+{
+ struct fsl_mpic_timer_wakeup *wakeup = dev_id;
+
+ schedule_work(&wakeup->free_work);
+
+ return wakeup->timer ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static ssize_t fsl_timer_wakeup_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct timeval interval;
+ int val = 0;
+
+ mutex_lock(&sysfs_lock);
+ if (fsl_wakeup->timer) {
+ mpic_get_remain_time(fsl_wakeup->timer, &interval);
+ val = interval.tv_sec + 1;
+ }
+ mutex_unlock(&sysfs_lock);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t fsl_timer_wakeup_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct timeval interval;
+ int ret;
+
+ interval.tv_usec = 0;
+ if (kstrtol(buf, 0, &interval.tv_sec))
+ return -EINVAL;
+
+ mutex_lock(&sysfs_lock);
+
+ if (fsl_wakeup->timer) {
+ disable_irq_wake(fsl_wakeup->timer->irq);
+ mpic_free_timer(fsl_wakeup->timer);
+ fsl_wakeup->timer = NULL;
+ }
+
+ if (!interval.tv_sec) {
+ mutex_unlock(&sysfs_lock);
+ return count;
+ }
+
+ fsl_wakeup->timer = mpic_request_timer(fsl_mpic_timer_irq,
+ fsl_wakeup, &interval);
+ if (!fsl_wakeup->timer) {
+ mutex_unlock(&sysfs_lock);
+ return -EINVAL;
+ }
+
+ ret = enable_irq_wake(fsl_wakeup->timer->irq);
+ if (ret) {
+ mpic_free_timer(fsl_wakeup->timer);
+ fsl_wakeup->timer = NULL;
+ mutex_unlock(&sysfs_lock);
+
+ return ret;
+ }
+
+ mpic_start_timer(fsl_wakeup->timer);
+
+ mutex_unlock(&sysfs_lock);
+
+ return count;
+}
+
+static struct device_attribute mpic_attributes = __ATTR(timer_wakeup, 0644,
+ fsl_timer_wakeup_show, fsl_timer_wakeup_store);
+
+static int __init fsl_wakeup_sys_init(void)
+{
+ int ret;
+
+ fsl_wakeup = kzalloc(sizeof(struct fsl_mpic_timer_wakeup), GFP_KERNEL);
+ if (!fsl_wakeup)
+ return -ENOMEM;
+
+ INIT_WORK(&fsl_wakeup->free_work, fsl_free_resource);
+
+ ret = device_create_file(mpic_subsys.dev_root, &mpic_attributes);
+ if (ret)
+ kfree(fsl_wakeup);
+
+ return ret;
+}
+
+static void __exit fsl_wakeup_sys_exit(void)
+{
+ device_remove_file(mpic_subsys.dev_root, &mpic_attributes);
+
+ mutex_lock(&sysfs_lock);
+
+ if (fsl_wakeup->timer) {
+ disable_irq_wake(fsl_wakeup->timer->irq);
+ mpic_free_timer(fsl_wakeup->timer);
+ }
+
+ kfree(fsl_wakeup);
+
+ mutex_unlock(&sysfs_lock);
+}
+
+module_init(fsl_wakeup_sys_init);
+module_exit(fsl_wakeup_sys_exit);
+
+MODULE_DESCRIPTION("Freescale MPIC global timer wakeup driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Wang Dongsheng <dongsheng.wang@freescale.com>");
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
new file mode 100644
index 00000000000..77efbaec7b9
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_msi.c
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2007-2011 Freescale Semiconductor, Inc.
+ *
+ * Author: Tony Li <tony.li@freescale.com>
+ * Jason Jin <Jason.jin@freescale.com>
+ *
+ * The hwirq alloc and free code reuse from sysdev/mpic_msi.c
+ *
+ * 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; version 2 of the
+ * License.
+ *
+ */
+#include <linux/irq.h>
+#include <linux/bootmem.h>
+#include <linux/msi.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/of_platform.h>
+#include <sysdev/fsl_soc.h>
+#include <asm/prom.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+#include <asm/mpic.h>
+#include <asm/fsl_hcalls.h>
+
+#include "fsl_msi.h"
+#include "fsl_pci.h"
+
+#define MSIIR_OFFSET_MASK 0xfffff
+#define MSIIR_IBS_SHIFT 0
+#define MSIIR_SRS_SHIFT 5
+#define MSIIR1_IBS_SHIFT 4
+#define MSIIR1_SRS_SHIFT 0
+#define MSI_SRS_MASK 0xf
+#define MSI_IBS_MASK 0x1f
+
+#define msi_hwirq(msi, msir_index, intr_index) \
+ ((msir_index) << (msi)->srs_shift | \
+ ((intr_index) << (msi)->ibs_shift))
+
+static LIST_HEAD(msi_head);
+
+struct fsl_msi_feature {
+ u32 fsl_pic_ip;
+ u32 msiir_offset; /* Offset of MSIIR, relative to start of MSIR bank */
+};
+
+struct fsl_msi_cascade_data {
+ struct fsl_msi *msi_data;
+ int index;
+};
+
+static inline u32 fsl_msi_read(u32 __iomem *base, unsigned int reg)
+{
+ return in_be32(base + (reg >> 2));
+}
+
+/*
+ * We do not need this actually. The MSIR register has been read once
+ * in the cascade interrupt. So, this MSI interrupt has been acked
+*/
+static void fsl_msi_end_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip fsl_msi_chip = {
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+ .irq_ack = fsl_msi_end_irq,
+ .name = "FSL-MSI",
+};
+
+static int fsl_msi_host_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct fsl_msi *msi_data = h->host_data;
+ struct irq_chip *chip = &fsl_msi_chip;
+
+ irq_set_status_flags(virq, IRQ_TYPE_EDGE_FALLING);
+
+ irq_set_chip_data(virq, msi_data);
+ irq_set_chip_and_handler(virq, chip, handle_edge_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops fsl_msi_host_ops = {
+ .map = fsl_msi_host_map,
+};
+
+static int fsl_msi_init_allocator(struct fsl_msi *msi_data)
+{
+ int rc, hwirq;
+
+ rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS_MAX,
+ msi_data->irqhost->of_node);
+ if (rc)
+ return rc;
+
+ /*
+ * Reserve all the hwirqs
+ * The available hwirqs will be released in fsl_msi_setup_hwirq()
+ */
+ for (hwirq = 0; hwirq < NR_MSI_IRQS_MAX; hwirq++)
+ msi_bitmap_reserve_hwirq(&msi_data->bitmap, hwirq);
+
+ return 0;
+}
+
+static int fsl_msi_check_device(struct pci_dev *pdev, int nvec, int type)
+{
+ if (type == PCI_CAP_ID_MSIX)
+ pr_debug("fslmsi: MSI-X untested, trying anyway.\n");
+
+ return 0;
+}
+
+static void fsl_teardown_msi_irqs(struct pci_dev *pdev)
+{
+ struct msi_desc *entry;
+ struct fsl_msi *msi_data;
+
+ list_for_each_entry(entry, &pdev->msi_list, list) {
+ if (entry->irq == NO_IRQ)
+ continue;
+ msi_data = irq_get_chip_data(entry->irq);
+ irq_set_msi_desc(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap,
+ virq_to_hw(entry->irq), 1);
+ irq_dispose_mapping(entry->irq);
+ }
+
+ return;
+}
+
+static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq,
+ struct msi_msg *msg,
+ struct fsl_msi *fsl_msi_data)
+{
+ struct fsl_msi *msi_data = fsl_msi_data;
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+ u64 address; /* Physical address of the MSIIR */
+ int len;
+ const __be64 *reg;
+
+ /* If the msi-address-64 property exists, then use it */
+ reg = of_get_property(hose->dn, "msi-address-64", &len);
+ if (reg && (len == sizeof(u64)))
+ address = be64_to_cpup(reg);
+ else
+ address = fsl_pci_immrbar_base(hose) + msi_data->msiir_offset;
+
+ msg->address_lo = lower_32_bits(address);
+ msg->address_hi = upper_32_bits(address);
+
+ msg->data = hwirq;
+
+ pr_debug("%s: allocated srs: %d, ibs: %d\n", __func__,
+ (hwirq >> msi_data->srs_shift) & MSI_SRS_MASK,
+ (hwirq >> msi_data->ibs_shift) & MSI_IBS_MASK);
+}
+
+static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
+{
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+ struct device_node *np;
+ phandle phandle = 0;
+ int rc, hwirq = -ENOMEM;
+ unsigned int virq;
+ struct msi_desc *entry;
+ struct msi_msg msg;
+ struct fsl_msi *msi_data;
+
+ /*
+ * If the PCI node has an fsl,msi property, then we need to use it
+ * to find the specific MSI.
+ */
+ np = of_parse_phandle(hose->dn, "fsl,msi", 0);
+ if (np) {
+ if (of_device_is_compatible(np, "fsl,mpic-msi") ||
+ of_device_is_compatible(np, "fsl,vmpic-msi"))
+ phandle = np->phandle;
+ else {
+ dev_err(&pdev->dev,
+ "node %s has an invalid fsl,msi phandle %u\n",
+ hose->dn->full_name, np->phandle);
+ return -EINVAL;
+ }
+ }
+
+ list_for_each_entry(entry, &pdev->msi_list, list) {
+ /*
+ * Loop over all the MSI devices until we find one that has an
+ * available interrupt.
+ */
+ list_for_each_entry(msi_data, &msi_head, list) {
+ /*
+ * If the PCI node has an fsl,msi property, then we
+ * restrict our search to the corresponding MSI node.
+ * The simplest way is to skip over MSI nodes with the
+ * wrong phandle. Under the Freescale hypervisor, this
+ * has the additional benefit of skipping over MSI
+ * nodes that are not mapped in the PAMU.
+ */
+ if (phandle && (phandle != msi_data->phandle))
+ continue;
+
+ hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
+ if (hwirq >= 0)
+ break;
+ }
+
+ if (hwirq < 0) {
+ rc = hwirq;
+ dev_err(&pdev->dev, "could not allocate MSI interrupt\n");
+ goto out_free;
+ }
+
+ virq = irq_create_mapping(msi_data->irqhost, hwirq);
+
+ if (virq == NO_IRQ) {
+ dev_err(&pdev->dev, "fail mapping hwirq %i\n", hwirq);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, 1);
+ rc = -ENOSPC;
+ goto out_free;
+ }
+ /* chip_data is msi_data via host->hostdata in host->map() */
+ irq_set_msi_desc(virq, entry);
+
+ fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data);
+ write_msi_msg(virq, &msg);
+ }
+ return 0;
+
+out_free:
+ /* free by the caller of this function */
+ return rc;
+}
+
+static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_data *idata = irq_desc_get_irq_data(desc);
+ unsigned int cascade_irq;
+ struct fsl_msi *msi_data;
+ int msir_index = -1;
+ u32 msir_value = 0;
+ u32 intr_index;
+ u32 have_shift = 0;
+ struct fsl_msi_cascade_data *cascade_data;
+
+ cascade_data = irq_get_handler_data(irq);
+ msi_data = cascade_data->msi_data;
+
+ raw_spin_lock(&desc->lock);
+ if ((msi_data->feature & FSL_PIC_IP_MASK) == FSL_PIC_IP_IPIC) {
+ if (chip->irq_mask_ack)
+ chip->irq_mask_ack(idata);
+ else {
+ chip->irq_mask(idata);
+ chip->irq_ack(idata);
+ }
+ }
+
+ if (unlikely(irqd_irq_inprogress(idata)))
+ goto unlock;
+
+ msir_index = cascade_data->index;
+
+ if (msir_index >= NR_MSI_REG_MAX)
+ cascade_irq = NO_IRQ;
+
+ irqd_set_chained_irq_inprogress(idata);
+ switch (msi_data->feature & FSL_PIC_IP_MASK) {
+ case FSL_PIC_IP_MPIC:
+ msir_value = fsl_msi_read(msi_data->msi_regs,
+ msir_index * 0x10);
+ break;
+ case FSL_PIC_IP_IPIC:
+ msir_value = fsl_msi_read(msi_data->msi_regs, msir_index * 0x4);
+ break;
+#ifdef CONFIG_EPAPR_PARAVIRT
+ case FSL_PIC_IP_VMPIC: {
+ unsigned int ret;
+ ret = fh_vmpic_get_msir(virq_to_hw(irq), &msir_value);
+ if (ret) {
+ pr_err("fsl-msi: fh_vmpic_get_msir() failed for "
+ "irq %u (ret=%u)\n", irq, ret);
+ msir_value = 0;
+ }
+ break;
+ }
+#endif
+ }
+
+ while (msir_value) {
+ intr_index = ffs(msir_value) - 1;
+
+ cascade_irq = irq_linear_revmap(msi_data->irqhost,
+ msi_hwirq(msi_data, msir_index,
+ intr_index + have_shift));
+ if (cascade_irq != NO_IRQ)
+ generic_handle_irq(cascade_irq);
+ have_shift += intr_index + 1;
+ msir_value = msir_value >> (intr_index + 1);
+ }
+ irqd_clr_chained_irq_inprogress(idata);
+
+ switch (msi_data->feature & FSL_PIC_IP_MASK) {
+ case FSL_PIC_IP_MPIC:
+ case FSL_PIC_IP_VMPIC:
+ chip->irq_eoi(idata);
+ break;
+ case FSL_PIC_IP_IPIC:
+ if (!irqd_irq_disabled(idata) && chip->irq_unmask)
+ chip->irq_unmask(idata);
+ break;
+ }
+unlock:
+ raw_spin_unlock(&desc->lock);
+}
+
+static int fsl_of_msi_remove(struct platform_device *ofdev)
+{
+ struct fsl_msi *msi = platform_get_drvdata(ofdev);
+ int virq, i;
+ struct fsl_msi_cascade_data *cascade_data;
+
+ if (msi->list.prev != NULL)
+ list_del(&msi->list);
+ for (i = 0; i < NR_MSI_REG_MAX; i++) {
+ virq = msi->msi_virqs[i];
+ if (virq != NO_IRQ) {
+ cascade_data = irq_get_handler_data(virq);
+ kfree(cascade_data);
+ irq_dispose_mapping(virq);
+ }
+ }
+ if (msi->bitmap.bitmap)
+ msi_bitmap_free(&msi->bitmap);
+ if ((msi->feature & FSL_PIC_IP_MASK) != FSL_PIC_IP_VMPIC)
+ iounmap(msi->msi_regs);
+ kfree(msi);
+
+ return 0;
+}
+
+static struct lock_class_key fsl_msi_irq_class;
+
+static int fsl_msi_setup_hwirq(struct fsl_msi *msi, struct platform_device *dev,
+ int offset, int irq_index)
+{
+ struct fsl_msi_cascade_data *cascade_data = NULL;
+ int virt_msir, i;
+
+ virt_msir = irq_of_parse_and_map(dev->dev.of_node, irq_index);
+ if (virt_msir == NO_IRQ) {
+ dev_err(&dev->dev, "%s: Cannot translate IRQ index %d\n",
+ __func__, irq_index);
+ return 0;
+ }
+
+ cascade_data = kzalloc(sizeof(struct fsl_msi_cascade_data), GFP_KERNEL);
+ if (!cascade_data) {
+ dev_err(&dev->dev, "No memory for MSI cascade data\n");
+ return -ENOMEM;
+ }
+ irq_set_lockdep_class(virt_msir, &fsl_msi_irq_class);
+ msi->msi_virqs[irq_index] = virt_msir;
+ cascade_data->index = offset;
+ cascade_data->msi_data = msi;
+ irq_set_handler_data(virt_msir, cascade_data);
+ irq_set_chained_handler(virt_msir, fsl_msi_cascade);
+
+ /* Release the hwirqs corresponding to this MSI register */
+ for (i = 0; i < IRQS_PER_MSI_REG; i++)
+ msi_bitmap_free_hwirqs(&msi->bitmap,
+ msi_hwirq(msi, offset, i), 1);
+
+ return 0;
+}
+
+static const struct of_device_id fsl_of_msi_ids[];
+static int fsl_of_msi_probe(struct platform_device *dev)
+{
+ const struct of_device_id *match;
+ struct fsl_msi *msi;
+ struct resource res, msiir;
+ int err, i, j, irq_index, count;
+ const u32 *p;
+ const struct fsl_msi_feature *features;
+ int len;
+ u32 offset;
+
+ match = of_match_device(fsl_of_msi_ids, &dev->dev);
+ if (!match)
+ return -EINVAL;
+ features = match->data;
+
+ printk(KERN_DEBUG "Setting up Freescale MSI support\n");
+
+ msi = kzalloc(sizeof(struct fsl_msi), GFP_KERNEL);
+ if (!msi) {
+ dev_err(&dev->dev, "No memory for MSI structure\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(dev, msi);
+
+ msi->irqhost = irq_domain_add_linear(dev->dev.of_node,
+ NR_MSI_IRQS_MAX, &fsl_msi_host_ops, msi);
+
+ if (msi->irqhost == NULL) {
+ dev_err(&dev->dev, "No memory for MSI irqhost\n");
+ err = -ENOMEM;
+ goto error_out;
+ }
+
+ /*
+ * Under the Freescale hypervisor, the msi nodes don't have a 'reg'
+ * property. Instead, we use hypercalls to access the MSI.
+ */
+ if ((features->fsl_pic_ip & FSL_PIC_IP_MASK) != FSL_PIC_IP_VMPIC) {
+ err = of_address_to_resource(dev->dev.of_node, 0, &res);
+ if (err) {
+ dev_err(&dev->dev, "invalid resource for node %s\n",
+ dev->dev.of_node->full_name);
+ goto error_out;
+ }
+
+ msi->msi_regs = ioremap(res.start, resource_size(&res));
+ if (!msi->msi_regs) {
+ err = -ENOMEM;
+ dev_err(&dev->dev, "could not map node %s\n",
+ dev->dev.of_node->full_name);
+ goto error_out;
+ }
+ msi->msiir_offset =
+ features->msiir_offset + (res.start & 0xfffff);
+
+ /*
+ * First read the MSIIR/MSIIR1 offset from dts
+ * On failure use the hardcode MSIIR offset
+ */
+ if (of_address_to_resource(dev->dev.of_node, 1, &msiir))
+ msi->msiir_offset = features->msiir_offset +
+ (res.start & MSIIR_OFFSET_MASK);
+ else
+ msi->msiir_offset = msiir.start & MSIIR_OFFSET_MASK;
+ }
+
+ msi->feature = features->fsl_pic_ip;
+
+ /*
+ * Remember the phandle, so that we can match with any PCI nodes
+ * that have an "fsl,msi" property.
+ */
+ msi->phandle = dev->dev.of_node->phandle;
+
+ err = fsl_msi_init_allocator(msi);
+ if (err) {
+ dev_err(&dev->dev, "Error allocating MSI bitmap\n");
+ goto error_out;
+ }
+
+ p = of_get_property(dev->dev.of_node, "msi-available-ranges", &len);
+
+ if (of_device_is_compatible(dev->dev.of_node, "fsl,mpic-msi-v4.3")) {
+ msi->srs_shift = MSIIR1_SRS_SHIFT;
+ msi->ibs_shift = MSIIR1_IBS_SHIFT;
+ if (p)
+ dev_warn(&dev->dev, "%s: dose not support msi-available-ranges property\n",
+ __func__);
+
+ for (irq_index = 0; irq_index < NR_MSI_REG_MSIIR1;
+ irq_index++) {
+ err = fsl_msi_setup_hwirq(msi, dev,
+ irq_index, irq_index);
+ if (err)
+ goto error_out;
+ }
+ } else {
+ static const u32 all_avail[] =
+ { 0, NR_MSI_REG_MSIIR * IRQS_PER_MSI_REG };
+
+ msi->srs_shift = MSIIR_SRS_SHIFT;
+ msi->ibs_shift = MSIIR_IBS_SHIFT;
+
+ if (p && len % (2 * sizeof(u32)) != 0) {
+ dev_err(&dev->dev, "%s: Malformed msi-available-ranges property\n",
+ __func__);
+ err = -EINVAL;
+ goto error_out;
+ }
+
+ if (!p) {
+ p = all_avail;
+ len = sizeof(all_avail);
+ }
+
+ for (irq_index = 0, i = 0; i < len / (2 * sizeof(u32)); i++) {
+ if (p[i * 2] % IRQS_PER_MSI_REG ||
+ p[i * 2 + 1] % IRQS_PER_MSI_REG) {
+ pr_warn("%s: %s: msi available range of %u at %u is not IRQ-aligned\n",
+ __func__, dev->dev.of_node->full_name,
+ p[i * 2 + 1], p[i * 2]);
+ err = -EINVAL;
+ goto error_out;
+ }
+
+ offset = p[i * 2] / IRQS_PER_MSI_REG;
+ count = p[i * 2 + 1] / IRQS_PER_MSI_REG;
+
+ for (j = 0; j < count; j++, irq_index++) {
+ err = fsl_msi_setup_hwirq(msi, dev, offset + j,
+ irq_index);
+ if (err)
+ goto error_out;
+ }
+ }
+ }
+
+ list_add_tail(&msi->list, &msi_head);
+
+ /* The multiple setting ppc_md.setup_msi_irqs will not harm things */
+ if (!ppc_md.setup_msi_irqs) {
+ ppc_md.setup_msi_irqs = fsl_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs;
+ ppc_md.msi_check_device = fsl_msi_check_device;
+ } else if (ppc_md.setup_msi_irqs != fsl_setup_msi_irqs) {
+ dev_err(&dev->dev, "Different MSI driver already installed!\n");
+ err = -ENODEV;
+ goto error_out;
+ }
+ return 0;
+error_out:
+ fsl_of_msi_remove(dev);
+ return err;
+}
+
+static const struct fsl_msi_feature mpic_msi_feature = {
+ .fsl_pic_ip = FSL_PIC_IP_MPIC,
+ .msiir_offset = 0x140,
+};
+
+static const struct fsl_msi_feature ipic_msi_feature = {
+ .fsl_pic_ip = FSL_PIC_IP_IPIC,
+ .msiir_offset = 0x38,
+};
+
+static const struct fsl_msi_feature vmpic_msi_feature = {
+ .fsl_pic_ip = FSL_PIC_IP_VMPIC,
+ .msiir_offset = 0,
+};
+
+static const struct of_device_id fsl_of_msi_ids[] = {
+ {
+ .compatible = "fsl,mpic-msi",
+ .data = &mpic_msi_feature,
+ },
+ {
+ .compatible = "fsl,mpic-msi-v4.3",
+ .data = &mpic_msi_feature,
+ },
+ {
+ .compatible = "fsl,ipic-msi",
+ .data = &ipic_msi_feature,
+ },
+#ifdef CONFIG_EPAPR_PARAVIRT
+ {
+ .compatible = "fsl,vmpic-msi",
+ .data = &vmpic_msi_feature,
+ },
+#endif
+ {}
+};
+
+static struct platform_driver fsl_of_msi_driver = {
+ .driver = {
+ .name = "fsl-msi",
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_of_msi_ids,
+ },
+ .probe = fsl_of_msi_probe,
+ .remove = fsl_of_msi_remove,
+};
+
+static __init int fsl_of_msi_init(void)
+{
+ return platform_driver_register(&fsl_of_msi_driver);
+}
+
+subsys_initcall(fsl_of_msi_init);
diff --git a/arch/powerpc/sysdev/fsl_msi.h b/arch/powerpc/sysdev/fsl_msi.h
new file mode 100644
index 00000000000..df9aa9fe093
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_msi.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Tony Li <tony.li@freescale.com>
+ * Jason Jin <Jason.jin@freescale.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; version 2 of the
+ * License.
+ *
+ */
+#ifndef _POWERPC_SYSDEV_FSL_MSI_H
+#define _POWERPC_SYSDEV_FSL_MSI_H
+
+#include <linux/of.h>
+#include <asm/msi_bitmap.h>
+
+#define NR_MSI_REG_MSIIR 8 /* MSIIR can index 8 MSI registers */
+#define NR_MSI_REG_MSIIR1 16 /* MSIIR1 can index 16 MSI registers */
+#define NR_MSI_REG_MAX NR_MSI_REG_MSIIR1
+#define IRQS_PER_MSI_REG 32
+#define NR_MSI_IRQS_MAX (NR_MSI_REG_MAX * IRQS_PER_MSI_REG)
+
+#define FSL_PIC_IP_MASK 0x0000000F
+#define FSL_PIC_IP_MPIC 0x00000001
+#define FSL_PIC_IP_IPIC 0x00000002
+#define FSL_PIC_IP_VMPIC 0x00000003
+
+struct fsl_msi {
+ struct irq_domain *irqhost;
+
+ unsigned long cascade_irq;
+
+ u32 msiir_offset; /* Offset of MSIIR, relative to start of CCSR */
+ u32 ibs_shift; /* Shift of interrupt bit select */
+ u32 srs_shift; /* Shift of the shared interrupt register select */
+ void __iomem *msi_regs;
+ u32 feature;
+ int msi_virqs[NR_MSI_REG_MAX];
+
+ struct msi_bitmap bitmap;
+
+ struct list_head list; /* support multiple MSI banks */
+
+ phandle phandle;
+};
+
+#endif /* _POWERPC_SYSDEV_FSL_MSI_H */
+
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index bf13c2174a4..4bd091a0558 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -1,12 +1,16 @@
/*
- * MPC85xx/86xx PCI/PCIE support routing.
+ * MPC83xx/85xx/86xx PCI/PCIE support routing.
*
- * Copyright 2007 Freescale Semiconductor, Inc
+ * Copyright 2007-2012 Freescale Semiconductor, Inc.
+ * Copyright 2008-2009 MontaVista Software, Inc.
*
* Initial author: Xianghua Xiao <x.xiao@freescale.com>
* Recode: ZHANG WEI <wei.zhang@freescale.com>
* Rewrite the routing for Frescale PCI and PCI Express
* Roy Zang <tie-fei.zang@freescale.com>
+ * MPC83xx PCI-Express support:
+ * Tony Li <tony.li@freescale.com>
+ * Anton Vorontsov <avorontsov@ru.mvista.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
@@ -18,74 +22,384 @@
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/bootmem.h>
+#include <linux/memblock.h>
+#include <linux/log2.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/syscore_ops.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
+#include <asm/ppc-pci.h>
#include <asm/machdep.h>
+#include <asm/disassemble.h>
+#include <asm/ppc-opcode.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
+static int fsl_pcie_bus_fixup, is_mpc83xx_pci;
+
+static void quirk_fsl_pcie_early(struct pci_dev *dev)
+{
+ u8 hdr_type;
+
+ /* if we aren't a PCIe don't bother */
+ if (!pci_is_pcie(dev))
+ return;
+
+ /* if we aren't in host mode don't bother */
+ pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
+ if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE)
+ return;
+
+ dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+ fsl_pcie_bus_fixup = 1;
+ return;
+}
+
+static int fsl_indirect_read_config(struct pci_bus *, unsigned int,
+ int, int, u32 *);
+
+static int fsl_pcie_check_link(struct pci_controller *hose)
+{
+ u32 val = 0;
+
+ if (hose->indirect_type & PPC_INDIRECT_TYPE_FSL_CFG_REG_LINK) {
+ if (hose->ops->read == fsl_indirect_read_config) {
+ struct pci_bus bus;
+ bus.number = hose->first_busno;
+ bus.sysdata = hose;
+ bus.ops = hose->ops;
+ indirect_read_config(&bus, 0, PCIE_LTSSM, 4, &val);
+ } else
+ early_read_config_dword(hose, 0, 0, PCIE_LTSSM, &val);
+ if (val < PCIE_LTSSM_L0)
+ return 1;
+ } else {
+ struct ccsr_pci __iomem *pci = hose->private_data;
+ /* for PCIe IP rev 3.0 or greater use CSR0 for link state */
+ val = (in_be32(&pci->pex_csr0) & PEX_CSR0_LTSSM_MASK)
+ >> PEX_CSR0_LTSSM_SHIFT;
+ if (val != PEX_CSR0_LTSSM_L0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int fsl_indirect_read_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 *val)
+{
+ struct pci_controller *hose = pci_bus_to_host(bus);
+
+ if (fsl_pcie_check_link(hose))
+ hose->indirect_type |= PPC_INDIRECT_TYPE_NO_PCIE_LINK;
+ else
+ hose->indirect_type &= ~PPC_INDIRECT_TYPE_NO_PCIE_LINK;
+
+ return indirect_read_config(bus, devfn, offset, len, val);
+}
+
+#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
+
+static struct pci_ops fsl_indirect_pcie_ops =
+{
+ .read = fsl_indirect_read_config,
+ .write = indirect_write_config,
+};
+
+#define MAX_PHYS_ADDR_BITS 40
+static u64 pci64_dma_offset = 1ull << MAX_PHYS_ADDR_BITS;
+
+static int fsl_pci_dma_set_mask(struct device *dev, u64 dma_mask)
+{
+ if (!dev->dma_mask || !dma_supported(dev, dma_mask))
+ return -EIO;
+
+ /*
+ * Fixup PCI devices that are able to DMA to above the physical
+ * address width of the SoC such that we can address any internal
+ * SoC address from across PCI if needed
+ */
+ if ((dev_is_pci(dev)) &&
+ dma_mask >= DMA_BIT_MASK(MAX_PHYS_ADDR_BITS)) {
+ set_dma_ops(dev, &dma_direct_ops);
+ set_dma_offset(dev, pci64_dma_offset);
+ }
+
+ *dev->dma_mask = dma_mask;
+ return 0;
+}
+
+static int setup_one_atmu(struct ccsr_pci __iomem *pci,
+ unsigned int index, const struct resource *res,
+ resource_size_t offset)
+{
+ resource_size_t pci_addr = res->start - offset;
+ resource_size_t phys_addr = res->start;
+ resource_size_t size = resource_size(res);
+ u32 flags = 0x80044000; /* enable & mem R/W */
+ unsigned int i;
+
+ pr_debug("PCI MEM resource start 0x%016llx, size 0x%016llx.\n",
+ (u64)res->start, (u64)size);
+
+ if (res->flags & IORESOURCE_PREFETCH)
+ flags |= 0x10000000; /* enable relaxed ordering */
+
+ for (i = 0; size > 0; i++) {
+ unsigned int bits = min(ilog2(size),
+ __ffs(pci_addr | phys_addr));
+
+ if (index + i >= 5)
+ return -1;
+
+ out_be32(&pci->pow[index + i].potar, pci_addr >> 12);
+ out_be32(&pci->pow[index + i].potear, (u64)pci_addr >> 44);
+ out_be32(&pci->pow[index + i].powbar, phys_addr >> 12);
+ out_be32(&pci->pow[index + i].powar, flags | (bits - 1));
+
+ pci_addr += (resource_size_t)1U << bits;
+ phys_addr += (resource_size_t)1U << bits;
+ size -= (resource_size_t)1U << bits;
+ }
+
+ return i;
+}
+
/* atmu setup for fsl pci/pcie controller */
-void __init setup_pci_atmu(struct pci_controller *hose, struct resource *rsrc)
+static void setup_pci_atmu(struct pci_controller *hose)
{
- struct ccsr_pci __iomem *pci;
- int i;
+ struct ccsr_pci __iomem *pci = hose->private_data;
+ int i, j, n, mem_log, win_idx = 3, start_idx = 1, end_idx = 4;
+ u64 mem, sz, paddr_hi = 0;
+ u64 offset = 0, paddr_lo = ULLONG_MAX;
+ u32 pcicsrbar = 0, pcicsrbar_sz;
+ u32 piwar = PIWAR_EN | PIWAR_PF | PIWAR_TGI_LOCAL |
+ PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP;
+ const char *name = hose->dn->full_name;
+ const u64 *reg;
+ int len;
- pr_debug("PCI memory map start 0x%016llx, size 0x%016llx\n",
- (u64)rsrc->start, (u64)rsrc->end - (u64)rsrc->start + 1);
- pci = ioremap(rsrc->start, rsrc->end - rsrc->start + 1);
+ if (early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) {
+ if (in_be32(&pci->block_rev1) >= PCIE_IP_REV_2_2) {
+ win_idx = 2;
+ start_idx = 0;
+ end_idx = 3;
+ }
+ }
- /* Disable all windows (except powar0 since its ignored) */
+ /* Disable all windows (except powar0 since it's ignored) */
for(i = 1; i < 5; i++)
out_be32(&pci->pow[i].powar, 0);
- for(i = 0; i < 3; i++)
+ for (i = start_idx; i < end_idx; i++)
out_be32(&pci->piw[i].piwar, 0);
/* Setup outbound MEM window */
- for(i = 0; i < 3; i++)
- if (hose->mem_resources[i].flags & IORESOURCE_MEM){
- resource_size_t pci_addr_start =
- hose->mem_resources[i].start -
- hose->pci_mem_offset;
- pr_debug("PCI MEM resource start 0x%016llx, size 0x%016llx.\n",
- (u64)hose->mem_resources[i].start,
- (u64)hose->mem_resources[i].end
- - (u64)hose->mem_resources[i].start + 1);
- out_be32(&pci->pow[i+1].potar, (pci_addr_start >> 12));
- out_be32(&pci->pow[i+1].potear, 0);
- out_be32(&pci->pow[i+1].powbar,
- (hose->mem_resources[i].start >> 12));
- /* Enable, Mem R/W */
- out_be32(&pci->pow[i+1].powar, 0x80044000
- | (__ilog2(hose->mem_resources[i].end
- - hose->mem_resources[i].start + 1) - 1));
- }
+ for(i = 0, j = 1; i < 3; i++) {
+ if (!(hose->mem_resources[i].flags & IORESOURCE_MEM))
+ continue;
+
+ paddr_lo = min(paddr_lo, (u64)hose->mem_resources[i].start);
+ paddr_hi = max(paddr_hi, (u64)hose->mem_resources[i].end);
+
+ /* We assume all memory resources have the same offset */
+ offset = hose->mem_offset[i];
+ n = setup_one_atmu(pci, j, &hose->mem_resources[i], offset);
+
+ if (n < 0 || j >= 5) {
+ pr_err("Ran out of outbound PCI ATMUs for resource %d!\n", i);
+ hose->mem_resources[i].flags |= IORESOURCE_DISABLED;
+ } else
+ j += n;
+ }
/* Setup outbound IO window */
- if (hose->io_resource.flags & IORESOURCE_IO){
- pr_debug("PCI IO resource start 0x%016llx, size 0x%016llx, "
- "phy base 0x%016llx.\n",
- (u64)hose->io_resource.start,
- (u64)hose->io_resource.end - (u64)hose->io_resource.start + 1,
- (u64)hose->io_base_phys);
- out_be32(&pci->pow[i+1].potar, (hose->io_resource.start >> 12));
- out_be32(&pci->pow[i+1].potear, 0);
- out_be32(&pci->pow[i+1].powbar, (hose->io_base_phys >> 12));
- /* Enable, IO R/W */
- out_be32(&pci->pow[i+1].powar, 0x80088000
- | (__ilog2(hose->io_resource.end
- - hose->io_resource.start + 1) - 1));
+ if (hose->io_resource.flags & IORESOURCE_IO) {
+ if (j >= 5) {
+ pr_err("Ran out of outbound PCI ATMUs for IO resource\n");
+ } else {
+ pr_debug("PCI IO resource start 0x%016llx, size 0x%016llx, "
+ "phy base 0x%016llx.\n",
+ (u64)hose->io_resource.start,
+ (u64)resource_size(&hose->io_resource),
+ (u64)hose->io_base_phys);
+ out_be32(&pci->pow[j].potar, (hose->io_resource.start >> 12));
+ out_be32(&pci->pow[j].potear, 0);
+ out_be32(&pci->pow[j].powbar, (hose->io_base_phys >> 12));
+ /* Enable, IO R/W */
+ out_be32(&pci->pow[j].powar, 0x80088000
+ | (ilog2(hose->io_resource.end
+ - hose->io_resource.start + 1) - 1));
+ }
+ }
+
+ /* convert to pci address space */
+ paddr_hi -= offset;
+ paddr_lo -= offset;
+
+ if (paddr_hi == paddr_lo) {
+ pr_err("%s: No outbound window space\n", name);
+ return;
+ }
+
+ if (paddr_lo == 0) {
+ pr_err("%s: No space for inbound window\n", name);
+ return;
+ }
+
+ /* setup PCSRBAR/PEXCSRBAR */
+ early_write_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, 0xffffffff);
+ early_read_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, &pcicsrbar_sz);
+ pcicsrbar_sz = ~pcicsrbar_sz + 1;
+
+ if (paddr_hi < (0x100000000ull - pcicsrbar_sz) ||
+ (paddr_lo > 0x100000000ull))
+ pcicsrbar = 0x100000000ull - pcicsrbar_sz;
+ else
+ pcicsrbar = (paddr_lo - pcicsrbar_sz) & -pcicsrbar_sz;
+ early_write_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, pcicsrbar);
+
+ paddr_lo = min(paddr_lo, (u64)pcicsrbar);
+
+ pr_info("%s: PCICSRBAR @ 0x%x\n", name, pcicsrbar);
+
+ /* Setup inbound mem window */
+ mem = memblock_end_of_DRAM();
+
+ /*
+ * The msi-address-64 property, if it exists, indicates the physical
+ * address of the MSIIR register. Normally, this register is located
+ * inside CCSR, so the ATMU that covers all of CCSR is used. But if
+ * this property exists, then we normally need to create a new ATMU
+ * for it. For now, however, we cheat. The only entity that creates
+ * this property is the Freescale hypervisor, and the address is
+ * specified in the partition configuration. Typically, the address
+ * is located in the page immediately after the end of DDR. If so, we
+ * can avoid allocating a new ATMU by extending the DDR ATMU by one
+ * page.
+ */
+ reg = of_get_property(hose->dn, "msi-address-64", &len);
+ if (reg && (len == sizeof(u64))) {
+ u64 address = be64_to_cpup(reg);
+
+ if ((address >= mem) && (address < (mem + PAGE_SIZE))) {
+ pr_info("%s: extending DDR ATMU to cover MSIIR", name);
+ mem += PAGE_SIZE;
+ } else {
+ /* TODO: Create a new ATMU for MSIIR */
+ pr_warn("%s: msi-address-64 address of %llx is "
+ "unsupported\n", name, address);
+ }
+ }
+
+ sz = min(mem, paddr_lo);
+ mem_log = ilog2(sz);
+
+ /* PCIe can overmap inbound & outbound since RX & TX are separated */
+ if (early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) {
+ /* Size window to exact size if power-of-two or one size up */
+ if ((1ull << mem_log) != mem) {
+ mem_log++;
+ if ((1ull << mem_log) > mem)
+ pr_info("%s: Setting PCI inbound window "
+ "greater than memory size\n", name);
+ }
+
+ piwar |= ((mem_log - 1) & PIWAR_SZ_MASK);
+
+ /* Setup inbound memory window */
+ out_be32(&pci->piw[win_idx].pitar, 0x00000000);
+ out_be32(&pci->piw[win_idx].piwbar, 0x00000000);
+ out_be32(&pci->piw[win_idx].piwar, piwar);
+ win_idx--;
+
+ hose->dma_window_base_cur = 0x00000000;
+ hose->dma_window_size = (resource_size_t)sz;
+
+ /*
+ * if we have >4G of memory setup second PCI inbound window to
+ * let devices that are 64-bit address capable to work w/o
+ * SWIOTLB and access the full range of memory
+ */
+ if (sz != mem) {
+ mem_log = ilog2(mem);
+
+ /* Size window up if we dont fit in exact power-of-2 */
+ if ((1ull << mem_log) != mem)
+ mem_log++;
+
+ piwar = (piwar & ~PIWAR_SZ_MASK) | (mem_log - 1);
+
+ /* Setup inbound memory window */
+ out_be32(&pci->piw[win_idx].pitar, 0x00000000);
+ out_be32(&pci->piw[win_idx].piwbear,
+ pci64_dma_offset >> 44);
+ out_be32(&pci->piw[win_idx].piwbar,
+ pci64_dma_offset >> 12);
+ out_be32(&pci->piw[win_idx].piwar, piwar);
+
+ /*
+ * install our own dma_set_mask handler to fixup dma_ops
+ * and dma_offset
+ */
+ ppc_md.dma_set_mask = fsl_pci_dma_set_mask;
+
+ pr_info("%s: Setup 64-bit PCI DMA window\n", name);
+ }
+ } else {
+ u64 paddr = 0;
+
+ /* Setup inbound memory window */
+ out_be32(&pci->piw[win_idx].pitar, paddr >> 12);
+ out_be32(&pci->piw[win_idx].piwbar, paddr >> 12);
+ out_be32(&pci->piw[win_idx].piwar, (piwar | (mem_log - 1)));
+ win_idx--;
+
+ paddr += 1ull << mem_log;
+ sz -= 1ull << mem_log;
+
+ if (sz) {
+ mem_log = ilog2(sz);
+ piwar |= (mem_log - 1);
+
+ out_be32(&pci->piw[win_idx].pitar, paddr >> 12);
+ out_be32(&pci->piw[win_idx].piwbar, paddr >> 12);
+ out_be32(&pci->piw[win_idx].piwar, piwar);
+ win_idx--;
+
+ paddr += 1ull << mem_log;
+ }
+
+ hose->dma_window_base_cur = 0x00000000;
+ hose->dma_window_size = (resource_size_t)paddr;
}
- /* Setup 2G inbound Memory Window @ 1 */
- out_be32(&pci->piw[2].pitar, 0x00000000);
- out_be32(&pci->piw[2].piwbar,0x00000000);
- out_be32(&pci->piw[2].piwar, PIWAR_2G);
+ if (hose->dma_window_size < mem) {
+#ifdef CONFIG_SWIOTLB
+ ppc_swiotlb_enable = 1;
+#else
+ pr_err("%s: ERROR: Memory size exceeds PCI ATMU ability to "
+ "map - enable CONFIG_SWIOTLB to avoid dma errors.\n",
+ name);
+#endif
+ /* adjusting outbound windows could reclaim space in mem map */
+ if (paddr_hi < 0xffffffffull)
+ pr_warning("%s: WARNING: Outbound window cfg leaves "
+ "gaps in memory map. Adjusting the memory map "
+ "could reduce unnecessary bounce buffering.\n",
+ name);
+
+ pr_info("%s: DMA window size is 0x%llx\n", name,
+ (u64)hose->dma_window_size);
+ }
}
-void __init setup_pci_cmd(struct pci_controller *hose)
+static void __init setup_pci_cmd(struct pci_controller *hose)
{
u16 cmd;
int cap_x;
@@ -106,61 +420,59 @@ void __init setup_pci_cmd(struct pci_controller *hose)
}
}
-static int fsl_pcie_bus_fixup;
-
-static void __init quirk_fsl_pcie_header(struct pci_dev *dev)
+void fsl_pcibios_fixup_bus(struct pci_bus *bus)
{
- /* if we aren't a PCIe don't bother */
- if (!pci_find_capability(dev, PCI_CAP_ID_EXP))
- return ;
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ int i, is_pcie = 0, no_link;
- dev->class = PCI_CLASS_BRIDGE_PCI << 8;
- fsl_pcie_bus_fixup = 1;
- return ;
-}
+ /* The root complex bridge comes up with bogus resources,
+ * we copy the PHB ones in.
+ *
+ * With the current generic PCI code, the PHB bus no longer
+ * has bus->resource[0..4] set, so things are a bit more
+ * tricky.
+ */
-int __init fsl_pcie_check_link(struct pci_controller *hose)
-{
- u32 val;
- early_read_config_dword(hose, 0, 0, PCIE_LTSSM, &val);
- if (val < PCIE_LTSSM_L0)
- return 1;
- return 0;
-}
+ if (fsl_pcie_bus_fixup)
+ is_pcie = early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP);
+ no_link = !!(hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK);
-void fsl_pcibios_fixup_bus(struct pci_bus *bus)
-{
- struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
- int i;
-
- if ((bus->parent == hose->bus) &&
- ((fsl_pcie_bus_fixup &&
- early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) ||
- (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK)))
- {
- for (i = 0; i < 4; ++i) {
+ if (bus->parent == hose->bus && (is_pcie || no_link)) {
+ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; ++i) {
struct resource *res = bus->resource[i];
- struct resource *par = bus->parent->resource[i];
- if (res) {
- res->start = 0;
- res->end = 0;
- res->flags = 0;
- }
- if (res && par) {
- res->start = par->start;
- res->end = par->end;
- res->flags = par->flags;
- }
+ struct resource *par;
+
+ if (!res)
+ continue;
+ if (i == 0)
+ par = &hose->io_resource;
+ else if (i < 4)
+ par = &hose->mem_resources[i-1];
+ else par = NULL;
+
+ res->start = par ? par->start : 0;
+ res->end = par ? par->end : 0;
+ res->flags = par ? par->flags : 0;
}
}
}
-int __init fsl_add_bridge(struct device_node *dev, int is_primary)
+int fsl_add_bridge(struct platform_device *pdev, int is_primary)
{
int len;
struct pci_controller *hose;
struct resource rsrc;
const int *bus_range;
+ u8 hdr_type, progif;
+ struct device_node *dev;
+ struct ccsr_pci __iomem *pci;
+
+ dev = pdev->dev.of_node;
+
+ if (!of_device_is_available(dev)) {
+ pr_warning("%s: disabled\n", dev->full_name);
+ return -ENODEV;
+ }
pr_debug("Adding PCI host bridge %s\n", dev->full_name);
@@ -176,16 +488,44 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary)
printk(KERN_WARNING "Can't get bus-range for %s, assume"
" bus 0\n", dev->full_name);
- ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
+ pci_add_flags(PCI_REASSIGN_ALL_BUS);
hose = pcibios_alloc_controller(dev);
if (!hose)
return -ENOMEM;
+ /* set platform device as the parent */
+ hose->parent = &pdev->dev;
hose->first_busno = bus_range ? bus_range[0] : 0x0;
hose->last_busno = bus_range ? bus_range[1] : 0xff;
+ pr_debug("PCI memory map start 0x%016llx, size 0x%016llx\n",
+ (u64)rsrc.start, (u64)resource_size(&rsrc));
+
+ pci = hose->private_data = ioremap(rsrc.start, resource_size(&rsrc));
+ if (!hose->private_data)
+ goto no_bridge;
+
setup_indirect_pci(hose, rsrc.start, rsrc.start + 0x4,
- PPC_INDIRECT_TYPE_BIG_ENDIAN);
+ PPC_INDIRECT_TYPE_BIG_ENDIAN);
+
+ if (in_be32(&pci->block_rev1) < PCIE_IP_REV_3_0)
+ hose->indirect_type |= PPC_INDIRECT_TYPE_FSL_CFG_REG_LINK;
+
+ if (early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) {
+ /* use fsl_indirect_read_config for PCIe */
+ hose->ops = &fsl_indirect_pcie_ops;
+ /* For PCIE read HEADER_TYPE to identify controler mode */
+ early_read_config_byte(hose, 0, 0, PCI_HEADER_TYPE, &hdr_type);
+ if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE)
+ goto no_bridge;
+
+ } else {
+ /* For PCI read PROG to identify controller mode */
+ early_read_config_byte(hose, 0, 0, PCI_CLASS_PROG, &progif);
+ if ((progif & 1) == 1)
+ goto no_bridge;
+ }
+
setup_pci_cmd(hose);
/* check PCI express link status */
@@ -209,28 +549,729 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary)
pci_process_bridge_OF_ranges(hose, dev, is_primary);
/* Setup PEX window registers */
- setup_pci_atmu(hose, &rsrc);
+ setup_pci_atmu(hose);
+
+ return 0;
+
+no_bridge:
+ iounmap(hose->private_data);
+ /* unmap cfg_data & cfg_addr separately if not on same page */
+ if (((unsigned long)hose->cfg_data & PAGE_MASK) !=
+ ((unsigned long)hose->cfg_addr & PAGE_MASK))
+ iounmap(hose->cfg_data);
+ iounmap(hose->cfg_addr);
+ pcibios_free_controller(hose);
+ return -ENODEV;
+}
+#endif /* CONFIG_FSL_SOC_BOOKE || CONFIG_PPC_86xx */
+
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID,
+ quirk_fsl_pcie_early);
+
+#if defined(CONFIG_PPC_83xx) || defined(CONFIG_PPC_MPC512x)
+struct mpc83xx_pcie_priv {
+ void __iomem *cfg_type0;
+ void __iomem *cfg_type1;
+ u32 dev_base;
+};
+
+struct pex_inbound_window {
+ u32 ar;
+ u32 tar;
+ u32 barl;
+ u32 barh;
+};
+
+/*
+ * With the convention of u-boot, the PCIE outbound window 0 serves
+ * as configuration transactions outbound.
+ */
+#define PEX_OUTWIN0_BAR 0xCA4
+#define PEX_OUTWIN0_TAL 0xCA8
+#define PEX_OUTWIN0_TAH 0xCAC
+#define PEX_RC_INWIN_BASE 0xE60
+#define PEX_RCIWARn_EN 0x1
+
+static int mpc83xx_pcie_exclude_device(struct pci_bus *bus, unsigned int devfn)
+{
+ struct pci_controller *hose = pci_bus_to_host(bus);
+
+ if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ /*
+ * Workaround for the HW bug: for Type 0 configure transactions the
+ * PCI-E controller does not check the device number bits and just
+ * assumes that the device number bits are 0.
+ */
+ if (bus->number == hose->first_busno ||
+ bus->primary == hose->first_busno) {
+ if (devfn & 0xf8)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ if (ppc_md.pci_exclude_device) {
+ if (ppc_md.pci_exclude_device(hose, bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static void __iomem *mpc83xx_pcie_remap_cfg(struct pci_bus *bus,
+ unsigned int devfn, int offset)
+{
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ struct mpc83xx_pcie_priv *pcie = hose->dn->data;
+ u32 dev_base = bus->number << 24 | devfn << 16;
+ int ret;
+
+ ret = mpc83xx_pcie_exclude_device(bus, devfn);
+ if (ret)
+ return NULL;
+
+ offset &= 0xfff;
+
+ /* Type 0 */
+ if (bus->number == hose->first_busno)
+ return pcie->cfg_type0 + offset;
+
+ if (pcie->dev_base == dev_base)
+ goto mapped;
+
+ out_le32(pcie->cfg_type0 + PEX_OUTWIN0_TAL, dev_base);
+
+ pcie->dev_base = dev_base;
+mapped:
+ return pcie->cfg_type1 + offset;
+}
+
+static int mpc83xx_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 *val)
+{
+ void __iomem *cfg_addr;
+
+ cfg_addr = mpc83xx_pcie_remap_cfg(bus, devfn, offset);
+ if (!cfg_addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ switch (len) {
+ case 1:
+ *val = in_8(cfg_addr);
+ break;
+ case 2:
+ *val = in_le16(cfg_addr);
+ break;
+ default:
+ *val = in_le32(cfg_addr);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int mpc83xx_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 val)
+{
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ void __iomem *cfg_addr;
+
+ cfg_addr = mpc83xx_pcie_remap_cfg(bus, devfn, offset);
+ if (!cfg_addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /* PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS */
+ if (offset == PCI_PRIMARY_BUS && bus->number == hose->first_busno)
+ val &= 0xffffff00;
+
+ switch (len) {
+ case 1:
+ out_8(cfg_addr, val);
+ break;
+ case 2:
+ out_le16(cfg_addr, val);
+ break;
+ default:
+ out_le32(cfg_addr, val);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops mpc83xx_pcie_ops = {
+ .read = mpc83xx_pcie_read_config,
+ .write = mpc83xx_pcie_write_config,
+};
+
+static int __init mpc83xx_pcie_setup(struct pci_controller *hose,
+ struct resource *reg)
+{
+ struct mpc83xx_pcie_priv *pcie;
+ u32 cfg_bar;
+ int ret = -ENOMEM;
+
+ pcie = zalloc_maybe_bootmem(sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return ret;
+
+ pcie->cfg_type0 = ioremap(reg->start, resource_size(reg));
+ if (!pcie->cfg_type0)
+ goto err0;
+
+ cfg_bar = in_le32(pcie->cfg_type0 + PEX_OUTWIN0_BAR);
+ if (!cfg_bar) {
+ /* PCI-E isn't configured. */
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ pcie->cfg_type1 = ioremap(cfg_bar, 0x1000);
+ if (!pcie->cfg_type1)
+ goto err1;
+
+ WARN_ON(hose->dn->data);
+ hose->dn->data = pcie;
+ hose->ops = &mpc83xx_pcie_ops;
+ hose->indirect_type |= PPC_INDIRECT_TYPE_FSL_CFG_REG_LINK;
+
+ out_le32(pcie->cfg_type0 + PEX_OUTWIN0_TAH, 0);
+ out_le32(pcie->cfg_type0 + PEX_OUTWIN0_TAL, 0);
+
+ if (fsl_pcie_check_link(hose))
+ hose->indirect_type |= PPC_INDIRECT_TYPE_NO_PCIE_LINK;
return 0;
+err1:
+ iounmap(pcie->cfg_type0);
+err0:
+ kfree(pcie);
+ return ret;
+
}
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8548E, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8548, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8543E, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8543, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8547E, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8545E, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8545, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8568E, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8568, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8567E, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8567, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8533E, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8533, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8544E, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8544, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8572E, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8572, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8641, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8641D, quirk_fsl_pcie_header);
-DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8610, quirk_fsl_pcie_header);
+int __init mpc83xx_add_bridge(struct device_node *dev)
+{
+ int ret;
+ int len;
+ struct pci_controller *hose;
+ struct resource rsrc_reg;
+ struct resource rsrc_cfg;
+ const int *bus_range;
+ int primary;
+
+ is_mpc83xx_pci = 1;
+
+ if (!of_device_is_available(dev)) {
+ pr_warning("%s: disabled by the firmware.\n",
+ dev->full_name);
+ return -ENODEV;
+ }
+ pr_debug("Adding PCI host bridge %s\n", dev->full_name);
+
+ /* Fetch host bridge registers address */
+ if (of_address_to_resource(dev, 0, &rsrc_reg)) {
+ printk(KERN_WARNING "Can't get pci register base!\n");
+ return -ENOMEM;
+ }
+
+ memset(&rsrc_cfg, 0, sizeof(rsrc_cfg));
+
+ if (of_address_to_resource(dev, 1, &rsrc_cfg)) {
+ printk(KERN_WARNING
+ "No pci config register base in dev tree, "
+ "using default\n");
+ /*
+ * MPC83xx supports up to two host controllers
+ * one at 0x8500 has config space registers at 0x8300
+ * one at 0x8600 has config space registers at 0x8380
+ */
+ if ((rsrc_reg.start & 0xfffff) == 0x8500)
+ rsrc_cfg.start = (rsrc_reg.start & 0xfff00000) + 0x8300;
+ else if ((rsrc_reg.start & 0xfffff) == 0x8600)
+ rsrc_cfg.start = (rsrc_reg.start & 0xfff00000) + 0x8380;
+ }
+ /*
+ * Controller at offset 0x8500 is primary
+ */
+ if ((rsrc_reg.start & 0xfffff) == 0x8500)
+ primary = 1;
+ else
+ primary = 0;
+
+ /* Get bus range if any */
+ bus_range = of_get_property(dev, "bus-range", &len);
+ if (bus_range == NULL || len < 2 * sizeof(int)) {
+ printk(KERN_WARNING "Can't get bus-range for %s, assume"
+ " bus 0\n", dev->full_name);
+ }
+
+ pci_add_flags(PCI_REASSIGN_ALL_BUS);
+ hose = pcibios_alloc_controller(dev);
+ if (!hose)
+ return -ENOMEM;
+
+ hose->first_busno = bus_range ? bus_range[0] : 0;
+ hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+ if (of_device_is_compatible(dev, "fsl,mpc8314-pcie")) {
+ ret = mpc83xx_pcie_setup(hose, &rsrc_reg);
+ if (ret)
+ goto err0;
+ } else {
+ setup_indirect_pci(hose, rsrc_cfg.start,
+ rsrc_cfg.start + 4, 0);
+ }
+
+ printk(KERN_INFO "Found FSL PCI host bridge at 0x%016llx. "
+ "Firmware bus number: %d->%d\n",
+ (unsigned long long)rsrc_reg.start, hose->first_busno,
+ hose->last_busno);
+
+ pr_debug(" ->Hose at 0x%p, cfg_addr=0x%p,cfg_data=0x%p\n",
+ hose, hose->cfg_addr, hose->cfg_data);
+
+ /* Interpret the "ranges" property */
+ /* This also maps the I/O region and sets isa_io/mem_base */
+ pci_process_bridge_OF_ranges(hose, dev, primary);
+
+ return 0;
+err0:
+ pcibios_free_controller(hose);
+ return ret;
+}
+#endif /* CONFIG_PPC_83xx */
+
+u64 fsl_pci_immrbar_base(struct pci_controller *hose)
+{
+#ifdef CONFIG_PPC_83xx
+ if (is_mpc83xx_pci) {
+ struct mpc83xx_pcie_priv *pcie = hose->dn->data;
+ struct pex_inbound_window *in;
+ int i;
+
+ /* Walk the Root Complex Inbound windows to match IMMR base */
+ in = pcie->cfg_type0 + PEX_RC_INWIN_BASE;
+ for (i = 0; i < 4; i++) {
+ /* not enabled, skip */
+ if (!in_le32(&in[i].ar) & PEX_RCIWARn_EN)
+ continue;
+
+ if (get_immrbase() == in_le32(&in[i].tar))
+ return (u64)in_le32(&in[i].barh) << 32 |
+ in_le32(&in[i].barl);
+ }
+
+ printk(KERN_WARNING "could not find PCI BAR matching IMMR\n");
+ }
+#endif
+
+#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
+ if (!is_mpc83xx_pci) {
+ u32 base;
+
+ pci_bus_read_config_dword(hose->bus,
+ PCI_DEVFN(0, 0), PCI_BASE_ADDRESS_0, &base);
+
+ /*
+ * For PEXCSRBAR, bit 3-0 indicate prefetchable and
+ * address type. So when getting base address, these
+ * bits should be masked
+ */
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ return base;
+ }
+#endif
+
+ return 0;
+}
+
+#ifdef CONFIG_E500
+static int mcheck_handle_load(struct pt_regs *regs, u32 inst)
+{
+ unsigned int rd, ra, rb, d;
+
+ rd = get_rt(inst);
+ ra = get_ra(inst);
+ rb = get_rb(inst);
+ d = get_d(inst);
+
+ switch (get_op(inst)) {
+ case 31:
+ switch (get_xop(inst)) {
+ case OP_31_XOP_LWZX:
+ case OP_31_XOP_LWBRX:
+ regs->gpr[rd] = 0xffffffff;
+ break;
+
+ case OP_31_XOP_LWZUX:
+ regs->gpr[rd] = 0xffffffff;
+ regs->gpr[ra] += regs->gpr[rb];
+ break;
+
+ case OP_31_XOP_LBZX:
+ regs->gpr[rd] = 0xff;
+ break;
+
+ case OP_31_XOP_LBZUX:
+ regs->gpr[rd] = 0xff;
+ regs->gpr[ra] += regs->gpr[rb];
+ break;
+
+ case OP_31_XOP_LHZX:
+ case OP_31_XOP_LHBRX:
+ regs->gpr[rd] = 0xffff;
+ break;
+
+ case OP_31_XOP_LHZUX:
+ regs->gpr[rd] = 0xffff;
+ regs->gpr[ra] += regs->gpr[rb];
+ break;
+
+ case OP_31_XOP_LHAX:
+ regs->gpr[rd] = ~0UL;
+ break;
+
+ case OP_31_XOP_LHAUX:
+ regs->gpr[rd] = ~0UL;
+ regs->gpr[ra] += regs->gpr[rb];
+ break;
+
+ default:
+ return 0;
+ }
+ break;
+
+ case OP_LWZ:
+ regs->gpr[rd] = 0xffffffff;
+ break;
+
+ case OP_LWZU:
+ regs->gpr[rd] = 0xffffffff;
+ regs->gpr[ra] += (s16)d;
+ break;
+
+ case OP_LBZ:
+ regs->gpr[rd] = 0xff;
+ break;
+
+ case OP_LBZU:
+ regs->gpr[rd] = 0xff;
+ regs->gpr[ra] += (s16)d;
+ break;
+
+ case OP_LHZ:
+ regs->gpr[rd] = 0xffff;
+ break;
+
+ case OP_LHZU:
+ regs->gpr[rd] = 0xffff;
+ regs->gpr[ra] += (s16)d;
+ break;
+
+ case OP_LHA:
+ regs->gpr[rd] = ~0UL;
+ break;
+
+ case OP_LHAU:
+ regs->gpr[rd] = ~0UL;
+ regs->gpr[ra] += (s16)d;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static int is_in_pci_mem_space(phys_addr_t addr)
+{
+ struct pci_controller *hose;
+ struct resource *res;
+ int i;
+
+ list_for_each_entry(hose, &hose_list, list_node) {
+ if (!(hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG))
+ continue;
+
+ for (i = 0; i < 3; i++) {
+ res = &hose->mem_resources[i];
+ if ((res->flags & IORESOURCE_MEM) &&
+ addr >= res->start && addr <= res->end)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int fsl_pci_mcheck_exception(struct pt_regs *regs)
+{
+ u32 inst;
+ int ret;
+ phys_addr_t addr = 0;
+
+ /* Let KVM/QEMU deal with the exception */
+ if (regs->msr & MSR_GS)
+ return 0;
+
+#ifdef CONFIG_PHYS_64BIT
+ addr = mfspr(SPRN_MCARU);
+ addr <<= 32;
+#endif
+ addr += mfspr(SPRN_MCAR);
+
+ if (is_in_pci_mem_space(addr)) {
+ if (user_mode(regs)) {
+ pagefault_disable();
+ ret = get_user(regs->nip, &inst);
+ pagefault_enable();
+ } else {
+ ret = probe_kernel_address(regs->nip, inst);
+ }
+
+ if (mcheck_handle_load(regs, inst)) {
+ regs->nip += 4;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
+static const struct of_device_id pci_ids[] = {
+ { .compatible = "fsl,mpc8540-pci", },
+ { .compatible = "fsl,mpc8548-pcie", },
+ { .compatible = "fsl,mpc8610-pci", },
+ { .compatible = "fsl,mpc8641-pcie", },
+ { .compatible = "fsl,qoriq-pcie", },
+ { .compatible = "fsl,qoriq-pcie-v2.1", },
+ { .compatible = "fsl,qoriq-pcie-v2.2", },
+ { .compatible = "fsl,qoriq-pcie-v2.3", },
+ { .compatible = "fsl,qoriq-pcie-v2.4", },
+ { .compatible = "fsl,qoriq-pcie-v3.0", },
+
+ /*
+ * The following entries are for compatibility with older device
+ * trees.
+ */
+ { .compatible = "fsl,p1022-pcie", },
+ { .compatible = "fsl,p4080-pcie", },
+
+ {},
+};
+
+struct device_node *fsl_pci_primary;
+
+void fsl_pci_assign_primary(void)
+{
+ struct device_node *np;
+
+ /* Callers can specify the primary bus using other means. */
+ if (fsl_pci_primary)
+ return;
+
+ /* If a PCI host bridge contains an ISA node, it's primary. */
+ np = of_find_node_by_type(NULL, "isa");
+ while ((fsl_pci_primary = of_get_parent(np))) {
+ of_node_put(np);
+ np = fsl_pci_primary;
+
+ if (of_match_node(pci_ids, np) && of_device_is_available(np))
+ return;
+ }
+
+ /*
+ * If there's no PCI host bridge with ISA, arbitrarily
+ * designate one as primary. This can go away once
+ * various bugs with primary-less systems are fixed.
+ */
+ for_each_matching_node(np, pci_ids) {
+ if (of_device_is_available(np)) {
+ fsl_pci_primary = np;
+ of_node_put(np);
+ return;
+ }
+ }
+}
+
+#ifdef CONFIG_PM_SLEEP
+static irqreturn_t fsl_pci_pme_handle(int irq, void *dev_id)
+{
+ struct pci_controller *hose = dev_id;
+ struct ccsr_pci __iomem *pci = hose->private_data;
+ u32 dr;
+
+ dr = in_be32(&pci->pex_pme_mes_dr);
+ if (!dr)
+ return IRQ_NONE;
+
+ out_be32(&pci->pex_pme_mes_dr, dr);
+
+ return IRQ_HANDLED;
+}
+
+static int fsl_pci_pme_probe(struct pci_controller *hose)
+{
+ struct ccsr_pci __iomem *pci;
+ struct pci_dev *dev;
+ int pme_irq;
+ int res;
+ u16 pms;
+
+ /* Get hose's pci_dev */
+ dev = list_first_entry(&hose->bus->devices, typeof(*dev), bus_list);
+
+ /* PME Disable */
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pms);
+ pms &= ~PCI_PM_CTRL_PME_ENABLE;
+ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pms);
+
+ pme_irq = irq_of_parse_and_map(hose->dn, 0);
+ if (!pme_irq) {
+ dev_err(&dev->dev, "Failed to map PME interrupt.\n");
+
+ return -ENXIO;
+ }
+
+ res = devm_request_irq(hose->parent, pme_irq,
+ fsl_pci_pme_handle,
+ IRQF_SHARED,
+ "[PCI] PME", hose);
+ if (res < 0) {
+ dev_err(&dev->dev, "Unable to requiest irq %d for PME\n", pme_irq);
+ irq_dispose_mapping(pme_irq);
+
+ return -ENODEV;
+ }
+
+ pci = hose->private_data;
+
+ /* Enable PTOD, ENL23D & EXL23D */
+ clrbits32(&pci->pex_pme_mes_disr,
+ PME_DISR_EN_PTOD | PME_DISR_EN_ENL23D | PME_DISR_EN_EXL23D);
+
+ out_be32(&pci->pex_pme_mes_ier, 0);
+ setbits32(&pci->pex_pme_mes_ier,
+ PME_DISR_EN_PTOD | PME_DISR_EN_ENL23D | PME_DISR_EN_EXL23D);
+
+ /* PME Enable */
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pms);
+ pms |= PCI_PM_CTRL_PME_ENABLE;
+ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pms);
+
+ return 0;
+}
+
+static void send_pme_turnoff_message(struct pci_controller *hose)
+{
+ struct ccsr_pci __iomem *pci = hose->private_data;
+ u32 dr;
+ int i;
+
+ /* Send PME_Turn_Off Message Request */
+ setbits32(&pci->pex_pmcr, PEX_PMCR_PTOMR);
+
+ /* Wait trun off done */
+ for (i = 0; i < 150; i++) {
+ dr = in_be32(&pci->pex_pme_mes_dr);
+ if (dr) {
+ out_be32(&pci->pex_pme_mes_dr, dr);
+ break;
+ }
+
+ udelay(1000);
+ }
+}
+
+static void fsl_pci_syscore_do_suspend(struct pci_controller *hose)
+{
+ send_pme_turnoff_message(hose);
+}
+
+static int fsl_pci_syscore_suspend(void)
+{
+ struct pci_controller *hose, *tmp;
+
+ list_for_each_entry_safe(hose, tmp, &hose_list, list_node)
+ fsl_pci_syscore_do_suspend(hose);
+
+ return 0;
+}
+
+static void fsl_pci_syscore_do_resume(struct pci_controller *hose)
+{
+ struct ccsr_pci __iomem *pci = hose->private_data;
+ u32 dr;
+ int i;
+
+ /* Send Exit L2 State Message */
+ setbits32(&pci->pex_pmcr, PEX_PMCR_EXL2S);
+
+ /* Wait exit done */
+ for (i = 0; i < 150; i++) {
+ dr = in_be32(&pci->pex_pme_mes_dr);
+ if (dr) {
+ out_be32(&pci->pex_pme_mes_dr, dr);
+ break;
+ }
+
+ udelay(1000);
+ }
+
+ setup_pci_atmu(hose);
+}
+
+static void fsl_pci_syscore_resume(void)
+{
+ struct pci_controller *hose, *tmp;
+
+ list_for_each_entry_safe(hose, tmp, &hose_list, list_node)
+ fsl_pci_syscore_do_resume(hose);
+}
+
+static struct syscore_ops pci_syscore_pm_ops = {
+ .suspend = fsl_pci_syscore_suspend,
+ .resume = fsl_pci_syscore_resume,
+};
+#endif
+
+void fsl_pcibios_fixup_phb(struct pci_controller *phb)
+{
+#ifdef CONFIG_PM_SLEEP
+ fsl_pci_pme_probe(phb);
+#endif
+}
+
+static int fsl_pci_probe(struct platform_device *pdev)
+{
+ struct device_node *node;
+ int ret;
+
+ node = pdev->dev.of_node;
+ ret = fsl_add_bridge(pdev, fsl_pci_primary == node);
+
+ mpc85xx_pci_err_probe(pdev);
+
+ return 0;
+}
+
+static struct platform_driver fsl_pci_driver = {
+ .driver = {
+ .name = "fsl-pci",
+ .of_match_table = pci_ids,
+ },
+ .probe = fsl_pci_probe,
+};
+
+static int __init fsl_pci_init(void)
+{
+#ifdef CONFIG_PM_SLEEP
+ register_syscore_ops(&pci_syscore_pm_ops);
+#endif
+ return platform_driver_register(&fsl_pci_driver);
+}
+arch_initcall(fsl_pci_init);
+#endif
diff --git a/arch/powerpc/sysdev/fsl_pci.h b/arch/powerpc/sysdev/fsl_pci.h
index 37b04ad2657..c1cec771d5e 100644
--- a/arch/powerpc/sysdev/fsl_pci.h
+++ b/arch/powerpc/sysdev/fsl_pci.h
@@ -1,7 +1,7 @@
/*
* MPC85xx/86xx PCI Express structure define
*
- * Copyright 2007 Freescale Semiconductor, Inc
+ * Copyright 2007,2011 Freescale Semiconductor, Inc
*
* 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
@@ -14,9 +14,30 @@
#ifndef __POWERPC_FSL_PCI_H
#define __POWERPC_FSL_PCI_H
+struct platform_device;
+
+
+/* FSL PCI controller BRR1 register */
+#define PCI_FSL_BRR1 0xbf8
+#define PCI_FSL_BRR1_VER 0xffff
+
#define PCIE_LTSSM 0x0404 /* PCIE Link Training and Status */
#define PCIE_LTSSM_L0 0x16 /* L0 state */
-#define PIWAR_2G 0xa0f5501e /* Enable, Prefetch, Local Mem, Snoop R/W, 2G */
+#define PCIE_IP_REV_2_2 0x02080202 /* PCIE IP block version Rev2.2 */
+#define PCIE_IP_REV_3_0 0x02080300 /* PCIE IP block version Rev3.0 */
+#define PIWAR_EN 0x80000000 /* Enable */
+#define PIWAR_PF 0x20000000 /* prefetch */
+#define PIWAR_TGI_LOCAL 0x00f00000 /* target - local memory */
+#define PIWAR_READ_SNOOP 0x00050000
+#define PIWAR_WRITE_SNOOP 0x00005000
+#define PIWAR_SZ_MASK 0x0000003f
+
+#define PEX_PMCR_PTOMR 0x1
+#define PEX_PMCR_EXL2S 0x2
+
+#define PME_DISR_EN_PTOD 0x00008000
+#define PME_DISR_EN_ENL23D 0x00002000
+#define PME_DISR_EN_EXL23D 0x00001000
/* PCI/PCI Express outbound window reg */
struct pci_outbound_window_regs {
@@ -45,12 +66,16 @@ struct ccsr_pci {
__be32 int_ack; /* 0x.008 - PCI Interrupt Acknowledge Register */
__be32 pex_otb_cpl_tor; /* 0x.00c - PCIE Outbound completion timeout register */
__be32 pex_conf_tor; /* 0x.010 - PCIE configuration timeout register */
- u8 res2[12];
+ __be32 pex_config; /* 0x.014 - PCIE CONFIG Register */
+ __be32 pex_int_status; /* 0x.018 - PCIE interrupt status */
+ u8 res2[4];
__be32 pex_pme_mes_dr; /* 0x.020 - PCIE PME and message detect register */
__be32 pex_pme_mes_disr; /* 0x.024 - PCIE PME and message disable register */
__be32 pex_pme_mes_ier; /* 0x.028 - PCIE PME and message interrupt enable register */
__be32 pex_pmcr; /* 0x.02c - PCIE power management command register */
- u8 res3[3024];
+ u8 res3[3016];
+ __be32 block_rev1; /* 0x.bf8 - PCIE Block Revision register 1 */
+ __be32 block_rev2; /* 0x.bfc - PCIE Block Revision register 2 */
/* PCI/PCI Express outbound window 0-4
* Window 0 is the default window and is the only window enabled upon reset.
@@ -58,14 +83,14 @@ struct ccsr_pci {
* in all of the other outbound windows.
*/
struct pci_outbound_window_regs pow[5];
-
- u8 res14[256];
-
-/* PCI/PCI Express inbound window 3-1
+ u8 res14[96];
+ struct pci_inbound_window_regs pmit; /* 0xd00 - 0xd9c Inbound MSI */
+ u8 res6[96];
+/* PCI/PCI Express inbound window 3-0
* inbound window 1 supports only a 32-bit base address and does not
* define an inbound window base extended address register.
*/
- struct pci_inbound_window_regs piw[3];
+ struct pci_inbound_window_regs piw[4];
__be32 pex_err_dr; /* 0x.e00 - PCI/PCIE error detect register */
u8 res21[4];
@@ -79,10 +104,46 @@ struct ccsr_pci {
__be32 pex_err_cap_r1; /* 0x.e2c - PCIE error capture register 0 */
__be32 pex_err_cap_r2; /* 0x.e30 - PCIE error capture register 0 */
__be32 pex_err_cap_r3; /* 0x.e34 - PCIE error capture register 0 */
+ u8 res_e38[200];
+ __be32 pdb_stat; /* 0x.f00 - PCIE Debug Status */
+ u8 res_f04[16];
+ __be32 pex_csr0; /* 0x.f14 - PEX Control/Status register 0*/
+#define PEX_CSR0_LTSSM_MASK 0xFC
+#define PEX_CSR0_LTSSM_SHIFT 2
+#define PEX_CSR0_LTSSM_L0 0x11
+ __be32 pex_csr1; /* 0x.f18 - PEX Control/Status register 1*/
+ u8 res_f1c[228];
+
};
-extern int fsl_add_bridge(struct device_node *dev, int is_primary);
+extern int fsl_add_bridge(struct platform_device *pdev, int is_primary);
extern void fsl_pcibios_fixup_bus(struct pci_bus *bus);
+extern void fsl_pcibios_fixup_phb(struct pci_controller *phb);
+extern int mpc83xx_add_bridge(struct device_node *dev);
+u64 fsl_pci_immrbar_base(struct pci_controller *hose);
+
+extern struct device_node *fsl_pci_primary;
+
+#ifdef CONFIG_PCI
+void fsl_pci_assign_primary(void);
+#else
+static inline void fsl_pci_assign_primary(void) {}
+#endif
+
+#ifdef CONFIG_EDAC_MPC85XX
+int mpc85xx_pci_err_probe(struct platform_device *op);
+#else
+static inline int mpc85xx_pci_err_probe(struct platform_device *op)
+{
+ return -ENOTSUPP;
+}
+#endif
+
+#ifdef CONFIG_FSL_PCI
+extern int fsl_pci_mcheck_exception(struct pt_regs *);
+#else
+static inline int fsl_pci_mcheck_exception(struct pt_regs *regs) {return 0; }
+#endif
#endif /* __POWERPC_FSL_PCI_H */
#endif /* __KERNEL__ */
diff --git a/arch/powerpc/sysdev/fsl_pmc.c b/arch/powerpc/sysdev/fsl_pmc.c
new file mode 100644
index 00000000000..8cf4aa0e3a2
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_pmc.c
@@ -0,0 +1,93 @@
+/*
+ * Suspend/resume support
+ *
+ * Copyright 2009 MontaVista Software, Inc.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.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 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/suspend.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+struct pmc_regs {
+ __be32 devdisr;
+ __be32 devdisr2;
+ __be32 :32;
+ __be32 :32;
+ __be32 pmcsr;
+#define PMCSR_SLP (1 << 17)
+};
+
+static struct device *pmc_dev;
+static struct pmc_regs __iomem *pmc_regs;
+
+static int pmc_suspend_enter(suspend_state_t state)
+{
+ int ret;
+
+ setbits32(&pmc_regs->pmcsr, PMCSR_SLP);
+ /* At this point, the CPU is asleep. */
+
+ /* Upon resume, wait for SLP bit to be clear. */
+ ret = spin_event_timeout((in_be32(&pmc_regs->pmcsr) & PMCSR_SLP) == 0,
+ 10000, 10) ? 0 : -ETIMEDOUT;
+ if (ret)
+ dev_err(pmc_dev, "tired waiting for SLP bit to clear\n");
+ return ret;
+}
+
+static int pmc_suspend_valid(suspend_state_t state)
+{
+ if (state != PM_SUSPEND_STANDBY)
+ return 0;
+ return 1;
+}
+
+static const struct platform_suspend_ops pmc_suspend_ops = {
+ .valid = pmc_suspend_valid,
+ .enter = pmc_suspend_enter,
+};
+
+static int pmc_probe(struct platform_device *ofdev)
+{
+ pmc_regs = of_iomap(ofdev->dev.of_node, 0);
+ if (!pmc_regs)
+ return -ENOMEM;
+
+ pmc_dev = &ofdev->dev;
+ suspend_set_ops(&pmc_suspend_ops);
+ return 0;
+}
+
+static const struct of_device_id pmc_ids[] = {
+ { .compatible = "fsl,mpc8548-pmc", },
+ { .compatible = "fsl,mpc8641d-pmc", },
+ { },
+};
+
+static struct platform_driver pmc_driver = {
+ .driver = {
+ .name = "fsl-pmc",
+ .owner = THIS_MODULE,
+ .of_match_table = pmc_ids,
+ },
+ .probe = pmc_probe,
+};
+
+static int __init pmc_init(void)
+{
+ return platform_driver_register(&pmc_driver);
+}
+device_initcall(pmc_init);
diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c
index af2425e4655..c04b718307c 100644
--- a/arch/powerpc/sysdev/fsl_rio.c
+++ b/arch/powerpc/sysdev/fsl_rio.c
@@ -1,5 +1,17 @@
/*
- * MPC85xx RapidIO support
+ * Freescale MPC85xx/MPC86xx RapidIO support
+ *
+ * Copyright 2009 Sysgo AG
+ * Thomas Moll <thomas.moll@sysgo.com>
+ * - fixed maintenance access routines, check for aligned access
+ *
+ * Copyright 2009 Integrated Device Technology, Inc.
+ * Alex Bounine <alexandre.bounine@idt.com>
+ * - Added Port-Write message handling
+ * - Added Machine Check exception handling
+ *
+ * Copyright (C) 2007, 2008, 2010, 2011 Freescale Semiconductor, Inc.
+ * Zhang Wei <wei.zhang@freescale.com>
*
* Copyright 2005 MontaVista Software, Inc.
* Matt Porter <mporter@kernel.crashing.org>
@@ -15,156 +27,92 @@
#include <linux/types.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
-#include <linux/rio.h>
-#include <linux/rio_drv.h>
-
-#include <asm/io.h>
-
-#define RIO_REGS_BASE (CCSRBAR + 0xc0000)
-#define RIO_ATMU_REGS_OFFSET 0x10c00
-#define RIO_MSG_REGS_OFFSET 0x11000
-#define RIO_MAINT_WIN_SIZE 0x400000
-#define RIO_DBELL_WIN_SIZE 0x1000
-
-#define RIO_MSG_OMR_MUI 0x00000002
-#define RIO_MSG_OSR_TE 0x00000080
-#define RIO_MSG_OSR_QOI 0x00000020
-#define RIO_MSG_OSR_QFI 0x00000010
-#define RIO_MSG_OSR_MUB 0x00000004
-#define RIO_MSG_OSR_EOMI 0x00000002
-#define RIO_MSG_OSR_QEI 0x00000001
-
-#define RIO_MSG_IMR_MI 0x00000002
-#define RIO_MSG_ISR_TE 0x00000080
-#define RIO_MSG_ISR_QFI 0x00000010
-#define RIO_MSG_ISR_DIQI 0x00000001
-
-#define RIO_MSG_DESC_SIZE 32
-#define RIO_MSG_BUFFER_SIZE 4096
-#define RIO_MIN_TX_RING_SIZE 2
-#define RIO_MAX_TX_RING_SIZE 2048
-#define RIO_MIN_RX_RING_SIZE 2
-#define RIO_MAX_RX_RING_SIZE 2048
-
-#define DOORBELL_DMR_DI 0x00000002
-#define DOORBELL_DSR_TE 0x00000080
-#define DOORBELL_DSR_QFI 0x00000010
-#define DOORBELL_DSR_DIQI 0x00000001
-#define DOORBELL_TID_OFFSET 0x03
-#define DOORBELL_SID_OFFSET 0x05
-#define DOORBELL_INFO_OFFSET 0x06
-
-#define DOORBELL_MESSAGE_SIZE 0x08
-#define DBELL_SID(x) (*(u8 *)(x + DOORBELL_SID_OFFSET))
-#define DBELL_TID(x) (*(u8 *)(x + DOORBELL_TID_OFFSET))
-#define DBELL_INF(x) (*(u16 *)(x + DOORBELL_INFO_OFFSET))
-
-struct rio_atmu_regs {
- u32 rowtar;
- u32 pad1;
- u32 rowbar;
- u32 pad2;
- u32 rowar;
- u32 pad3[3];
-};
-
-struct rio_msg_regs {
- u32 omr;
- u32 osr;
- u32 pad1;
- u32 odqdpar;
- u32 pad2;
- u32 osar;
- u32 odpr;
- u32 odatr;
- u32 odcr;
- u32 pad3;
- u32 odqepar;
- u32 pad4[13];
- u32 imr;
- u32 isr;
- u32 pad5;
- u32 ifqdpar;
- u32 pad6;
- u32 ifqepar;
- u32 pad7[250];
- u32 dmr;
- u32 dsr;
- u32 pad8;
- u32 dqdpar;
- u32 pad9;
- u32 dqepar;
- u32 pad10[26];
- u32 pwmr;
- u32 pwsr;
- u32 pad11;
- u32 pwqbar;
-};
-
-struct rio_tx_desc {
- u32 res1;
- u32 saddr;
- u32 dport;
- u32 dattr;
- u32 res2;
- u32 res3;
- u32 dwcnt;
- u32 res4;
-};
+#include <linux/device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/machdep.h>
+
+#include "fsl_rio.h"
+
+#undef DEBUG_PW /* Port-Write debugging */
+
+#define RIO_PORT1_EDCSR 0x0640
+#define RIO_PORT2_EDCSR 0x0680
+#define RIO_PORT1_IECSR 0x10130
+#define RIO_PORT2_IECSR 0x101B0
+
+#define RIO_GCCSR 0x13c
+#define RIO_ESCSR 0x158
+#define ESCSR_CLEAR 0x07120204
+#define RIO_PORT2_ESCSR 0x178
+#define RIO_CCSR 0x15c
+#define RIO_LTLEDCSR_IER 0x80000000
+#define RIO_LTLEDCSR_PRT 0x01000000
+#define IECSR_CLEAR 0x80000000
+#define RIO_ISR_AACR 0x10120
+#define RIO_ISR_AACR_AA 0x1 /* Accept All ID */
+
+#define __fsl_read_rio_config(x, addr, err, op) \
+ __asm__ __volatile__( \
+ "1: "op" %1,0(%2)\n" \
+ " eieio\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: li %1,-1\n" \
+ " li %0,%3\n" \
+ " b 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ PPC_LONG_ALIGN "\n" \
+ PPC_LONG "1b,3b\n" \
+ ".text" \
+ : "=r" (err), "=r" (x) \
+ : "b" (addr), "i" (-EFAULT), "0" (err))
+
+void __iomem *rio_regs_win;
+void __iomem *rmu_regs_win;
+resource_size_t rio_law_start;
+
+struct fsl_rio_dbell *dbell;
+struct fsl_rio_pw *pw;
+
+#ifdef CONFIG_E500
+int fsl_rio_mcheck_exception(struct pt_regs *regs)
+{
+ const struct exception_table_entry *entry;
+ unsigned long reason;
-static u32 regs_win;
-static struct rio_atmu_regs *atmu_regs;
-static struct rio_atmu_regs *maint_atmu_regs;
-static struct rio_atmu_regs *dbell_atmu_regs;
-static u32 dbell_win;
-static u32 maint_win;
-static struct rio_msg_regs *msg_regs;
-
-static struct rio_dbell_ring {
- void *virt;
- dma_addr_t phys;
-} dbell_ring;
-
-static struct rio_msg_tx_ring {
- void *virt;
- dma_addr_t phys;
- void *virt_buffer[RIO_MAX_TX_RING_SIZE];
- dma_addr_t phys_buffer[RIO_MAX_TX_RING_SIZE];
- int tx_slot;
- int size;
- void *dev_id;
-} msg_tx_ring;
-
-static struct rio_msg_rx_ring {
- void *virt;
- dma_addr_t phys;
- void *virt_buffer[RIO_MAX_RX_RING_SIZE];
- int rx_slot;
- int size;
- void *dev_id;
-} msg_rx_ring;
+ if (!rio_regs_win)
+ return 0;
-/**
- * mpc85xx_rio_doorbell_send - Send a MPC85xx doorbell message
- * @index: ID of RapidIO interface
- * @destid: Destination ID of target device
- * @data: 16-bit info field of RapidIO doorbell message
- *
- * Sends a MPC85xx doorbell message. Returns %0 on success or
- * %-EINVAL on failure.
- */
-static int mpc85xx_rio_doorbell_send(int index, u16 destid, u16 data)
-{
- pr_debug("mpc85xx_doorbell_send: index %d destid %4.4x data %4.4x\n",
- index, destid, data);
- out_be32((void *)&dbell_atmu_regs->rowtar, destid << 22);
- out_be16((void *)(dbell_win), data);
+ reason = in_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR));
+ if (reason & (RIO_LTLEDCSR_IER | RIO_LTLEDCSR_PRT)) {
+ /* Check if we are prepared to handle this fault */
+ entry = search_exception_tables(regs->nip);
+ if (entry) {
+ pr_debug("RIO: %s - MC Exception handled\n",
+ __func__);
+ out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR),
+ 0);
+ regs->msr |= MSR_RI;
+ regs->nip = entry->fixup;
+ return 1;
+ }
+ }
return 0;
}
+EXPORT_SYMBOL_GPL(fsl_rio_mcheck_exception);
+#endif
/**
- * mpc85xx_local_config_read - Generate a MPC85xx local config space read
+ * fsl_local_config_read - Generate a MPC85xx local config space read
+ * @mport: RapidIO master port info
* @index: ID of RapdiIO interface
* @offset: Offset into configuration space
* @len: Length (in bytes) of the maintenance transaction
@@ -173,17 +121,20 @@ static int mpc85xx_rio_doorbell_send(int index, u16 destid, u16 data)
* Generates a MPC85xx local configuration space read. Returns %0 on
* success or %-EINVAL on failure.
*/
-static int mpc85xx_local_config_read(int index, u32 offset, int len, u32 * data)
+static int fsl_local_config_read(struct rio_mport *mport,
+ int index, u32 offset, int len, u32 *data)
{
- pr_debug("mpc85xx_local_config_read: index %d offset %8.8x\n", index,
+ struct rio_priv *priv = mport->priv;
+ pr_debug("fsl_local_config_read: index %d offset %8.8x\n", index,
offset);
- *data = in_be32((void *)(regs_win + offset));
+ *data = in_be32(priv->regs_win + offset);
return 0;
}
/**
- * mpc85xx_local_config_write - Generate a MPC85xx local config space write
+ * fsl_local_config_write - Generate a MPC85xx local config space write
+ * @mport: RapidIO master port info
* @index: ID of RapdiIO interface
* @offset: Offset into configuration space
* @len: Length (in bytes) of the maintenance transaction
@@ -192,18 +143,21 @@ static int mpc85xx_local_config_read(int index, u32 offset, int len, u32 * data)
* Generates a MPC85xx local configuration space write. Returns %0 on
* success or %-EINVAL on failure.
*/
-static int mpc85xx_local_config_write(int index, u32 offset, int len, u32 data)
+static int fsl_local_config_write(struct rio_mport *mport,
+ int index, u32 offset, int len, u32 data)
{
+ struct rio_priv *priv = mport->priv;
pr_debug
- ("mpc85xx_local_config_write: index %d offset %8.8x data %8.8x\n",
- index, offset, data);
- out_be32((void *)(regs_win + offset), data);
+ ("fsl_local_config_write: index %d offset %8.8x data %8.8x\n",
+ index, offset, data);
+ out_be32(priv->regs_win + offset, data);
return 0;
}
/**
- * mpc85xx_rio_config_read - Generate a MPC85xx read maintenance transaction
+ * fsl_rio_config_read - Generate a MPC85xx read maintenance transaction
+ * @mport: RapidIO master port info
* @index: ID of RapdiIO interface
* @destid: Destination ID of transaction
* @hopcount: Number of hops to target device
@@ -215,35 +169,55 @@ static int mpc85xx_local_config_write(int index, u32 offset, int len, u32 data)
* success or %-EINVAL on failure.
*/
static int
-mpc85xx_rio_config_read(int index, u16 destid, u8 hopcount, u32 offset, int len,
- u32 * val)
+fsl_rio_config_read(struct rio_mport *mport, int index, u16 destid,
+ u8 hopcount, u32 offset, int len, u32 *val)
{
+ struct rio_priv *priv = mport->priv;
u8 *data;
+ u32 rval, err = 0;
pr_debug
- ("mpc85xx_rio_config_read: index %d destid %d hopcount %d offset %8.8x len %d\n",
- index, destid, hopcount, offset, len);
- out_be32((void *)&maint_atmu_regs->rowtar,
- (destid << 22) | (hopcount << 12) | ((offset & ~0x3) >> 9));
+ ("fsl_rio_config_read:"
+ " index %d destid %d hopcount %d offset %8.8x len %d\n",
+ index, destid, hopcount, offset, len);
+
+ /* 16MB maintenance window possible */
+ /* allow only aligned access to maintenance registers */
+ if (offset > (0x1000000 - len) || !IS_ALIGNED(offset, len))
+ return -EINVAL;
+
+ out_be32(&priv->maint_atmu_regs->rowtar,
+ (destid << 22) | (hopcount << 12) | (offset >> 12));
+ out_be32(&priv->maint_atmu_regs->rowtear, (destid >> 10));
- data = (u8 *) maint_win + offset;
+ data = (u8 *) priv->maint_win + (offset & (RIO_MAINT_WIN_SIZE - 1));
switch (len) {
case 1:
- *val = in_8((u8 *) data);
+ __fsl_read_rio_config(rval, data, err, "lbz");
break;
case 2:
- *val = in_be16((u16 *) data);
+ __fsl_read_rio_config(rval, data, err, "lhz");
break;
- default:
- *val = in_be32((u32 *) data);
+ case 4:
+ __fsl_read_rio_config(rval, data, err, "lwz");
break;
+ default:
+ return -EINVAL;
}
- return 0;
+ if (err) {
+ pr_debug("RIO: cfg_read error %d for %x:%x:%x\n",
+ err, destid, hopcount, offset);
+ }
+
+ *val = rval;
+
+ return err;
}
/**
- * mpc85xx_rio_config_write - Generate a MPC85xx write maintenance transaction
+ * fsl_rio_config_write - Generate a MPC85xx write maintenance transaction
+ * @mport: RapidIO master port info
* @index: ID of RapdiIO interface
* @destid: Destination ID of transaction
* @hopcount: Number of hops to target device
@@ -255,17 +229,26 @@ mpc85xx_rio_config_read(int index, u16 destid, u8 hopcount, u32 offset, int len,
* success or %-EINVAL on failure.
*/
static int
-mpc85xx_rio_config_write(int index, u16 destid, u8 hopcount, u32 offset,
- int len, u32 val)
+fsl_rio_config_write(struct rio_mport *mport, int index, u16 destid,
+ u8 hopcount, u32 offset, int len, u32 val)
{
+ struct rio_priv *priv = mport->priv;
u8 *data;
pr_debug
- ("mpc85xx_rio_config_write: index %d destid %d hopcount %d offset %8.8x len %d val %8.8x\n",
- index, destid, hopcount, offset, len, val);
- out_be32((void *)&maint_atmu_regs->rowtar,
- (destid << 22) | (hopcount << 12) | ((offset & ~0x3) >> 9));
+ ("fsl_rio_config_write:"
+ " index %d destid %d hopcount %d offset %8.8x len %d val %8.8x\n",
+ index, destid, hopcount, offset, len, val);
- data = (u8 *) maint_win + offset;
+ /* 16MB maintenance windows possible */
+ /* allow only aligned access to maintenance registers */
+ if (offset > (0x1000000 - len) || !IS_ALIGNED(offset, len))
+ return -EINVAL;
+
+ out_be32(&priv->maint_atmu_regs->rowtar,
+ (destid << 22) | (hopcount << 12) | (offset >> 12));
+ out_be32(&priv->maint_atmu_regs->rowtear, (destid >> 10));
+
+ data = (u8 *) priv->maint_win + (offset & (RIO_MAINT_WIN_SIZE - 1));
switch (len) {
case 1:
out_8((u8 *) data, val);
@@ -273,660 +256,432 @@ mpc85xx_rio_config_write(int index, u16 destid, u8 hopcount, u32 offset,
case 2:
out_be16((u16 *) data, val);
break;
- default:
+ case 4:
out_be32((u32 *) data, val);
break;
+ default:
+ return -EINVAL;
}
return 0;
}
-/**
- * rio_hw_add_outb_message - Add message to the MPC85xx outbound message queue
- * @mport: Master port with outbound message queue
- * @rdev: Target of outbound message
- * @mbox: Outbound mailbox
- * @buffer: Message to add to outbound queue
- * @len: Length of message
- *
- * Adds the @buffer message to the MPC85xx outbound message queue. Returns
- * %0 on success or %-EINVAL on failure.
- */
-int
-rio_hw_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox,
- void *buffer, size_t len)
+void fsl_rio_port_error_handler(int offset)
{
- u32 omr;
- struct rio_tx_desc *desc =
- (struct rio_tx_desc *)msg_tx_ring.virt + msg_tx_ring.tx_slot;
- int ret = 0;
-
- pr_debug
- ("RIO: rio_hw_add_outb_message(): destid %4.4x mbox %d buffer %8.8x len %8.8x\n",
- rdev->destid, mbox, (int)buffer, len);
-
- if ((len < 8) || (len > RIO_MAX_MSG_SIZE)) {
- ret = -EINVAL;
- goto out;
+ /*XXX: Error recovery is not implemented, we just clear errors */
+ out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), 0);
+
+ if (offset == 0) {
+ out_be32((u32 *)(rio_regs_win + RIO_PORT1_EDCSR), 0);
+ out_be32((u32 *)(rio_regs_win + RIO_PORT1_IECSR), IECSR_CLEAR);
+ out_be32((u32 *)(rio_regs_win + RIO_ESCSR), ESCSR_CLEAR);
+ } else {
+ out_be32((u32 *)(rio_regs_win + RIO_PORT2_EDCSR), 0);
+ out_be32((u32 *)(rio_regs_win + RIO_PORT2_IECSR), IECSR_CLEAR);
+ out_be32((u32 *)(rio_regs_win + RIO_PORT2_ESCSR), ESCSR_CLEAR);
}
-
- /* Copy and clear rest of buffer */
- memcpy(msg_tx_ring.virt_buffer[msg_tx_ring.tx_slot], buffer, len);
- if (len < (RIO_MAX_MSG_SIZE - 4))
- memset((void *)((u32) msg_tx_ring.
- virt_buffer[msg_tx_ring.tx_slot] + len), 0,
- RIO_MAX_MSG_SIZE - len);
-
- /* Set mbox field for message */
- desc->dport = mbox & 0x3;
-
- /* Enable EOMI interrupt, set priority, and set destid */
- desc->dattr = 0x28000000 | (rdev->destid << 2);
-
- /* Set transfer size aligned to next power of 2 (in double words) */
- desc->dwcnt = is_power_of_2(len) ? len : 1 << get_bitmask_order(len);
-
- /* Set snooping and source buffer address */
- desc->saddr = 0x00000004 | msg_tx_ring.phys_buffer[msg_tx_ring.tx_slot];
-
- /* Increment enqueue pointer */
- omr = in_be32((void *)&msg_regs->omr);
- out_be32((void *)&msg_regs->omr, omr | RIO_MSG_OMR_MUI);
-
- /* Go to next descriptor */
- if (++msg_tx_ring.tx_slot == msg_tx_ring.size)
- msg_tx_ring.tx_slot = 0;
-
- out:
- return ret;
}
-
-EXPORT_SYMBOL_GPL(rio_hw_add_outb_message);
-
-/**
- * mpc85xx_rio_tx_handler - MPC85xx outbound message interrupt handler
- * @irq: Linux interrupt number
- * @dev_instance: Pointer to interrupt-specific data
- *
- * Handles outbound message interrupts. Executes a register outbound
- * mailbox event handler and acks the interrupt occurrence.
- */
-static irqreturn_t
-mpc85xx_rio_tx_handler(int irq, void *dev_instance)
+static inline void fsl_rio_info(struct device *dev, u32 ccsr)
{
- int osr;
- struct rio_mport *port = (struct rio_mport *)dev_instance;
-
- osr = in_be32((void *)&msg_regs->osr);
-
- if (osr & RIO_MSG_OSR_TE) {
- pr_info("RIO: outbound message transmission error\n");
- out_be32((void *)&msg_regs->osr, RIO_MSG_OSR_TE);
- goto out;
- }
-
- if (osr & RIO_MSG_OSR_QOI) {
- pr_info("RIO: outbound message queue overflow\n");
- out_be32((void *)&msg_regs->osr, RIO_MSG_OSR_QOI);
- goto out;
- }
-
- if (osr & RIO_MSG_OSR_EOMI) {
- u32 dqp = in_be32((void *)&msg_regs->odqdpar);
- int slot = (dqp - msg_tx_ring.phys) >> 5;
- port->outb_msg[0].mcback(port, msg_tx_ring.dev_id, -1, slot);
-
- /* Ack the end-of-message interrupt */
- out_be32((void *)&msg_regs->osr, RIO_MSG_OSR_EOMI);
+ const char *str;
+ if (ccsr & 1) {
+ /* Serial phy */
+ switch (ccsr >> 30) {
+ case 0:
+ str = "1";
+ break;
+ case 1:
+ str = "4";
+ break;
+ default:
+ str = "Unknown";
+ break;
+ }
+ dev_info(dev, "Hardware port width: %s\n", str);
+
+ switch ((ccsr >> 27) & 7) {
+ case 0:
+ str = "Single-lane 0";
+ break;
+ case 1:
+ str = "Single-lane 2";
+ break;
+ case 2:
+ str = "Four-lane";
+ break;
+ default:
+ str = "Unknown";
+ break;
+ }
+ dev_info(dev, "Training connection status: %s\n", str);
+ } else {
+ /* Parallel phy */
+ if (!(ccsr & 0x80000000))
+ dev_info(dev, "Output port operating in 8-bit mode\n");
+ if (!(ccsr & 0x08000000))
+ dev_info(dev, "Input port operating in 8-bit mode\n");
}
-
- out:
- return IRQ_HANDLED;
}
/**
- * rio_open_outb_mbox - Initialize MPC85xx outbound mailbox
- * @mport: Master port implementing the outbound message unit
- * @dev_id: Device specific pointer to pass on event
- * @mbox: Mailbox to open
- * @entries: Number of entries in the outbound mailbox ring
+ * fsl_rio_setup - Setup Freescale PowerPC RapidIO interface
+ * @dev: platform_device pointer
*
- * Initializes buffer ring, request the outbound message interrupt,
- * and enables the outbound message unit. Returns %0 on success and
- * %-EINVAL or %-ENOMEM on failure.
+ * Initializes MPC85xx RapidIO hardware interface, configures
+ * master port with system-specific info, and registers the
+ * master port with the RapidIO subsystem.
*/
-int rio_open_outb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries)
+int fsl_rio_setup(struct platform_device *dev)
{
- int i, j, rc = 0;
-
- if ((entries < RIO_MIN_TX_RING_SIZE) ||
- (entries > RIO_MAX_TX_RING_SIZE) || (!is_power_of_2(entries))) {
- rc = -EINVAL;
- goto out;
+ struct rio_ops *ops;
+ struct rio_mport *port;
+ struct rio_priv *priv;
+ int rc = 0;
+ const u32 *dt_range, *cell, *port_index;
+ u32 active_ports = 0;
+ struct resource regs, rmu_regs;
+ struct device_node *np, *rmu_node;
+ int rlen;
+ u32 ccsr;
+ u64 range_start, range_size;
+ int paw, aw, sw;
+ u32 i;
+ static int tmp;
+ struct device_node *rmu_np[MAX_MSG_UNIT_NUM] = {NULL};
+
+ if (!dev->dev.of_node) {
+ dev_err(&dev->dev, "Device OF-Node is NULL");
+ return -ENODEV;
}
- /* Initialize shadow copy ring */
- msg_tx_ring.dev_id = dev_id;
- msg_tx_ring.size = entries;
-
- for (i = 0; i < msg_tx_ring.size; i++) {
- if (!
- (msg_tx_ring.virt_buffer[i] =
- dma_alloc_coherent(NULL, RIO_MSG_BUFFER_SIZE,
- &msg_tx_ring.phys_buffer[i],
- GFP_KERNEL))) {
- rc = -ENOMEM;
- for (j = 0; j < msg_tx_ring.size; j++)
- if (msg_tx_ring.virt_buffer[j])
- dma_free_coherent(NULL,
- RIO_MSG_BUFFER_SIZE,
- msg_tx_ring.
- virt_buffer[j],
- msg_tx_ring.
- phys_buffer[j]);
- goto out;
- }
+ rc = of_address_to_resource(dev->dev.of_node, 0, &regs);
+ if (rc) {
+ dev_err(&dev->dev, "Can't get %s property 'reg'\n",
+ dev->dev.of_node->full_name);
+ return -EFAULT;
}
+ dev_info(&dev->dev, "Of-device full name %s\n",
+ dev->dev.of_node->full_name);
+ dev_info(&dev->dev, "Regs: %pR\n", &regs);
- /* Initialize outbound message descriptor ring */
- if (!(msg_tx_ring.virt = dma_alloc_coherent(NULL,
- msg_tx_ring.size *
- RIO_MSG_DESC_SIZE,
- &msg_tx_ring.phys,
- GFP_KERNEL))) {
+ rio_regs_win = ioremap(regs.start, resource_size(&regs));
+ if (!rio_regs_win) {
+ dev_err(&dev->dev, "Unable to map rio register window\n");
rc = -ENOMEM;
- goto out_dma;
+ goto err_rio_regs;
}
- memset(msg_tx_ring.virt, 0, msg_tx_ring.size * RIO_MSG_DESC_SIZE);
- msg_tx_ring.tx_slot = 0;
-
- /* Point dequeue/enqueue pointers at first entry in ring */
- out_be32((void *)&msg_regs->odqdpar, msg_tx_ring.phys);
- out_be32((void *)&msg_regs->odqepar, msg_tx_ring.phys);
-
- /* Configure for snooping */
- out_be32((void *)&msg_regs->osar, 0x00000004);
-
- /* Clear interrupt status */
- out_be32((void *)&msg_regs->osr, 0x000000b3);
-
- /* Hook up outbound message handler */
- if ((rc =
- request_irq(MPC85xx_IRQ_RIO_TX, mpc85xx_rio_tx_handler, 0,
- "msg_tx", (void *)mport)) < 0)
- goto out_irq;
-
- /*
- * Configure outbound message unit
- * Snooping
- * Interrupts (all enabled, except QEIE)
- * Chaining mode
- * Disable
- */
- out_be32((void *)&msg_regs->omr, 0x00100220);
-
- /* Set number of entries */
- out_be32((void *)&msg_regs->omr,
- in_be32((void *)&msg_regs->omr) |
- ((get_bitmask_order(entries) - 2) << 12));
-
- /* Now enable the unit */
- out_be32((void *)&msg_regs->omr, in_be32((void *)&msg_regs->omr) | 0x1);
-
- out:
- return rc;
-
- out_irq:
- dma_free_coherent(NULL, msg_tx_ring.size * RIO_MSG_DESC_SIZE,
- msg_tx_ring.virt, msg_tx_ring.phys);
-
- out_dma:
- for (i = 0; i < msg_tx_ring.size; i++)
- dma_free_coherent(NULL, RIO_MSG_BUFFER_SIZE,
- msg_tx_ring.virt_buffer[i],
- msg_tx_ring.phys_buffer[i]);
-
- return rc;
-}
-
-/**
- * rio_close_outb_mbox - Shut down MPC85xx outbound mailbox
- * @mport: Master port implementing the outbound message unit
- * @mbox: Mailbox to close
- *
- * Disables the outbound message unit, free all buffers, and
- * frees the outbound message interrupt.
- */
-void rio_close_outb_mbox(struct rio_mport *mport, int mbox)
-{
- /* Disable inbound message unit */
- out_be32((void *)&msg_regs->omr, 0);
-
- /* Free ring */
- dma_free_coherent(NULL, msg_tx_ring.size * RIO_MSG_DESC_SIZE,
- msg_tx_ring.virt, msg_tx_ring.phys);
-
- /* Free interrupt */
- free_irq(MPC85xx_IRQ_RIO_TX, (void *)mport);
-}
-
-/**
- * mpc85xx_rio_rx_handler - MPC85xx inbound message interrupt handler
- * @irq: Linux interrupt number
- * @dev_instance: Pointer to interrupt-specific data
- *
- * Handles inbound message interrupts. Executes a registered inbound
- * mailbox event handler and acks the interrupt occurrence.
- */
-static irqreturn_t
-mpc85xx_rio_rx_handler(int irq, void *dev_instance)
-{
- int isr;
- struct rio_mport *port = (struct rio_mport *)dev_instance;
- isr = in_be32((void *)&msg_regs->isr);
-
- if (isr & RIO_MSG_ISR_TE) {
- pr_info("RIO: inbound message reception error\n");
- out_be32((void *)&msg_regs->isr, RIO_MSG_ISR_TE);
- goto out;
+ ops = kzalloc(sizeof(struct rio_ops), GFP_KERNEL);
+ if (!ops) {
+ rc = -ENOMEM;
+ goto err_ops;
}
-
- /* XXX Need to check/dispatch until queue empty */
- if (isr & RIO_MSG_ISR_DIQI) {
- /*
- * We implement *only* mailbox 0, but can receive messages
- * for any mailbox/letter to that mailbox destination. So,
- * make the callback with an unknown/invalid mailbox number
- * argument.
- */
- port->inb_msg[0].mcback(port, msg_rx_ring.dev_id, -1, -1);
-
- /* Ack the queueing interrupt */
- out_be32((void *)&msg_regs->isr, RIO_MSG_ISR_DIQI);
+ ops->lcread = fsl_local_config_read;
+ ops->lcwrite = fsl_local_config_write;
+ ops->cread = fsl_rio_config_read;
+ ops->cwrite = fsl_rio_config_write;
+ ops->dsend = fsl_rio_doorbell_send;
+ ops->pwenable = fsl_rio_pw_enable;
+ ops->open_outb_mbox = fsl_open_outb_mbox;
+ ops->open_inb_mbox = fsl_open_inb_mbox;
+ ops->close_outb_mbox = fsl_close_outb_mbox;
+ ops->close_inb_mbox = fsl_close_inb_mbox;
+ ops->add_outb_message = fsl_add_outb_message;
+ ops->add_inb_buffer = fsl_add_inb_buffer;
+ ops->get_inb_message = fsl_get_inb_message;
+
+ rmu_node = of_parse_phandle(dev->dev.of_node, "fsl,srio-rmu-handle", 0);
+ if (!rmu_node) {
+ dev_err(&dev->dev, "No valid fsl,srio-rmu-handle property\n");
+ goto err_rmu;
}
-
- out:
- return IRQ_HANDLED;
-}
-
-/**
- * rio_open_inb_mbox - Initialize MPC85xx inbound mailbox
- * @mport: Master port implementing the inbound message unit
- * @dev_id: Device specific pointer to pass on event
- * @mbox: Mailbox to open
- * @entries: Number of entries in the inbound mailbox ring
- *
- * Initializes buffer ring, request the inbound message interrupt,
- * and enables the inbound message unit. Returns %0 on success
- * and %-EINVAL or %-ENOMEM on failure.
- */
-int rio_open_inb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries)
-{
- int i, rc = 0;
-
- if ((entries < RIO_MIN_RX_RING_SIZE) ||
- (entries > RIO_MAX_RX_RING_SIZE) || (!is_power_of_2(entries))) {
- rc = -EINVAL;
- goto out;
+ rc = of_address_to_resource(rmu_node, 0, &rmu_regs);
+ if (rc) {
+ dev_err(&dev->dev, "Can't get %s property 'reg'\n",
+ rmu_node->full_name);
+ goto err_rmu;
}
-
- /* Initialize client buffer ring */
- msg_rx_ring.dev_id = dev_id;
- msg_rx_ring.size = entries;
- msg_rx_ring.rx_slot = 0;
- for (i = 0; i < msg_rx_ring.size; i++)
- msg_rx_ring.virt_buffer[i] = NULL;
-
- /* Initialize inbound message ring */
- if (!(msg_rx_ring.virt = dma_alloc_coherent(NULL,
- msg_rx_ring.size *
- RIO_MAX_MSG_SIZE,
- &msg_rx_ring.phys,
- GFP_KERNEL))) {
+ rmu_regs_win = ioremap(rmu_regs.start, resource_size(&rmu_regs));
+ if (!rmu_regs_win) {
+ dev_err(&dev->dev, "Unable to map rmu register window\n");
rc = -ENOMEM;
- goto out;
+ goto err_rmu;
}
-
- /* Point dequeue/enqueue pointers at first entry in ring */
- out_be32((void *)&msg_regs->ifqdpar, (u32) msg_rx_ring.phys);
- out_be32((void *)&msg_regs->ifqepar, (u32) msg_rx_ring.phys);
-
- /* Clear interrupt status */
- out_be32((void *)&msg_regs->isr, 0x00000091);
-
- /* Hook up inbound message handler */
- if ((rc =
- request_irq(MPC85xx_IRQ_RIO_RX, mpc85xx_rio_rx_handler, 0,
- "msg_rx", (void *)mport)) < 0) {
- dma_free_coherent(NULL, RIO_MSG_BUFFER_SIZE,
- msg_tx_ring.virt_buffer[i],
- msg_tx_ring.phys_buffer[i]);
- goto out;
+ for_each_compatible_node(np, NULL, "fsl,srio-msg-unit") {
+ rmu_np[tmp] = np;
+ tmp++;
}
- /*
- * Configure inbound message unit:
- * Snooping
- * 4KB max message size
- * Unmask all interrupt sources
- * Disable
- */
- out_be32((void *)&msg_regs->imr, 0x001b0060);
-
- /* Set number of queue entries */
- out_be32((void *)&msg_regs->imr,
- in_be32((void *)&msg_regs->imr) |
- ((get_bitmask_order(entries) - 2) << 12));
-
- /* Now enable the unit */
- out_be32((void *)&msg_regs->imr, in_be32((void *)&msg_regs->imr) | 0x1);
-
- out:
- return rc;
-}
-
-/**
- * rio_close_inb_mbox - Shut down MPC85xx inbound mailbox
- * @mport: Master port implementing the inbound message unit
- * @mbox: Mailbox to close
- *
- * Disables the inbound message unit, free all buffers, and
- * frees the inbound message interrupt.
- */
-void rio_close_inb_mbox(struct rio_mport *mport, int mbox)
-{
- /* Disable inbound message unit */
- out_be32((void *)&msg_regs->imr, 0);
-
- /* Free ring */
- dma_free_coherent(NULL, msg_rx_ring.size * RIO_MAX_MSG_SIZE,
- msg_rx_ring.virt, msg_rx_ring.phys);
-
- /* Free interrupt */
- free_irq(MPC85xx_IRQ_RIO_RX, (void *)mport);
-}
-
-/**
- * rio_hw_add_inb_buffer - Add buffer to the MPC85xx inbound message queue
- * @mport: Master port implementing the inbound message unit
- * @mbox: Inbound mailbox number
- * @buf: Buffer to add to inbound queue
- *
- * Adds the @buf buffer to the MPC85xx inbound message queue. Returns
- * %0 on success or %-EINVAL on failure.
- */
-int rio_hw_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf)
-{
- int rc = 0;
-
- pr_debug("RIO: rio_hw_add_inb_buffer(), msg_rx_ring.rx_slot %d\n",
- msg_rx_ring.rx_slot);
-
- if (msg_rx_ring.virt_buffer[msg_rx_ring.rx_slot]) {
- printk(KERN_ERR
- "RIO: error adding inbound buffer %d, buffer exists\n",
- msg_rx_ring.rx_slot);
- rc = -EINVAL;
- goto out;
+ /*set up doobell node*/
+ np = of_find_compatible_node(NULL, NULL, "fsl,srio-dbell-unit");
+ if (!np) {
+ dev_err(&dev->dev, "No fsl,srio-dbell-unit node\n");
+ rc = -ENODEV;
+ goto err_dbell;
}
-
- msg_rx_ring.virt_buffer[msg_rx_ring.rx_slot] = buf;
- if (++msg_rx_ring.rx_slot == msg_rx_ring.size)
- msg_rx_ring.rx_slot = 0;
-
- out:
- return rc;
-}
-
-EXPORT_SYMBOL_GPL(rio_hw_add_inb_buffer);
-
-/**
- * rio_hw_get_inb_message - Fetch inbound message from the MPC85xx message unit
- * @mport: Master port implementing the inbound message unit
- * @mbox: Inbound mailbox number
- *
- * Gets the next available inbound message from the inbound message queue.
- * A pointer to the message is returned on success or NULL on failure.
- */
-void *rio_hw_get_inb_message(struct rio_mport *mport, int mbox)
-{
- u32 imr;
- u32 phys_buf, virt_buf;
- void *buf = NULL;
- int buf_idx;
-
- phys_buf = in_be32((void *)&msg_regs->ifqdpar);
-
- /* If no more messages, then bail out */
- if (phys_buf == in_be32((void *)&msg_regs->ifqepar))
- goto out2;
-
- virt_buf = (u32) msg_rx_ring.virt + (phys_buf - msg_rx_ring.phys);
- buf_idx = (phys_buf - msg_rx_ring.phys) / RIO_MAX_MSG_SIZE;
- buf = msg_rx_ring.virt_buffer[buf_idx];
-
- if (!buf) {
- printk(KERN_ERR
- "RIO: inbound message copy failed, no buffers\n");
- goto out1;
+ dbell = kzalloc(sizeof(struct fsl_rio_dbell), GFP_KERNEL);
+ if (!(dbell)) {
+ dev_err(&dev->dev, "Can't alloc memory for 'fsl_rio_dbell'\n");
+ rc = -ENOMEM;
+ goto err_dbell;
}
+ dbell->dev = &dev->dev;
+ dbell->bellirq = irq_of_parse_and_map(np, 1);
+ dev_info(&dev->dev, "bellirq: %d\n", dbell->bellirq);
+
+ aw = of_n_addr_cells(np);
+ dt_range = of_get_property(np, "reg", &rlen);
+ if (!dt_range) {
+ pr_err("%s: unable to find 'reg' property\n",
+ np->full_name);
+ rc = -ENOMEM;
+ goto err_pw;
+ }
+ range_start = of_read_number(dt_range, aw);
+ dbell->dbell_regs = (struct rio_dbell_regs *)(rmu_regs_win +
+ (u32)range_start);
+
+ /*set up port write node*/
+ np = of_find_compatible_node(NULL, NULL, "fsl,srio-port-write-unit");
+ if (!np) {
+ dev_err(&dev->dev, "No fsl,srio-port-write-unit node\n");
+ rc = -ENODEV;
+ goto err_pw;
+ }
+ pw = kzalloc(sizeof(struct fsl_rio_pw), GFP_KERNEL);
+ if (!(pw)) {
+ dev_err(&dev->dev, "Can't alloc memory for 'fsl_rio_pw'\n");
+ rc = -ENOMEM;
+ goto err_pw;
+ }
+ pw->dev = &dev->dev;
+ pw->pwirq = irq_of_parse_and_map(np, 0);
+ dev_info(&dev->dev, "pwirq: %d\n", pw->pwirq);
+ aw = of_n_addr_cells(np);
+ dt_range = of_get_property(np, "reg", &rlen);
+ if (!dt_range) {
+ pr_err("%s: unable to find 'reg' property\n",
+ np->full_name);
+ rc = -ENOMEM;
+ goto err;
+ }
+ range_start = of_read_number(dt_range, aw);
+ pw->pw_regs = (struct rio_pw_regs *)(rmu_regs_win + (u32)range_start);
+
+ /*set up ports node*/
+ for_each_child_of_node(dev->dev.of_node, np) {
+ port_index = of_get_property(np, "cell-index", NULL);
+ if (!port_index) {
+ dev_err(&dev->dev, "Can't get %s property 'cell-index'\n",
+ np->full_name);
+ continue;
+ }
- /* Copy max message size, caller is expected to allocate that big */
- memcpy(buf, (void *)virt_buf, RIO_MAX_MSG_SIZE);
-
- /* Clear the available buffer */
- msg_rx_ring.virt_buffer[buf_idx] = NULL;
-
- out1:
- imr = in_be32((void *)&msg_regs->imr);
- out_be32((void *)&msg_regs->imr, imr | RIO_MSG_IMR_MI);
+ dt_range = of_get_property(np, "ranges", &rlen);
+ if (!dt_range) {
+ dev_err(&dev->dev, "Can't get %s property 'ranges'\n",
+ np->full_name);
+ continue;
+ }
- out2:
- return buf;
-}
+ /* Get node address wide */
+ cell = of_get_property(np, "#address-cells", NULL);
+ if (cell)
+ aw = *cell;
+ else
+ aw = of_n_addr_cells(np);
+ /* Get node size wide */
+ cell = of_get_property(np, "#size-cells", NULL);
+ if (cell)
+ sw = *cell;
+ else
+ sw = of_n_size_cells(np);
+ /* Get parent address wide wide */
+ paw = of_n_addr_cells(np);
+ range_start = of_read_number(dt_range + aw, paw);
+ range_size = of_read_number(dt_range + aw + paw, sw);
+
+ dev_info(&dev->dev, "%s: LAW start 0x%016llx, size 0x%016llx.\n",
+ np->full_name, range_start, range_size);
+
+ port = kzalloc(sizeof(struct rio_mport), GFP_KERNEL);
+ if (!port)
+ continue;
+
+ i = *port_index - 1;
+ port->index = (unsigned char)i;
+
+ priv = kzalloc(sizeof(struct rio_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&dev->dev, "Can't alloc memory for 'priv'\n");
+ kfree(port);
+ continue;
+ }
-EXPORT_SYMBOL_GPL(rio_hw_get_inb_message);
+ INIT_LIST_HEAD(&port->dbells);
+ port->iores.start = range_start;
+ port->iores.end = port->iores.start + range_size - 1;
+ port->iores.flags = IORESOURCE_MEM;
+ port->iores.name = "rio_io_win";
+
+ if (request_resource(&iomem_resource, &port->iores) < 0) {
+ dev_err(&dev->dev, "RIO: Error requesting master port region"
+ " 0x%016llx-0x%016llx\n",
+ (u64)port->iores.start, (u64)port->iores.end);
+ kfree(priv);
+ kfree(port);
+ continue;
+ }
+ sprintf(port->name, "RIO mport %d", i);
+
+ priv->dev = &dev->dev;
+ port->dev.parent = &dev->dev;
+ port->ops = ops;
+ port->priv = priv;
+ port->phys_efptr = 0x100;
+ priv->regs_win = rio_regs_win;
+
+ /* Probe the master port phy type */
+ ccsr = in_be32(priv->regs_win + RIO_CCSR + i*0x20);
+ port->phy_type = (ccsr & 1) ? RIO_PHY_SERIAL : RIO_PHY_PARALLEL;
+ if (port->phy_type == RIO_PHY_PARALLEL) {
+ dev_err(&dev->dev, "RIO: Parallel PHY type, unsupported port type!\n");
+ release_resource(&port->iores);
+ kfree(priv);
+ kfree(port);
+ continue;
+ }
+ dev_info(&dev->dev, "RapidIO PHY type: Serial\n");
+ /* Checking the port training status */
+ if (in_be32((priv->regs_win + RIO_ESCSR + i*0x20)) & 1) {
+ dev_err(&dev->dev, "Port %d is not ready. "
+ "Try to restart connection...\n", i);
+ /* Disable ports */
+ out_be32(priv->regs_win
+ + RIO_CCSR + i*0x20, 0);
+ /* Set 1x lane */
+ setbits32(priv->regs_win
+ + RIO_CCSR + i*0x20, 0x02000000);
+ /* Enable ports */
+ setbits32(priv->regs_win
+ + RIO_CCSR + i*0x20, 0x00600000);
+ msleep(100);
+ if (in_be32((priv->regs_win
+ + RIO_ESCSR + i*0x20)) & 1) {
+ dev_err(&dev->dev,
+ "Port %d restart failed.\n", i);
+ release_resource(&port->iores);
+ kfree(priv);
+ kfree(port);
+ continue;
+ }
+ dev_info(&dev->dev, "Port %d restart success!\n", i);
+ }
+ fsl_rio_info(&dev->dev, ccsr);
+
+ port->sys_size = (in_be32((priv->regs_win + RIO_PEF_CAR))
+ & RIO_PEF_CTLS) >> 4;
+ dev_info(&dev->dev, "RapidIO Common Transport System size: %d\n",
+ port->sys_size ? 65536 : 256);
+
+ if (rio_register_mport(port)) {
+ release_resource(&port->iores);
+ kfree(priv);
+ kfree(port);
+ continue;
+ }
+ if (port->host_deviceid >= 0)
+ out_be32(priv->regs_win + RIO_GCCSR, RIO_PORT_GEN_HOST |
+ RIO_PORT_GEN_MASTER | RIO_PORT_GEN_DISCOVERED);
+ else
+ out_be32(priv->regs_win + RIO_GCCSR,
+ RIO_PORT_GEN_MASTER);
-/**
- * mpc85xx_rio_dbell_handler - MPC85xx doorbell interrupt handler
- * @irq: Linux interrupt number
- * @dev_instance: Pointer to interrupt-specific data
- *
- * Handles doorbell interrupts. Parses a list of registered
- * doorbell event handlers and executes a matching event handler.
- */
-static irqreturn_t
-mpc85xx_rio_dbell_handler(int irq, void *dev_instance)
-{
- int dsr;
- struct rio_mport *port = (struct rio_mport *)dev_instance;
+ priv->atmu_regs = (struct rio_atmu_regs *)(priv->regs_win
+ + ((i == 0) ? RIO_ATMU_REGS_PORT1_OFFSET :
+ RIO_ATMU_REGS_PORT2_OFFSET));
- dsr = in_be32((void *)&msg_regs->dsr);
+ priv->maint_atmu_regs = priv->atmu_regs + 1;
- if (dsr & DOORBELL_DSR_TE) {
- pr_info("RIO: doorbell reception error\n");
- out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_TE);
- goto out;
- }
+ /* Set to receive any dist ID for serial RapidIO controller. */
+ if (port->phy_type == RIO_PHY_SERIAL)
+ out_be32((priv->regs_win
+ + RIO_ISR_AACR + i*0x80), RIO_ISR_AACR_AA);
- if (dsr & DOORBELL_DSR_QFI) {
- pr_info("RIO: doorbell queue full\n");
- out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_QFI);
- goto out;
- }
+ /* Configure maintenance transaction window */
+ out_be32(&priv->maint_atmu_regs->rowbar,
+ port->iores.start >> 12);
+ out_be32(&priv->maint_atmu_regs->rowar,
+ 0x80077000 | (ilog2(RIO_MAINT_WIN_SIZE) - 1));
- /* XXX Need to check/dispatch until queue empty */
- if (dsr & DOORBELL_DSR_DIQI) {
- u32 dmsg =
- (u32) dbell_ring.virt +
- (in_be32((void *)&msg_regs->dqdpar) & 0xfff);
- u32 dmr;
- struct rio_dbell *dbell;
- int found = 0;
-
- pr_debug
- ("RIO: processing doorbell, sid %2.2x tid %2.2x info %4.4x\n",
- DBELL_SID(dmsg), DBELL_TID(dmsg), DBELL_INF(dmsg));
-
- list_for_each_entry(dbell, &port->dbells, node) {
- if ((dbell->res->start <= DBELL_INF(dmsg)) &&
- (dbell->res->end >= DBELL_INF(dmsg))) {
- found = 1;
- break;
- }
- }
- if (found) {
- dbell->dinb(port, dbell->dev_id, DBELL_SID(dmsg), DBELL_TID(dmsg),
- DBELL_INF(dmsg));
- } else {
- pr_debug
- ("RIO: spurious doorbell, sid %2.2x tid %2.2x info %4.4x\n",
- DBELL_SID(dmsg), DBELL_TID(dmsg), DBELL_INF(dmsg));
- }
- dmr = in_be32((void *)&msg_regs->dmr);
- out_be32((void *)&msg_regs->dmr, dmr | DOORBELL_DMR_DI);
- out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_DIQI);
- }
+ priv->maint_win = ioremap(port->iores.start,
+ RIO_MAINT_WIN_SIZE);
- out:
- return IRQ_HANDLED;
-}
+ rio_law_start = range_start;
-/**
- * mpc85xx_rio_doorbell_init - MPC85xx doorbell interface init
- * @mport: Master port implementing the inbound doorbell unit
- *
- * Initializes doorbell unit hardware and inbound DMA buffer
- * ring. Called from mpc85xx_rio_setup(). Returns %0 on success
- * or %-ENOMEM on failure.
- */
-static int mpc85xx_rio_doorbell_init(struct rio_mport *mport)
-{
- int rc = 0;
+ fsl_rio_setup_rmu(port, rmu_np[i]);
- /* Map outbound doorbell window immediately after maintenance window */
- if (!(dbell_win =
- (u32) ioremap(mport->iores.start + RIO_MAINT_WIN_SIZE,
- RIO_DBELL_WIN_SIZE))) {
- printk(KERN_ERR
- "RIO: unable to map outbound doorbell window\n");
- rc = -ENOMEM;
- goto out;
- }
+ dbell->mport[i] = port;
- /* Initialize inbound doorbells */
- if (!(dbell_ring.virt = dma_alloc_coherent(NULL,
- 512 * DOORBELL_MESSAGE_SIZE,
- &dbell_ring.phys,
- GFP_KERNEL))) {
- printk(KERN_ERR "RIO: unable allocate inbound doorbell ring\n");
- rc = -ENOMEM;
- iounmap((void *)dbell_win);
- goto out;
+ active_ports++;
}
- /* Point dequeue/enqueue pointers at first entry in ring */
- out_be32((void *)&msg_regs->dqdpar, (u32) dbell_ring.phys);
- out_be32((void *)&msg_regs->dqepar, (u32) dbell_ring.phys);
-
- /* Clear interrupt status */
- out_be32((void *)&msg_regs->dsr, 0x00000091);
-
- /* Hook up doorbell handler */
- if ((rc =
- request_irq(MPC85xx_IRQ_RIO_BELL, mpc85xx_rio_dbell_handler, 0,
- "dbell_rx", (void *)mport) < 0)) {
- iounmap((void *)dbell_win);
- dma_free_coherent(NULL, 512 * DOORBELL_MESSAGE_SIZE,
- dbell_ring.virt, dbell_ring.phys);
- printk(KERN_ERR
- "MPC85xx RIO: unable to request inbound doorbell irq");
- goto out;
+ if (!active_ports) {
+ rc = -ENOLINK;
+ goto err;
}
- /* Configure doorbells for snooping, 512 entries, and enable */
- out_be32((void *)&msg_regs->dmr, 0x00108161);
+ fsl_rio_doorbell_init(dbell);
+ fsl_rio_port_write_init(pw);
- out:
+ return 0;
+err:
+ kfree(pw);
+ pw = NULL;
+err_pw:
+ kfree(dbell);
+ dbell = NULL;
+err_dbell:
+ iounmap(rmu_regs_win);
+ rmu_regs_win = NULL;
+err_rmu:
+ kfree(ops);
+err_ops:
+ iounmap(rio_regs_win);
+ rio_regs_win = NULL;
+err_rio_regs:
return rc;
}
-static char *cmdline = NULL;
-
-static int mpc85xx_rio_get_hdid(int index)
+/* The probe function for RapidIO peer-to-peer network.
+ */
+static int fsl_of_rio_rpn_probe(struct platform_device *dev)
{
- /* XXX Need to parse multiple entries in some format */
- if (!cmdline)
- return -1;
-
- return simple_strtol(cmdline, NULL, 0);
-}
+ printk(KERN_INFO "Setting up RapidIO peer-to-peer network %s\n",
+ dev->dev.of_node->full_name);
-static int mpc85xx_rio_get_cmdline(char *s)
-{
- if (!s)
- return 0;
+ return fsl_rio_setup(dev);
+};
- cmdline = s;
- return 1;
-}
+static const struct of_device_id fsl_of_rio_rpn_ids[] = {
+ {
+ .compatible = "fsl,srio",
+ },
+ {},
+};
-__setup("riohdid=", mpc85xx_rio_get_cmdline);
+static struct platform_driver fsl_of_rio_rpn_driver = {
+ .driver = {
+ .name = "fsl-of-rio",
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_of_rio_rpn_ids,
+ },
+ .probe = fsl_of_rio_rpn_probe,
+};
-/**
- * mpc85xx_rio_setup - Setup MPC85xx RapidIO interface
- * @law_start: Starting physical address of RapidIO LAW
- * @law_size: Size of RapidIO LAW
- *
- * Initializes MPC85xx RapidIO hardware interface, configures
- * master port with system-specific info, and registers the
- * master port with the RapidIO subsystem.
- */
-void mpc85xx_rio_setup(int law_start, int law_size)
+static __init int fsl_of_rio_rpn_init(void)
{
- struct rio_ops *ops;
- struct rio_mport *port;
-
- ops = kmalloc(sizeof(struct rio_ops), GFP_KERNEL);
- ops->lcread = mpc85xx_local_config_read;
- ops->lcwrite = mpc85xx_local_config_write;
- ops->cread = mpc85xx_rio_config_read;
- ops->cwrite = mpc85xx_rio_config_write;
- ops->dsend = mpc85xx_rio_doorbell_send;
-
- port = kmalloc(sizeof(struct rio_mport), GFP_KERNEL);
- port->id = 0;
- port->index = 0;
- INIT_LIST_HEAD(&port->dbells);
- port->iores.start = law_start;
- port->iores.end = law_start + law_size;
- port->iores.flags = IORESOURCE_MEM;
-
- rio_init_dbell_res(&port->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff);
- rio_init_mbox_res(&port->riores[RIO_INB_MBOX_RESOURCE], 0, 0);
- rio_init_mbox_res(&port->riores[RIO_OUTB_MBOX_RESOURCE], 0, 0);
- strcpy(port->name, "RIO0 mport");
-
- port->ops = ops;
- port->host_deviceid = mpc85xx_rio_get_hdid(port->id);
-
- rio_register_mport(port);
-
- regs_win = (u32) ioremap(RIO_REGS_BASE, 0x20000);
- atmu_regs = (struct rio_atmu_regs *)(regs_win + RIO_ATMU_REGS_OFFSET);
- maint_atmu_regs = atmu_regs + 1;
- dbell_atmu_regs = atmu_regs + 2;
- msg_regs = (struct rio_msg_regs *)(regs_win + RIO_MSG_REGS_OFFSET);
-
- /* Configure maintenance transaction window */
- out_be32((void *)&maint_atmu_regs->rowbar, 0x000c0000);
- out_be32((void *)&maint_atmu_regs->rowar, 0x80077015);
-
- maint_win = (u32) ioremap(law_start, RIO_MAINT_WIN_SIZE);
-
- /* Configure outbound doorbell window */
- out_be32((void *)&dbell_atmu_regs->rowbar, 0x000c0400);
- out_be32((void *)&dbell_atmu_regs->rowar, 0x8004200b);
- mpc85xx_rio_doorbell_init(port);
+ return platform_driver_register(&fsl_of_rio_rpn_driver);
}
+
+subsys_initcall(fsl_of_rio_rpn_init);
diff --git a/arch/powerpc/sysdev/fsl_rio.h b/arch/powerpc/sysdev/fsl_rio.h
index 6d3ff30b157..ae8e27405a0 100644
--- a/arch/powerpc/sysdev/fsl_rio.h
+++ b/arch/powerpc/sysdev/fsl_rio.h
@@ -1,5 +1,19 @@
/*
- * MPC85xx RapidIO definitions
+ * Freescale MPC85xx/MPC86xx RapidIO support
+ *
+ * Copyright 2009 Sysgo AG
+ * Thomas Moll <thomas.moll@sysgo.com>
+ * - fixed maintenance access routines, check for aligned access
+ *
+ * Copyright 2009 Integrated Device Technology, Inc.
+ * Alex Bounine <alexandre.bounine@idt.com>
+ * - Added Port-Write message handling
+ * - Added Machine Check exception handling
+ *
+ * Copyright (C) 2007, 2008, 2010, 2011 Freescale Semiconductor, Inc.
+ * Zhang Wei <wei.zhang@freescale.com>
+ * Lian Minghuan-B31939 <Minghuan.Lian@freescale.com>
+ * Liu Gang <Gang.Liu@freescale.com>
*
* Copyright 2005 MontaVista Software, Inc.
* Matt Porter <mporter@kernel.crashing.org>
@@ -10,11 +24,112 @@
* option) any later version.
*/
-#ifndef __PPC_SYSLIB_PPC85XX_RIO_H
-#define __PPC_SYSLIB_PPC85XX_RIO_H
+#ifndef __FSL_RIO_H
+#define __FSL_RIO_H
+
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/kfifo.h>
+
+#define RIO_REGS_WIN(mport) (((struct rio_priv *)(mport->priv))->regs_win)
+
+#define RIO_MAINT_WIN_SIZE 0x400000
+#define RIO_LTLEDCSR 0x0608
+
+#define DOORBELL_ROWAR_EN 0x80000000
+#define DOORBELL_ROWAR_TFLOWLV 0x08000000 /* highest priority level */
+#define DOORBELL_ROWAR_PCI 0x02000000 /* PCI window */
+#define DOORBELL_ROWAR_NREAD 0x00040000 /* NREAD */
+#define DOORBELL_ROWAR_MAINTRD 0x00070000 /* maintenance read */
+#define DOORBELL_ROWAR_RES 0x00002000 /* wrtpy: reserverd */
+#define DOORBELL_ROWAR_MAINTWD 0x00007000
+#define DOORBELL_ROWAR_SIZE 0x0000000b /* window size is 4k */
+
+#define RIO_ATMU_REGS_PORT1_OFFSET 0x10c00
+#define RIO_ATMU_REGS_PORT2_OFFSET 0x10e00
+#define RIO_S_DBELL_REGS_OFFSET 0x13400
+#define RIO_S_PW_REGS_OFFSET 0x134e0
+#define RIO_ATMU_REGS_DBELL_OFFSET 0x10C40
+
+#define MAX_MSG_UNIT_NUM 2
+#define MAX_PORT_NUM 4
+
+struct rio_atmu_regs {
+ u32 rowtar;
+ u32 rowtear;
+ u32 rowbar;
+ u32 pad1;
+ u32 rowar;
+ u32 pad2[3];
+};
+
+struct rio_dbell_ring {
+ void *virt;
+ dma_addr_t phys;
+};
+
+struct rio_port_write_msg {
+ void *virt;
+ dma_addr_t phys;
+ u32 msg_count;
+ u32 err_count;
+ u32 discard_count;
+};
+
+struct fsl_rio_dbell {
+ struct rio_mport *mport[MAX_PORT_NUM];
+ struct device *dev;
+ struct rio_dbell_regs __iomem *dbell_regs;
+ struct rio_dbell_ring dbell_ring;
+ int bellirq;
+};
+
+struct fsl_rio_pw {
+ struct device *dev;
+ struct rio_pw_regs __iomem *pw_regs;
+ struct rio_port_write_msg port_write_msg;
+ int pwirq;
+ struct work_struct pw_work;
+ struct kfifo pw_fifo;
+ spinlock_t pw_fifo_lock;
+};
+
+struct rio_priv {
+ struct device *dev;
+ void __iomem *regs_win;
+ struct rio_atmu_regs __iomem *atmu_regs;
+ struct rio_atmu_regs __iomem *maint_atmu_regs;
+ void __iomem *maint_win;
+ void *rmm_handle; /* RapidIO message manager(unit) Handle */
+};
+
+extern void __iomem *rio_regs_win;
+extern void __iomem *rmu_regs_win;
+
+extern resource_size_t rio_law_start;
+
+extern struct fsl_rio_dbell *dbell;
+extern struct fsl_rio_pw *pw;
-#include <linux/init.h>
+extern int fsl_rio_setup_rmu(struct rio_mport *mport,
+ struct device_node *node);
+extern int fsl_rio_port_write_init(struct fsl_rio_pw *pw);
+extern int fsl_rio_pw_enable(struct rio_mport *mport, int enable);
+extern void fsl_rio_port_error_handler(int offset);
+extern int fsl_rio_doorbell_init(struct fsl_rio_dbell *dbell);
-extern void mpc85xx_rio_setup(int law_start, int law_size);
+extern int fsl_rio_doorbell_send(struct rio_mport *mport,
+ int index, u16 destid, u16 data);
+extern int fsl_add_outb_message(struct rio_mport *mport,
+ struct rio_dev *rdev,
+ int mbox, void *buffer, size_t len);
+extern int fsl_open_outb_mbox(struct rio_mport *mport,
+ void *dev_id, int mbox, int entries);
+extern void fsl_close_outb_mbox(struct rio_mport *mport, int mbox);
+extern int fsl_open_inb_mbox(struct rio_mport *mport,
+ void *dev_id, int mbox, int entries);
+extern void fsl_close_inb_mbox(struct rio_mport *mport, int mbox);
+extern int fsl_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf);
+extern void *fsl_get_inb_message(struct rio_mport *mport, int mbox);
-#endif /* __PPC_SYSLIB_PPC85XX_RIO_H */
+#endif
diff --git a/arch/powerpc/sysdev/fsl_rmu.c b/arch/powerpc/sysdev/fsl_rmu.c
new file mode 100644
index 00000000000..b48197ae44d
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_rmu.c
@@ -0,0 +1,1107 @@
+/*
+ * Freescale MPC85xx/MPC86xx RapidIO RMU support
+ *
+ * Copyright 2009 Sysgo AG
+ * Thomas Moll <thomas.moll@sysgo.com>
+ * - fixed maintenance access routines, check for aligned access
+ *
+ * Copyright 2009 Integrated Device Technology, Inc.
+ * Alex Bounine <alexandre.bounine@idt.com>
+ * - Added Port-Write message handling
+ * - Added Machine Check exception handling
+ *
+ * Copyright (C) 2007, 2008, 2010, 2011 Freescale Semiconductor, Inc.
+ * Zhang Wei <wei.zhang@freescale.com>
+ * Lian Minghuan-B31939 <Minghuan.Lian@freescale.com>
+ * Liu Gang <Gang.Liu@freescale.com>
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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/types.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#include "fsl_rio.h"
+
+#define GET_RMM_HANDLE(mport) \
+ (((struct rio_priv *)(mport->priv))->rmm_handle)
+
+/* RapidIO definition irq, which read from OF-tree */
+#define IRQ_RIO_PW(m) (((struct fsl_rio_pw *)(m))->pwirq)
+#define IRQ_RIO_BELL(m) (((struct fsl_rio_dbell *)(m))->bellirq)
+#define IRQ_RIO_TX(m) (((struct fsl_rmu *)(GET_RMM_HANDLE(m)))->txirq)
+#define IRQ_RIO_RX(m) (((struct fsl_rmu *)(GET_RMM_HANDLE(m)))->rxirq)
+
+#define RIO_MIN_TX_RING_SIZE 2
+#define RIO_MAX_TX_RING_SIZE 2048
+#define RIO_MIN_RX_RING_SIZE 2
+#define RIO_MAX_RX_RING_SIZE 2048
+
+#define RIO_IPWMR_SEN 0x00100000
+#define RIO_IPWMR_QFIE 0x00000100
+#define RIO_IPWMR_EIE 0x00000020
+#define RIO_IPWMR_CQ 0x00000002
+#define RIO_IPWMR_PWE 0x00000001
+
+#define RIO_IPWSR_QF 0x00100000
+#define RIO_IPWSR_TE 0x00000080
+#define RIO_IPWSR_QFI 0x00000010
+#define RIO_IPWSR_PWD 0x00000008
+#define RIO_IPWSR_PWB 0x00000004
+
+#define RIO_EPWISR 0x10010
+/* EPWISR Error match value */
+#define RIO_EPWISR_PINT1 0x80000000
+#define RIO_EPWISR_PINT2 0x40000000
+#define RIO_EPWISR_MU 0x00000002
+#define RIO_EPWISR_PW 0x00000001
+
+#define IPWSR_CLEAR 0x98
+#define OMSR_CLEAR 0x1cb3
+#define IMSR_CLEAR 0x491
+#define IDSR_CLEAR 0x91
+#define ODSR_CLEAR 0x1c00
+#define LTLEECSR_ENABLE_ALL 0xFFC000FC
+#define RIO_LTLEECSR 0x060c
+
+#define RIO_IM0SR 0x64
+#define RIO_IM1SR 0x164
+#define RIO_OM0SR 0x4
+#define RIO_OM1SR 0x104
+
+#define RIO_DBELL_WIN_SIZE 0x1000
+
+#define RIO_MSG_OMR_MUI 0x00000002
+#define RIO_MSG_OSR_TE 0x00000080
+#define RIO_MSG_OSR_QOI 0x00000020
+#define RIO_MSG_OSR_QFI 0x00000010
+#define RIO_MSG_OSR_MUB 0x00000004
+#define RIO_MSG_OSR_EOMI 0x00000002
+#define RIO_MSG_OSR_QEI 0x00000001
+
+#define RIO_MSG_IMR_MI 0x00000002
+#define RIO_MSG_ISR_TE 0x00000080
+#define RIO_MSG_ISR_QFI 0x00000010
+#define RIO_MSG_ISR_DIQI 0x00000001
+
+#define RIO_MSG_DESC_SIZE 32
+#define RIO_MSG_BUFFER_SIZE 4096
+
+#define DOORBELL_DMR_DI 0x00000002
+#define DOORBELL_DSR_TE 0x00000080
+#define DOORBELL_DSR_QFI 0x00000010
+#define DOORBELL_DSR_DIQI 0x00000001
+
+#define DOORBELL_MESSAGE_SIZE 0x08
+
+struct rio_msg_regs {
+ u32 omr;
+ u32 osr;
+ u32 pad1;
+ u32 odqdpar;
+ u32 pad2;
+ u32 osar;
+ u32 odpr;
+ u32 odatr;
+ u32 odcr;
+ u32 pad3;
+ u32 odqepar;
+ u32 pad4[13];
+ u32 imr;
+ u32 isr;
+ u32 pad5;
+ u32 ifqdpar;
+ u32 pad6;
+ u32 ifqepar;
+};
+
+struct rio_dbell_regs {
+ u32 odmr;
+ u32 odsr;
+ u32 pad1[4];
+ u32 oddpr;
+ u32 oddatr;
+ u32 pad2[3];
+ u32 odretcr;
+ u32 pad3[12];
+ u32 dmr;
+ u32 dsr;
+ u32 pad4;
+ u32 dqdpar;
+ u32 pad5;
+ u32 dqepar;
+};
+
+struct rio_pw_regs {
+ u32 pwmr;
+ u32 pwsr;
+ u32 epwqbar;
+ u32 pwqbar;
+};
+
+
+struct rio_tx_desc {
+ u32 pad1;
+ u32 saddr;
+ u32 dport;
+ u32 dattr;
+ u32 pad2;
+ u32 pad3;
+ u32 dwcnt;
+ u32 pad4;
+};
+
+struct rio_msg_tx_ring {
+ void *virt;
+ dma_addr_t phys;
+ void *virt_buffer[RIO_MAX_TX_RING_SIZE];
+ dma_addr_t phys_buffer[RIO_MAX_TX_RING_SIZE];
+ int tx_slot;
+ int size;
+ void *dev_id;
+};
+
+struct rio_msg_rx_ring {
+ void *virt;
+ dma_addr_t phys;
+ void *virt_buffer[RIO_MAX_RX_RING_SIZE];
+ int rx_slot;
+ int size;
+ void *dev_id;
+};
+
+struct fsl_rmu {
+ struct rio_msg_regs __iomem *msg_regs;
+ struct rio_msg_tx_ring msg_tx_ring;
+ struct rio_msg_rx_ring msg_rx_ring;
+ int txirq;
+ int rxirq;
+};
+
+struct rio_dbell_msg {
+ u16 pad1;
+ u16 tid;
+ u16 sid;
+ u16 info;
+};
+
+/**
+ * fsl_rio_tx_handler - MPC85xx outbound message interrupt handler
+ * @irq: Linux interrupt number
+ * @dev_instance: Pointer to interrupt-specific data
+ *
+ * Handles outbound message interrupts. Executes a register outbound
+ * mailbox event handler and acks the interrupt occurrence.
+ */
+static irqreturn_t
+fsl_rio_tx_handler(int irq, void *dev_instance)
+{
+ int osr;
+ struct rio_mport *port = (struct rio_mport *)dev_instance;
+ struct fsl_rmu *rmu = GET_RMM_HANDLE(port);
+
+ osr = in_be32(&rmu->msg_regs->osr);
+
+ if (osr & RIO_MSG_OSR_TE) {
+ pr_info("RIO: outbound message transmission error\n");
+ out_be32(&rmu->msg_regs->osr, RIO_MSG_OSR_TE);
+ goto out;
+ }
+
+ if (osr & RIO_MSG_OSR_QOI) {
+ pr_info("RIO: outbound message queue overflow\n");
+ out_be32(&rmu->msg_regs->osr, RIO_MSG_OSR_QOI);
+ goto out;
+ }
+
+ if (osr & RIO_MSG_OSR_EOMI) {
+ u32 dqp = in_be32(&rmu->msg_regs->odqdpar);
+ int slot = (dqp - rmu->msg_tx_ring.phys) >> 5;
+ if (port->outb_msg[0].mcback != NULL) {
+ port->outb_msg[0].mcback(port, rmu->msg_tx_ring.dev_id,
+ -1,
+ slot);
+ }
+ /* Ack the end-of-message interrupt */
+ out_be32(&rmu->msg_regs->osr, RIO_MSG_OSR_EOMI);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+/**
+ * fsl_rio_rx_handler - MPC85xx inbound message interrupt handler
+ * @irq: Linux interrupt number
+ * @dev_instance: Pointer to interrupt-specific data
+ *
+ * Handles inbound message interrupts. Executes a registered inbound
+ * mailbox event handler and acks the interrupt occurrence.
+ */
+static irqreturn_t
+fsl_rio_rx_handler(int irq, void *dev_instance)
+{
+ int isr;
+ struct rio_mport *port = (struct rio_mport *)dev_instance;
+ struct fsl_rmu *rmu = GET_RMM_HANDLE(port);
+
+ isr = in_be32(&rmu->msg_regs->isr);
+
+ if (isr & RIO_MSG_ISR_TE) {
+ pr_info("RIO: inbound message reception error\n");
+ out_be32((void *)&rmu->msg_regs->isr, RIO_MSG_ISR_TE);
+ goto out;
+ }
+
+ /* XXX Need to check/dispatch until queue empty */
+ if (isr & RIO_MSG_ISR_DIQI) {
+ /*
+ * Can receive messages for any mailbox/letter to that
+ * mailbox destination. So, make the callback with an
+ * unknown/invalid mailbox number argument.
+ */
+ if (port->inb_msg[0].mcback != NULL)
+ port->inb_msg[0].mcback(port, rmu->msg_rx_ring.dev_id,
+ -1,
+ -1);
+
+ /* Ack the queueing interrupt */
+ out_be32(&rmu->msg_regs->isr, RIO_MSG_ISR_DIQI);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+/**
+ * fsl_rio_dbell_handler - MPC85xx doorbell interrupt handler
+ * @irq: Linux interrupt number
+ * @dev_instance: Pointer to interrupt-specific data
+ *
+ * Handles doorbell interrupts. Parses a list of registered
+ * doorbell event handlers and executes a matching event handler.
+ */
+static irqreturn_t
+fsl_rio_dbell_handler(int irq, void *dev_instance)
+{
+ int dsr;
+ struct fsl_rio_dbell *fsl_dbell = (struct fsl_rio_dbell *)dev_instance;
+ int i;
+
+ dsr = in_be32(&fsl_dbell->dbell_regs->dsr);
+
+ if (dsr & DOORBELL_DSR_TE) {
+ pr_info("RIO: doorbell reception error\n");
+ out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_TE);
+ goto out;
+ }
+
+ if (dsr & DOORBELL_DSR_QFI) {
+ pr_info("RIO: doorbell queue full\n");
+ out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_QFI);
+ }
+
+ /* XXX Need to check/dispatch until queue empty */
+ if (dsr & DOORBELL_DSR_DIQI) {
+ struct rio_dbell_msg *dmsg =
+ fsl_dbell->dbell_ring.virt +
+ (in_be32(&fsl_dbell->dbell_regs->dqdpar) & 0xfff);
+ struct rio_dbell *dbell;
+ int found = 0;
+
+ pr_debug
+ ("RIO: processing doorbell,"
+ " sid %2.2x tid %2.2x info %4.4x\n",
+ dmsg->sid, dmsg->tid, dmsg->info);
+
+ for (i = 0; i < MAX_PORT_NUM; i++) {
+ if (fsl_dbell->mport[i]) {
+ list_for_each_entry(dbell,
+ &fsl_dbell->mport[i]->dbells, node) {
+ if ((dbell->res->start
+ <= dmsg->info)
+ && (dbell->res->end
+ >= dmsg->info)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found && dbell->dinb) {
+ dbell->dinb(fsl_dbell->mport[i],
+ dbell->dev_id, dmsg->sid,
+ dmsg->tid,
+ dmsg->info);
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ pr_debug
+ ("RIO: spurious doorbell,"
+ " sid %2.2x tid %2.2x info %4.4x\n",
+ dmsg->sid, dmsg->tid,
+ dmsg->info);
+ }
+ setbits32(&fsl_dbell->dbell_regs->dmr, DOORBELL_DMR_DI);
+ out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_DIQI);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+void msg_unit_error_handler(void)
+{
+
+ /*XXX: Error recovery is not implemented, we just clear errors */
+ out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), 0);
+
+ out_be32((u32 *)(rmu_regs_win + RIO_IM0SR), IMSR_CLEAR);
+ out_be32((u32 *)(rmu_regs_win + RIO_IM1SR), IMSR_CLEAR);
+ out_be32((u32 *)(rmu_regs_win + RIO_OM0SR), OMSR_CLEAR);
+ out_be32((u32 *)(rmu_regs_win + RIO_OM1SR), OMSR_CLEAR);
+
+ out_be32(&dbell->dbell_regs->odsr, ODSR_CLEAR);
+ out_be32(&dbell->dbell_regs->dsr, IDSR_CLEAR);
+
+ out_be32(&pw->pw_regs->pwsr, IPWSR_CLEAR);
+}
+
+/**
+ * fsl_rio_port_write_handler - MPC85xx port write interrupt handler
+ * @irq: Linux interrupt number
+ * @dev_instance: Pointer to interrupt-specific data
+ *
+ * Handles port write interrupts. Parses a list of registered
+ * port write event handlers and executes a matching event handler.
+ */
+static irqreturn_t
+fsl_rio_port_write_handler(int irq, void *dev_instance)
+{
+ u32 ipwmr, ipwsr;
+ struct fsl_rio_pw *pw = (struct fsl_rio_pw *)dev_instance;
+ u32 epwisr, tmp;
+
+ epwisr = in_be32(rio_regs_win + RIO_EPWISR);
+ if (!(epwisr & RIO_EPWISR_PW))
+ goto pw_done;
+
+ ipwmr = in_be32(&pw->pw_regs->pwmr);
+ ipwsr = in_be32(&pw->pw_regs->pwsr);
+
+#ifdef DEBUG_PW
+ pr_debug("PW Int->IPWMR: 0x%08x IPWSR: 0x%08x (", ipwmr, ipwsr);
+ if (ipwsr & RIO_IPWSR_QF)
+ pr_debug(" QF");
+ if (ipwsr & RIO_IPWSR_TE)
+ pr_debug(" TE");
+ if (ipwsr & RIO_IPWSR_QFI)
+ pr_debug(" QFI");
+ if (ipwsr & RIO_IPWSR_PWD)
+ pr_debug(" PWD");
+ if (ipwsr & RIO_IPWSR_PWB)
+ pr_debug(" PWB");
+ pr_debug(" )\n");
+#endif
+ /* Schedule deferred processing if PW was received */
+ if (ipwsr & RIO_IPWSR_QFI) {
+ /* Save PW message (if there is room in FIFO),
+ * otherwise discard it.
+ */
+ if (kfifo_avail(&pw->pw_fifo) >= RIO_PW_MSG_SIZE) {
+ pw->port_write_msg.msg_count++;
+ kfifo_in(&pw->pw_fifo, pw->port_write_msg.virt,
+ RIO_PW_MSG_SIZE);
+ } else {
+ pw->port_write_msg.discard_count++;
+ pr_debug("RIO: ISR Discarded Port-Write Msg(s) (%d)\n",
+ pw->port_write_msg.discard_count);
+ }
+ /* Clear interrupt and issue Clear Queue command. This allows
+ * another port-write to be received.
+ */
+ out_be32(&pw->pw_regs->pwsr, RIO_IPWSR_QFI);
+ out_be32(&pw->pw_regs->pwmr, ipwmr | RIO_IPWMR_CQ);
+
+ schedule_work(&pw->pw_work);
+ }
+
+ if ((ipwmr & RIO_IPWMR_EIE) && (ipwsr & RIO_IPWSR_TE)) {
+ pw->port_write_msg.err_count++;
+ pr_debug("RIO: Port-Write Transaction Err (%d)\n",
+ pw->port_write_msg.err_count);
+ /* Clear Transaction Error: port-write controller should be
+ * disabled when clearing this error
+ */
+ out_be32(&pw->pw_regs->pwmr, ipwmr & ~RIO_IPWMR_PWE);
+ out_be32(&pw->pw_regs->pwsr, RIO_IPWSR_TE);
+ out_be32(&pw->pw_regs->pwmr, ipwmr);
+ }
+
+ if (ipwsr & RIO_IPWSR_PWD) {
+ pw->port_write_msg.discard_count++;
+ pr_debug("RIO: Port Discarded Port-Write Msg(s) (%d)\n",
+ pw->port_write_msg.discard_count);
+ out_be32(&pw->pw_regs->pwsr, RIO_IPWSR_PWD);
+ }
+
+pw_done:
+ if (epwisr & RIO_EPWISR_PINT1) {
+ tmp = in_be32(rio_regs_win + RIO_LTLEDCSR);
+ pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp);
+ fsl_rio_port_error_handler(0);
+ }
+
+ if (epwisr & RIO_EPWISR_PINT2) {
+ tmp = in_be32(rio_regs_win + RIO_LTLEDCSR);
+ pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp);
+ fsl_rio_port_error_handler(1);
+ }
+
+ if (epwisr & RIO_EPWISR_MU) {
+ tmp = in_be32(rio_regs_win + RIO_LTLEDCSR);
+ pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp);
+ msg_unit_error_handler();
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void fsl_pw_dpc(struct work_struct *work)
+{
+ struct fsl_rio_pw *pw = container_of(work, struct fsl_rio_pw, pw_work);
+ u32 msg_buffer[RIO_PW_MSG_SIZE/sizeof(u32)];
+
+ /*
+ * Process port-write messages
+ */
+ while (kfifo_out_spinlocked(&pw->pw_fifo, (unsigned char *)msg_buffer,
+ RIO_PW_MSG_SIZE, &pw->pw_fifo_lock)) {
+ /* Process one message */
+#ifdef DEBUG_PW
+ {
+ u32 i;
+ pr_debug("%s : Port-Write Message:", __func__);
+ for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32); i++) {
+ if ((i%4) == 0)
+ pr_debug("\n0x%02x: 0x%08x", i*4,
+ msg_buffer[i]);
+ else
+ pr_debug(" 0x%08x", msg_buffer[i]);
+ }
+ pr_debug("\n");
+ }
+#endif
+ /* Pass the port-write message to RIO core for processing */
+ rio_inb_pwrite_handler((union rio_pw_msg *)msg_buffer);
+ }
+}
+
+/**
+ * fsl_rio_pw_enable - enable/disable port-write interface init
+ * @mport: Master port implementing the port write unit
+ * @enable: 1=enable; 0=disable port-write message handling
+ */
+int fsl_rio_pw_enable(struct rio_mport *mport, int enable)
+{
+ u32 rval;
+
+ rval = in_be32(&pw->pw_regs->pwmr);
+
+ if (enable)
+ rval |= RIO_IPWMR_PWE;
+ else
+ rval &= ~RIO_IPWMR_PWE;
+
+ out_be32(&pw->pw_regs->pwmr, rval);
+
+ return 0;
+}
+
+/**
+ * fsl_rio_port_write_init - MPC85xx port write interface init
+ * @mport: Master port implementing the port write unit
+ *
+ * Initializes port write unit hardware and DMA buffer
+ * ring. Called from fsl_rio_setup(). Returns %0 on success
+ * or %-ENOMEM on failure.
+ */
+
+int fsl_rio_port_write_init(struct fsl_rio_pw *pw)
+{
+ int rc = 0;
+
+ /* Following configurations require a disabled port write controller */
+ out_be32(&pw->pw_regs->pwmr,
+ in_be32(&pw->pw_regs->pwmr) & ~RIO_IPWMR_PWE);
+
+ /* Initialize port write */
+ pw->port_write_msg.virt = dma_alloc_coherent(pw->dev,
+ RIO_PW_MSG_SIZE,
+ &pw->port_write_msg.phys, GFP_KERNEL);
+ if (!pw->port_write_msg.virt) {
+ pr_err("RIO: unable allocate port write queue\n");
+ return -ENOMEM;
+ }
+
+ pw->port_write_msg.err_count = 0;
+ pw->port_write_msg.discard_count = 0;
+
+ /* Point dequeue/enqueue pointers at first entry */
+ out_be32(&pw->pw_regs->epwqbar, 0);
+ out_be32(&pw->pw_regs->pwqbar, (u32) pw->port_write_msg.phys);
+
+ pr_debug("EIPWQBAR: 0x%08x IPWQBAR: 0x%08x\n",
+ in_be32(&pw->pw_regs->epwqbar),
+ in_be32(&pw->pw_regs->pwqbar));
+
+ /* Clear interrupt status IPWSR */
+ out_be32(&pw->pw_regs->pwsr,
+ (RIO_IPWSR_TE | RIO_IPWSR_QFI | RIO_IPWSR_PWD));
+
+ /* Configure port write contoller for snooping enable all reporting,
+ clear queue full */
+ out_be32(&pw->pw_regs->pwmr,
+ RIO_IPWMR_SEN | RIO_IPWMR_QFIE | RIO_IPWMR_EIE | RIO_IPWMR_CQ);
+
+
+ /* Hook up port-write handler */
+ rc = request_irq(IRQ_RIO_PW(pw), fsl_rio_port_write_handler,
+ IRQF_SHARED, "port-write", (void *)pw);
+ if (rc < 0) {
+ pr_err("MPC85xx RIO: unable to request inbound doorbell irq");
+ goto err_out;
+ }
+ /* Enable Error Interrupt */
+ out_be32((u32 *)(rio_regs_win + RIO_LTLEECSR), LTLEECSR_ENABLE_ALL);
+
+ INIT_WORK(&pw->pw_work, fsl_pw_dpc);
+ spin_lock_init(&pw->pw_fifo_lock);
+ if (kfifo_alloc(&pw->pw_fifo, RIO_PW_MSG_SIZE * 32, GFP_KERNEL)) {
+ pr_err("FIFO allocation failed\n");
+ rc = -ENOMEM;
+ goto err_out_irq;
+ }
+
+ pr_debug("IPWMR: 0x%08x IPWSR: 0x%08x\n",
+ in_be32(&pw->pw_regs->pwmr),
+ in_be32(&pw->pw_regs->pwsr));
+
+ return rc;
+
+err_out_irq:
+ free_irq(IRQ_RIO_PW(pw), (void *)pw);
+err_out:
+ dma_free_coherent(pw->dev, RIO_PW_MSG_SIZE,
+ pw->port_write_msg.virt,
+ pw->port_write_msg.phys);
+ return rc;
+}
+
+/**
+ * fsl_rio_doorbell_send - Send a MPC85xx doorbell message
+ * @mport: RapidIO master port info
+ * @index: ID of RapidIO interface
+ * @destid: Destination ID of target device
+ * @data: 16-bit info field of RapidIO doorbell message
+ *
+ * Sends a MPC85xx doorbell message. Returns %0 on success or
+ * %-EINVAL on failure.
+ */
+int fsl_rio_doorbell_send(struct rio_mport *mport,
+ int index, u16 destid, u16 data)
+{
+ pr_debug("fsl_doorbell_send: index %d destid %4.4x data %4.4x\n",
+ index, destid, data);
+
+ /* In the serial version silicons, such as MPC8548, MPC8641,
+ * below operations is must be.
+ */
+ out_be32(&dbell->dbell_regs->odmr, 0x00000000);
+ out_be32(&dbell->dbell_regs->odretcr, 0x00000004);
+ out_be32(&dbell->dbell_regs->oddpr, destid << 16);
+ out_be32(&dbell->dbell_regs->oddatr, (index << 20) | data);
+ out_be32(&dbell->dbell_regs->odmr, 0x00000001);
+
+ return 0;
+}
+
+/**
+ * fsl_add_outb_message - Add message to the MPC85xx outbound message queue
+ * @mport: Master port with outbound message queue
+ * @rdev: Target of outbound message
+ * @mbox: Outbound mailbox
+ * @buffer: Message to add to outbound queue
+ * @len: Length of message
+ *
+ * Adds the @buffer message to the MPC85xx outbound message queue. Returns
+ * %0 on success or %-EINVAL on failure.
+ */
+int
+fsl_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox,
+ void *buffer, size_t len)
+{
+ struct fsl_rmu *rmu = GET_RMM_HANDLE(mport);
+ u32 omr;
+ struct rio_tx_desc *desc = (struct rio_tx_desc *)rmu->msg_tx_ring.virt
+ + rmu->msg_tx_ring.tx_slot;
+ int ret = 0;
+
+ pr_debug("RIO: fsl_add_outb_message(): destid %4.4x mbox %d buffer " \
+ "%p len %8.8zx\n", rdev->destid, mbox, buffer, len);
+ if ((len < 8) || (len > RIO_MAX_MSG_SIZE)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Copy and clear rest of buffer */
+ memcpy(rmu->msg_tx_ring.virt_buffer[rmu->msg_tx_ring.tx_slot], buffer,
+ len);
+ if (len < (RIO_MAX_MSG_SIZE - 4))
+ memset(rmu->msg_tx_ring.virt_buffer[rmu->msg_tx_ring.tx_slot]
+ + len, 0, RIO_MAX_MSG_SIZE - len);
+
+ /* Set mbox field for message, and set destid */
+ desc->dport = (rdev->destid << 16) | (mbox & 0x3);
+
+ /* Enable EOMI interrupt and priority */
+ desc->dattr = 0x28000000 | ((mport->index) << 20);
+
+ /* Set transfer size aligned to next power of 2 (in double words) */
+ desc->dwcnt = is_power_of_2(len) ? len : 1 << get_bitmask_order(len);
+
+ /* Set snooping and source buffer address */
+ desc->saddr = 0x00000004
+ | rmu->msg_tx_ring.phys_buffer[rmu->msg_tx_ring.tx_slot];
+
+ /* Increment enqueue pointer */
+ omr = in_be32(&rmu->msg_regs->omr);
+ out_be32(&rmu->msg_regs->omr, omr | RIO_MSG_OMR_MUI);
+
+ /* Go to next descriptor */
+ if (++rmu->msg_tx_ring.tx_slot == rmu->msg_tx_ring.size)
+ rmu->msg_tx_ring.tx_slot = 0;
+
+out:
+ return ret;
+}
+
+/**
+ * fsl_open_outb_mbox - Initialize MPC85xx outbound mailbox
+ * @mport: Master port implementing the outbound message unit
+ * @dev_id: Device specific pointer to pass on event
+ * @mbox: Mailbox to open
+ * @entries: Number of entries in the outbound mailbox ring
+ *
+ * Initializes buffer ring, request the outbound message interrupt,
+ * and enables the outbound message unit. Returns %0 on success and
+ * %-EINVAL or %-ENOMEM on failure.
+ */
+int
+fsl_open_outb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries)
+{
+ int i, j, rc = 0;
+ struct rio_priv *priv = mport->priv;
+ struct fsl_rmu *rmu = GET_RMM_HANDLE(mport);
+
+ if ((entries < RIO_MIN_TX_RING_SIZE) ||
+ (entries > RIO_MAX_TX_RING_SIZE) || (!is_power_of_2(entries))) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Initialize shadow copy ring */
+ rmu->msg_tx_ring.dev_id = dev_id;
+ rmu->msg_tx_ring.size = entries;
+
+ for (i = 0; i < rmu->msg_tx_ring.size; i++) {
+ rmu->msg_tx_ring.virt_buffer[i] =
+ dma_alloc_coherent(priv->dev, RIO_MSG_BUFFER_SIZE,
+ &rmu->msg_tx_ring.phys_buffer[i], GFP_KERNEL);
+ if (!rmu->msg_tx_ring.virt_buffer[i]) {
+ rc = -ENOMEM;
+ for (j = 0; j < rmu->msg_tx_ring.size; j++)
+ if (rmu->msg_tx_ring.virt_buffer[j])
+ dma_free_coherent(priv->dev,
+ RIO_MSG_BUFFER_SIZE,
+ rmu->msg_tx_ring.
+ virt_buffer[j],
+ rmu->msg_tx_ring.
+ phys_buffer[j]);
+ goto out;
+ }
+ }
+
+ /* Initialize outbound message descriptor ring */
+ rmu->msg_tx_ring.virt = dma_alloc_coherent(priv->dev,
+ rmu->msg_tx_ring.size * RIO_MSG_DESC_SIZE,
+ &rmu->msg_tx_ring.phys, GFP_KERNEL);
+ if (!rmu->msg_tx_ring.virt) {
+ rc = -ENOMEM;
+ goto out_dma;
+ }
+ memset(rmu->msg_tx_ring.virt, 0,
+ rmu->msg_tx_ring.size * RIO_MSG_DESC_SIZE);
+ rmu->msg_tx_ring.tx_slot = 0;
+
+ /* Point dequeue/enqueue pointers at first entry in ring */
+ out_be32(&rmu->msg_regs->odqdpar, rmu->msg_tx_ring.phys);
+ out_be32(&rmu->msg_regs->odqepar, rmu->msg_tx_ring.phys);
+
+ /* Configure for snooping */
+ out_be32(&rmu->msg_regs->osar, 0x00000004);
+
+ /* Clear interrupt status */
+ out_be32(&rmu->msg_regs->osr, 0x000000b3);
+
+ /* Hook up outbound message handler */
+ rc = request_irq(IRQ_RIO_TX(mport), fsl_rio_tx_handler, 0,
+ "msg_tx", (void *)mport);
+ if (rc < 0)
+ goto out_irq;
+
+ /*
+ * Configure outbound message unit
+ * Snooping
+ * Interrupts (all enabled, except QEIE)
+ * Chaining mode
+ * Disable
+ */
+ out_be32(&rmu->msg_regs->omr, 0x00100220);
+
+ /* Set number of entries */
+ out_be32(&rmu->msg_regs->omr,
+ in_be32(&rmu->msg_regs->omr) |
+ ((get_bitmask_order(entries) - 2) << 12));
+
+ /* Now enable the unit */
+ out_be32(&rmu->msg_regs->omr, in_be32(&rmu->msg_regs->omr) | 0x1);
+
+out:
+ return rc;
+
+out_irq:
+ dma_free_coherent(priv->dev,
+ rmu->msg_tx_ring.size * RIO_MSG_DESC_SIZE,
+ rmu->msg_tx_ring.virt, rmu->msg_tx_ring.phys);
+
+out_dma:
+ for (i = 0; i < rmu->msg_tx_ring.size; i++)
+ dma_free_coherent(priv->dev, RIO_MSG_BUFFER_SIZE,
+ rmu->msg_tx_ring.virt_buffer[i],
+ rmu->msg_tx_ring.phys_buffer[i]);
+
+ return rc;
+}
+
+/**
+ * fsl_close_outb_mbox - Shut down MPC85xx outbound mailbox
+ * @mport: Master port implementing the outbound message unit
+ * @mbox: Mailbox to close
+ *
+ * Disables the outbound message unit, free all buffers, and
+ * frees the outbound message interrupt.
+ */
+void fsl_close_outb_mbox(struct rio_mport *mport, int mbox)
+{
+ struct rio_priv *priv = mport->priv;
+ struct fsl_rmu *rmu = GET_RMM_HANDLE(mport);
+
+ /* Disable inbound message unit */
+ out_be32(&rmu->msg_regs->omr, 0);
+
+ /* Free ring */
+ dma_free_coherent(priv->dev,
+ rmu->msg_tx_ring.size * RIO_MSG_DESC_SIZE,
+ rmu->msg_tx_ring.virt, rmu->msg_tx_ring.phys);
+
+ /* Free interrupt */
+ free_irq(IRQ_RIO_TX(mport), (void *)mport);
+}
+
+/**
+ * fsl_open_inb_mbox - Initialize MPC85xx inbound mailbox
+ * @mport: Master port implementing the inbound message unit
+ * @dev_id: Device specific pointer to pass on event
+ * @mbox: Mailbox to open
+ * @entries: Number of entries in the inbound mailbox ring
+ *
+ * Initializes buffer ring, request the inbound message interrupt,
+ * and enables the inbound message unit. Returns %0 on success
+ * and %-EINVAL or %-ENOMEM on failure.
+ */
+int
+fsl_open_inb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries)
+{
+ int i, rc = 0;
+ struct rio_priv *priv = mport->priv;
+ struct fsl_rmu *rmu = GET_RMM_HANDLE(mport);
+
+ if ((entries < RIO_MIN_RX_RING_SIZE) ||
+ (entries > RIO_MAX_RX_RING_SIZE) || (!is_power_of_2(entries))) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Initialize client buffer ring */
+ rmu->msg_rx_ring.dev_id = dev_id;
+ rmu->msg_rx_ring.size = entries;
+ rmu->msg_rx_ring.rx_slot = 0;
+ for (i = 0; i < rmu->msg_rx_ring.size; i++)
+ rmu->msg_rx_ring.virt_buffer[i] = NULL;
+
+ /* Initialize inbound message ring */
+ rmu->msg_rx_ring.virt = dma_alloc_coherent(priv->dev,
+ rmu->msg_rx_ring.size * RIO_MAX_MSG_SIZE,
+ &rmu->msg_rx_ring.phys, GFP_KERNEL);
+ if (!rmu->msg_rx_ring.virt) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Point dequeue/enqueue pointers at first entry in ring */
+ out_be32(&rmu->msg_regs->ifqdpar, (u32) rmu->msg_rx_ring.phys);
+ out_be32(&rmu->msg_regs->ifqepar, (u32) rmu->msg_rx_ring.phys);
+
+ /* Clear interrupt status */
+ out_be32(&rmu->msg_regs->isr, 0x00000091);
+
+ /* Hook up inbound message handler */
+ rc = request_irq(IRQ_RIO_RX(mport), fsl_rio_rx_handler, 0,
+ "msg_rx", (void *)mport);
+ if (rc < 0) {
+ dma_free_coherent(priv->dev,
+ rmu->msg_rx_ring.size * RIO_MAX_MSG_SIZE,
+ rmu->msg_rx_ring.virt, rmu->msg_rx_ring.phys);
+ goto out;
+ }
+
+ /*
+ * Configure inbound message unit:
+ * Snooping
+ * 4KB max message size
+ * Unmask all interrupt sources
+ * Disable
+ */
+ out_be32(&rmu->msg_regs->imr, 0x001b0060);
+
+ /* Set number of queue entries */
+ setbits32(&rmu->msg_regs->imr, (get_bitmask_order(entries) - 2) << 12);
+
+ /* Now enable the unit */
+ setbits32(&rmu->msg_regs->imr, 0x1);
+
+out:
+ return rc;
+}
+
+/**
+ * fsl_close_inb_mbox - Shut down MPC85xx inbound mailbox
+ * @mport: Master port implementing the inbound message unit
+ * @mbox: Mailbox to close
+ *
+ * Disables the inbound message unit, free all buffers, and
+ * frees the inbound message interrupt.
+ */
+void fsl_close_inb_mbox(struct rio_mport *mport, int mbox)
+{
+ struct rio_priv *priv = mport->priv;
+ struct fsl_rmu *rmu = GET_RMM_HANDLE(mport);
+
+ /* Disable inbound message unit */
+ out_be32(&rmu->msg_regs->imr, 0);
+
+ /* Free ring */
+ dma_free_coherent(priv->dev, rmu->msg_rx_ring.size * RIO_MAX_MSG_SIZE,
+ rmu->msg_rx_ring.virt, rmu->msg_rx_ring.phys);
+
+ /* Free interrupt */
+ free_irq(IRQ_RIO_RX(mport), (void *)mport);
+}
+
+/**
+ * fsl_add_inb_buffer - Add buffer to the MPC85xx inbound message queue
+ * @mport: Master port implementing the inbound message unit
+ * @mbox: Inbound mailbox number
+ * @buf: Buffer to add to inbound queue
+ *
+ * Adds the @buf buffer to the MPC85xx inbound message queue. Returns
+ * %0 on success or %-EINVAL on failure.
+ */
+int fsl_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf)
+{
+ int rc = 0;
+ struct fsl_rmu *rmu = GET_RMM_HANDLE(mport);
+
+ pr_debug("RIO: fsl_add_inb_buffer(), msg_rx_ring.rx_slot %d\n",
+ rmu->msg_rx_ring.rx_slot);
+
+ if (rmu->msg_rx_ring.virt_buffer[rmu->msg_rx_ring.rx_slot]) {
+ printk(KERN_ERR
+ "RIO: error adding inbound buffer %d, buffer exists\n",
+ rmu->msg_rx_ring.rx_slot);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rmu->msg_rx_ring.virt_buffer[rmu->msg_rx_ring.rx_slot] = buf;
+ if (++rmu->msg_rx_ring.rx_slot == rmu->msg_rx_ring.size)
+ rmu->msg_rx_ring.rx_slot = 0;
+
+out:
+ return rc;
+}
+
+/**
+ * fsl_get_inb_message - Fetch inbound message from the MPC85xx message unit
+ * @mport: Master port implementing the inbound message unit
+ * @mbox: Inbound mailbox number
+ *
+ * Gets the next available inbound message from the inbound message queue.
+ * A pointer to the message is returned on success or NULL on failure.
+ */
+void *fsl_get_inb_message(struct rio_mport *mport, int mbox)
+{
+ struct fsl_rmu *rmu = GET_RMM_HANDLE(mport);
+ u32 phys_buf;
+ void *virt_buf;
+ void *buf = NULL;
+ int buf_idx;
+
+ phys_buf = in_be32(&rmu->msg_regs->ifqdpar);
+
+ /* If no more messages, then bail out */
+ if (phys_buf == in_be32(&rmu->msg_regs->ifqepar))
+ goto out2;
+
+ virt_buf = rmu->msg_rx_ring.virt + (phys_buf
+ - rmu->msg_rx_ring.phys);
+ buf_idx = (phys_buf - rmu->msg_rx_ring.phys) / RIO_MAX_MSG_SIZE;
+ buf = rmu->msg_rx_ring.virt_buffer[buf_idx];
+
+ if (!buf) {
+ printk(KERN_ERR
+ "RIO: inbound message copy failed, no buffers\n");
+ goto out1;
+ }
+
+ /* Copy max message size, caller is expected to allocate that big */
+ memcpy(buf, virt_buf, RIO_MAX_MSG_SIZE);
+
+ /* Clear the available buffer */
+ rmu->msg_rx_ring.virt_buffer[buf_idx] = NULL;
+
+out1:
+ setbits32(&rmu->msg_regs->imr, RIO_MSG_IMR_MI);
+
+out2:
+ return buf;
+}
+
+/**
+ * fsl_rio_doorbell_init - MPC85xx doorbell interface init
+ * @mport: Master port implementing the inbound doorbell unit
+ *
+ * Initializes doorbell unit hardware and inbound DMA buffer
+ * ring. Called from fsl_rio_setup(). Returns %0 on success
+ * or %-ENOMEM on failure.
+ */
+int fsl_rio_doorbell_init(struct fsl_rio_dbell *dbell)
+{
+ int rc = 0;
+
+ /* Initialize inbound doorbells */
+ dbell->dbell_ring.virt = dma_alloc_coherent(dbell->dev, 512 *
+ DOORBELL_MESSAGE_SIZE, &dbell->dbell_ring.phys, GFP_KERNEL);
+ if (!dbell->dbell_ring.virt) {
+ printk(KERN_ERR "RIO: unable allocate inbound doorbell ring\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Point dequeue/enqueue pointers at first entry in ring */
+ out_be32(&dbell->dbell_regs->dqdpar, (u32) dbell->dbell_ring.phys);
+ out_be32(&dbell->dbell_regs->dqepar, (u32) dbell->dbell_ring.phys);
+
+ /* Clear interrupt status */
+ out_be32(&dbell->dbell_regs->dsr, 0x00000091);
+
+ /* Hook up doorbell handler */
+ rc = request_irq(IRQ_RIO_BELL(dbell), fsl_rio_dbell_handler, 0,
+ "dbell_rx", (void *)dbell);
+ if (rc < 0) {
+ dma_free_coherent(dbell->dev, 512 * DOORBELL_MESSAGE_SIZE,
+ dbell->dbell_ring.virt, dbell->dbell_ring.phys);
+ printk(KERN_ERR
+ "MPC85xx RIO: unable to request inbound doorbell irq");
+ goto out;
+ }
+
+ /* Configure doorbells for snooping, 512 entries, and enable */
+ out_be32(&dbell->dbell_regs->dmr, 0x00108161);
+
+out:
+ return rc;
+}
+
+int fsl_rio_setup_rmu(struct rio_mport *mport, struct device_node *node)
+{
+ struct rio_priv *priv;
+ struct fsl_rmu *rmu;
+ u64 msg_start;
+ const u32 *msg_addr;
+ int mlen;
+ int aw;
+
+ if (!mport || !mport->priv)
+ return -EINVAL;
+
+ priv = mport->priv;
+
+ if (!node) {
+ dev_warn(priv->dev, "Can't get %s property 'fsl,rmu'\n",
+ priv->dev->of_node->full_name);
+ return -EINVAL;
+ }
+
+ rmu = kzalloc(sizeof(struct fsl_rmu), GFP_KERNEL);
+ if (!rmu)
+ return -ENOMEM;
+
+ aw = of_n_addr_cells(node);
+ msg_addr = of_get_property(node, "reg", &mlen);
+ if (!msg_addr) {
+ pr_err("%s: unable to find 'reg' property of message-unit\n",
+ node->full_name);
+ kfree(rmu);
+ return -ENOMEM;
+ }
+ msg_start = of_read_number(msg_addr, aw);
+
+ rmu->msg_regs = (struct rio_msg_regs *)
+ (rmu_regs_win + (u32)msg_start);
+
+ rmu->txirq = irq_of_parse_and_map(node, 0);
+ rmu->rxirq = irq_of_parse_and_map(node, 1);
+ printk(KERN_INFO "%s: txirq: %d, rxirq %d\n",
+ node->full_name, rmu->txirq, rmu->rxirq);
+
+ priv->rmm_handle = rmu;
+
+ rio_init_dbell_res(&mport->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff);
+ rio_init_mbox_res(&mport->riores[RIO_INB_MBOX_RESOURCE], 0, 0);
+ rio_init_mbox_res(&mport->riores[RIO_OUTB_MBOX_RESOURCE], 0, 0);
+
+ return 0;
+}
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 2c5388ce902..ffd1169ebaa 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -19,26 +19,27 @@
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/irq.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
-#include <linux/phy_fixed.h>
#include <linux/spi/spi.h>
#include <linux/fsl_devices.h>
#include <linux/fs_enet_pd.h>
#include <linux/fs_uart_pd.h>
-#include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/time.h>
#include <asm/prom.h>
+#include <asm/machdep.h>
#include <sysdev/fsl_soc.h>
#include <mm/mmu_decl.h>
#include <asm/cpm2.h>
+#include <asm/fsl_hcalls.h> /* For the Freescale hypervisor */
extern void init_fcc_ioports(struct fs_platform_info*);
extern void init_fec_ioports(struct fs_platform_info*);
@@ -56,10 +57,10 @@ phys_addr_t get_immrbase(void)
if (soc) {
int size;
u32 naddr;
- const u32 *prop = of_get_property(soc, "#address-cells", &size);
+ const __be32 *prop = of_get_property(soc, "#address-cells", &size);
if (prop && size == 4)
- naddr = *prop;
+ naddr = be32_to_cpup(prop);
else
naddr = 2;
@@ -75,6 +76,33 @@ phys_addr_t get_immrbase(void)
EXPORT_SYMBOL(get_immrbase);
+static u32 sysfreq = -1;
+
+u32 fsl_get_sys_freq(void)
+{
+ struct device_node *soc;
+ const u32 *prop;
+ int size;
+
+ if (sysfreq != -1)
+ return sysfreq;
+
+ soc = of_find_node_by_type(NULL, "soc");
+ if (!soc)
+ return -1;
+
+ prop = of_get_property(soc, "clock-frequency", &size);
+ if (!prop || size != sizeof(*prop) || *prop == 0)
+ prop = of_get_property(soc, "bus-frequency", &size);
+
+ if (prop && size == sizeof(*prop))
+ sysfreq = *prop;
+
+ of_node_put(soc);
+ return sysfreq;
+}
+EXPORT_SYMBOL(fsl_get_sys_freq);
+
#if defined(CONFIG_CPM2) || defined(CONFIG_QUICC_ENGINE) || defined(CONFIG_8xx)
static u32 brgfreq = -1;
@@ -149,1292 +177,74 @@ u32 get_baudrate(void)
EXPORT_SYMBOL(get_baudrate);
#endif /* CONFIG_CPM2 */
-#ifdef CONFIG_FIXED_PHY
-static int __init of_add_fixed_phys(void)
-{
- int ret;
- struct device_node *np;
- u32 *fixed_link;
- struct fixed_phy_status status = {};
-
- for_each_node_by_name(np, "ethernet") {
- fixed_link = (u32 *)of_get_property(np, "fixed-link", NULL);
- if (!fixed_link)
- continue;
-
- status.link = 1;
- status.duplex = fixed_link[1];
- status.speed = fixed_link[2];
- status.pause = fixed_link[3];
- status.asym_pause = fixed_link[4];
-
- ret = fixed_phy_add(PHY_POLL, fixed_link[0], &status);
- if (ret) {
- of_node_put(np);
- return ret;
- }
- }
-
- return 0;
-}
-arch_initcall(of_add_fixed_phys);
-#endif /* CONFIG_FIXED_PHY */
-
-static int __init gfar_mdio_of_init(void)
-{
- struct device_node *np = NULL;
- struct platform_device *mdio_dev;
- struct resource res;
- int ret;
-
- np = of_find_compatible_node(np, NULL, "fsl,gianfar-mdio");
-
- /* try the deprecated version */
- if (!np)
- np = of_find_compatible_node(np, "mdio", "gianfar");
-
- if (np) {
- int k;
- struct device_node *child = NULL;
- struct gianfar_mdio_data mdio_data;
-
- memset(&res, 0, sizeof(res));
- memset(&mdio_data, 0, sizeof(mdio_data));
-
- ret = of_address_to_resource(np, 0, &res);
- if (ret)
- goto err;
-
- mdio_dev =
- platform_device_register_simple("fsl-gianfar_mdio",
- res.start, &res, 1);
- if (IS_ERR(mdio_dev)) {
- ret = PTR_ERR(mdio_dev);
- goto err;
- }
-
- for (k = 0; k < 32; k++)
- mdio_data.irq[k] = PHY_POLL;
-
- while ((child = of_get_next_child(np, child)) != NULL) {
- int irq = irq_of_parse_and_map(child, 0);
- if (irq != NO_IRQ) {
- const u32 *id = of_get_property(child,
- "reg", NULL);
- mdio_data.irq[*id] = irq;
- }
- }
-
- ret =
- platform_device_add_data(mdio_dev, &mdio_data,
- sizeof(struct gianfar_mdio_data));
- if (ret)
- goto unreg;
- }
-
- of_node_put(np);
- return 0;
-
-unreg:
- platform_device_unregister(mdio_dev);
-err:
- of_node_put(np);
- return ret;
-}
-
-arch_initcall(gfar_mdio_of_init);
-
-static const char *gfar_tx_intr = "tx";
-static const char *gfar_rx_intr = "rx";
-static const char *gfar_err_intr = "error";
+#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
+static __be32 __iomem *rstcr;
-static int __init gfar_of_init(void)
+static int __init setup_rstcr(void)
{
struct device_node *np;
- unsigned int i;
- struct platform_device *gfar_dev;
- struct resource res;
- int ret;
-
- for (np = NULL, i = 0;
- (np = of_find_compatible_node(np, "network", "gianfar")) != NULL;
- i++) {
- struct resource r[4];
- struct device_node *phy, *mdio;
- struct gianfar_platform_data gfar_data;
- const unsigned int *id;
- const char *model;
- const char *ctype;
- const void *mac_addr;
- const phandle *ph;
- int n_res = 2;
-
- memset(r, 0, sizeof(r));
- memset(&gfar_data, 0, sizeof(gfar_data));
-
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto err;
-
- of_irq_to_resource(np, 0, &r[1]);
- model = of_get_property(np, "model", NULL);
-
- /* If we aren't the FEC we have multiple interrupts */
- if (model && strcasecmp(model, "FEC")) {
- r[1].name = gfar_tx_intr;
-
- r[2].name = gfar_rx_intr;
- of_irq_to_resource(np, 1, &r[2]);
-
- r[3].name = gfar_err_intr;
- of_irq_to_resource(np, 2, &r[3]);
-
- n_res += 2;
- }
-
- gfar_dev =
- platform_device_register_simple("fsl-gianfar", i, &r[0],
- n_res);
-
- if (IS_ERR(gfar_dev)) {
- ret = PTR_ERR(gfar_dev);
- goto err;
- }
-
- mac_addr = of_get_mac_address(np);
- if (mac_addr)
- memcpy(gfar_data.mac_addr, mac_addr, 6);
-
- if (model && !strcasecmp(model, "TSEC"))
- gfar_data.device_flags =
- FSL_GIANFAR_DEV_HAS_GIGABIT |
- FSL_GIANFAR_DEV_HAS_COALESCE |
- FSL_GIANFAR_DEV_HAS_RMON |
- FSL_GIANFAR_DEV_HAS_MULTI_INTR;
- if (model && !strcasecmp(model, "eTSEC"))
- gfar_data.device_flags =
- FSL_GIANFAR_DEV_HAS_GIGABIT |
- FSL_GIANFAR_DEV_HAS_COALESCE |
- FSL_GIANFAR_DEV_HAS_RMON |
- FSL_GIANFAR_DEV_HAS_MULTI_INTR |
- FSL_GIANFAR_DEV_HAS_CSUM |
- FSL_GIANFAR_DEV_HAS_VLAN |
- FSL_GIANFAR_DEV_HAS_EXTENDED_HASH;
-
- ctype = of_get_property(np, "phy-connection-type", NULL);
-
- /* We only care about rgmii-id. The rest are autodetected */
- if (ctype && !strcmp(ctype, "rgmii-id"))
- gfar_data.interface = PHY_INTERFACE_MODE_RGMII_ID;
- else
- gfar_data.interface = PHY_INTERFACE_MODE_MII;
-
- ph = of_get_property(np, "phy-handle", NULL);
- if (ph == NULL) {
- u32 *fixed_link;
-
- fixed_link = (u32 *)of_get_property(np, "fixed-link",
- NULL);
- if (!fixed_link) {
- ret = -ENODEV;
- goto unreg;
- }
-
- gfar_data.bus_id = 0;
- gfar_data.phy_id = fixed_link[0];
- } else {
- phy = of_find_node_by_phandle(*ph);
-
- if (phy == NULL) {
- ret = -ENODEV;
- goto unreg;
- }
-
- mdio = of_get_parent(phy);
-
- id = of_get_property(phy, "reg", NULL);
- ret = of_address_to_resource(mdio, 0, &res);
- if (ret) {
- of_node_put(phy);
- of_node_put(mdio);
- goto unreg;
- }
-
- gfar_data.phy_id = *id;
- gfar_data.bus_id = res.start;
-
- of_node_put(phy);
- of_node_put(mdio);
- }
-
- ret =
- platform_device_add_data(gfar_dev, &gfar_data,
- sizeof(struct
- gianfar_platform_data));
- if (ret)
- goto unreg;
- }
-
- return 0;
-
-unreg:
- platform_device_unregister(gfar_dev);
-err:
- return ret;
-}
-
-arch_initcall(gfar_of_init);
-
-#ifdef CONFIG_I2C_BOARDINFO
-#include <linux/i2c.h>
-struct i2c_driver_device {
- char *of_device;
- char *i2c_driver;
- char *i2c_type;
-};
-
-static struct i2c_driver_device i2c_devices[] __initdata = {
- {"ricoh,rs5c372a", "rtc-rs5c372", "rs5c372a",},
- {"ricoh,rs5c372b", "rtc-rs5c372", "rs5c372b",},
- {"ricoh,rv5c386", "rtc-rs5c372", "rv5c386",},
- {"ricoh,rv5c387a", "rtc-rs5c372", "rv5c387a",},
- {"dallas,ds1307", "rtc-ds1307", "ds1307",},
- {"dallas,ds1337", "rtc-ds1307", "ds1337",},
- {"dallas,ds1338", "rtc-ds1307", "ds1338",},
- {"dallas,ds1339", "rtc-ds1307", "ds1339",},
- {"dallas,ds1340", "rtc-ds1307", "ds1340",},
- {"stm,m41t00", "rtc-ds1307", "m41t00"},
- {"dallas,ds1374", "rtc-ds1374", "rtc-ds1374",},
-};
-
-static int __init of_find_i2c_driver(struct device_node *node,
- struct i2c_board_info *info)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(i2c_devices); i++) {
- if (!of_device_is_compatible(node, i2c_devices[i].of_device))
- continue;
- if (strlcpy(info->driver_name, i2c_devices[i].i2c_driver,
- KOBJ_NAME_LEN) >= KOBJ_NAME_LEN ||
- strlcpy(info->type, i2c_devices[i].i2c_type,
- I2C_NAME_SIZE) >= I2C_NAME_SIZE)
- return -ENOMEM;
- return 0;
- }
- return -ENODEV;
-}
-
-static void __init of_register_i2c_devices(struct device_node *adap_node,
- int bus_num)
-{
- struct device_node *node = NULL;
-
- while ((node = of_get_next_child(adap_node, node))) {
- struct i2c_board_info info = {};
- const u32 *addr;
- int len;
-
- addr = of_get_property(node, "reg", &len);
- if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) {
- printk(KERN_WARNING "fsl_soc.c: invalid i2c device entry\n");
- continue;
+ for_each_node_by_name(np, "global-utilities") {
+ if ((of_get_property(np, "fsl,has-rstcr", NULL))) {
+ rstcr = of_iomap(np, 0) + 0xb0;
+ if (!rstcr)
+ printk (KERN_ERR "Error: reset control "
+ "register not mapped!\n");
+ break;
}
-
- info.irq = irq_of_parse_and_map(node, 0);
- if (info.irq == NO_IRQ)
- info.irq = -1;
-
- if (of_find_i2c_driver(node, &info) < 0)
- continue;
-
- info.addr = *addr;
-
- i2c_register_board_info(bus_num, &info, 1);
}
-}
-
-static int __init fsl_i2c_of_init(void)
-{
- struct device_node *np;
- unsigned int i = 0;
- struct platform_device *i2c_dev;
- int ret;
-
- for_each_compatible_node(np, NULL, "fsl-i2c") {
- struct resource r[2];
- struct fsl_i2c_platform_data i2c_data;
- const unsigned char *flags = NULL;
-
- memset(&r, 0, sizeof(r));
- memset(&i2c_data, 0, sizeof(i2c_data));
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto err;
-
- of_irq_to_resource(np, 0, &r[1]);
-
- i2c_dev = platform_device_register_simple("fsl-i2c", i, r, 2);
- if (IS_ERR(i2c_dev)) {
- ret = PTR_ERR(i2c_dev);
- goto err;
- }
+ if (!rstcr && ppc_md.restart == fsl_rstcr_restart)
+ printk(KERN_ERR "No RSTCR register, warm reboot won't work\n");
- i2c_data.device_flags = 0;
- flags = of_get_property(np, "dfsrr", NULL);
- if (flags)
- i2c_data.device_flags |= FSL_I2C_DEV_SEPARATE_DFSRR;
-
- flags = of_get_property(np, "fsl5200-clocking", NULL);
- if (flags)
- i2c_data.device_flags |= FSL_I2C_DEV_CLOCK_5200;
-
- ret =
- platform_device_add_data(i2c_dev, &i2c_data,
- sizeof(struct
- fsl_i2c_platform_data));
- if (ret)
- goto unreg;
-
- of_register_i2c_devices(np, i++);
- }
+ if (np)
+ of_node_put(np);
return 0;
-
-unreg:
- platform_device_unregister(i2c_dev);
-err:
- return ret;
}
-arch_initcall(fsl_i2c_of_init);
-#endif
+arch_initcall(setup_rstcr);
-#ifdef CONFIG_PPC_83xx
-static int __init mpc83xx_wdt_init(void)
+void fsl_rstcr_restart(char *cmd)
{
- struct resource r;
- struct device_node *soc, *np;
- struct platform_device *dev;
- const unsigned int *freq;
- int ret;
-
- np = of_find_compatible_node(NULL, "watchdog", "mpc83xx_wdt");
-
- if (!np) {
- ret = -ENODEV;
- goto nodev;
- }
-
- soc = of_find_node_by_type(NULL, "soc");
-
- if (!soc) {
- ret = -ENODEV;
- goto nosoc;
- }
-
- freq = of_get_property(soc, "bus-frequency", NULL);
- if (!freq) {
- ret = -ENODEV;
- goto err;
- }
-
- memset(&r, 0, sizeof(r));
-
- ret = of_address_to_resource(np, 0, &r);
- if (ret)
- goto err;
-
- dev = platform_device_register_simple("mpc83xx_wdt", 0, &r, 1);
- if (IS_ERR(dev)) {
- ret = PTR_ERR(dev);
- goto err;
- }
-
- ret = platform_device_add_data(dev, freq, sizeof(int));
- if (ret)
- goto unreg;
-
- of_node_put(soc);
- of_node_put(np);
-
- return 0;
+ local_irq_disable();
+ if (rstcr)
+ /* set reset control register */
+ out_be32(rstcr, 0x2); /* HRESET_REQ */
-unreg:
- platform_device_unregister(dev);
-err:
- of_node_put(soc);
-nosoc:
- of_node_put(np);
-nodev:
- return ret;
+ while (1) ;
}
-
-arch_initcall(mpc83xx_wdt_init);
#endif
-static enum fsl_usb2_phy_modes determine_usb_phy(const char *phy_type)
-{
- if (!phy_type)
- return FSL_USB2_PHY_NONE;
- if (!strcasecmp(phy_type, "ulpi"))
- return FSL_USB2_PHY_ULPI;
- if (!strcasecmp(phy_type, "utmi"))
- return FSL_USB2_PHY_UTMI;
- if (!strcasecmp(phy_type, "utmi_wide"))
- return FSL_USB2_PHY_UTMI_WIDE;
- if (!strcasecmp(phy_type, "serial"))
- return FSL_USB2_PHY_SERIAL;
-
- return FSL_USB2_PHY_NONE;
-}
-
-static int __init fsl_usb_of_init(void)
-{
- struct device_node *np;
- unsigned int i = 0;
- struct platform_device *usb_dev_mph = NULL, *usb_dev_dr_host = NULL,
- *usb_dev_dr_client = NULL;
- int ret;
-
- for_each_compatible_node(np, NULL, "fsl-usb2-mph") {
- struct resource r[2];
- struct fsl_usb2_platform_data usb_data;
- const unsigned char *prop = NULL;
-
- memset(&r, 0, sizeof(r));
- memset(&usb_data, 0, sizeof(usb_data));
-
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto err;
-
- of_irq_to_resource(np, 0, &r[1]);
-
- usb_dev_mph =
- platform_device_register_simple("fsl-ehci", i, r, 2);
- if (IS_ERR(usb_dev_mph)) {
- ret = PTR_ERR(usb_dev_mph);
- goto err;
- }
-
- usb_dev_mph->dev.coherent_dma_mask = 0xffffffffUL;
- usb_dev_mph->dev.dma_mask = &usb_dev_mph->dev.coherent_dma_mask;
-
- usb_data.operating_mode = FSL_USB2_MPH_HOST;
-
- prop = of_get_property(np, "port0", NULL);
- if (prop)
- usb_data.port_enables |= FSL_USB2_PORT0_ENABLED;
-
- prop = of_get_property(np, "port1", NULL);
- if (prop)
- usb_data.port_enables |= FSL_USB2_PORT1_ENABLED;
-
- prop = of_get_property(np, "phy_type", NULL);
- usb_data.phy_mode = determine_usb_phy(prop);
-
- ret =
- platform_device_add_data(usb_dev_mph, &usb_data,
- sizeof(struct
- fsl_usb2_platform_data));
- if (ret)
- goto unreg_mph;
- i++;
- }
-
- for_each_compatible_node(np, NULL, "fsl-usb2-dr") {
- struct resource r[2];
- struct fsl_usb2_platform_data usb_data;
- const unsigned char *prop = NULL;
-
- memset(&r, 0, sizeof(r));
- memset(&usb_data, 0, sizeof(usb_data));
-
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto unreg_mph;
-
- of_irq_to_resource(np, 0, &r[1]);
-
- prop = of_get_property(np, "dr_mode", NULL);
-
- if (!prop || !strcmp(prop, "host")) {
- usb_data.operating_mode = FSL_USB2_DR_HOST;
- usb_dev_dr_host = platform_device_register_simple(
- "fsl-ehci", i, r, 2);
- if (IS_ERR(usb_dev_dr_host)) {
- ret = PTR_ERR(usb_dev_dr_host);
- goto err;
- }
- } else if (prop && !strcmp(prop, "peripheral")) {
- usb_data.operating_mode = FSL_USB2_DR_DEVICE;
- usb_dev_dr_client = platform_device_register_simple(
- "fsl-usb2-udc", i, r, 2);
- if (IS_ERR(usb_dev_dr_client)) {
- ret = PTR_ERR(usb_dev_dr_client);
- goto err;
- }
- } else if (prop && !strcmp(prop, "otg")) {
- usb_data.operating_mode = FSL_USB2_DR_OTG;
- usb_dev_dr_host = platform_device_register_simple(
- "fsl-ehci", i, r, 2);
- if (IS_ERR(usb_dev_dr_host)) {
- ret = PTR_ERR(usb_dev_dr_host);
- goto err;
- }
- usb_dev_dr_client = platform_device_register_simple(
- "fsl-usb2-udc", i, r, 2);
- if (IS_ERR(usb_dev_dr_client)) {
- ret = PTR_ERR(usb_dev_dr_client);
- goto err;
- }
- } else {
- ret = -EINVAL;
- goto err;
- }
-
- prop = of_get_property(np, "phy_type", NULL);
- usb_data.phy_mode = determine_usb_phy(prop);
-
- if (usb_dev_dr_host) {
- usb_dev_dr_host->dev.coherent_dma_mask = 0xffffffffUL;
- usb_dev_dr_host->dev.dma_mask = &usb_dev_dr_host->
- dev.coherent_dma_mask;
- if ((ret = platform_device_add_data(usb_dev_dr_host,
- &usb_data, sizeof(struct
- fsl_usb2_platform_data))))
- goto unreg_dr;
- }
- if (usb_dev_dr_client) {
- usb_dev_dr_client->dev.coherent_dma_mask = 0xffffffffUL;
- usb_dev_dr_client->dev.dma_mask = &usb_dev_dr_client->
- dev.coherent_dma_mask;
- if ((ret = platform_device_add_data(usb_dev_dr_client,
- &usb_data, sizeof(struct
- fsl_usb2_platform_data))))
- goto unreg_dr;
- }
- i++;
- }
- return 0;
-
-unreg_dr:
- if (usb_dev_dr_host)
- platform_device_unregister(usb_dev_dr_host);
- if (usb_dev_dr_client)
- platform_device_unregister(usb_dev_dr_client);
-unreg_mph:
- if (usb_dev_mph)
- platform_device_unregister(usb_dev_mph);
-err:
- return ret;
-}
-
-arch_initcall(fsl_usb_of_init);
-
-#ifndef CONFIG_PPC_CPM_NEW_BINDING
-#ifdef CONFIG_CPM2
-
-extern void init_scc_ioports(struct fs_uart_platform_info*);
-
-static const char fcc_regs[] = "fcc_regs";
-static const char fcc_regs_c[] = "fcc_regs_c";
-static const char fcc_pram[] = "fcc_pram";
-static char bus_id[9][BUS_ID_SIZE];
-
-static int __init fs_enet_of_init(void)
-{
- struct device_node *np;
- unsigned int i;
- struct platform_device *fs_enet_dev;
- struct resource res;
- int ret;
-
- for (np = NULL, i = 0;
- (np = of_find_compatible_node(np, "network", "fs_enet")) != NULL;
- i++) {
- struct resource r[4];
- struct device_node *phy, *mdio;
- struct fs_platform_info fs_enet_data;
- const unsigned int *id, *phy_addr, *phy_irq;
- const void *mac_addr;
- const phandle *ph;
- const char *model;
-
- memset(r, 0, sizeof(r));
- memset(&fs_enet_data, 0, sizeof(fs_enet_data));
-
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto err;
- r[0].name = fcc_regs;
-
- ret = of_address_to_resource(np, 1, &r[1]);
- if (ret)
- goto err;
- r[1].name = fcc_pram;
-
- ret = of_address_to_resource(np, 2, &r[2]);
- if (ret)
- goto err;
- r[2].name = fcc_regs_c;
- fs_enet_data.fcc_regs_c = r[2].start;
-
- of_irq_to_resource(np, 0, &r[3]);
-
- fs_enet_dev =
- platform_device_register_simple("fsl-cpm-fcc", i, &r[0], 4);
-
- if (IS_ERR(fs_enet_dev)) {
- ret = PTR_ERR(fs_enet_dev);
- goto err;
- }
-
- model = of_get_property(np, "model", NULL);
- if (model == NULL) {
- ret = -ENODEV;
- goto unreg;
- }
-
- mac_addr = of_get_mac_address(np);
- if (mac_addr)
- memcpy(fs_enet_data.macaddr, mac_addr, 6);
-
- ph = of_get_property(np, "phy-handle", NULL);
- phy = of_find_node_by_phandle(*ph);
-
- if (phy == NULL) {
- ret = -ENODEV;
- goto unreg;
- }
-
- phy_addr = of_get_property(phy, "reg", NULL);
- fs_enet_data.phy_addr = *phy_addr;
-
- phy_irq = of_get_property(phy, "interrupts", NULL);
-
- id = of_get_property(np, "device-id", NULL);
- fs_enet_data.fs_no = *id;
- strcpy(fs_enet_data.fs_type, model);
-
- mdio = of_get_parent(phy);
- ret = of_address_to_resource(mdio, 0, &res);
- if (ret) {
- of_node_put(phy);
- of_node_put(mdio);
- goto unreg;
- }
-
- fs_enet_data.clk_rx = *((u32 *)of_get_property(np,
- "rx-clock", NULL));
- fs_enet_data.clk_tx = *((u32 *)of_get_property(np,
- "tx-clock", NULL));
-
- if (strstr(model, "FCC")) {
- int fcc_index = *id - 1;
- const unsigned char *mdio_bb_prop;
-
- fs_enet_data.dpram_offset = (u32)cpm_dpram_addr(0);
- fs_enet_data.rx_ring = 32;
- fs_enet_data.tx_ring = 32;
- fs_enet_data.rx_copybreak = 240;
- fs_enet_data.use_napi = 0;
- fs_enet_data.napi_weight = 17;
- fs_enet_data.mem_offset = FCC_MEM_OFFSET(fcc_index);
- fs_enet_data.cp_page = CPM_CR_FCC_PAGE(fcc_index);
- fs_enet_data.cp_block = CPM_CR_FCC_SBLOCK(fcc_index);
-
- snprintf((char*)&bus_id[(*id)], BUS_ID_SIZE, "%x:%02x",
- (u32)res.start, fs_enet_data.phy_addr);
- fs_enet_data.bus_id = (char*)&bus_id[(*id)];
- fs_enet_data.init_ioports = init_fcc_ioports;
-
- mdio_bb_prop = of_get_property(phy, "bitbang", NULL);
- if (mdio_bb_prop) {
- struct platform_device *fs_enet_mdio_bb_dev;
- struct fs_mii_bb_platform_info fs_enet_mdio_bb_data;
-
- fs_enet_mdio_bb_dev =
- platform_device_register_simple("fsl-bb-mdio",
- i, NULL, 0);
- memset(&fs_enet_mdio_bb_data, 0,
- sizeof(struct fs_mii_bb_platform_info));
- fs_enet_mdio_bb_data.mdio_dat.bit =
- mdio_bb_prop[0];
- fs_enet_mdio_bb_data.mdio_dir.bit =
- mdio_bb_prop[1];
- fs_enet_mdio_bb_data.mdc_dat.bit =
- mdio_bb_prop[2];
- fs_enet_mdio_bb_data.mdio_port =
- mdio_bb_prop[3];
- fs_enet_mdio_bb_data.mdc_port =
- mdio_bb_prop[4];
- fs_enet_mdio_bb_data.delay =
- mdio_bb_prop[5];
-
- fs_enet_mdio_bb_data.irq[0] = phy_irq[0];
- fs_enet_mdio_bb_data.irq[1] = -1;
- fs_enet_mdio_bb_data.irq[2] = -1;
- fs_enet_mdio_bb_data.irq[3] = phy_irq[0];
- fs_enet_mdio_bb_data.irq[31] = -1;
-
- fs_enet_mdio_bb_data.mdio_dat.offset =
- (u32)&cpm2_immr->im_ioport.iop_pdatc;
- fs_enet_mdio_bb_data.mdio_dir.offset =
- (u32)&cpm2_immr->im_ioport.iop_pdirc;
- fs_enet_mdio_bb_data.mdc_dat.offset =
- (u32)&cpm2_immr->im_ioport.iop_pdatc;
-
- ret = platform_device_add_data(
- fs_enet_mdio_bb_dev,
- &fs_enet_mdio_bb_data,
- sizeof(struct fs_mii_bb_platform_info));
- if (ret)
- goto unreg;
- }
-
- of_node_put(phy);
- of_node_put(mdio);
-
- ret = platform_device_add_data(fs_enet_dev, &fs_enet_data,
- sizeof(struct
- fs_platform_info));
- if (ret)
- goto unreg;
- }
- }
- return 0;
-
-unreg:
- platform_device_unregister(fs_enet_dev);
-err:
- return ret;
-}
-
-arch_initcall(fs_enet_of_init);
-
-static const char scc_regs[] = "regs";
-static const char scc_pram[] = "pram";
-
-static int __init cpm_uart_of_init(void)
-{
- struct device_node *np;
- unsigned int i;
- struct platform_device *cpm_uart_dev;
- int ret;
-
- for (np = NULL, i = 0;
- (np = of_find_compatible_node(np, "serial", "cpm_uart")) != NULL;
- i++) {
- struct resource r[3];
- struct fs_uart_platform_info cpm_uart_data;
- const int *id;
- const char *model;
-
- memset(r, 0, sizeof(r));
- memset(&cpm_uart_data, 0, sizeof(cpm_uart_data));
-
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto err;
-
- r[0].name = scc_regs;
-
- ret = of_address_to_resource(np, 1, &r[1]);
- if (ret)
- goto err;
- r[1].name = scc_pram;
-
- of_irq_to_resource(np, 0, &r[2]);
-
- cpm_uart_dev =
- platform_device_register_simple("fsl-cpm-scc:uart", i, &r[0], 3);
-
- if (IS_ERR(cpm_uart_dev)) {
- ret = PTR_ERR(cpm_uart_dev);
- goto err;
- }
-
- id = of_get_property(np, "device-id", NULL);
- cpm_uart_data.fs_no = *id;
-
- model = of_get_property(np, "model", NULL);
- strcpy(cpm_uart_data.fs_type, model);
-
- cpm_uart_data.uart_clk = ppc_proc_freq;
-
- cpm_uart_data.tx_num_fifo = 4;
- cpm_uart_data.tx_buf_size = 32;
- cpm_uart_data.rx_num_fifo = 4;
- cpm_uart_data.rx_buf_size = 32;
- cpm_uart_data.clk_rx = *((u32 *)of_get_property(np,
- "rx-clock", NULL));
- cpm_uart_data.clk_tx = *((u32 *)of_get_property(np,
- "tx-clock", NULL));
-
- ret =
- platform_device_add_data(cpm_uart_dev, &cpm_uart_data,
- sizeof(struct
- fs_uart_platform_info));
- if (ret)
- goto unreg;
- }
-
- return 0;
-
-unreg:
- platform_device_unregister(cpm_uart_dev);
-err:
- return ret;
-}
-
-arch_initcall(cpm_uart_of_init);
-#endif /* CONFIG_CPM2 */
-
-#ifdef CONFIG_8xx
-
-extern void init_scc_ioports(struct fs_platform_info*);
-extern int platform_device_skip(const char *model, int id);
-
-static int __init fs_enet_mdio_of_init(void)
-{
- struct device_node *np;
- unsigned int i;
- struct platform_device *mdio_dev;
- struct resource res;
- int ret;
-
- for (np = NULL, i = 0;
- (np = of_find_compatible_node(np, "mdio", "fs_enet")) != NULL;
- i++) {
- struct fs_mii_fec_platform_info mdio_data;
-
- memset(&res, 0, sizeof(res));
- memset(&mdio_data, 0, sizeof(mdio_data));
-
- ret = of_address_to_resource(np, 0, &res);
- if (ret)
- goto err;
-
- mdio_dev =
- platform_device_register_simple("fsl-cpm-fec-mdio",
- res.start, &res, 1);
- if (IS_ERR(mdio_dev)) {
- ret = PTR_ERR(mdio_dev);
- goto err;
- }
-
- mdio_data.mii_speed = ((((ppc_proc_freq + 4999999) / 2500000) / 2) & 0x3F) << 1;
-
- ret =
- platform_device_add_data(mdio_dev, &mdio_data,
- sizeof(struct fs_mii_fec_platform_info));
- if (ret)
- goto unreg;
- }
- return 0;
-
-unreg:
- platform_device_unregister(mdio_dev);
-err:
- return ret;
-}
-
-arch_initcall(fs_enet_mdio_of_init);
-
-static const char *enet_regs = "regs";
-static const char *enet_pram = "pram";
-static const char *enet_irq = "interrupt";
-static char bus_id[9][BUS_ID_SIZE];
-
-static int __init fs_enet_of_init(void)
-{
- struct device_node *np;
- unsigned int i;
- struct platform_device *fs_enet_dev = NULL;
- struct resource res;
- int ret;
-
- for (np = NULL, i = 0;
- (np = of_find_compatible_node(np, "network", "fs_enet")) != NULL;
- i++) {
- struct resource r[4];
- struct device_node *phy = NULL, *mdio = NULL;
- struct fs_platform_info fs_enet_data;
- const unsigned int *id;
- const unsigned int *phy_addr;
- const void *mac_addr;
- const phandle *ph;
- const char *model;
-
- memset(r, 0, sizeof(r));
- memset(&fs_enet_data, 0, sizeof(fs_enet_data));
-
- model = of_get_property(np, "model", NULL);
- if (model == NULL) {
- ret = -ENODEV;
- goto unreg;
- }
-
- id = of_get_property(np, "device-id", NULL);
- fs_enet_data.fs_no = *id;
-
- if (platform_device_skip(model, *id))
- continue;
-
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto err;
- r[0].name = enet_regs;
-
- mac_addr = of_get_mac_address(np);
- if (mac_addr)
- memcpy(fs_enet_data.macaddr, mac_addr, 6);
-
- ph = of_get_property(np, "phy-handle", NULL);
- if (ph != NULL)
- phy = of_find_node_by_phandle(*ph);
-
- if (phy != NULL) {
- phy_addr = of_get_property(phy, "reg", NULL);
- fs_enet_data.phy_addr = *phy_addr;
- fs_enet_data.has_phy = 1;
-
- mdio = of_get_parent(phy);
- ret = of_address_to_resource(mdio, 0, &res);
- if (ret) {
- of_node_put(phy);
- of_node_put(mdio);
- goto unreg;
- }
- }
-
- model = of_get_property(np, "model", NULL);
- strcpy(fs_enet_data.fs_type, model);
-
- if (strstr(model, "FEC")) {
- r[1].start = r[1].end = irq_of_parse_and_map(np, 0);
- r[1].flags = IORESOURCE_IRQ;
- r[1].name = enet_irq;
-
- fs_enet_dev =
- platform_device_register_simple("fsl-cpm-fec", i, &r[0], 2);
-
- if (IS_ERR(fs_enet_dev)) {
- ret = PTR_ERR(fs_enet_dev);
- goto err;
- }
-
- fs_enet_data.rx_ring = 128;
- fs_enet_data.tx_ring = 16;
- fs_enet_data.rx_copybreak = 240;
- fs_enet_data.use_napi = 1;
- fs_enet_data.napi_weight = 17;
-
- snprintf((char*)&bus_id[i], BUS_ID_SIZE, "%x:%02x",
- (u32)res.start, fs_enet_data.phy_addr);
- fs_enet_data.bus_id = (char*)&bus_id[i];
- fs_enet_data.init_ioports = init_fec_ioports;
- }
- if (strstr(model, "SCC")) {
- ret = of_address_to_resource(np, 1, &r[1]);
- if (ret)
- goto err;
- r[1].name = enet_pram;
-
- r[2].start = r[2].end = irq_of_parse_and_map(np, 0);
- r[2].flags = IORESOURCE_IRQ;
- r[2].name = enet_irq;
-
- fs_enet_dev =
- platform_device_register_simple("fsl-cpm-scc", i, &r[0], 3);
-
- if (IS_ERR(fs_enet_dev)) {
- ret = PTR_ERR(fs_enet_dev);
- goto err;
- }
-
- fs_enet_data.rx_ring = 64;
- fs_enet_data.tx_ring = 8;
- fs_enet_data.rx_copybreak = 240;
- fs_enet_data.use_napi = 1;
- fs_enet_data.napi_weight = 17;
-
- snprintf((char*)&bus_id[i], BUS_ID_SIZE, "%s", "fixed@10:1");
- fs_enet_data.bus_id = (char*)&bus_id[i];
- fs_enet_data.init_ioports = init_scc_ioports;
- }
-
- of_node_put(phy);
- of_node_put(mdio);
-
- ret = platform_device_add_data(fs_enet_dev, &fs_enet_data,
- sizeof(struct
- fs_platform_info));
- if (ret)
- goto unreg;
- }
- return 0;
-
-unreg:
- platform_device_unregister(fs_enet_dev);
-err:
- return ret;
-}
-
-arch_initcall(fs_enet_of_init);
-
-static int __init fsl_pcmcia_of_init(void)
-{
- struct device_node *np;
- /*
- * Register all the devices which type is "pcmcia"
- */
- for_each_compatible_node(np, "pcmcia", "fsl,pq-pcmcia")
- of_platform_device_create(np, "m8xx-pcmcia", NULL);
- return 0;
-}
-
-arch_initcall(fsl_pcmcia_of_init);
-
-static const char *smc_regs = "regs";
-static const char *smc_pram = "pram";
-
-static int __init cpm_smc_uart_of_init(void)
-{
- struct device_node *np;
- unsigned int i;
- struct platform_device *cpm_uart_dev;
- int ret;
-
- for (np = NULL, i = 0;
- (np = of_find_compatible_node(np, "serial", "cpm_uart")) != NULL;
- i++) {
- struct resource r[3];
- struct fs_uart_platform_info cpm_uart_data;
- const int *id;
- const char *model;
-
- memset(r, 0, sizeof(r));
- memset(&cpm_uart_data, 0, sizeof(cpm_uart_data));
-
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto err;
-
- r[0].name = smc_regs;
-
- ret = of_address_to_resource(np, 1, &r[1]);
- if (ret)
- goto err;
- r[1].name = smc_pram;
-
- r[2].start = r[2].end = irq_of_parse_and_map(np, 0);
- r[2].flags = IORESOURCE_IRQ;
-
- cpm_uart_dev =
- platform_device_register_simple("fsl-cpm-smc:uart", i, &r[0], 3);
-
- if (IS_ERR(cpm_uart_dev)) {
- ret = PTR_ERR(cpm_uart_dev);
- goto err;
- }
-
- model = of_get_property(np, "model", NULL);
- strcpy(cpm_uart_data.fs_type, model);
-
- id = of_get_property(np, "device-id", NULL);
- cpm_uart_data.fs_no = *id;
- cpm_uart_data.uart_clk = ppc_proc_freq;
-
- cpm_uart_data.tx_num_fifo = 4;
- cpm_uart_data.tx_buf_size = 32;
- cpm_uart_data.rx_num_fifo = 4;
- cpm_uart_data.rx_buf_size = 32;
-
- ret =
- platform_device_add_data(cpm_uart_dev, &cpm_uart_data,
- sizeof(struct
- fs_uart_platform_info));
- if (ret)
- goto unreg;
- }
-
- return 0;
-
-unreg:
- platform_device_unregister(cpm_uart_dev);
-err:
- return ret;
-}
-
-arch_initcall(cpm_smc_uart_of_init);
-
-#endif /* CONFIG_8xx */
-#endif /* CONFIG_PPC_CPM_NEW_BINDING */
-
-static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk,
- struct spi_board_info *board_infos,
- unsigned int num_board_infos,
- void (*activate_cs)(u8 cs, u8 polarity),
- void (*deactivate_cs)(u8 cs, u8 polarity))
-{
- struct device_node *np;
- unsigned int i = 0;
-
- for_each_compatible_node(np, type, compatible) {
- int ret;
- unsigned int j;
- const void *prop;
- struct resource res[2];
- struct platform_device *pdev;
- struct fsl_spi_platform_data pdata = {
- .activate_cs = activate_cs,
- .deactivate_cs = deactivate_cs,
- };
-
- memset(res, 0, sizeof(res));
-
- pdata.sysclk = sysclk;
-
- prop = of_get_property(np, "reg", NULL);
- if (!prop)
- goto err;
- pdata.bus_num = *(u32 *)prop;
-
- prop = of_get_property(np, "cell-index", NULL);
- if (prop)
- i = *(u32 *)prop;
-
- prop = of_get_property(np, "mode", NULL);
- if (prop && !strcmp(prop, "cpu-qe"))
- pdata.qe_mode = 1;
-
- for (j = 0; j < num_board_infos; j++) {
- if (board_infos[j].bus_num == pdata.bus_num)
- pdata.max_chipselect++;
- }
-
- if (!pdata.max_chipselect)
- continue;
-
- ret = of_address_to_resource(np, 0, &res[0]);
- if (ret)
- goto err;
-
- ret = of_irq_to_resource(np, 0, &res[1]);
- if (ret == NO_IRQ)
- goto err;
-
- pdev = platform_device_alloc("mpc83xx_spi", i);
- if (!pdev)
- goto err;
-
- ret = platform_device_add_data(pdev, &pdata, sizeof(pdata));
- if (ret)
- goto unreg;
-
- ret = platform_device_add_resources(pdev, res,
- ARRAY_SIZE(res));
- if (ret)
- goto unreg;
-
- ret = platform_device_add(pdev);
- if (ret)
- goto unreg;
-
- goto next;
-unreg:
- platform_device_del(pdev);
-err:
- pr_err("%s: registration failed\n", np->full_name);
-next:
- i++;
- }
-
- return i;
-}
-
-int __init fsl_spi_init(struct spi_board_info *board_infos,
- unsigned int num_board_infos,
- void (*activate_cs)(u8 cs, u8 polarity),
- void (*deactivate_cs)(u8 cs, u8 polarity))
-{
- u32 sysclk = -1;
- int ret;
-
-#ifdef CONFIG_QUICC_ENGINE
- /* SPI controller is either clocked from QE or SoC clock */
- sysclk = get_brgfreq();
+#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
+struct platform_diu_data_ops diu_ops;
+EXPORT_SYMBOL(diu_ops);
#endif
- if (sysclk == -1) {
- struct device_node *np;
- const u32 *freq;
- int size;
-
- np = of_find_node_by_type(NULL, "soc");
- if (!np)
- return -ENODEV;
-
- freq = of_get_property(np, "clock-frequency", &size);
- if (!freq || size != sizeof(*freq) || *freq == 0) {
- freq = of_get_property(np, "bus-frequency", &size);
- if (!freq || size != sizeof(*freq) || *freq == 0) {
- of_node_put(np);
- return -ENODEV;
- }
- }
-
- sysclk = *freq;
- of_node_put(np);
- }
-
- ret = of_fsl_spi_probe(NULL, "fsl,spi", sysclk, board_infos,
- num_board_infos, activate_cs, deactivate_cs);
- if (!ret)
- of_fsl_spi_probe("spi", "fsl_spi", sysclk, board_infos,
- num_board_infos, activate_cs, deactivate_cs);
-
- return spi_register_board_info(board_infos, num_board_infos);
-}
-
-#if defined(CONFIG_PPC_85xx) || defined(CONFIG_PPC_86xx)
-static __be32 __iomem *rstcr;
-static int __init setup_rstcr(void)
+#ifdef CONFIG_EPAPR_PARAVIRT
+/*
+ * Restart the current partition
+ *
+ * This function should be assigned to the ppc_md.restart function pointer,
+ * to initiate a partition restart when we're running under the Freescale
+ * hypervisor.
+ */
+void fsl_hv_restart(char *cmd)
{
- struct device_node *np;
- np = of_find_node_by_name(NULL, "global-utilities");
- if ((np && of_get_property(np, "fsl,has-rstcr", NULL))) {
- const u32 *prop = of_get_property(np, "reg", NULL);
- if (prop) {
- /* map reset control register
- * 0xE00B0 is offset of reset control register
- */
- rstcr = ioremap(get_immrbase() + *prop + 0xB0, 0xff);
- if (!rstcr)
- printk (KERN_EMERG "Error: reset control "
- "register not mapped!\n");
- }
- } else
- printk (KERN_INFO "rstcr compatible register does not exist!\n");
- if (np)
- of_node_put(np);
- return 0;
+ pr_info("hv restart\n");
+ fh_partition_restart(-1);
}
-arch_initcall(setup_rstcr);
-
-void fsl_rstcr_restart(char *cmd)
+/*
+ * Halt the current partition
+ *
+ * This function should be assigned to the ppc_md.power_off and ppc_md.halt
+ * function pointers, to shut down the partition when we're running under
+ * the Freescale hypervisor.
+ */
+void fsl_hv_halt(void)
{
- local_irq_disable();
- if (rstcr)
- /* set reset control register */
- out_be32(rstcr, 0x2); /* HRESET_REQ */
-
- while (1) ;
+ pr_info("hv exit\n");
+ fh_partition_stop(-1);
}
#endif
diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h
index 63e7db30a4c..4c5a19ef4f0 100644
--- a/arch/powerpc/sysdev/fsl_soc.h
+++ b/arch/powerpc/sysdev/fsl_soc.h
@@ -4,17 +4,46 @@
#include <asm/mmu.h>
+struct spi_device;
+
extern phys_addr_t get_immrbase(void);
+#if defined(CONFIG_CPM2) || defined(CONFIG_QUICC_ENGINE) || defined(CONFIG_8xx)
extern u32 get_brgfreq(void);
extern u32 get_baudrate(void);
+#else
+static inline u32 get_brgfreq(void) { return -1; }
+static inline u32 get_baudrate(void) { return -1; }
+#endif
+extern u32 fsl_get_sys_freq(void);
struct spi_board_info;
-
-extern int fsl_spi_init(struct spi_board_info *board_infos,
- unsigned int num_board_infos,
- void (*activate_cs)(u8 cs, u8 polarity),
- void (*deactivate_cs)(u8 cs, u8 polarity));
+struct device_node;
extern void fsl_rstcr_restart(char *cmd);
+
+/* The different ports that the DIU can be connected to */
+enum fsl_diu_monitor_port {
+ FSL_DIU_PORT_DVI, /* DVI */
+ FSL_DIU_PORT_LVDS, /* Single-link LVDS */
+ FSL_DIU_PORT_DLVDS /* Dual-link LVDS */
+};
+
+struct platform_diu_data_ops {
+ u32 (*get_pixel_format)(enum fsl_diu_monitor_port port,
+ unsigned int bpp);
+ void (*set_gamma_table)(enum fsl_diu_monitor_port port,
+ char *gamma_table_base);
+ void (*set_monitor_port)(enum fsl_diu_monitor_port port);
+ void (*set_pixel_clock)(unsigned int pixclock);
+ enum fsl_diu_monitor_port (*valid_monitor_port)
+ (enum fsl_diu_monitor_port port);
+ void (*release_bootmem)(void);
+};
+
+extern struct platform_diu_data_ops diu_ops;
+
+void fsl_hv_restart(char *cmd);
+void fsl_hv_halt(void);
+
#endif
#endif
diff --git a/arch/powerpc/sysdev/ge/Makefile b/arch/powerpc/sysdev/ge/Makefile
new file mode 100644
index 00000000000..8731ffcb79b
--- /dev/null
+++ b/arch/powerpc/sysdev/ge/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_GE_FPGA) += ge_pic.o
diff --git a/arch/powerpc/sysdev/ge/ge_pic.c b/arch/powerpc/sysdev/ge/ge_pic.c
new file mode 100644
index 00000000000..2bcb78bb3a1
--- /dev/null
+++ b/arch/powerpc/sysdev/ge/ge_pic.c
@@ -0,0 +1,251 @@
+/*
+ * Interrupt handling for GE FPGA based PIC
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ *
+ * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/irq.h>
+
+#include "ge_pic.h"
+
+#define DEBUG
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(KERN_DEBUG "gef_pic: " fmt); } while (0)
+#else
+#define DBG(fmt...) do { } while (0)
+#endif
+
+#define GEF_PIC_NUM_IRQS 32
+
+/* Interrupt Controller Interface Registers */
+#define GEF_PIC_INTR_STATUS 0x0000
+
+#define GEF_PIC_INTR_MASK(cpu) (0x0010 + (0x4 * cpu))
+#define GEF_PIC_CPU0_INTR_MASK GEF_PIC_INTR_MASK(0)
+#define GEF_PIC_CPU1_INTR_MASK GEF_PIC_INTR_MASK(1)
+
+#define GEF_PIC_MCP_MASK(cpu) (0x0018 + (0x4 * cpu))
+#define GEF_PIC_CPU0_MCP_MASK GEF_PIC_MCP_MASK(0)
+#define GEF_PIC_CPU1_MCP_MASK GEF_PIC_MCP_MASK(1)
+
+
+static DEFINE_RAW_SPINLOCK(gef_pic_lock);
+
+static void __iomem *gef_pic_irq_reg_base;
+static struct irq_domain *gef_pic_irq_host;
+static int gef_pic_cascade_irq;
+
+/*
+ * Interrupt Controller Handling
+ *
+ * The interrupt controller handles interrupts for most on board interrupts,
+ * apart from PCI interrupts. For example on SBC610:
+ *
+ * 17:31 RO Reserved
+ * 16 RO PCI Express Doorbell 3 Status
+ * 15 RO PCI Express Doorbell 2 Status
+ * 14 RO PCI Express Doorbell 1 Status
+ * 13 RO PCI Express Doorbell 0 Status
+ * 12 RO Real Time Clock Interrupt Status
+ * 11 RO Temperature Interrupt Status
+ * 10 RO Temperature Critical Interrupt Status
+ * 9 RO Ethernet PHY1 Interrupt Status
+ * 8 RO Ethernet PHY3 Interrupt Status
+ * 7 RO PEX8548 Interrupt Status
+ * 6 RO Reserved
+ * 5 RO Watchdog 0 Interrupt Status
+ * 4 RO Watchdog 1 Interrupt Status
+ * 3 RO AXIS Message FIFO A Interrupt Status
+ * 2 RO AXIS Message FIFO B Interrupt Status
+ * 1 RO AXIS Message FIFO C Interrupt Status
+ * 0 RO AXIS Message FIFO D Interrupt Status
+ *
+ * Interrupts can be forwarded to one of two output lines. Nothing
+ * clever is done, so if the masks are incorrectly set, a single input
+ * interrupt could generate interrupts on both output lines!
+ *
+ * The dual lines are there to allow the chained interrupts to be easily
+ * passed into two different cores. We currently do not use this functionality
+ * in this driver.
+ *
+ * Controller can also be configured to generate Machine checks (MCP), again on
+ * two lines, to be attached to two different cores. It is suggested that these
+ * should be masked out.
+ */
+
+void gef_pic_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned int cascade_irq;
+
+ /*
+ * See if we actually have an interrupt, call generic handling code if
+ * we do.
+ */
+ cascade_irq = gef_pic_get_irq();
+
+ if (cascade_irq != NO_IRQ)
+ generic_handle_irq(cascade_irq);
+
+ chip->irq_eoi(&desc->irq_data);
+}
+
+static void gef_pic_mask(struct irq_data *d)
+{
+ unsigned long flags;
+ unsigned int hwirq = irqd_to_hwirq(d);
+ u32 mask;
+
+ raw_spin_lock_irqsave(&gef_pic_lock, flags);
+ mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
+ mask &= ~(1 << hwirq);
+ out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask);
+ raw_spin_unlock_irqrestore(&gef_pic_lock, flags);
+}
+
+static void gef_pic_mask_ack(struct irq_data *d)
+{
+ /* Don't think we actually have to do anything to ack an interrupt,
+ * we just need to clear down the devices interrupt and it will go away
+ */
+ gef_pic_mask(d);
+}
+
+static void gef_pic_unmask(struct irq_data *d)
+{
+ unsigned long flags;
+ unsigned int hwirq = irqd_to_hwirq(d);
+ u32 mask;
+
+ raw_spin_lock_irqsave(&gef_pic_lock, flags);
+ mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
+ mask |= (1 << hwirq);
+ out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask);
+ raw_spin_unlock_irqrestore(&gef_pic_lock, flags);
+}
+
+static struct irq_chip gef_pic_chip = {
+ .name = "gefp",
+ .irq_mask = gef_pic_mask,
+ .irq_mask_ack = gef_pic_mask_ack,
+ .irq_unmask = gef_pic_unmask,
+};
+
+
+/* When an interrupt is being configured, this call allows some flexibilty
+ * in deciding which irq_chip structure is used
+ */
+static int gef_pic_host_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ /* All interrupts are LEVEL sensitive */
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ irq_set_chip_and_handler(virq, &gef_pic_chip, handle_level_irq);
+
+ return 0;
+}
+
+static int gef_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)
+{
+
+ *out_hwirq = intspec[0];
+ if (intsize > 1)
+ *out_flags = intspec[1];
+ else
+ *out_flags = IRQ_TYPE_LEVEL_HIGH;
+
+ return 0;
+}
+
+static const struct irq_domain_ops gef_pic_host_ops = {
+ .map = gef_pic_host_map,
+ .xlate = gef_pic_host_xlate,
+};
+
+
+/*
+ * Initialisation of PIC, this should be called in BSP
+ */
+void __init gef_pic_init(struct device_node *np)
+{
+ unsigned long flags;
+
+ /* Map the devices registers into memory */
+ gef_pic_irq_reg_base = of_iomap(np, 0);
+
+ raw_spin_lock_irqsave(&gef_pic_lock, flags);
+
+ /* Initialise everything as masked. */
+ out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_INTR_MASK, 0);
+ out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_INTR_MASK, 0);
+
+ out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_MCP_MASK, 0);
+ out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_MCP_MASK, 0);
+
+ raw_spin_unlock_irqrestore(&gef_pic_lock, flags);
+
+ /* Map controller */
+ gef_pic_cascade_irq = irq_of_parse_and_map(np, 0);
+ if (gef_pic_cascade_irq == NO_IRQ) {
+ printk(KERN_ERR "SBC610: failed to map cascade interrupt");
+ return;
+ }
+
+ /* Setup an irq_domain structure */
+ gef_pic_irq_host = irq_domain_add_linear(np, GEF_PIC_NUM_IRQS,
+ &gef_pic_host_ops, NULL);
+ if (gef_pic_irq_host == NULL)
+ return;
+
+ /* Chain with parent controller */
+ irq_set_chained_handler(gef_pic_cascade_irq, gef_pic_cascade);
+}
+
+/*
+ * This is called when we receive an interrupt with apparently comes from this
+ * chip - check, returning the highest interrupt generated or return NO_IRQ
+ */
+unsigned int gef_pic_get_irq(void)
+{
+ u32 cause, mask, active;
+ unsigned int virq = NO_IRQ;
+ int hwirq;
+
+ cause = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_STATUS);
+
+ mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
+
+ active = cause & mask;
+
+ if (active) {
+ for (hwirq = GEF_PIC_NUM_IRQS - 1; hwirq > -1; hwirq--) {
+ if (active & (0x1 << hwirq))
+ break;
+ }
+ virq = irq_linear_revmap(gef_pic_irq_host,
+ (irq_hw_number_t)hwirq);
+ }
+
+ return virq;
+}
+
diff --git a/arch/powerpc/sysdev/ge/ge_pic.h b/arch/powerpc/sysdev/ge/ge_pic.h
new file mode 100644
index 00000000000..908dbd9826b
--- /dev/null
+++ b/arch/powerpc/sysdev/ge/ge_pic.h
@@ -0,0 +1,10 @@
+#ifndef __GEF_PIC_H__
+#define __GEF_PIC_H__
+
+
+void gef_pic_cascade(unsigned int, struct irq_desc *);
+unsigned int gef_pic_get_irq(void);
+void gef_pic_init(struct device_node *);
+
+#endif /* __GEF_PIC_H__ */
+
diff --git a/arch/powerpc/sysdev/grackle.c b/arch/powerpc/sysdev/grackle.c
index d502927644c..08abe91ae79 100644
--- a/arch/powerpc/sysdev/grackle.c
+++ b/arch/powerpc/sysdev/grackle.c
@@ -56,9 +56,9 @@ static inline void grackle_set_loop_snoop(struct pci_controller *bp, int enable)
void __init setup_grackle(struct pci_controller *hose)
{
setup_indirect_pci(hose, 0xfec00000, 0xfee00000, 0);
- if (machine_is_compatible("PowerMac1,1"))
- ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
- if (machine_is_compatible("AAPL,PowerBook1998"))
+ if (of_machine_is_compatible("PowerMac1,1"))
+ pci_add_flags(PCI_REASSIGN_ALL_BUS);
+ if (of_machine_is_compatible("AAPL,PowerBook1998"))
grackle_set_loop_snoop(hose, 1);
#if 0 /* Disabled for now, HW problems ??? */
grackle_set_stg(hose, 1);
diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c
index 216c0f5680d..45598da0b32 100644
--- a/arch/powerpc/sysdev/i8259.c
+++ b/arch/powerpc/sysdev/i8259.c
@@ -8,7 +8,6 @@
*/
#undef DEBUG
-#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -23,9 +22,9 @@ static unsigned char cached_8259[2] = { 0xff, 0xff };
#define cached_A1 (cached_8259[0])
#define cached_21 (cached_8259[1])
-static DEFINE_SPINLOCK(i8259_lock);
+static DEFINE_RAW_SPINLOCK(i8259_lock);
-static struct irq_host *i8259_host;
+static struct irq_domain *i8259_host;
/*
* Acknowledge the IRQ using either the PCI host bridge's interrupt
@@ -42,7 +41,7 @@ unsigned int i8259_irq(void)
if (pci_intack)
irq = readb(pci_intack);
else {
- spin_lock(&i8259_lock);
+ raw_spin_lock(&i8259_lock);
lock = 1;
/* Perform an interrupt acknowledge cycle on controller 1. */
@@ -74,28 +73,28 @@ unsigned int i8259_irq(void)
irq = NO_IRQ;
if (lock)
- spin_unlock(&i8259_lock);
+ raw_spin_unlock(&i8259_lock);
return irq;
}
-static void i8259_mask_and_ack_irq(unsigned int irq_nr)
+static void i8259_mask_and_ack_irq(struct irq_data *d)
{
unsigned long flags;
- spin_lock_irqsave(&i8259_lock, flags);
- if (irq_nr > 7) {
- cached_A1 |= 1 << (irq_nr-8);
+ raw_spin_lock_irqsave(&i8259_lock, flags);
+ if (d->irq > 7) {
+ cached_A1 |= 1 << (d->irq-8);
inb(0xA1); /* DUMMY */
outb(cached_A1, 0xA1);
outb(0x20, 0xA0); /* Non-specific EOI */
outb(0x20, 0x20); /* Non-specific EOI to cascade */
} else {
- cached_21 |= 1 << irq_nr;
+ cached_21 |= 1 << d->irq;
inb(0x21); /* DUMMY */
outb(cached_21, 0x21);
outb(0x20, 0x20); /* Non-specific EOI */
}
- spin_unlock_irqrestore(&i8259_lock, flags);
+ raw_spin_unlock_irqrestore(&i8259_lock, flags);
}
static void i8259_set_irq_mask(int irq_nr)
@@ -104,42 +103,42 @@ static void i8259_set_irq_mask(int irq_nr)
outb(cached_21,0x21);
}
-static void i8259_mask_irq(unsigned int irq_nr)
+static void i8259_mask_irq(struct irq_data *d)
{
unsigned long flags;
- pr_debug("i8259_mask_irq(%d)\n", irq_nr);
+ pr_debug("i8259_mask_irq(%d)\n", d->irq);
- spin_lock_irqsave(&i8259_lock, flags);
- if (irq_nr < 8)
- cached_21 |= 1 << irq_nr;
+ raw_spin_lock_irqsave(&i8259_lock, flags);
+ if (d->irq < 8)
+ cached_21 |= 1 << d->irq;
else
- cached_A1 |= 1 << (irq_nr-8);
- i8259_set_irq_mask(irq_nr);
- spin_unlock_irqrestore(&i8259_lock, flags);
+ cached_A1 |= 1 << (d->irq-8);
+ i8259_set_irq_mask(d->irq);
+ raw_spin_unlock_irqrestore(&i8259_lock, flags);
}
-static void i8259_unmask_irq(unsigned int irq_nr)
+static void i8259_unmask_irq(struct irq_data *d)
{
unsigned long flags;
- pr_debug("i8259_unmask_irq(%d)\n", irq_nr);
+ pr_debug("i8259_unmask_irq(%d)\n", d->irq);
- spin_lock_irqsave(&i8259_lock, flags);
- if (irq_nr < 8)
- cached_21 &= ~(1 << irq_nr);
+ raw_spin_lock_irqsave(&i8259_lock, flags);
+ if (d->irq < 8)
+ cached_21 &= ~(1 << d->irq);
else
- cached_A1 &= ~(1 << (irq_nr-8));
- i8259_set_irq_mask(irq_nr);
- spin_unlock_irqrestore(&i8259_lock, flags);
+ cached_A1 &= ~(1 << (d->irq-8));
+ i8259_set_irq_mask(d->irq);
+ raw_spin_unlock_irqrestore(&i8259_lock, flags);
}
static struct irq_chip i8259_pic = {
- .typename = " i8259 ",
- .mask = i8259_mask_irq,
- .disable = i8259_mask_irq,
- .unmask = i8259_unmask_irq,
- .mask_ack = i8259_mask_and_ack_irq,
+ .name = "i8259",
+ .irq_mask = i8259_mask_irq,
+ .irq_disable = i8259_mask_irq,
+ .irq_unmask = i8259_unmask_irq,
+ .irq_mask_ack = i8259_mask_and_ack_irq,
};
static struct resource pic1_iores = {
@@ -163,42 +162,30 @@ static struct resource pic_edgectrl_iores = {
.flags = IORESOURCE_BUSY,
};
-static int i8259_host_match(struct irq_host *h, struct device_node *node)
+static int i8259_host_match(struct irq_domain *h, struct device_node *node)
{
return h->of_node == NULL || h->of_node == node;
}
-static int i8259_host_map(struct irq_host *h, unsigned int virq,
+static int i8259_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
pr_debug("i8259_host_map(%d, 0x%lx)\n", virq, hw);
/* We block the internal cascade */
if (hw == 2)
- get_irq_desc(virq)->status |= IRQ_NOREQUEST;
+ irq_set_status_flags(virq, IRQ_NOREQUEST);
/* We use the level handler only for now, we might want to
* be more cautious here but that works for now
*/
- get_irq_desc(virq)->status |= IRQ_LEVEL;
- set_irq_chip_and_handler(virq, &i8259_pic, handle_level_irq);
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ irq_set_chip_and_handler(virq, &i8259_pic, handle_level_irq);
return 0;
}
-static void i8259_host_unmap(struct irq_host *h, unsigned int virq)
-{
- /* Make sure irq is masked in hardware */
- i8259_mask_irq(virq);
-
- /* remove chip and handler */
- set_irq_chip_and_handler(virq, NULL, NULL);
-
- /* Make sure it's completed */
- synchronize_irq(virq);
-}
-
-static int i8259_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
+static int i8259_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)
{
static unsigned char map_isa_senses[4] = {
@@ -217,14 +204,13 @@ static int i8259_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
-static struct irq_host_ops i8259_host_ops = {
+static struct irq_domain_ops i8259_host_ops = {
.match = i8259_host_match,
.map = i8259_host_map,
- .unmap = i8259_host_unmap,
.xlate = i8259_host_xlate,
};
-struct irq_host *i8259_get_host(void)
+struct irq_domain *i8259_get_host(void)
{
return i8259_host;
}
@@ -241,7 +227,7 @@ void i8259_init(struct device_node *node, unsigned long intack_addr)
unsigned long flags;
/* initialize the controller */
- spin_lock_irqsave(&i8259_lock, flags);
+ raw_spin_lock_irqsave(&i8259_lock, flags);
/* Mask all first */
outb(0xff, 0xA1);
@@ -273,11 +259,10 @@ void i8259_init(struct device_node *node, unsigned long intack_addr)
outb(cached_A1, 0xA1);
outb(cached_21, 0x21);
- spin_unlock_irqrestore(&i8259_lock, flags);
+ raw_spin_unlock_irqrestore(&i8259_lock, flags);
/* create a legacy host */
- i8259_host = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LEGACY,
- 0, &i8259_host_ops, 0);
+ i8259_host = irq_domain_add_legacy_isa(node, &i8259_host_ops, NULL);
if (i8259_host == NULL) {
printk(KERN_ERR "i8259: failed to allocate irq host !\n");
return;
diff --git a/arch/powerpc/sysdev/indirect_pci.c b/arch/powerpc/sysdev/indirect_pci.c
index cfbd2aae93e..1f6c570d66d 100644
--- a/arch/powerpc/sysdev/indirect_pci.c
+++ b/arch/powerpc/sysdev/indirect_pci.c
@@ -20,11 +20,10 @@
#include <asm/pci-bridge.h>
#include <asm/machdep.h>
-static int
-indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
- int len, u32 *val)
+int indirect_read_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 *val)
{
- struct pci_controller *hose = bus->sysdata;
+ struct pci_controller *hose = pci_bus_to_host(bus);
volatile void __iomem *cfg_data;
u8 cfg_type = 0;
u32 bus_no, reg;
@@ -78,11 +77,10 @@ indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
return PCIBIOS_SUCCESSFUL;
}
-static int
-indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
- int len, u32 val)
+int indirect_write_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 val)
{
- struct pci_controller *hose = bus->sysdata;
+ struct pci_controller *hose = pci_bus_to_host(bus);
volatile void __iomem *cfg_data;
u8 cfg_type = 0;
u32 bus_no, reg;
@@ -117,12 +115,18 @@ indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
(devfn << 8) | reg | cfg_type));
- /* surpress setting of PCI_PRIMARY_BUS */
+ /* suppress setting of PCI_PRIMARY_BUS */
if (hose->indirect_type & PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS)
if ((offset == PCI_PRIMARY_BUS) &&
(bus->number == hose->first_busno))
val &= 0xffffff00;
+ /* Workaround for PCI_28 Errata in 440EPx/GRx */
+ if ((hose->indirect_type & PPC_INDIRECT_TYPE_BROKEN_MRM) &&
+ offset == PCI_CACHE_LINE_SIZE) {
+ val = 0;
+ }
+
/*
* Note: the caller has already checked that offset is
* suitably aligned and that len is 1, 2 or 4.
@@ -148,10 +152,8 @@ static struct pci_ops indirect_pci_ops =
.write = indirect_write_config,
};
-void __init
-setup_indirect_pci(struct pci_controller* hose,
- resource_size_t cfg_addr,
- resource_size_t cfg_data, u32 flags)
+void setup_indirect_pci(struct pci_controller *hose, resource_size_t cfg_addr,
+ resource_size_t cfg_data, u32 flags)
{
resource_size_t base = cfg_addr & PAGE_MASK;
void __iomem *mbase;
diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c
index 0f2dfb0aaa6..b50f97811c2 100644
--- a/arch/powerpc/sysdev/ipic.c
+++ b/arch/powerpc/sysdev/ipic.c
@@ -18,10 +18,11 @@
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/signal.h>
-#include <linux/sysdev.h>
+#include <linux/syscore_ops.h>
#include <linux/device.h>
#include <linux/bootmem.h>
#include <linux/spinlock.h>
+#include <linux/fsl_devices.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/prom.h>
@@ -31,7 +32,7 @@
static struct ipic * primary_ipic;
static struct irq_chip ipic_level_irq_chip, ipic_edge_irq_chip;
-static DEFINE_SPINLOCK(ipic_lock);
+static DEFINE_RAW_SPINLOCK(ipic_lock);
static struct ipic_info ipic_info[] = {
[1] = {
@@ -520,32 +521,30 @@ static inline struct ipic * ipic_from_irq(unsigned int virq)
return primary_ipic;
}
-#define ipic_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq)
-
-static void ipic_unmask_irq(unsigned int virq)
+static void ipic_unmask_irq(struct irq_data *d)
{
- struct ipic *ipic = ipic_from_irq(virq);
- unsigned int src = ipic_irq_to_hw(virq);
+ struct ipic *ipic = ipic_from_irq(d->irq);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
u32 temp;
- spin_lock_irqsave(&ipic_lock, flags);
+ raw_spin_lock_irqsave(&ipic_lock, flags);
temp = ipic_read(ipic->regs, ipic_info[src].mask);
temp |= (1 << (31 - ipic_info[src].bit));
ipic_write(ipic->regs, ipic_info[src].mask, temp);
- spin_unlock_irqrestore(&ipic_lock, flags);
+ raw_spin_unlock_irqrestore(&ipic_lock, flags);
}
-static void ipic_mask_irq(unsigned int virq)
+static void ipic_mask_irq(struct irq_data *d)
{
- struct ipic *ipic = ipic_from_irq(virq);
- unsigned int src = ipic_irq_to_hw(virq);
+ struct ipic *ipic = ipic_from_irq(d->irq);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
u32 temp;
- spin_lock_irqsave(&ipic_lock, flags);
+ raw_spin_lock_irqsave(&ipic_lock, flags);
temp = ipic_read(ipic->regs, ipic_info[src].mask);
temp &= ~(1 << (31 - ipic_info[src].bit));
@@ -555,58 +554,55 @@ static void ipic_mask_irq(unsigned int virq)
* for nearly all cases. */
mb();
- spin_unlock_irqrestore(&ipic_lock, flags);
+ raw_spin_unlock_irqrestore(&ipic_lock, flags);
}
-static void ipic_ack_irq(unsigned int virq)
+static void ipic_ack_irq(struct irq_data *d)
{
- struct ipic *ipic = ipic_from_irq(virq);
- unsigned int src = ipic_irq_to_hw(virq);
+ struct ipic *ipic = ipic_from_irq(d->irq);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
u32 temp;
- spin_lock_irqsave(&ipic_lock, flags);
+ raw_spin_lock_irqsave(&ipic_lock, flags);
- temp = ipic_read(ipic->regs, ipic_info[src].ack);
- temp |= (1 << (31 - ipic_info[src].bit));
+ temp = 1 << (31 - ipic_info[src].bit);
ipic_write(ipic->regs, ipic_info[src].ack, temp);
/* mb() can't guarantee that ack is finished. But it does finish
* for nearly all cases. */
mb();
- spin_unlock_irqrestore(&ipic_lock, flags);
+ raw_spin_unlock_irqrestore(&ipic_lock, flags);
}
-static void ipic_mask_irq_and_ack(unsigned int virq)
+static void ipic_mask_irq_and_ack(struct irq_data *d)
{
- struct ipic *ipic = ipic_from_irq(virq);
- unsigned int src = ipic_irq_to_hw(virq);
+ struct ipic *ipic = ipic_from_irq(d->irq);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
u32 temp;
- spin_lock_irqsave(&ipic_lock, flags);
+ raw_spin_lock_irqsave(&ipic_lock, flags);
temp = ipic_read(ipic->regs, ipic_info[src].mask);
temp &= ~(1 << (31 - ipic_info[src].bit));
ipic_write(ipic->regs, ipic_info[src].mask, temp);
- temp = ipic_read(ipic->regs, ipic_info[src].ack);
- temp |= (1 << (31 - ipic_info[src].bit));
+ temp = 1 << (31 - ipic_info[src].bit);
ipic_write(ipic->regs, ipic_info[src].ack, temp);
/* mb() can't guarantee that ack is finished. But it does finish
* for nearly all cases. */
mb();
- spin_unlock_irqrestore(&ipic_lock, flags);
+ raw_spin_unlock_irqrestore(&ipic_lock, flags);
}
-static int ipic_set_irq_type(unsigned int virq, unsigned int flow_type)
+static int ipic_set_irq_type(struct irq_data *d, unsigned int flow_type)
{
- struct ipic *ipic = ipic_from_irq(virq);
- unsigned int src = ipic_irq_to_hw(virq);
- struct irq_desc *desc = get_irq_desc(virq);
+ struct ipic *ipic = ipic_from_irq(d->irq);
+ unsigned int src = irqd_to_hwirq(d);
unsigned int vold, vnew, edibit;
if (flow_type == IRQ_TYPE_NONE)
@@ -624,17 +620,16 @@ static int ipic_set_irq_type(unsigned int virq, unsigned int flow_type)
printk(KERN_ERR "ipic: edge sense not supported on internal "
"interrupts\n");
return -EINVAL;
+
}
- desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
- desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
+ irqd_set_trigger_type(d, flow_type);
if (flow_type & IRQ_TYPE_LEVEL_LOW) {
- desc->status |= IRQ_LEVEL;
- desc->handle_irq = handle_level_irq;
- desc->chip = &ipic_level_irq_chip;
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ d->chip = &ipic_level_irq_chip;
} else {
- desc->handle_irq = handle_edge_irq;
- desc->chip = &ipic_edge_irq_chip;
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+ d->chip = &ipic_edge_irq_chip;
}
/* only EXT IRQ senses are programmable on ipic
@@ -656,67 +651,51 @@ static int ipic_set_irq_type(unsigned int virq, unsigned int flow_type)
}
if (vold != vnew)
ipic_write(ipic->regs, IPIC_SECNR, vnew);
- return 0;
+ return IRQ_SET_MASK_OK_NOCOPY;
}
/* level interrupts and edge interrupts have different ack operations */
static struct irq_chip ipic_level_irq_chip = {
- .typename = " IPIC ",
- .unmask = ipic_unmask_irq,
- .mask = ipic_mask_irq,
- .mask_ack = ipic_mask_irq,
- .set_type = ipic_set_irq_type,
+ .name = "IPIC",
+ .irq_unmask = ipic_unmask_irq,
+ .irq_mask = ipic_mask_irq,
+ .irq_mask_ack = ipic_mask_irq,
+ .irq_set_type = ipic_set_irq_type,
};
static struct irq_chip ipic_edge_irq_chip = {
- .typename = " IPIC ",
- .unmask = ipic_unmask_irq,
- .mask = ipic_mask_irq,
- .mask_ack = ipic_mask_irq_and_ack,
- .ack = ipic_ack_irq,
- .set_type = ipic_set_irq_type,
+ .name = "IPIC",
+ .irq_unmask = ipic_unmask_irq,
+ .irq_mask = ipic_mask_irq,
+ .irq_mask_ack = ipic_mask_irq_and_ack,
+ .irq_ack = ipic_ack_irq,
+ .irq_set_type = ipic_set_irq_type,
};
-static int ipic_host_match(struct irq_host *h, struct device_node *node)
+static int ipic_host_match(struct irq_domain *h, struct device_node *node)
{
/* Exact match, unless ipic node is NULL */
return h->of_node == NULL || h->of_node == node;
}
-static int ipic_host_map(struct irq_host *h, unsigned int virq,
+static int ipic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct ipic *ipic = h->host_data;
- set_irq_chip_data(virq, ipic);
- set_irq_chip_and_handler(virq, &ipic_level_irq_chip, handle_level_irq);
+ irq_set_chip_data(virq, ipic);
+ irq_set_chip_and_handler(virq, &ipic_level_irq_chip, 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 ipic_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
- irq_hw_number_t *out_hwirq, unsigned int *out_flags)
-
-{
- /* interrupt sense values coming from the device tree equal either
- * LEVEL_LOW (low assertion) or EDGE_FALLING (high-to-low change)
- */
- *out_hwirq = intspec[0];
- if (intsize > 1)
- *out_flags = intspec[1];
- else
- *out_flags = IRQ_TYPE_NONE;
- return 0;
-}
-
-static struct irq_host_ops ipic_host_ops = {
+static struct irq_domain_ops ipic_host_ops = {
.match = ipic_host_match,
.map = ipic_host_map,
- .xlate = ipic_host_xlate,
+ .xlate = irq_domain_xlate_onetwocell,
};
struct ipic * __init ipic_init(struct device_node *node, unsigned int flags)
@@ -725,29 +704,22 @@ struct ipic * __init ipic_init(struct device_node *node, unsigned int flags)
struct resource res;
u32 temp = 0, ret;
- ipic = alloc_bootmem(sizeof(struct ipic));
- if (ipic == NULL)
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret)
return NULL;
- memset(ipic, 0, sizeof(struct ipic));
-
- ipic->irqhost = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LINEAR,
- NR_IPIC_INTS,
- &ipic_host_ops, 0);
- if (ipic->irqhost == NULL) {
- of_node_put(node);
+ ipic = kzalloc(sizeof(*ipic), GFP_KERNEL);
+ if (ipic == NULL)
return NULL;
- }
- ret = of_address_to_resource(node, 0, &res);
- if (ret) {
- of_node_put(node);
+ ipic->irqhost = irq_domain_add_linear(node, NR_IPIC_INTS,
+ &ipic_host_ops, ipic);
+ if (ipic->irqhost == NULL) {
+ kfree(ipic);
return NULL;
}
- ipic->regs = ioremap(res.start, res.end - res.start + 1);
-
- ipic->irqhost->host_data = ipic;
+ ipic->regs = ioremap(res.start, resource_size(&res));
/* init hw */
ipic_write(ipic->regs, IPIC_SICNR, 0x0);
@@ -788,6 +760,9 @@ struct ipic * __init ipic_init(struct device_node *node, unsigned int flags)
primary_ipic = ipic;
irq_set_default_host(primary_ipic->irqhost);
+ ipic_write(ipic->regs, IPIC_SIMSR_H, 0);
+ ipic_write(ipic->regs, IPIC_SIMSR_L, 0);
+
printk ("IPIC (%d IRQ sources) at %p\n", NR_IPIC_INTS,
primary_ipic->regs);
@@ -797,7 +772,7 @@ struct ipic * __init ipic_init(struct device_node *node, unsigned int flags)
int ipic_set_priority(unsigned int virq, unsigned int priority)
{
struct ipic *ipic = ipic_from_irq(virq);
- unsigned int src = ipic_irq_to_hw(virq);
+ unsigned int src = virq_to_hw(virq);
u32 temp;
if (priority > 7)
@@ -825,7 +800,7 @@ int ipic_set_priority(unsigned int virq, unsigned int priority)
void ipic_set_highest_priority(unsigned int virq)
{
struct ipic *ipic = ipic_from_irq(virq);
- unsigned int src = ipic_irq_to_hw(virq);
+ unsigned int src = virq_to_hw(virq);
u32 temp;
temp = ipic_read(ipic->regs, IPIC_SICFR);
@@ -893,34 +868,86 @@ unsigned int ipic_get_irq(void)
return irq_linear_revmap(primary_ipic->irqhost, irq);
}
-static struct sysdev_class ipic_sysclass = {
- .name = "ipic",
-};
+#ifdef CONFIG_SUSPEND
+static struct {
+ u32 sicfr;
+ u32 siprr[2];
+ u32 simsr[2];
+ u32 sicnr;
+ u32 smprr[2];
+ u32 semsr;
+ u32 secnr;
+ u32 sermr;
+ u32 sercr;
+} ipic_saved_state;
+
+static int ipic_suspend(void)
+{
+ struct ipic *ipic = primary_ipic;
-static struct sys_device device_ipic = {
- .id = 0,
- .cls = &ipic_sysclass,
-};
+ ipic_saved_state.sicfr = ipic_read(ipic->regs, IPIC_SICFR);
+ ipic_saved_state.siprr[0] = ipic_read(ipic->regs, IPIC_SIPRR_A);
+ ipic_saved_state.siprr[1] = ipic_read(ipic->regs, IPIC_SIPRR_D);
+ ipic_saved_state.simsr[0] = ipic_read(ipic->regs, IPIC_SIMSR_H);
+ ipic_saved_state.simsr[1] = ipic_read(ipic->regs, IPIC_SIMSR_L);
+ ipic_saved_state.sicnr = ipic_read(ipic->regs, IPIC_SICNR);
+ ipic_saved_state.smprr[0] = ipic_read(ipic->regs, IPIC_SMPRR_A);
+ ipic_saved_state.smprr[1] = ipic_read(ipic->regs, IPIC_SMPRR_B);
+ ipic_saved_state.semsr = ipic_read(ipic->regs, IPIC_SEMSR);
+ ipic_saved_state.secnr = ipic_read(ipic->regs, IPIC_SECNR);
+ ipic_saved_state.sermr = ipic_read(ipic->regs, IPIC_SERMR);
+ ipic_saved_state.sercr = ipic_read(ipic->regs, IPIC_SERCR);
+
+ if (fsl_deep_sleep()) {
+ /* In deep sleep, make sure there can be no
+ * pending interrupts, as this can cause
+ * problems on 831x.
+ */
+ ipic_write(ipic->regs, IPIC_SIMSR_H, 0);
+ ipic_write(ipic->regs, IPIC_SIMSR_L, 0);
+ ipic_write(ipic->regs, IPIC_SEMSR, 0);
+ ipic_write(ipic->regs, IPIC_SERMR, 0);
+ }
+
+ return 0;
+}
-static int __init init_ipic_sysfs(void)
+static void ipic_resume(void)
{
- int rc;
+ struct ipic *ipic = primary_ipic;
+
+ ipic_write(ipic->regs, IPIC_SICFR, ipic_saved_state.sicfr);
+ ipic_write(ipic->regs, IPIC_SIPRR_A, ipic_saved_state.siprr[0]);
+ ipic_write(ipic->regs, IPIC_SIPRR_D, ipic_saved_state.siprr[1]);
+ ipic_write(ipic->regs, IPIC_SIMSR_H, ipic_saved_state.simsr[0]);
+ ipic_write(ipic->regs, IPIC_SIMSR_L, ipic_saved_state.simsr[1]);
+ ipic_write(ipic->regs, IPIC_SICNR, ipic_saved_state.sicnr);
+ ipic_write(ipic->regs, IPIC_SMPRR_A, ipic_saved_state.smprr[0]);
+ ipic_write(ipic->regs, IPIC_SMPRR_B, ipic_saved_state.smprr[1]);
+ ipic_write(ipic->regs, IPIC_SEMSR, ipic_saved_state.semsr);
+ ipic_write(ipic->regs, IPIC_SECNR, ipic_saved_state.secnr);
+ ipic_write(ipic->regs, IPIC_SERMR, ipic_saved_state.sermr);
+ ipic_write(ipic->regs, IPIC_SERCR, ipic_saved_state.sercr);
+}
+#else
+#define ipic_suspend NULL
+#define ipic_resume NULL
+#endif
+
+static struct syscore_ops ipic_syscore_ops = {
+ .suspend = ipic_suspend,
+ .resume = ipic_resume,
+};
+static int __init init_ipic_syscore(void)
+{
if (!primary_ipic || !primary_ipic->regs)
return -ENODEV;
- printk(KERN_DEBUG "Registering ipic with sysfs...\n");
- rc = sysdev_class_register(&ipic_sysclass);
- if (rc) {
- printk(KERN_ERR "Failed registering ipic sys class\n");
- return -ENODEV;
- }
- rc = sysdev_register(&device_ipic);
- if (rc) {
- printk(KERN_ERR "Failed registering ipic sys device\n");
- return -ENODEV;
- }
+ printk(KERN_DEBUG "Registering ipic system core operations\n");
+ register_syscore_ops(&ipic_syscore_ops);
+
return 0;
}
-subsys_initcall(init_ipic_sysfs);
+subsys_initcall(init_ipic_syscore);
diff --git a/arch/powerpc/sysdev/ipic.h b/arch/powerpc/sysdev/ipic.h
index 9391c57b0c5..90031d1282e 100644
--- a/arch/powerpc/sysdev/ipic.h
+++ b/arch/powerpc/sysdev/ipic.h
@@ -43,7 +43,7 @@ struct ipic {
volatile u32 __iomem *regs;
/* The remapper for this IPIC */
- struct irq_host *irqhost;
+ struct irq_domain *irqhost;
};
struct ipic_info {
diff --git a/arch/powerpc/sysdev/micropatch.c b/arch/powerpc/sysdev/micropatch.c
index d8d60284075..c0bb76ef724 100644
--- a/arch/powerpc/sysdev/micropatch.c
+++ b/arch/powerpc/sysdev/micropatch.c
@@ -4,6 +4,7 @@
* also relocates SMC2, but this would require additional changes
* to uart.c, so I am holding off on that for a moment.
*/
+#include <linux/init.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -16,6 +17,7 @@
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/8xx_immap.h>
+#include <asm/cpm.h>
#include <asm/cpm1.h>
/*
@@ -24,7 +26,7 @@
#ifdef CONFIG_I2C_SPI_UCODE_PATCH
-uint patch_2000[] = {
+static uint patch_2000[] __initdata = {
0x7FFFEFD9,
0x3FFD0000,
0x7FFB49F7,
@@ -143,7 +145,7 @@ uint patch_2000[] = {
0x5F8247F8
};
-uint patch_2f00[] = {
+static uint patch_2f00[] __initdata = {
0x3E303430,
0x34343737,
0xABF7BF9B,
@@ -182,7 +184,7 @@ uint patch_2f00[] = {
#ifdef CONFIG_I2C_SPI_SMC1_UCODE_PATCH
-uint patch_2000[] = {
+static uint patch_2000[] __initdata = {
0x3fff0000,
0x3ffd0000,
0x3ffb0000,
@@ -505,7 +507,7 @@ uint patch_2000[] = {
0x6079e2bb
};
-uint patch_2f00[] = {
+static uint patch_2f00[] __initdata = {
0x30303030,
0x3e3e3434,
0xabbf9b99,
@@ -572,7 +574,7 @@ uint patch_2f00[] = {
0xf22f3f23
};
-uint patch_2e00[] = {
+static uint patch_2e00[] __initdata = {
0x27eeeeee,
0xeeeeeeee,
0xeeeeeeee,
@@ -598,7 +600,7 @@ uint patch_2e00[] = {
#ifdef CONFIG_USB_SOF_UCODE_PATCH
-uint patch_2000[] = {
+static uint patch_2000[] __initdata = {
0x7fff0000,
0x7ffd0000,
0x7ffb0000,
@@ -613,21 +615,25 @@ uint patch_2000[] = {
0x60750000
};
-uint patch_2f00[] = {
+static uint patch_2f00[] __initdata = {
0x3030304c,
0xcab9e441,
0xa1aaf220
};
#endif
-void
-cpm_load_patch(cpm8xx_t *cp)
+void __init cpm_load_patch(cpm8xx_t *cp)
{
volatile uint *dp; /* Dual-ported RAM. */
volatile cpm8xx_t *commproc;
+#if defined(CONFIG_I2C_SPI_UCODE_PATCH) || \
+ defined(CONFIG_I2C_SPI_SMC1_UCODE_PATCH)
volatile iic_t *iip;
- volatile spi_t *spp;
+ volatile struct spi_pram *spp;
+#ifdef CONFIG_I2C_SPI_SMC1_UCODE_PATCH
volatile smc_uart_t *smp;
+#endif
+#endif
int i;
commproc = cp;
@@ -668,8 +674,8 @@ cpm_load_patch(cpm8xx_t *cp)
/* Put SPI above the IIC, also 32-byte aligned.
*/
i = (RPBASE + sizeof(iic_t) + 31) & ~31;
- spp = (spi_t *)&commproc->cp_dparam[PROFF_SPI];
- spp->spi_rpbase = i;
+ spp = (struct spi_pram *)&commproc->cp_dparam[PROFF_SPI];
+ spp->rpbase = i;
# if defined(CONFIG_I2C_SPI_UCODE_PATCH)
commproc->cp_cpmcr1 = 0x802a;
diff --git a/arch/powerpc/sysdev/mmio_nvram.c b/arch/powerpc/sysdev/mmio_nvram.c
index 7b49633a4bd..69f5814ae6d 100644
--- a/arch/powerpc/sysdev/mmio_nvram.c
+++ b/arch/powerpc/sysdev/mmio_nvram.c
@@ -53,6 +53,23 @@ static ssize_t mmio_nvram_read(char *buf, size_t count, loff_t *index)
return count;
}
+static unsigned char mmio_nvram_read_val(int addr)
+{
+ unsigned long flags;
+ unsigned char val;
+
+ if (addr >= mmio_nvram_len)
+ return 0xff;
+
+ spin_lock_irqsave(&mmio_nvram_lock, flags);
+
+ val = ioread8(mmio_nvram_start + addr);
+
+ spin_unlock_irqrestore(&mmio_nvram_lock, flags);
+
+ return val;
+}
+
static ssize_t mmio_nvram_write(char *buf, size_t count, loff_t *index)
{
unsigned long flags;
@@ -72,6 +89,19 @@ static ssize_t mmio_nvram_write(char *buf, size_t count, loff_t *index)
return count;
}
+void mmio_nvram_write_val(int addr, unsigned char val)
+{
+ unsigned long flags;
+
+ if (addr < mmio_nvram_len) {
+ spin_lock_irqsave(&mmio_nvram_lock, flags);
+
+ iowrite8(val, mmio_nvram_start + addr);
+
+ spin_unlock_irqrestore(&mmio_nvram_lock, flags);
+ }
+}
+
static ssize_t mmio_nvram_get_size(void)
{
return mmio_nvram_len;
@@ -85,6 +115,8 @@ int __init mmio_nvram_init(void)
int ret;
nvram_node = of_find_node_by_type(NULL, "nvram");
+ if (!nvram_node)
+ nvram_node = of_find_compatible_node(NULL, NULL, "nvram");
if (!nvram_node) {
printk(KERN_WARNING "nvram: no node found in device-tree\n");
return -ENODEV;
@@ -97,7 +129,7 @@ int __init mmio_nvram_init(void)
goto out;
}
nvram_addr = r.start;
- mmio_nvram_len = r.end - r.start + 1;
+ mmio_nvram_len = resource_size(&r);
if ( (!mmio_nvram_len) || (!nvram_addr) ) {
printk(KERN_WARNING "nvram: address or length is 0\n");
ret = -EIO;
@@ -114,6 +146,8 @@ int __init mmio_nvram_init(void)
printk(KERN_INFO "mmio NVRAM, %luk at 0x%lx mapped to %p\n",
mmio_nvram_len >> 10, nvram_addr, mmio_nvram_start);
+ ppc_md.nvram_read_val = mmio_nvram_read_val;
+ ppc_md.nvram_write_val = mmio_nvram_write_val;
ppc_md.nvram_read = mmio_nvram_read;
ppc_md.nvram_write = mmio_nvram_write;
ppc_md.nvram_size = mmio_nvram_get_size;
diff --git a/arch/powerpc/sysdev/mpc5xxx_clocks.c b/arch/powerpc/sysdev/mpc5xxx_clocks.c
new file mode 100644
index 00000000000..5492dc5f56f
--- /dev/null
+++ b/arch/powerpc/sysdev/mpc5xxx_clocks.c
@@ -0,0 +1,34 @@
+/**
+ * mpc5xxx_get_bus_frequency - Find the bus frequency for a device
+ * @node: device node
+ *
+ * Returns bus frequency (IPS on MPC512x, IPB on MPC52xx),
+ * or 0 if the bus frequency cannot be found.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/export.h>
+#include <asm/mpc5xxx.h>
+
+unsigned long mpc5xxx_get_bus_frequency(struct device_node *node)
+{
+ struct device_node *np;
+ const unsigned int *p_bus_freq = NULL;
+
+ of_node_get(node);
+ while (node) {
+ p_bus_freq = of_get_property(node, "bus-frequency", NULL);
+ if (p_bus_freq)
+ break;
+
+ np = of_get_parent(node);
+ of_node_put(node);
+ node = np;
+ }
+ if (node)
+ of_node_put(node);
+
+ return p_bus_freq ? *p_bus_freq : 0;
+}
+EXPORT_SYMBOL(mpc5xxx_get_bus_frequency);
diff --git a/arch/powerpc/sysdev/mpc8xx_pic.c b/arch/powerpc/sysdev/mpc8xx_pic.c
index 5d2d5522ef4..c4828c0be5b 100644
--- a/arch/powerpc/sysdev/mpc8xx_pic.c
+++ b/arch/powerpc/sysdev/mpc8xx_pic.c
@@ -1,7 +1,5 @@
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/stddef.h>
-#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/irq.h>
@@ -18,88 +16,57 @@
extern int cpm_get_irq(struct pt_regs *regs);
-static struct irq_host *mpc8xx_pic_host;
-#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
-static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
+static struct irq_domain *mpc8xx_pic_host;
+static unsigned long mpc8xx_cached_irq_mask;
static sysconf8xx_t __iomem *siu_reg;
-int cpm_get_irq(struct pt_regs *regs);
-
-static void mpc8xx_unmask_irq(unsigned int virq)
+static inline unsigned long mpc8xx_irqd_to_bit(struct irq_data *d)
{
- int bit, word;
- unsigned int irq_nr = (unsigned int)irq_map[virq].hwirq;
-
- bit = irq_nr & 0x1f;
- word = irq_nr >> 5;
-
- ppc_cached_irq_mask[word] |= (1 << (31-bit));
- out_be32(&siu_reg->sc_simask, ppc_cached_irq_mask[word]);
+ return 0x80000000 >> irqd_to_hwirq(d);
}
-static void mpc8xx_mask_irq(unsigned int virq)
+static void mpc8xx_unmask_irq(struct irq_data *d)
{
- int bit, word;
- unsigned int irq_nr = (unsigned int)irq_map[virq].hwirq;
-
- bit = irq_nr & 0x1f;
- word = irq_nr >> 5;
-
- ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
- out_be32(&siu_reg->sc_simask, ppc_cached_irq_mask[word]);
+ mpc8xx_cached_irq_mask |= mpc8xx_irqd_to_bit(d);
+ out_be32(&siu_reg->sc_simask, mpc8xx_cached_irq_mask);
}
-static void mpc8xx_ack(unsigned int virq)
+static void mpc8xx_mask_irq(struct irq_data *d)
{
- int bit;
- unsigned int irq_nr = (unsigned int)irq_map[virq].hwirq;
-
- bit = irq_nr & 0x1f;
- out_be32(&siu_reg->sc_sipend, 1 << (31-bit));
+ mpc8xx_cached_irq_mask &= ~mpc8xx_irqd_to_bit(d);
+ out_be32(&siu_reg->sc_simask, mpc8xx_cached_irq_mask);
}
-static void mpc8xx_end_irq(unsigned int virq)
+static void mpc8xx_ack(struct irq_data *d)
{
- int bit, word;
- unsigned int irq_nr = (unsigned int)irq_map[virq].hwirq;
-
- bit = irq_nr & 0x1f;
- word = irq_nr >> 5;
-
- ppc_cached_irq_mask[word] |= (1 << (31-bit));
- out_be32(&siu_reg->sc_simask, ppc_cached_irq_mask[word]);
+ out_be32(&siu_reg->sc_sipend, mpc8xx_irqd_to_bit(d));
}
-static int mpc8xx_set_irq_type(unsigned int virq, unsigned int flow_type)
+static void mpc8xx_end_irq(struct irq_data *d)
{
- struct irq_desc *desc = get_irq_desc(virq);
-
- desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
- desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
- if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
- desc->status |= IRQ_LEVEL;
+ mpc8xx_cached_irq_mask |= mpc8xx_irqd_to_bit(d);
+ out_be32(&siu_reg->sc_simask, mpc8xx_cached_irq_mask);
+}
- if (flow_type & IRQ_TYPE_EDGE_FALLING) {
- irq_hw_number_t hw = (unsigned int)irq_map[virq].hwirq;
+static int mpc8xx_set_irq_type(struct irq_data *d, unsigned int flow_type)
+{
+ /* only external IRQ senses are programmable */
+ if ((flow_type & IRQ_TYPE_EDGE_FALLING) && !(irqd_to_hwirq(d) & 1)) {
unsigned int siel = in_be32(&siu_reg->sc_siel);
-
- /* only external IRQ senses are programmable */
- if ((hw & 1) == 0) {
- siel |= (0x80000000 >> hw);
- out_be32(&siu_reg->sc_siel, siel);
- desc->handle_irq = handle_edge_irq;
- }
+ siel |= mpc8xx_irqd_to_bit(d);
+ out_be32(&siu_reg->sc_siel, siel);
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
}
return 0;
}
static struct irq_chip mpc8xx_pic = {
- .typename = " MPC8XX SIU ",
- .unmask = mpc8xx_unmask_irq,
- .mask = mpc8xx_mask_irq,
- .ack = mpc8xx_ack,
- .eoi = mpc8xx_end_irq,
- .set_type = mpc8xx_set_irq_type,
+ .name = "MPC8XX SIU",
+ .irq_unmask = mpc8xx_unmask_irq,
+ .irq_mask = mpc8xx_mask_irq,
+ .irq_ack = mpc8xx_ack,
+ .irq_eoi = mpc8xx_end_irq,
+ .irq_set_type = mpc8xx_set_irq_type,
};
unsigned int mpc8xx_get_irq(void)
@@ -118,19 +85,19 @@ unsigned int mpc8xx_get_irq(void)
}
-static int mpc8xx_pic_host_map(struct irq_host *h, unsigned int virq,
+static int mpc8xx_pic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
pr_debug("mpc8xx_pic_host_map(%d, 0x%lx)\n", virq, hw);
/* Set default irq handle */
- set_irq_chip_and_handler(virq, &mpc8xx_pic, handle_level_irq);
+ irq_set_chip_and_handler(virq, &mpc8xx_pic, handle_level_irq);
return 0;
}
-static int mpc8xx_pic_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
+static int mpc8xx_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)
{
static unsigned char map_pic_senses[4] = {
@@ -140,6 +107,9 @@ static int mpc8xx_pic_host_xlate(struct irq_host *h, struct device_node *ct,
IRQ_TYPE_EDGE_FALLING,
};
+ if (intspec[0] > 0x1f)
+ return 0;
+
*out_hwirq = intspec[0];
if (intsize > 1 && intspec[1] < 4)
*out_flags = map_pic_senses[intspec[1]];
@@ -150,7 +120,7 @@ static int mpc8xx_pic_host_xlate(struct irq_host *h, struct device_node *ct,
}
-static struct irq_host_ops mpc8xx_pic_host_ops = {
+static struct irq_domain_ops mpc8xx_pic_host_ops = {
.map = mpc8xx_pic_host_map,
.xlate = mpc8xx_pic_host_xlate,
};
@@ -173,14 +143,13 @@ int mpc8xx_pic_init(void)
if (ret)
goto out;
- siu_reg = ioremap(res.start, res.end - res.start + 1);
+ siu_reg = ioremap(res.start, resource_size(&res));
if (siu_reg == NULL) {
ret = -EINVAL;
goto out;
}
- mpc8xx_pic_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR,
- 64, &mpc8xx_pic_host_ops, 64);
+ mpc8xx_pic_host = irq_domain_add_linear(np, 64, &mpc8xx_pic_host_ops, NULL);
if (mpc8xx_pic_host == NULL) {
printk(KERN_ERR "MPC8xx PIC: failed to allocate irq host!\n");
ret = -ENOMEM;
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 6ffdda244bb..be33c9768ea 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -6,6 +6,7 @@
* with various broken implementations of this HW.
*
* Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp.
+ * Copyright 2010-2012 Freescale Semiconductor, Inc.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
@@ -26,6 +27,9 @@
#include <linux/bootmem.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <linux/ratelimit.h>
#include <asm/ptrace.h>
#include <asm/signal.h>
@@ -44,9 +48,15 @@
#define DBG(fmt...)
#endif
+struct bus_type mpic_subsys = {
+ .name = "mpic",
+ .dev_name = "mpic",
+};
+EXPORT_SYMBOL_GPL(mpic_subsys);
+
static struct mpic *mpics;
static struct mpic *mpic_primary;
-static DEFINE_SPINLOCK(mpic_lock);
+static DEFINE_RAW_SPINLOCK(mpic_lock);
#ifdef CONFIG_PPC32 /* XXX for now */
#ifdef CONFIG_IRQ_ALL_CPUS
@@ -146,6 +156,16 @@ static u32 mpic_infos[][MPIC_IDX_END] = {
#endif /* CONFIG_MPIC_WEIRD */
+static inline unsigned int mpic_processor_id(struct mpic *mpic)
+{
+ unsigned int cpu = 0;
+
+ if (!(mpic->flags & MPIC_SECONDARY))
+ cpu = hard_smp_processor_id();
+
+ return cpu;
+}
+
/*
* Register accessor functions
*/
@@ -175,13 +195,16 @@ static inline void _mpic_write(enum mpic_reg_type type,
switch(type) {
#ifdef CONFIG_PPC_DCR
case mpic_access_dcr:
- return dcr_write(rb->dhost, reg, value);
+ dcr_write(rb->dhost, reg, value);
+ break;
#endif
case mpic_access_mmio_be:
- return out_be32(rb->base + (reg >> 2), value);
+ out_be32(rb->base + (reg >> 2), value);
+ break;
case mpic_access_mmio_le:
default:
- return out_le32(rb->base + (reg >> 2), value);
+ out_le32(rb->base + (reg >> 2), value);
+ break;
}
}
@@ -204,21 +227,38 @@ static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 valu
_mpic_write(mpic->reg_type, &mpic->gregs, offset, value);
}
+static inline unsigned int mpic_tm_offset(struct mpic *mpic, unsigned int tm)
+{
+ return (tm >> 2) * MPIC_TIMER_GROUP_STRIDE +
+ (tm & 3) * MPIC_INFO(TIMER_STRIDE);
+}
+
+static inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm)
+{
+ unsigned int offset = mpic_tm_offset(mpic, tm) +
+ MPIC_INFO(TIMER_VECTOR_PRI);
+
+ return _mpic_read(mpic->reg_type, &mpic->tmregs, offset);
+}
+
+static inline void _mpic_tm_write(struct mpic *mpic, unsigned int tm, u32 value)
+{
+ unsigned int offset = mpic_tm_offset(mpic, tm) +
+ MPIC_INFO(TIMER_VECTOR_PRI);
+
+ _mpic_write(mpic->reg_type, &mpic->tmregs, offset, value);
+}
+
static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg)
{
- unsigned int cpu = 0;
+ unsigned int cpu = mpic_processor_id(mpic);
- if (mpic->flags & MPIC_PRIMARY)
- cpu = hard_smp_processor_id();
return _mpic_read(mpic->reg_type, &mpic->cpuregs[cpu], reg);
}
static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value)
{
- unsigned int cpu = 0;
-
- if (mpic->flags & MPIC_PRIMARY)
- cpu = hard_smp_processor_id();
+ unsigned int cpu = mpic_processor_id(mpic);
_mpic_write(mpic->reg_type, &mpic->cpuregs[cpu], reg, value);
}
@@ -227,14 +267,16 @@ static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigne
{
unsigned int isu = src_no >> mpic->isu_shift;
unsigned int idx = src_no & mpic->isu_mask;
+ unsigned int val;
+ val = _mpic_read(mpic->reg_type, &mpic->isus[isu],
+ reg + (idx * MPIC_INFO(IRQ_STRIDE)));
#ifdef CONFIG_MPIC_BROKEN_REGREAD
if (reg == 0)
- return mpic->isu_reg0_shadow[idx];
- else
+ val = (val & (MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY)) |
+ mpic->isu_reg0_shadow[src_no];
#endif
- return _mpic_read(mpic->reg_type, &mpic->isus[isu],
- reg + (idx * MPIC_INFO(IRQ_STRIDE)));
+ return val;
}
static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
@@ -248,7 +290,8 @@ static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
#ifdef CONFIG_MPIC_BROKEN_REGREAD
if (reg == 0)
- mpic->isu_reg0_shadow[idx] = value;
+ mpic->isu_reg0_shadow[src_no] =
+ value & ~(MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY);
#endif
}
@@ -256,6 +299,8 @@ static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
#define mpic_write(b,r,v) _mpic_write(mpic->reg_type,&(b),(r),(v))
#define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i))
#define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v))
+#define mpic_tm_read(i) _mpic_tm_read(mpic,(i))
+#define mpic_tm_write(i,v) _mpic_tm_write(mpic,(i),(v))
#define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i))
#define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v))
#define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r))
@@ -279,17 +324,14 @@ static void _mpic_map_mmio(struct mpic *mpic, phys_addr_t phys_addr,
static void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb,
unsigned int offset, unsigned int size)
{
- const u32 *dbasep;
-
- dbasep = of_get_property(mpic->irqhost->of_node, "dcr-reg", NULL);
-
- rb->dhost = dcr_map(mpic->irqhost->of_node, *dbasep + offset, size);
+ phys_addr_t phys_addr = dcr_resource_start(mpic->node, 0);
+ rb->dhost = dcr_map(mpic->node, phys_addr + offset, size);
BUG_ON(!DCR_MAP_OK(rb->dhost));
}
-static inline void mpic_map(struct mpic *mpic, phys_addr_t phys_addr,
- struct mpic_reg_bank *rb, unsigned int offset,
- unsigned int size)
+static inline void mpic_map(struct mpic *mpic,
+ phys_addr_t phys_addr, struct mpic_reg_bank *rb,
+ unsigned int offset, unsigned int size)
{
if (mpic->flags & MPIC_USES_DCR)
_mpic_map_dcr(mpic, rb, offset, size);
@@ -340,15 +382,15 @@ static inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source)
unsigned int mask = 1U << (fixup->index & 0x1f);
writel(mask, fixup->applebase + soff);
} else {
- spin_lock(&mpic->fixup_lock);
+ raw_spin_lock(&mpic->fixup_lock);
writeb(0x11 + 2 * fixup->index, fixup->base + 2);
writel(fixup->data, fixup->base + 4);
- spin_unlock(&mpic->fixup_lock);
+ raw_spin_unlock(&mpic->fixup_lock);
}
}
static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source,
- unsigned int irqflags)
+ bool level)
{
struct mpic_irq_fixup *fixup = &mpic->fixups[source];
unsigned long flags;
@@ -357,17 +399,17 @@ static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source,
if (fixup->base == NULL)
return;
- DBG("startup_ht_interrupt(0x%x, 0x%x) index: %d\n",
- source, irqflags, fixup->index);
- spin_lock_irqsave(&mpic->fixup_lock, flags);
+ DBG("startup_ht_interrupt(0x%x) index: %d\n",
+ source, fixup->index);
+ raw_spin_lock_irqsave(&mpic->fixup_lock, flags);
/* Enable and configure */
writeb(0x10 + 2 * fixup->index, fixup->base + 2);
tmp = readl(fixup->base + 4);
tmp &= ~(0x23U);
- if (irqflags & IRQ_LEVEL)
+ if (level)
tmp |= 0x22;
writel(tmp, fixup->base + 4);
- spin_unlock_irqrestore(&mpic->fixup_lock, flags);
+ raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags);
#ifdef CONFIG_PM
/* use the lowest bit inverted to the actual HW,
@@ -376,8 +418,7 @@ static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source,
#endif
}
-static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source,
- unsigned int irqflags)
+static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source)
{
struct mpic_irq_fixup *fixup = &mpic->fixups[source];
unsigned long flags;
@@ -386,15 +427,15 @@ static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source,
if (fixup->base == NULL)
return;
- DBG("shutdown_ht_interrupt(0x%x, 0x%x)\n", source, irqflags);
+ DBG("shutdown_ht_interrupt(0x%x)\n", source);
/* Disable */
- spin_lock_irqsave(&mpic->fixup_lock, flags);
+ raw_spin_lock_irqsave(&mpic->fixup_lock, flags);
writeb(0x10 + 2 * fixup->index, fixup->base + 2);
tmp = readl(fixup->base + 4);
tmp |= 1;
writel(tmp, fixup->base + 4);
- spin_unlock_irqrestore(&mpic->fixup_lock, flags);
+ raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags);
#ifdef CONFIG_PM
/* use the lowest bit inverted to the actual HW,
@@ -432,7 +473,7 @@ static void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase,
addr = addr | ((u64)readl(base + HT_MSI_ADDR_HI) << 32);
}
- printk(KERN_DEBUG "mpic: - HT:%02x.%x %s MSI mapping found @ 0x%lx\n",
+ printk(KERN_DEBUG "mpic: - HT:%02x.%x %s MSI mapping found @ 0x%llx\n",
PCI_SLOT(devfn), PCI_FUNC(devfn),
flags & HT_MSI_FLAGS_ENABLE ? "enabled" : "disabled", addr);
@@ -494,7 +535,7 @@ static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase,
mpic->fixups[irq].data = readl(base + 4) | 0x80000000;
}
}
-
+
static void __init mpic_scan_ht_pics(struct mpic *mpic)
{
@@ -504,12 +545,11 @@ static void __init mpic_scan_ht_pics(struct mpic *mpic)
printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\n");
/* Allocate fixups array */
- mpic->fixups = alloc_bootmem(128 * sizeof(struct mpic_irq_fixup));
+ mpic->fixups = kzalloc(128 * sizeof(*mpic->fixups), GFP_KERNEL);
BUG_ON(mpic->fixups == NULL);
- memset(mpic->fixups, 0, 128 * sizeof(struct mpic_irq_fixup));
/* Init spinlock */
- spin_lock_init(&mpic->fixup_lock);
+ raw_spin_lock_init(&mpic->fixup_lock);
/* Map U3 config space. We assume all IO-APICs are on the primary bus
* so we only need to map 64kB.
@@ -560,25 +600,25 @@ static void __init mpic_scan_ht_pics(struct mpic *mpic)
#endif /* CONFIG_MPIC_U3_HT_IRQS */
-
-#define mpic_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq)
-
/* Find an mpic associated with a given linux interrupt */
-static struct mpic *mpic_find(unsigned int irq, unsigned int *is_ipi)
+static struct mpic *mpic_find(unsigned int irq)
{
- unsigned int src = mpic_irq_to_hw(irq);
- struct mpic *mpic;
-
if (irq < NUM_ISA_INTERRUPTS)
return NULL;
- mpic = irq_desc[irq].chip_data;
+ return irq_get_chip_data(irq);
+}
- if (is_ipi)
- *is_ipi = (src >= mpic->ipi_vecs[0] &&
- src <= mpic->ipi_vecs[3]);
+/* Determine if the linux irq is an IPI */
+static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int src)
+{
+ return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]);
+}
- return mpic;
+/* Determine if the linux irq is a timer */
+static unsigned int mpic_is_tm(struct mpic *mpic, unsigned int src)
+{
+ return (src >= mpic->timer_vecs[0] && src <= mpic->timer_vecs[7]);
}
/* Convert a cpu mask from logical to physical cpu numbers. */
@@ -587,23 +627,29 @@ static inline u32 mpic_physmask(u32 cpumask)
int i;
u32 mask = 0;
- for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
+ for (i = 0; i < min(32, NR_CPUS); ++i, cpumask >>= 1)
mask |= (cpumask & 1) << get_hard_smp_processor_id(i);
return mask;
}
#ifdef CONFIG_SMP
/* Get the mpic structure from the IPI number */
-static inline struct mpic * mpic_from_ipi(unsigned int ipi)
+static inline struct mpic * mpic_from_ipi(struct irq_data *d)
{
- return irq_desc[ipi].chip_data;
+ return irq_data_get_irq_chip_data(d);
}
#endif
/* Get the mpic structure from the irq number */
static inline struct mpic * mpic_from_irq(unsigned int irq)
{
- return irq_desc[irq].chip_data;
+ return irq_get_chip_data(irq);
+}
+
+/* Get the mpic structure from the irq data */
+static inline struct mpic * mpic_from_irq_data(struct irq_data *d)
+{
+ return irq_data_get_irq_chip_data(d);
}
/* Send an EOI */
@@ -613,29 +659,18 @@ static inline void mpic_eoi(struct mpic *mpic)
(void)mpic_cpu_read(MPIC_INFO(CPU_WHOAMI));
}
-#ifdef CONFIG_SMP
-static irqreturn_t mpic_ipi_action(int irq, void *data)
-{
- long ipi = (long)data;
-
- smp_message_recv(ipi);
-
- return IRQ_HANDLED;
-}
-#endif /* CONFIG_SMP */
-
/*
* Linux descriptor level callbacks
*/
-void mpic_unmask_irq(unsigned int irq)
+void mpic_unmask_irq(struct irq_data *d)
{
unsigned int loops = 100000;
- struct mpic *mpic = mpic_from_irq(irq);
- unsigned int src = mpic_irq_to_hw(irq);
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
- DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src);
+ DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, d->irq, src);
mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI),
mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) &
@@ -643,19 +678,20 @@ void mpic_unmask_irq(unsigned int irq)
/* make sure mask gets to controller before we return to user */
do {
if (!loops--) {
- printk(KERN_ERR "mpic_enable_irq timeout\n");
+ printk(KERN_ERR "%s: timeout on hwirq %u\n",
+ __func__, src);
break;
}
} while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK);
}
-void mpic_mask_irq(unsigned int irq)
+void mpic_mask_irq(struct irq_data *d)
{
unsigned int loops = 100000;
- struct mpic *mpic = mpic_from_irq(irq);
- unsigned int src = mpic_irq_to_hw(irq);
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
- DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src);
+ DBG("%s: disable_irq: %d (src %d)\n", mpic->name, d->irq, src);
mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI),
mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) |
@@ -664,18 +700,19 @@ void mpic_mask_irq(unsigned int irq)
/* make sure mask gets to controller before we return to user */
do {
if (!loops--) {
- printk(KERN_ERR "mpic_enable_irq timeout\n");
+ printk(KERN_ERR "%s: timeout on hwirq %u\n",
+ __func__, src);
break;
}
} while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK));
}
-void mpic_end_irq(unsigned int irq)
+void mpic_end_irq(struct irq_data *d)
{
- struct mpic *mpic = mpic_from_irq(irq);
+ struct mpic *mpic = mpic_from_irq_data(d);
#ifdef DEBUG_IRQ
- DBG("%s: end_irq: %d\n", mpic->name, irq);
+ DBG("%s: end_irq: %d\n", mpic->name, d->irq);
#endif
/* We always EOI on end_irq() even for edge interrupts since that
* should only lower the priority, the MPIC should have properly
@@ -687,51 +724,51 @@ void mpic_end_irq(unsigned int irq)
#ifdef CONFIG_MPIC_U3_HT_IRQS
-static void mpic_unmask_ht_irq(unsigned int irq)
+static void mpic_unmask_ht_irq(struct irq_data *d)
{
- struct mpic *mpic = mpic_from_irq(irq);
- unsigned int src = mpic_irq_to_hw(irq);
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
- mpic_unmask_irq(irq);
+ mpic_unmask_irq(d);
- if (irq_desc[irq].status & IRQ_LEVEL)
+ if (irqd_is_level_type(d))
mpic_ht_end_irq(mpic, src);
}
-static unsigned int mpic_startup_ht_irq(unsigned int irq)
+static unsigned int mpic_startup_ht_irq(struct irq_data *d)
{
- struct mpic *mpic = mpic_from_irq(irq);
- unsigned int src = mpic_irq_to_hw(irq);
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
- mpic_unmask_irq(irq);
- mpic_startup_ht_interrupt(mpic, src, irq_desc[irq].status);
+ mpic_unmask_irq(d);
+ mpic_startup_ht_interrupt(mpic, src, irqd_is_level_type(d));
return 0;
}
-static void mpic_shutdown_ht_irq(unsigned int irq)
+static void mpic_shutdown_ht_irq(struct irq_data *d)
{
- struct mpic *mpic = mpic_from_irq(irq);
- unsigned int src = mpic_irq_to_hw(irq);
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
- mpic_shutdown_ht_interrupt(mpic, src, irq_desc[irq].status);
- mpic_mask_irq(irq);
+ mpic_shutdown_ht_interrupt(mpic, src);
+ mpic_mask_irq(d);
}
-static void mpic_end_ht_irq(unsigned int irq)
+static void mpic_end_ht_irq(struct irq_data *d)
{
- struct mpic *mpic = mpic_from_irq(irq);
- unsigned int src = mpic_irq_to_hw(irq);
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
#ifdef DEBUG_IRQ
- DBG("%s: end_irq: %d\n", mpic->name, irq);
+ DBG("%s: end_irq: %d\n", mpic->name, d->irq);
#endif
/* We always EOI on end_irq() even for edge interrupts since that
* should only lower the priority, the MPIC should have properly
* latched another edge interrupt coming in anyway
*/
- if (irq_desc[irq].status & IRQ_LEVEL)
+ if (irqd_is_level_type(d))
mpic_ht_end_irq(mpic, src);
mpic_eoi(mpic);
}
@@ -739,47 +776,73 @@ static void mpic_end_ht_irq(unsigned int irq)
#ifdef CONFIG_SMP
-static void mpic_unmask_ipi(unsigned int irq)
+static void mpic_unmask_ipi(struct irq_data *d)
{
- struct mpic *mpic = mpic_from_ipi(irq);
- unsigned int src = mpic_irq_to_hw(irq) - mpic->ipi_vecs[0];
+ struct mpic *mpic = mpic_from_ipi(d);
+ unsigned int src = virq_to_hw(d->irq) - mpic->ipi_vecs[0];
- DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, irq, src);
+ DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, d->irq, src);
mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK);
}
-static void mpic_mask_ipi(unsigned int irq)
+static void mpic_mask_ipi(struct irq_data *d)
{
/* NEVER disable an IPI... that's just plain wrong! */
}
-static void mpic_end_ipi(unsigned int irq)
+static void mpic_end_ipi(struct irq_data *d)
{
- struct mpic *mpic = mpic_from_ipi(irq);
+ struct mpic *mpic = mpic_from_ipi(d);
/*
* IPIs are marked IRQ_PER_CPU. This has the side effect of
* preventing the IRQ_PENDING/IRQ_INPROGRESS logic from
* applying to them. We EOI them late to avoid re-entering.
- * We mark IPI's with IRQF_DISABLED as they must run with
- * irqs disabled.
*/
mpic_eoi(mpic);
}
#endif /* CONFIG_SMP */
-void mpic_set_affinity(unsigned int irq, cpumask_t cpumask)
+static void mpic_unmask_tm(struct irq_data *d)
+{
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0];
+
+ DBG("%s: enable_tm: %d (tm %d)\n", mpic->name, d->irq, src);
+ mpic_tm_write(src, mpic_tm_read(src) & ~MPIC_VECPRI_MASK);
+ mpic_tm_read(src);
+}
+
+static void mpic_mask_tm(struct irq_data *d)
+{
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0];
+
+ mpic_tm_write(src, mpic_tm_read(src) | MPIC_VECPRI_MASK);
+ mpic_tm_read(src);
+}
+
+int mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask,
+ bool force)
{
- struct mpic *mpic = mpic_from_irq(irq);
- unsigned int src = mpic_irq_to_hw(irq);
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
- cpumask_t tmp;
+ if (mpic->flags & MPIC_SINGLE_DEST_CPU) {
+ int cpuid = irq_choose_cpu(cpumask);
- cpus_and(tmp, cpumask, cpu_online_map);
+ mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid);
+ } else {
+ u32 mask = cpumask_bits(cpumask)[0];
+
+ mask &= cpumask_bits(cpu_online_mask)[0];
- mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION),
- mpic_physmask(cpus_addr(tmp)[0]));
+ mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION),
+ mpic_physmask(mask));
+ }
+
+ return IRQ_SET_MASK_OK;
}
static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type)
@@ -803,56 +866,92 @@ static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type)
}
}
-int mpic_set_irq_type(unsigned int virq, unsigned int flow_type)
+int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type)
{
- struct mpic *mpic = mpic_from_irq(virq);
- unsigned int src = mpic_irq_to_hw(virq);
- struct irq_desc *desc = get_irq_desc(virq);
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
unsigned int vecpri, vold, vnew;
DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n",
- mpic, virq, src, flow_type);
+ mpic, d->irq, src, flow_type);
- if (src >= mpic->irq_count)
+ if (src >= mpic->num_sources)
return -EINVAL;
+ vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
+
+ /* We don't support "none" type */
if (flow_type == IRQ_TYPE_NONE)
- if (mpic->senses && src < mpic->senses_count)
- flow_type = mpic->senses[src];
- if (flow_type == IRQ_TYPE_NONE)
- flow_type = IRQ_TYPE_LEVEL_LOW;
+ flow_type = IRQ_TYPE_DEFAULT;
+
+ /* Default: read HW settings */
+ if (flow_type == IRQ_TYPE_DEFAULT) {
+ int vold_ps;
+
+ vold_ps = vold & (MPIC_INFO(VECPRI_POLARITY_MASK) |
+ MPIC_INFO(VECPRI_SENSE_MASK));
+
+ if (vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) |
+ MPIC_INFO(VECPRI_POLARITY_POSITIVE)))
+ flow_type = IRQ_TYPE_EDGE_RISING;
+ else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) |
+ MPIC_INFO(VECPRI_POLARITY_NEGATIVE)))
+ flow_type = IRQ_TYPE_EDGE_FALLING;
+ else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) |
+ MPIC_INFO(VECPRI_POLARITY_POSITIVE)))
+ flow_type = IRQ_TYPE_LEVEL_HIGH;
+ else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) |
+ MPIC_INFO(VECPRI_POLARITY_NEGATIVE)))
+ flow_type = IRQ_TYPE_LEVEL_LOW;
+ else
+ WARN_ONCE(1, "mpic: unknown IRQ type %d\n", vold);
+ }
- desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
- desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
- if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
- desc->status |= IRQ_LEVEL;
+ /* Apply to irq desc */
+ irqd_set_trigger_type(d, flow_type);
+ /* Apply to HW */
if (mpic_is_ht_interrupt(mpic, src))
vecpri = MPIC_VECPRI_POLARITY_POSITIVE |
MPIC_VECPRI_SENSE_EDGE;
else
vecpri = mpic_type_to_vecpri(mpic, flow_type);
- vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) |
MPIC_INFO(VECPRI_SENSE_MASK));
vnew |= vecpri;
if (vold != vnew)
mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew);
+ return IRQ_SET_MASK_OK_NOCOPY;
+}
+
+static int mpic_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct irq_desc *desc = container_of(d, struct irq_desc, irq_data);
+ struct mpic *mpic = mpic_from_irq_data(d);
+
+ if (!(mpic->flags & MPIC_FSL))
+ return -ENXIO;
+
+ if (on)
+ desc->action->flags |= IRQF_NO_SUSPEND;
+ else
+ desc->action->flags &= ~IRQF_NO_SUSPEND;
+
return 0;
}
void mpic_set_vector(unsigned int virq, unsigned int vector)
{
struct mpic *mpic = mpic_from_irq(virq);
- unsigned int src = mpic_irq_to_hw(virq);
+ unsigned int src = virq_to_hw(virq);
unsigned int vecpri;
DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n",
mpic, virq, src, vector);
- if (src >= mpic->irq_count)
+ if (src >= mpic->num_sources)
return;
vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
@@ -861,40 +960,62 @@ void mpic_set_vector(unsigned int virq, unsigned int vector)
mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vecpri);
}
+void mpic_set_destination(unsigned int virq, unsigned int cpuid)
+{
+ struct mpic *mpic = mpic_from_irq(virq);
+ unsigned int src = virq_to_hw(virq);
+
+ DBG("mpic: set_destination(mpic:@%p,virq:%d,src:%d,cpuid:0x%x)\n",
+ mpic, virq, src, cpuid);
+
+ if (src >= mpic->num_sources)
+ return;
+
+ mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid);
+}
+
static struct irq_chip mpic_irq_chip = {
- .mask = mpic_mask_irq,
- .unmask = mpic_unmask_irq,
- .eoi = mpic_end_irq,
- .set_type = mpic_set_irq_type,
+ .irq_mask = mpic_mask_irq,
+ .irq_unmask = mpic_unmask_irq,
+ .irq_eoi = mpic_end_irq,
+ .irq_set_type = mpic_set_irq_type,
+ .irq_set_wake = mpic_irq_set_wake,
};
#ifdef CONFIG_SMP
static struct irq_chip mpic_ipi_chip = {
- .mask = mpic_mask_ipi,
- .unmask = mpic_unmask_ipi,
- .eoi = mpic_end_ipi,
+ .irq_mask = mpic_mask_ipi,
+ .irq_unmask = mpic_unmask_ipi,
+ .irq_eoi = mpic_end_ipi,
};
#endif /* CONFIG_SMP */
+static struct irq_chip mpic_tm_chip = {
+ .irq_mask = mpic_mask_tm,
+ .irq_unmask = mpic_unmask_tm,
+ .irq_eoi = mpic_end_irq,
+ .irq_set_wake = mpic_irq_set_wake,
+};
+
#ifdef CONFIG_MPIC_U3_HT_IRQS
static struct irq_chip mpic_irq_ht_chip = {
- .startup = mpic_startup_ht_irq,
- .shutdown = mpic_shutdown_ht_irq,
- .mask = mpic_mask_irq,
- .unmask = mpic_unmask_ht_irq,
- .eoi = mpic_end_ht_irq,
- .set_type = mpic_set_irq_type,
+ .irq_startup = mpic_startup_ht_irq,
+ .irq_shutdown = mpic_shutdown_ht_irq,
+ .irq_mask = mpic_mask_irq,
+ .irq_unmask = mpic_unmask_ht_irq,
+ .irq_eoi = mpic_end_ht_irq,
+ .irq_set_type = mpic_set_irq_type,
};
#endif /* CONFIG_MPIC_U3_HT_IRQS */
-static int mpic_host_match(struct irq_host *h, struct device_node *node)
+static int mpic_host_match(struct irq_domain *h, struct device_node *node)
{
/* Exact match, unless mpic node is NULL */
return h->of_node == NULL || h->of_node == node;
}
-static int mpic_host_map(struct irq_host *h, unsigned int virq,
+static int mpic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct mpic *mpic = h->host_data;
@@ -904,23 +1025,44 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq,
if (hw == mpic->spurious_vec)
return -EINVAL;
- if (mpic->protected && test_bit(hw, mpic->protected))
- return -EINVAL;
+ if (mpic->protected && test_bit(hw, mpic->protected)) {
+ pr_warning("mpic: Mapping of source 0x%x failed, "
+ "source protected by firmware !\n",\
+ (unsigned int)hw);
+ return -EPERM;
+ }
#ifdef CONFIG_SMP
else if (hw >= mpic->ipi_vecs[0]) {
- WARN_ON(!(mpic->flags & MPIC_PRIMARY));
+ WARN_ON(mpic->flags & MPIC_SECONDARY);
DBG("mpic: mapping as IPI\n");
- set_irq_chip_data(virq, mpic);
- set_irq_chip_and_handler(virq, &mpic->hc_ipi,
+ irq_set_chip_data(virq, mpic);
+ irq_set_chip_and_handler(virq, &mpic->hc_ipi,
handle_percpu_irq);
return 0;
}
#endif /* CONFIG_SMP */
- if (hw >= mpic->irq_count)
+ if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) {
+ WARN_ON(mpic->flags & MPIC_SECONDARY);
+
+ DBG("mpic: mapping as timer\n");
+ irq_set_chip_data(virq, mpic);
+ irq_set_chip_and_handler(virq, &mpic->hc_tm,
+ handle_fasteoi_irq);
+ return 0;
+ }
+
+ if (mpic_map_error_int(mpic, virq, hw))
+ return 0;
+
+ if (hw >= mpic->num_sources) {
+ pr_warning("mpic: Mapping of source 0x%x failed, "
+ "source out of range !\n",\
+ (unsigned int)hw);
return -EINVAL;
+ }
mpic_msi_reserve_hwirq(mpic, hw);
@@ -935,20 +1077,37 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq,
DBG("mpic: mapping to irq chip @%p\n", chip);
- set_irq_chip_data(virq, mpic);
- set_irq_chip_and_handler(virq, chip, handle_fasteoi_irq);
+ irq_set_chip_data(virq, mpic);
+ irq_set_chip_and_handler(virq, chip, handle_fasteoi_irq);
/* Set default irq type */
- set_irq_type(virq, IRQ_TYPE_NONE);
+ irq_set_irq_type(virq, IRQ_TYPE_DEFAULT);
+
+ /* If the MPIC was reset, then all vectors have already been
+ * initialized. Otherwise, a per source lazy initialization
+ * is done here.
+ */
+ if (!mpic_is_ipi(mpic, hw) && (mpic->flags & MPIC_NO_RESET)) {
+ int cpu;
+
+ preempt_disable();
+ cpu = mpic_processor_id(mpic);
+ preempt_enable();
+
+ mpic_set_vector(virq, hw);
+ mpic_set_destination(virq, cpu);
+ mpic_irq_set_priority(virq, 8);
+ }
return 0;
}
-static int mpic_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
+static int mpic_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)
{
+ struct mpic *mpic = h->host_data;
static unsigned char map_mpic_senses[4] = {
IRQ_TYPE_EDGE_RISING,
IRQ_TYPE_LEVEL_LOW,
@@ -957,7 +1116,47 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct,
};
*out_hwirq = intspec[0];
- if (intsize > 1) {
+ if (intsize >= 4 && (mpic->flags & MPIC_FSL)) {
+ /*
+ * Freescale MPIC with extended intspec:
+ * First two cells are as usual. Third specifies
+ * an "interrupt type". Fourth is type-specific data.
+ *
+ * See Documentation/devicetree/bindings/powerpc/fsl/mpic.txt
+ */
+ switch (intspec[2]) {
+ case 0:
+ break;
+ case 1:
+ if (!(mpic->flags & MPIC_FSL_HAS_EIMR))
+ break;
+
+ if (intspec[3] >= ARRAY_SIZE(mpic->err_int_vecs))
+ return -EINVAL;
+
+ *out_hwirq = mpic->err_int_vecs[intspec[3]];
+
+ break;
+ case 2:
+ if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs))
+ return -EINVAL;
+
+ *out_hwirq = mpic->ipi_vecs[intspec[0]];
+ break;
+ case 3:
+ if (intspec[0] >= ARRAY_SIZE(mpic->timer_vecs))
+ return -EINVAL;
+
+ *out_hwirq = mpic->timer_vecs[intspec[0]];
+ break;
+ default:
+ pr_debug("%s: unknown irq type %u\n",
+ __func__, intspec[2]);
+ return -EINVAL;
+ }
+
+ *out_flags = map_mpic_senses[intspec[1] & 3];
+ } else if (intsize > 1) {
u32 mask = 0x3;
/* Apple invented a new race of encoding on machines with
@@ -982,16 +1181,55 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
-static struct irq_host_ops mpic_host_ops = {
+/* IRQ handler for a secondary MPIC cascaded from another IRQ controller */
+static void mpic_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct mpic *mpic = irq_desc_get_handler_data(desc);
+ unsigned int virq;
+
+ BUG_ON(!(mpic->flags & MPIC_SECONDARY));
+
+ virq = mpic_get_one_irq(mpic);
+ if (virq)
+ generic_handle_irq(virq);
+
+ chip->irq_eoi(&desc->irq_data);
+}
+
+static struct irq_domain_ops mpic_host_ops = {
.match = mpic_host_match,
.map = mpic_host_map,
.xlate = mpic_host_xlate,
};
+static u32 fsl_mpic_get_version(struct mpic *mpic)
+{
+ u32 brr1;
+
+ if (!(mpic->flags & MPIC_FSL))
+ return 0;
+
+ brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs,
+ MPIC_FSL_BRR1);
+
+ return brr1 & MPIC_FSL_BRR1_VER;
+}
+
/*
* Exported functions
*/
+u32 fsl_mpic_primary_get_version(void)
+{
+ struct mpic *mpic = mpic_primary;
+
+ if (mpic)
+ return fsl_mpic_get_version(mpic);
+
+ return 0;
+}
+
struct mpic * __init mpic_alloc(struct device_node *node,
phys_addr_t phys_addr,
unsigned int flags,
@@ -999,127 +1237,203 @@ struct mpic * __init mpic_alloc(struct device_node *node,
unsigned int irq_count,
const char *name)
{
- struct mpic *mpic;
- u32 reg;
- const char *vers;
- int i;
- int intvec_top;
- u64 paddr = phys_addr;
+ int i, psize, intvec_top;
+ struct mpic *mpic;
+ u32 greg_feature;
+ const char *vers;
+ const u32 *psrc;
+ u32 last_irq;
+ u32 fsl_version = 0;
+
+ /* Default MPIC search parameters */
+ static const struct of_device_id __initconst mpic_device_id[] = {
+ { .type = "open-pic", },
+ { .compatible = "open-pic", },
+ {},
+ };
- mpic = alloc_bootmem(sizeof(struct mpic));
- if (mpic == NULL)
- return NULL;
-
- memset(mpic, 0, sizeof(struct mpic));
- mpic->name = name;
+ /*
+ * If we were not passed a device-tree node, then perform the default
+ * search for standardized a standardized OpenPIC.
+ */
+ if (node) {
+ node = of_node_get(node);
+ } else {
+ node = of_find_matching_node(NULL, mpic_device_id);
+ if (!node)
+ return NULL;
+ }
- mpic->irqhost = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LINEAR,
- isu_size, &mpic_host_ops,
- flags & MPIC_LARGE_VECTORS ? 2048 : 256);
- if (mpic->irqhost == NULL) {
- of_node_put(node);
- return NULL;
+ /* Pick the physical address from the device tree if unspecified */
+ if (!phys_addr) {
+ /* Check if it is DCR-based */
+ if (of_get_property(node, "dcr-reg", NULL)) {
+ flags |= MPIC_USES_DCR;
+ } else {
+ struct resource r;
+ if (of_address_to_resource(node, 0, &r))
+ goto err_of_node_put;
+ phys_addr = r.start;
+ }
}
- mpic->irqhost->host_data = mpic;
+ /* Read extra device-tree properties into the flags variable */
+ if (of_get_property(node, "big-endian", NULL))
+ flags |= MPIC_BIG_ENDIAN;
+ if (of_get_property(node, "pic-no-reset", NULL))
+ flags |= MPIC_NO_RESET;
+ if (of_get_property(node, "single-cpu-affinity", NULL))
+ flags |= MPIC_SINGLE_DEST_CPU;
+ if (of_device_is_compatible(node, "fsl,mpic"))
+ flags |= MPIC_FSL | MPIC_LARGE_VECTORS;
+
+ mpic = kzalloc(sizeof(struct mpic), GFP_KERNEL);
+ if (mpic == NULL)
+ goto err_of_node_put;
+
+ mpic->name = name;
+ mpic->node = node;
+ mpic->paddr = phys_addr;
+ mpic->flags = flags;
+
mpic->hc_irq = mpic_irq_chip;
- mpic->hc_irq.typename = name;
- if (flags & MPIC_PRIMARY)
- mpic->hc_irq.set_affinity = mpic_set_affinity;
+ mpic->hc_irq.name = name;
+ if (!(mpic->flags & MPIC_SECONDARY))
+ mpic->hc_irq.irq_set_affinity = mpic_set_affinity;
#ifdef CONFIG_MPIC_U3_HT_IRQS
mpic->hc_ht_irq = mpic_irq_ht_chip;
- mpic->hc_ht_irq.typename = name;
- if (flags & MPIC_PRIMARY)
- mpic->hc_ht_irq.set_affinity = mpic_set_affinity;
+ mpic->hc_ht_irq.name = name;
+ if (!(mpic->flags & MPIC_SECONDARY))
+ mpic->hc_ht_irq.irq_set_affinity = mpic_set_affinity;
#endif /* CONFIG_MPIC_U3_HT_IRQS */
#ifdef CONFIG_SMP
mpic->hc_ipi = mpic_ipi_chip;
- mpic->hc_ipi.typename = name;
+ mpic->hc_ipi.name = name;
#endif /* CONFIG_SMP */
- mpic->flags = flags;
- mpic->isu_size = isu_size;
- mpic->irq_count = irq_count;
+ mpic->hc_tm = mpic_tm_chip;
+ mpic->hc_tm.name = name;
+
mpic->num_sources = 0; /* so far */
- if (flags & MPIC_LARGE_VECTORS)
+ if (mpic->flags & MPIC_LARGE_VECTORS)
intvec_top = 2047;
else
intvec_top = 255;
- mpic->timer_vecs[0] = intvec_top - 8;
- mpic->timer_vecs[1] = intvec_top - 7;
- mpic->timer_vecs[2] = intvec_top - 6;
- mpic->timer_vecs[3] = intvec_top - 5;
+ mpic->timer_vecs[0] = intvec_top - 12;
+ mpic->timer_vecs[1] = intvec_top - 11;
+ mpic->timer_vecs[2] = intvec_top - 10;
+ mpic->timer_vecs[3] = intvec_top - 9;
+ mpic->timer_vecs[4] = intvec_top - 8;
+ mpic->timer_vecs[5] = intvec_top - 7;
+ mpic->timer_vecs[6] = intvec_top - 6;
+ mpic->timer_vecs[7] = intvec_top - 5;
mpic->ipi_vecs[0] = intvec_top - 4;
mpic->ipi_vecs[1] = intvec_top - 3;
mpic->ipi_vecs[2] = intvec_top - 2;
mpic->ipi_vecs[3] = intvec_top - 1;
mpic->spurious_vec = intvec_top;
- /* Check for "big-endian" in device-tree */
- if (node && of_get_property(node, "big-endian", NULL) != NULL)
- mpic->flags |= MPIC_BIG_ENDIAN;
-
/* Look for protected sources */
- if (node) {
- unsigned int psize, bits, mapsize;
- const u32 *psrc =
- of_get_property(node, "protected-sources", &psize);
- if (psrc) {
- psize /= 4;
- bits = intvec_top + 1;
- mapsize = BITS_TO_LONGS(bits) * sizeof(unsigned long);
- mpic->protected = alloc_bootmem(mapsize);
- BUG_ON(mpic->protected == NULL);
- memset(mpic->protected, 0, mapsize);
- for (i = 0; i < psize; i++) {
- if (psrc[i] > intvec_top)
- continue;
- __set_bit(psrc[i], mpic->protected);
- }
+ psrc = of_get_property(mpic->node, "protected-sources", &psize);
+ if (psrc) {
+ /* Allocate a bitmap with one bit per interrupt */
+ unsigned int mapsize = BITS_TO_LONGS(intvec_top + 1);
+ mpic->protected = kzalloc(mapsize*sizeof(long), GFP_KERNEL);
+ BUG_ON(mpic->protected == NULL);
+ for (i = 0; i < psize/sizeof(u32); i++) {
+ if (psrc[i] > intvec_top)
+ continue;
+ __set_bit(psrc[i], mpic->protected);
}
}
#ifdef CONFIG_MPIC_WEIRD
- mpic->hw_set = mpic_infos[MPIC_GET_REGSET(flags)];
+ mpic->hw_set = mpic_infos[MPIC_GET_REGSET(mpic->flags)];
#endif
/* default register type */
- mpic->reg_type = (flags & MPIC_BIG_ENDIAN) ?
- mpic_access_mmio_be : mpic_access_mmio_le;
-
- /* If no physical address is passed in, a device-node is mandatory */
- BUG_ON(paddr == 0 && node == NULL);
+ if (mpic->flags & MPIC_BIG_ENDIAN)
+ mpic->reg_type = mpic_access_mmio_be;
+ else
+ mpic->reg_type = mpic_access_mmio_le;
- /* If no physical address passed in, check if it's dcr based */
- if (paddr == 0 && of_get_property(node, "dcr-reg", NULL) != NULL) {
+ /*
+ * An MPIC with a "dcr-reg" property must be accessed that way, but
+ * only if the kernel includes DCR support.
+ */
#ifdef CONFIG_PPC_DCR
- mpic->flags |= MPIC_USES_DCR;
+ if (mpic->flags & MPIC_USES_DCR)
mpic->reg_type = mpic_access_dcr;
#else
- BUG();
-#endif /* CONFIG_PPC_DCR */
+ BUG_ON(mpic->flags & MPIC_USES_DCR);
+#endif
+
+ /* Map the global registers */
+ mpic_map(mpic, mpic->paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000);
+ mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000);
+
+ if (mpic->flags & MPIC_FSL) {
+ int ret;
+
+ /*
+ * Yes, Freescale really did put global registers in the
+ * magic per-cpu area -- and they don't even show up in the
+ * non-magic per-cpu copies that this driver normally uses.
+ */
+ mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs,
+ MPIC_CPU_THISBASE, 0x1000);
+
+ fsl_version = fsl_mpic_get_version(mpic);
+
+ /* Error interrupt mask register (EIMR) is required for
+ * handling individual device error interrupts. EIMR
+ * was added in MPIC version 4.1.
+ *
+ * Over here we reserve vector number space for error
+ * interrupt vectors. This space is stolen from the
+ * global vector number space, as in case of ipis
+ * and timer interrupts.
+ *
+ * Available vector space = intvec_top - 12, where 12
+ * is the number of vectors which have been consumed by
+ * ipis and timer interrupts.
+ */
+ if (fsl_version >= 0x401) {
+ ret = mpic_setup_error_int(mpic, intvec_top - 12);
+ if (ret)
+ return NULL;
+ }
+
}
- /* If the MPIC is not DCR based, and no physical address was passed
- * in, try to obtain one
+ /*
+ * EPR is only available starting with v4.0. To support
+ * platforms that don't know the MPIC version at compile-time,
+ * such as qemu-e500, turn off coreint if this MPIC doesn't
+ * support it. Note that we never enable it if it wasn't
+ * requested in the first place.
+ *
+ * This is done outside the MPIC_FSL check, so that we
+ * also disable coreint if the MPIC node doesn't have
+ * an "fsl,mpic" compatible at all. This will be the case
+ * with device trees generated by older versions of QEMU.
+ * fsl_version will be zero if MPIC_FSL is not set.
*/
- if (paddr == 0 && !(mpic->flags & MPIC_USES_DCR)) {
- const u32 *reg;
- reg = of_get_property(node, "reg", NULL);
- BUG_ON(reg == NULL);
- paddr = of_translate_address(node, reg);
- BUG_ON(paddr == OF_BAD_ADDR);
+ if (fsl_version < 0x400 && (flags & MPIC_ENABLE_COREINT)) {
+ WARN_ON(ppc_md.get_irq != mpic_get_coreint_irq);
+ ppc_md.get_irq = mpic_get_irq;
}
- /* Map the global registers */
- mpic_map(mpic, paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000);
- mpic_map(mpic, paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000);
-
/* Reset */
- if (flags & MPIC_WANTS_RESET) {
+
+ /* When using a device-node, reset requests are only honored if the MPIC
+ * is allowed to reset.
+ */
+ if (!(mpic->flags & MPIC_NO_RESET)) {
+ printk(KERN_DEBUG "mpic: Resetting\n");
mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
| MPIC_GREG_GCONF_RESET);
@@ -1128,40 +1442,78 @@ struct mpic * __init mpic_alloc(struct device_node *node,
mb();
}
- if (flags & MPIC_ENABLE_MCK)
+ /* CoreInt */
+ if (mpic->flags & MPIC_ENABLE_COREINT)
+ mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
+ mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
+ | MPIC_GREG_GCONF_COREINT);
+
+ if (mpic->flags & MPIC_ENABLE_MCK)
mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
| MPIC_GREG_GCONF_MCK);
- /* Read feature register, calculate num CPUs and, for non-ISU
- * MPICs, num sources as well. On ISU MPICs, sources are counted
- * as ISUs are added
+ /*
+ * The MPIC driver will crash if there are more cores than we
+ * can initialize, so we may as well catch that problem here.
*/
- reg = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0));
- mpic->num_cpus = ((reg & MPIC_GREG_FEATURE_LAST_CPU_MASK)
- >> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1;
- if (isu_size == 0)
- mpic->num_sources = ((reg & MPIC_GREG_FEATURE_LAST_SRC_MASK)
- >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT) + 1;
+ BUG_ON(num_possible_cpus() > MPIC_MAX_CPUS);
/* Map the per-CPU registers */
- for (i = 0; i < mpic->num_cpus; i++) {
- mpic_map(mpic, paddr, &mpic->cpuregs[i],
- MPIC_INFO(CPU_BASE) + i * MPIC_INFO(CPU_STRIDE),
+ for_each_possible_cpu(i) {
+ unsigned int cpu = get_hard_smp_processor_id(i);
+
+ mpic_map(mpic, mpic->paddr, &mpic->cpuregs[cpu],
+ MPIC_INFO(CPU_BASE) + cpu * MPIC_INFO(CPU_STRIDE),
0x1000);
}
+ /*
+ * Read feature register. For non-ISU MPICs, num sources as well. On
+ * ISU MPICs, sources are counted as ISUs are added
+ */
+ greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0));
+
+ /*
+ * By default, the last source number comes from the MPIC, but the
+ * device-tree and board support code can override it on buggy hw.
+ * If we get passed an isu_size (multi-isu MPIC) then we use that
+ * as a default instead of the value read from the HW.
+ */
+ last_irq = (greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK)
+ >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT;
+ if (isu_size)
+ last_irq = isu_size * MPIC_MAX_ISU - 1;
+ of_property_read_u32(mpic->node, "last-interrupt-source", &last_irq);
+ if (irq_count)
+ last_irq = irq_count - 1;
+
/* Initialize main ISU if none provided */
- if (mpic->isu_size == 0) {
- mpic->isu_size = mpic->num_sources;
- mpic_map(mpic, paddr, &mpic->isus[0],
- MPIC_INFO(IRQ_BASE), MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
+ if (!isu_size) {
+ isu_size = last_irq + 1;
+ mpic->num_sources = isu_size;
+ mpic_map(mpic, mpic->paddr, &mpic->isus[0],
+ MPIC_INFO(IRQ_BASE),
+ MPIC_INFO(IRQ_STRIDE) * isu_size);
}
+
+ mpic->isu_size = isu_size;
mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
mpic->isu_mask = (1 << mpic->isu_shift) - 1;
+ mpic->irqhost = irq_domain_add_linear(mpic->node,
+ intvec_top,
+ &mpic_host_ops, mpic);
+
+ /*
+ * FIXME: The code leaks the MPIC object and mappings here; this
+ * is very unlikely to fail but it ought to be fixed anyways.
+ */
+ if (mpic->irqhost == NULL)
+ return NULL;
+
/* Display version */
- switch (reg & MPIC_GREG_FEATURE_VERSION_MASK) {
+ switch (greg_feature & MPIC_GREG_FEATURE_VERSION_MASK) {
case 1:
vers = "1.0";
break;
@@ -1177,19 +1529,23 @@ struct mpic * __init mpic_alloc(struct device_node *node,
}
printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx,"
" max %d CPUs\n",
- name, vers, (unsigned long long)paddr, mpic->num_cpus);
+ name, vers, (unsigned long long)mpic->paddr, num_possible_cpus());
printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n",
mpic->isu_size, mpic->isu_shift, mpic->isu_mask);
mpic->next = mpics;
mpics = mpic;
- if (flags & MPIC_PRIMARY) {
+ if (!(mpic->flags & MPIC_SECONDARY)) {
mpic_primary = mpic;
irq_set_default_host(mpic->irqhost);
}
return mpic;
+
+err_of_node_put:
+ of_node_put(node);
+ return NULL;
}
void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
@@ -1199,21 +1555,18 @@ void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
BUG_ON(isu_num >= MPIC_MAX_ISU);
- mpic_map(mpic, paddr, &mpic->isus[isu_num], 0,
+ mpic_map(mpic,
+ paddr, &mpic->isus[isu_num], 0,
MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
+
if ((isu_first + mpic->isu_size) > mpic->num_sources)
mpic->num_sources = isu_first + mpic->isu_size;
}
-void __init mpic_set_default_senses(struct mpic *mpic, u8 *senses, int count)
-{
- mpic->senses = senses;
- mpic->senses_count = count;
-}
-
void __init mpic_init(struct mpic *mpic)
{
- int i;
+ int i, cpu;
+ int num_timers = 4;
BUG_ON(mpic->num_sources == 0);
@@ -1222,15 +1575,30 @@ void __init mpic_init(struct mpic *mpic)
/* Set current processor priority to max */
mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf);
- /* Initialize timers: just disable them all */
- for (i = 0; i < 4; i++) {
+ if (mpic->flags & MPIC_FSL) {
+ u32 version = fsl_mpic_get_version(mpic);
+
+ /*
+ * Timer group B is present at the latest in MPIC 3.1 (e.g.
+ * mpc8536). It is not present in MPIC 2.0 (e.g. mpc8544).
+ * I don't know about the status of intermediate versions (or
+ * whether they even exist).
+ */
+ if (version >= 0x0301)
+ num_timers = 8;
+ }
+
+ /* Initialize timers to our reserved vectors and mask them for now */
+ for (i = 0; i < num_timers; i++) {
+ unsigned int offset = mpic_tm_offset(mpic, i);
+
mpic_write(mpic->tmregs,
- i * MPIC_INFO(TIMER_STRIDE) +
- MPIC_INFO(TIMER_DESTINATION), 0);
+ offset + MPIC_INFO(TIMER_DESTINATION),
+ 1 << hard_smp_processor_id());
mpic_write(mpic->tmregs,
- i * MPIC_INFO(TIMER_STRIDE) +
- MPIC_INFO(TIMER_VECTOR_PRI),
+ offset + MPIC_INFO(TIMER_VECTOR_PRI),
MPIC_VECPRI_MASK |
+ (9 << MPIC_VECPRI_PRIORITY_SHIFT) |
(mpic->timer_vecs[0] + i));
}
@@ -1243,33 +1611,32 @@ void __init mpic_init(struct mpic *mpic)
(mpic->ipi_vecs[0] + i));
}
- /* Initialize interrupt sources */
- if (mpic->irq_count == 0)
- mpic->irq_count = mpic->num_sources;
-
/* Do the HT PIC fixups on U3 broken mpic */
DBG("MPIC flags: %x\n", mpic->flags);
- if ((mpic->flags & MPIC_U3_HT_IRQS) && (mpic->flags & MPIC_PRIMARY)) {
+ if ((mpic->flags & MPIC_U3_HT_IRQS) && !(mpic->flags & MPIC_SECONDARY)) {
mpic_scan_ht_pics(mpic);
mpic_u3msi_init(mpic);
}
mpic_pasemi_msi_init(mpic);
- for (i = 0; i < mpic->num_sources; i++) {
- /* start with vector = source number, and masked */
- u32 vecpri = MPIC_VECPRI_MASK | i |
- (8 << MPIC_VECPRI_PRIORITY_SHIFT);
-
- /* check if protected */
- if (mpic->protected && test_bit(i, mpic->protected))
- continue;
- /* init hw */
- mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri);
- mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
- 1 << hard_smp_processor_id());
+ cpu = mpic_processor_id(mpic);
+
+ if (!(mpic->flags & MPIC_NO_RESET)) {
+ for (i = 0; i < mpic->num_sources; i++) {
+ /* start with vector = source number, and masked */
+ u32 vecpri = MPIC_VECPRI_MASK | i |
+ (8 << MPIC_VECPRI_PRIORITY_SHIFT);
+
+ /* check if protected */
+ if (mpic->protected && test_bit(i, mpic->protected))
+ continue;
+ /* init hw */
+ mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri);
+ mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 1 << cpu);
+ }
}
-
+
/* Init spurious vector */
mpic_write(mpic->gregs, MPIC_INFO(GREG_SPURIOUS), mpic->spurious_vec);
@@ -1289,9 +1656,25 @@ void __init mpic_init(struct mpic *mpic)
#ifdef CONFIG_PM
/* allocate memory to save mpic state */
- mpic->save_data = alloc_bootmem(mpic->num_sources * sizeof(struct mpic_irq_save));
+ mpic->save_data = kmalloc(mpic->num_sources * sizeof(*mpic->save_data),
+ GFP_KERNEL);
BUG_ON(mpic->save_data == NULL);
#endif
+
+ /* Check if this MPIC is chained from a parent interrupt controller */
+ if (mpic->flags & MPIC_SECONDARY) {
+ int virq = irq_of_parse_and_map(mpic->node, 0);
+ if (virq != NO_IRQ) {
+ printk(KERN_INFO "%s: hooking up to IRQ %d\n",
+ mpic->node->full_name, virq);
+ irq_set_handler_data(virq, mpic);
+ irq_set_chained_handler(virq, &mpic_cascade);
+ }
+ }
+
+ /* FSL mpic error interrupt intialization */
+ if (mpic->flags & MPIC_FSL_HAS_EIMR)
+ mpic_err_int_init(mpic, MPIC_FSL_ERR_INT);
}
void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio)
@@ -1309,54 +1692,44 @@ void __init mpic_set_serial_int(struct mpic *mpic, int enable)
unsigned long flags;
u32 v;
- spin_lock_irqsave(&mpic_lock, flags);
+ raw_spin_lock_irqsave(&mpic_lock, flags);
v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1);
if (enable)
v |= MPIC_GREG_GLOBAL_CONF_1_SIE;
else
v &= ~MPIC_GREG_GLOBAL_CONF_1_SIE;
mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v);
- spin_unlock_irqrestore(&mpic_lock, flags);
+ raw_spin_unlock_irqrestore(&mpic_lock, flags);
}
void mpic_irq_set_priority(unsigned int irq, unsigned int pri)
{
- int is_ipi;
- struct mpic *mpic = mpic_find(irq, &is_ipi);
- unsigned int src = mpic_irq_to_hw(irq);
+ struct mpic *mpic = mpic_find(irq);
+ unsigned int src = virq_to_hw(irq);
unsigned long flags;
u32 reg;
- spin_lock_irqsave(&mpic_lock, flags);
- if (is_ipi) {
+ if (!mpic)
+ return;
+
+ raw_spin_lock_irqsave(&mpic_lock, flags);
+ if (mpic_is_ipi(mpic, src)) {
reg = mpic_ipi_read(src - mpic->ipi_vecs[0]) &
~MPIC_VECPRI_PRIORITY_MASK;
mpic_ipi_write(src - mpic->ipi_vecs[0],
reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
+ } else if (mpic_is_tm(mpic, src)) {
+ reg = mpic_tm_read(src - mpic->timer_vecs[0]) &
+ ~MPIC_VECPRI_PRIORITY_MASK;
+ mpic_tm_write(src - mpic->timer_vecs[0],
+ reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
} else {
reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI))
& ~MPIC_VECPRI_PRIORITY_MASK;
mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI),
reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
}
- spin_unlock_irqrestore(&mpic_lock, flags);
-}
-
-unsigned int mpic_irq_get_priority(unsigned int irq)
-{
- int is_ipi;
- struct mpic *mpic = mpic_find(irq, &is_ipi);
- unsigned int src = mpic_irq_to_hw(irq);
- unsigned long flags;
- u32 reg;
-
- spin_lock_irqsave(&mpic_lock, flags);
- if (is_ipi)
- reg = mpic_ipi_read(src = mpic->ipi_vecs[0]);
- else
- reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
- spin_unlock_irqrestore(&mpic_lock, flags);
- return (reg & MPIC_VECPRI_PRIORITY_MASK) >> MPIC_VECPRI_PRIORITY_SHIFT;
+ raw_spin_unlock_irqrestore(&mpic_lock, flags);
}
void mpic_setup_this_cpu(void)
@@ -1371,14 +1744,14 @@ void mpic_setup_this_cpu(void)
DBG("%s: setup_this_cpu(%d)\n", mpic->name, hard_smp_processor_id());
- spin_lock_irqsave(&mpic_lock, flags);
+ raw_spin_lock_irqsave(&mpic_lock, flags);
/* let the mpic know we want intrs. default affinity is 0xffffffff
* until changed via /proc. That's how it's done on x86. If we want
* it differently, then we should make sure we also change the default
* values of irq_desc[].affinity in irq.c.
*/
- if (distribute_irqs) {
+ if (distribute_irqs && !(mpic->flags & MPIC_SINGLE_DEST_CPU)) {
for (i = 0; i < mpic->num_sources ; i++)
mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) | msk);
@@ -1387,7 +1760,7 @@ void mpic_setup_this_cpu(void)
/* Set current processor priority to 0 */
mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0);
- spin_unlock_irqrestore(&mpic_lock, flags);
+ raw_spin_unlock_irqrestore(&mpic_lock, flags);
#endif /* CONFIG_SMP */
}
@@ -1406,11 +1779,6 @@ void mpic_cpu_set_priority(int prio)
mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), prio);
}
-/*
- * XXX: someone who knows mpic should check this.
- * do we need to eoi the ipi including for kexec cpu here (see xics comments)?
- * or can we reset the mpic in the new kernel?
- */
void mpic_teardown_this_cpu(int secondary)
{
struct mpic *mpic = mpic_primary;
@@ -1421,7 +1789,7 @@ void mpic_teardown_this_cpu(int secondary)
BUG_ON(mpic == NULL);
DBG("%s: teardown_this_cpu(%d)\n", mpic->name, hard_smp_processor_id());
- spin_lock_irqsave(&mpic_lock, flags);
+ raw_spin_lock_irqsave(&mpic_lock, flags);
/* let the mpic know we don't want intrs. */
for (i = 0; i < mpic->num_sources ; i++)
@@ -1430,26 +1798,15 @@ void mpic_teardown_this_cpu(int secondary)
/* Set current processor priority to max */
mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf);
+ /* We need to EOI the IPI since not all platforms reset the MPIC
+ * on boot and new interrupts wouldn't get delivered otherwise.
+ */
+ mpic_eoi(mpic);
- spin_unlock_irqrestore(&mpic_lock, flags);
+ raw_spin_unlock_irqrestore(&mpic_lock, flags);
}
-void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask)
-{
- struct mpic *mpic = mpic_primary;
-
- BUG_ON(mpic == NULL);
-
-#ifdef DEBUG_IPI
- DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
-#endif
-
- mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) +
- ipi_no * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE),
- mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
-}
-
static unsigned int _mpic_get_one_irq(struct mpic *mpic, int reg)
{
u32 src;
@@ -1464,9 +1821,8 @@ static unsigned int _mpic_get_one_irq(struct mpic *mpic, int reg)
return NO_IRQ;
}
if (unlikely(mpic->protected && test_bit(src, mpic->protected))) {
- if (printk_ratelimit())
- printk(KERN_WARNING "%s: Got protected source %d !\n",
- mpic->name, (int)src);
+ printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n",
+ mpic->name, (int)src);
mpic_eoi(mpic);
return NO_IRQ;
}
@@ -1488,6 +1844,33 @@ unsigned int mpic_get_irq(void)
return mpic_get_one_irq(mpic);
}
+unsigned int mpic_get_coreint_irq(void)
+{
+#ifdef CONFIG_BOOKE
+ struct mpic *mpic = mpic_primary;
+ u32 src;
+
+ BUG_ON(mpic == NULL);
+
+ src = mfspr(SPRN_EPR);
+
+ if (unlikely(src == mpic->spurious_vec)) {
+ if (mpic->flags & MPIC_SPV_EOI)
+ mpic_eoi(mpic);
+ return NO_IRQ;
+ }
+ if (unlikely(mpic->protected && test_bit(src, mpic->protected))) {
+ printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n",
+ mpic->name, (int)src);
+ return NO_IRQ;
+ }
+
+ return irq_linear_revmap(mpic->irqhost, src);
+#else
+ return NO_IRQ;
+#endif
+}
+
unsigned int mpic_get_mcirq(void)
{
struct mpic *mpic = mpic_primary;
@@ -1501,54 +1884,44 @@ unsigned int mpic_get_mcirq(void)
void mpic_request_ipis(void)
{
struct mpic *mpic = mpic_primary;
- long i, err;
- static char *ipi_names[] = {
- "IPI0 (call function)",
- "IPI1 (reschedule)",
- "IPI2 (unused)",
- "IPI3 (debugger break)",
- };
+ int i;
BUG_ON(mpic == NULL);
- printk(KERN_INFO "mpic: requesting IPIs ... \n");
+ printk(KERN_INFO "mpic: requesting IPIs...\n");
for (i = 0; i < 4; i++) {
unsigned int vipi = irq_create_mapping(mpic->irqhost,
mpic->ipi_vecs[0] + i);
if (vipi == NO_IRQ) {
- printk(KERN_ERR "Failed to map IPI %ld\n", i);
- break;
- }
- err = request_irq(vipi, mpic_ipi_action,
- IRQF_DISABLED|IRQF_PERCPU,
- ipi_names[i], (void *)i);
- if (err) {
- printk(KERN_ERR "Request of irq %d for IPI %ld failed\n",
- vipi, i);
- break;
+ printk(KERN_ERR "Failed to map %s\n", smp_ipi_name[i]);
+ continue;
}
+ smp_request_message_ipi(vipi, i);
}
}
-void smp_mpic_message_pass(int target, int msg)
+void smp_mpic_message_pass(int cpu, int msg)
{
+ struct mpic *mpic = mpic_primary;
+ u32 physmask;
+
+ BUG_ON(mpic == NULL);
+
/* make sure we're sending something that translates to an IPI */
if ((unsigned int)msg > 3) {
printk("SMP %d: smp_message_pass: unknown msg %d\n",
smp_processor_id(), msg);
return;
}
- switch (target) {
- case MSG_ALL:
- mpic_send_ipi(msg, 0xffffffff);
- break;
- case MSG_ALL_BUT_SELF:
- mpic_send_ipi(msg, 0xffffffff & ~(1 << smp_processor_id()));
- break;
- default:
- mpic_send_ipi(msg, 1 << target);
- break;
- }
+
+#ifdef DEBUG_IPI
+ DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, msg);
+#endif
+
+ physmask = 1 << get_hard_smp_processor_id(cpu);
+
+ mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) +
+ msg * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE), physmask);
}
int __init smp_mpic_probe(void)
@@ -1557,7 +1930,7 @@ int __init smp_mpic_probe(void)
DBG("smp_mpic_probe()...\n");
- nr_cpus = cpus_weight(cpu_possible_map);
+ nr_cpus = cpumask_weight(cpu_possible_mask);
DBG("nr_cpus: %d\n", nr_cpus);
@@ -1567,16 +1940,43 @@ int __init smp_mpic_probe(void)
return nr_cpus;
}
-void __devinit smp_mpic_setup_cpu(int cpu)
+void smp_mpic_setup_cpu(int cpu)
{
mpic_setup_this_cpu();
}
+
+void mpic_reset_core(int cpu)
+{
+ struct mpic *mpic = mpic_primary;
+ u32 pir;
+ int cpuid = get_hard_smp_processor_id(cpu);
+ int i;
+
+ /* Set target bit for core reset */
+ pir = mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT));
+ pir |= (1 << cpuid);
+ mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir);
+ mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT));
+
+ /* Restore target bit after reset complete */
+ pir &= ~(1 << cpuid);
+ mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir);
+ mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT));
+
+ /* Perform 15 EOI on each reset core to clear pending interrupts.
+ * This is required for FSL CoreNet based devices */
+ if (mpic->flags & MPIC_FSL) {
+ for (i = 0; i < 15; i++) {
+ _mpic_write(mpic->reg_type, &mpic->cpuregs[cpuid],
+ MPIC_CPU_EOI, 0);
+ }
+ }
+}
#endif /* CONFIG_SMP */
#ifdef CONFIG_PM
-static int mpic_suspend(struct sys_device *dev, pm_message_t state)
+static void mpic_suspend_one(struct mpic *mpic)
{
- struct mpic *mpic = container_of(dev, struct mpic, sysdev);
int i;
for (i = 0; i < mpic->num_sources; i++) {
@@ -1585,13 +1985,22 @@ static int mpic_suspend(struct sys_device *dev, pm_message_t state)
mpic->save_data[i].dest =
mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION));
}
+}
+
+static int mpic_suspend(void)
+{
+ struct mpic *mpic = mpics;
+
+ while (mpic) {
+ mpic_suspend_one(mpic);
+ mpic = mpic->next;
+ }
return 0;
}
-static int mpic_resume(struct sys_device *dev)
+static void mpic_resume_one(struct mpic *mpic)
{
- struct mpic *mpic = container_of(dev, struct mpic, sysdev);
int i;
for (i = 0; i < mpic->num_sources; i++) {
@@ -1601,7 +2010,7 @@ static int mpic_resume(struct sys_device *dev)
mpic->save_data[i].dest);
#ifdef CONFIG_MPIC_U3_HT_IRQS
- {
+ if (mpic->fixups) {
struct mpic_irq_fixup *fixup = &mpic->fixups[i];
if (fixup->base) {
@@ -1618,33 +2027,30 @@ static int mpic_resume(struct sys_device *dev)
}
#endif
} /* end for loop */
+}
- return 0;
+static void mpic_resume(void)
+{
+ struct mpic *mpic = mpics;
+
+ while (mpic) {
+ mpic_resume_one(mpic);
+ mpic = mpic->next;
+ }
}
-#endif
-static struct sysdev_class mpic_sysclass = {
-#ifdef CONFIG_PM
+static struct syscore_ops mpic_syscore_ops = {
.resume = mpic_resume,
.suspend = mpic_suspend,
-#endif
- .name = "mpic",
};
static int mpic_init_sys(void)
{
- struct mpic *mpic = mpics;
- int error, id = 0;
+ register_syscore_ops(&mpic_syscore_ops);
+ subsys_system_register(&mpic_subsys, NULL);
- error = sysdev_class_register(&mpic_sysclass);
-
- while (mpic && !error) {
- mpic->sysdev.cls = &mpic_sysclass;
- mpic->sysdev.id = id++;
- error = sysdev_register(&mpic->sysdev);
- mpic = mpic->next;
- }
- return error;
+ return 0;
}
device_initcall(mpic_init_sys);
+#endif
diff --git a/arch/powerpc/sysdev/mpic.h b/arch/powerpc/sysdev/mpic.h
index fbf8a266941..24bf07a6392 100644
--- a/arch/powerpc/sysdev/mpic.h
+++ b/arch/powerpc/sysdev/mpic.h
@@ -14,8 +14,6 @@
#ifdef CONFIG_PCI_MSI
extern void mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq);
extern int mpic_msi_init_allocator(struct mpic *mpic);
-extern irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num);
-extern void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num);
extern int mpic_u3msi_init(struct mpic *mpic);
extern int mpic_pasemi_msi_init(struct mpic *mpic);
#else
@@ -36,8 +34,32 @@ static inline int mpic_pasemi_msi_init(struct mpic *mpic)
}
#endif
-extern int mpic_set_irq_type(unsigned int virq, unsigned int flow_type);
+extern int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type);
extern void mpic_set_vector(unsigned int virq, unsigned int vector);
-extern void mpic_set_affinity(unsigned int irq, cpumask_t cpumask);
+extern int mpic_set_affinity(struct irq_data *d,
+ const struct cpumask *cpumask, bool force);
+extern void mpic_reset_core(int cpu);
+
+#ifdef CONFIG_FSL_SOC
+extern int mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw);
+extern void mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum);
+extern int mpic_setup_error_int(struct mpic *mpic, int intvec);
+#else
+static inline int mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw)
+{
+ return 0;
+}
+
+
+static inline void mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum)
+{
+ return;
+}
+
+static inline int mpic_setup_error_int(struct mpic *mpic, int intvec)
+{
+ return -1;
+}
+#endif
#endif /* _POWERPC_SYSDEV_MPIC_H */
diff --git a/arch/powerpc/sysdev/mpic_msgr.c b/arch/powerpc/sysdev/mpic_msgr.c
new file mode 100644
index 00000000000..2c9b52aa266
--- /dev/null
+++ b/arch/powerpc/sysdev/mpic_msgr.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2011-2012, Meador Inge, Mentor Graphics Corporation.
+ *
+ * Some ideas based on un-pushed work done by Vivek Mahajan, Jason Jin, and
+ * Mingkai Hu from Freescale Semiconductor, Inc.
+ *
+ * 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; version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/of_platform.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <asm/prom.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+#include <asm/mpic_msgr.h>
+
+#define MPIC_MSGR_REGISTERS_PER_BLOCK 4
+#define MPIC_MSGR_STRIDE 0x10
+#define MPIC_MSGR_MER_OFFSET 0x100
+#define MSGR_INUSE 0
+#define MSGR_FREE 1
+
+static struct mpic_msgr **mpic_msgrs;
+static unsigned int mpic_msgr_count;
+static DEFINE_RAW_SPINLOCK(msgrs_lock);
+
+static inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value)
+{
+ out_be32(msgr->mer, value);
+}
+
+static inline u32 _mpic_msgr_mer_read(struct mpic_msgr *msgr)
+{
+ return in_be32(msgr->mer);
+}
+
+static inline void _mpic_msgr_disable(struct mpic_msgr *msgr)
+{
+ u32 mer = _mpic_msgr_mer_read(msgr);
+
+ _mpic_msgr_mer_write(msgr, mer & ~(1 << msgr->num));
+}
+
+struct mpic_msgr *mpic_msgr_get(unsigned int reg_num)
+{
+ unsigned long flags;
+ struct mpic_msgr *msgr;
+
+ /* Assume busy until proven otherwise. */
+ msgr = ERR_PTR(-EBUSY);
+
+ if (reg_num >= mpic_msgr_count)
+ return ERR_PTR(-ENODEV);
+
+ raw_spin_lock_irqsave(&msgrs_lock, flags);
+ msgr = mpic_msgrs[reg_num];
+ if (msgr->in_use == MSGR_FREE)
+ msgr->in_use = MSGR_INUSE;
+ raw_spin_unlock_irqrestore(&msgrs_lock, flags);
+
+ return msgr;
+}
+EXPORT_SYMBOL_GPL(mpic_msgr_get);
+
+void mpic_msgr_put(struct mpic_msgr *msgr)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&msgr->lock, flags);
+ msgr->in_use = MSGR_FREE;
+ _mpic_msgr_disable(msgr);
+ raw_spin_unlock_irqrestore(&msgr->lock, flags);
+}
+EXPORT_SYMBOL_GPL(mpic_msgr_put);
+
+void mpic_msgr_enable(struct mpic_msgr *msgr)
+{
+ unsigned long flags;
+ u32 mer;
+
+ raw_spin_lock_irqsave(&msgr->lock, flags);
+ mer = _mpic_msgr_mer_read(msgr);
+ _mpic_msgr_mer_write(msgr, mer | (1 << msgr->num));
+ raw_spin_unlock_irqrestore(&msgr->lock, flags);
+}
+EXPORT_SYMBOL_GPL(mpic_msgr_enable);
+
+void mpic_msgr_disable(struct mpic_msgr *msgr)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&msgr->lock, flags);
+ _mpic_msgr_disable(msgr);
+ raw_spin_unlock_irqrestore(&msgr->lock, flags);
+}
+EXPORT_SYMBOL_GPL(mpic_msgr_disable);
+
+/* The following three functions are used to compute the order and number of
+ * the message register blocks. They are clearly very inefficent. However,
+ * they are called *only* a few times during device initialization.
+ */
+static unsigned int mpic_msgr_number_of_blocks(void)
+{
+ unsigned int count;
+ struct device_node *aliases;
+
+ count = 0;
+ aliases = of_find_node_by_name(NULL, "aliases");
+
+ if (aliases) {
+ char buf[32];
+
+ for (;;) {
+ snprintf(buf, sizeof(buf), "mpic-msgr-block%d", count);
+ if (!of_find_property(aliases, buf, NULL))
+ break;
+
+ count += 1;
+ }
+ }
+
+ return count;
+}
+
+static unsigned int mpic_msgr_number_of_registers(void)
+{
+ return mpic_msgr_number_of_blocks() * MPIC_MSGR_REGISTERS_PER_BLOCK;
+}
+
+static int mpic_msgr_block_number(struct device_node *node)
+{
+ struct device_node *aliases;
+ unsigned int index, number_of_blocks;
+ char buf[64];
+
+ number_of_blocks = mpic_msgr_number_of_blocks();
+ aliases = of_find_node_by_name(NULL, "aliases");
+ if (!aliases)
+ return -1;
+
+ for (index = 0; index < number_of_blocks; ++index) {
+ struct property *prop;
+
+ snprintf(buf, sizeof(buf), "mpic-msgr-block%d", index);
+ prop = of_find_property(aliases, buf, NULL);
+ if (node == of_find_node_by_path(prop->value))
+ break;
+ }
+
+ return index == number_of_blocks ? -1 : index;
+}
+
+/* The probe function for a single message register block.
+ */
+static int mpic_msgr_probe(struct platform_device *dev)
+{
+ void __iomem *msgr_block_addr;
+ int block_number;
+ struct resource rsrc;
+ unsigned int i;
+ unsigned int irq_index;
+ struct device_node *np = dev->dev.of_node;
+ unsigned int receive_mask;
+ const unsigned int *prop;
+
+ if (!np) {
+ dev_err(&dev->dev, "Device OF-Node is NULL");
+ return -EFAULT;
+ }
+
+ /* Allocate the message register array upon the first device
+ * registered.
+ */
+ if (!mpic_msgrs) {
+ mpic_msgr_count = mpic_msgr_number_of_registers();
+ dev_info(&dev->dev, "Found %d message registers\n",
+ mpic_msgr_count);
+
+ mpic_msgrs = kzalloc(sizeof(struct mpic_msgr) * mpic_msgr_count,
+ GFP_KERNEL);
+ if (!mpic_msgrs) {
+ dev_err(&dev->dev,
+ "No memory for message register blocks\n");
+ return -ENOMEM;
+ }
+ }
+ dev_info(&dev->dev, "Of-device full name %s\n", np->full_name);
+
+ /* IO map the message register block. */
+ of_address_to_resource(np, 0, &rsrc);
+ msgr_block_addr = ioremap(rsrc.start, rsrc.end - rsrc.start);
+ if (!msgr_block_addr) {
+ dev_err(&dev->dev, "Failed to iomap MPIC message registers");
+ return -EFAULT;
+ }
+
+ /* Ensure the block has a defined order. */
+ block_number = mpic_msgr_block_number(np);
+ if (block_number < 0) {
+ dev_err(&dev->dev,
+ "Failed to find message register block alias\n");
+ return -ENODEV;
+ }
+ dev_info(&dev->dev, "Setting up message register block %d\n",
+ block_number);
+
+ /* Grab the receive mask which specifies what registers can receive
+ * interrupts.
+ */
+ prop = of_get_property(np, "mpic-msgr-receive-mask", NULL);
+ receive_mask = (prop) ? *prop : 0xF;
+
+ /* Build up the appropriate message register data structures. */
+ for (i = 0, irq_index = 0; i < MPIC_MSGR_REGISTERS_PER_BLOCK; ++i) {
+ struct mpic_msgr *msgr;
+ unsigned int reg_number;
+
+ msgr = kzalloc(sizeof(struct mpic_msgr), GFP_KERNEL);
+ if (!msgr) {
+ dev_err(&dev->dev, "No memory for message register\n");
+ return -ENOMEM;
+ }
+
+ reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i;
+ msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE;
+ msgr->mer = (u32 *)((u8 *)msgr->base + MPIC_MSGR_MER_OFFSET);
+ msgr->in_use = MSGR_FREE;
+ msgr->num = i;
+ raw_spin_lock_init(&msgr->lock);
+
+ if (receive_mask & (1 << i)) {
+ msgr->irq = irq_of_parse_and_map(np, irq_index);
+ if (msgr->irq == NO_IRQ) {
+ dev_err(&dev->dev,
+ "Missing interrupt specifier");
+ kfree(msgr);
+ return -EFAULT;
+ }
+ irq_index += 1;
+ } else {
+ msgr->irq = NO_IRQ;
+ }
+
+ mpic_msgrs[reg_number] = msgr;
+ mpic_msgr_disable(msgr);
+ dev_info(&dev->dev, "Register %d initialized: irq %d\n",
+ reg_number, msgr->irq);
+
+ }
+
+ return 0;
+}
+
+static const struct of_device_id mpic_msgr_ids[] = {
+ {
+ .compatible = "fsl,mpic-v3.1-msgr",
+ .data = NULL,
+ },
+ {}
+};
+
+static struct platform_driver mpic_msgr_driver = {
+ .driver = {
+ .name = "mpic-msgr",
+ .owner = THIS_MODULE,
+ .of_match_table = mpic_msgr_ids,
+ },
+ .probe = mpic_msgr_probe,
+};
+
+static __init int mpic_msgr_init(void)
+{
+ return platform_driver_register(&mpic_msgr_driver);
+}
+subsys_initcall(mpic_msgr_init);
diff --git a/arch/powerpc/sysdev/mpic_msi.c b/arch/powerpc/sysdev/mpic_msi.c
index d272a52ecd2..7dc39f35a4c 100644
--- a/arch/powerpc/sysdev/mpic_msi.c
+++ b/arch/powerpc/sysdev/mpic_msi.c
@@ -15,90 +15,58 @@
#include <asm/prom.h>
#include <asm/hw_irq.h>
#include <asm/ppc-pci.h>
+#include <asm/msi_bitmap.h>
-
-static void __mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq)
-{
- pr_debug("mpic: reserving hwirq 0x%lx\n", hwirq);
- bitmap_allocate_region(mpic->hwirq_bitmap, hwirq, 0);
-}
+#include <sysdev/mpic.h>
void mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq)
{
- unsigned long flags;
-
/* The mpic calls this even when there is no allocator setup */
- if (!mpic->hwirq_bitmap)
+ if (!mpic->msi_bitmap.bitmap)
return;
- spin_lock_irqsave(&mpic->bitmap_lock, flags);
- __mpic_msi_reserve_hwirq(mpic, hwirq);
- spin_unlock_irqrestore(&mpic->bitmap_lock, flags);
-}
-
-irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num)
-{
- unsigned long flags;
- int offset, order = get_count_order(num);
-
- spin_lock_irqsave(&mpic->bitmap_lock, flags);
- /*
- * This is fast, but stricter than we need. We might want to add
- * a fallback routine which does a linear search with no alignment.
- */
- offset = bitmap_find_free_region(mpic->hwirq_bitmap, mpic->irq_count,
- order);
- spin_unlock_irqrestore(&mpic->bitmap_lock, flags);
-
- pr_debug("mpic: allocated 0x%x (2^%d) at offset 0x%x\n",
- num, order, offset);
-
- return offset;
-}
-
-void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num)
-{
- unsigned long flags;
- int order = get_count_order(num);
-
- pr_debug("mpic: freeing 0x%x (2^%d) at offset 0x%x\n",
- num, order, offset);
-
- spin_lock_irqsave(&mpic->bitmap_lock, flags);
- bitmap_release_region(mpic->hwirq_bitmap, offset, order);
- spin_unlock_irqrestore(&mpic->bitmap_lock, flags);
+ msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, hwirq);
}
#ifdef CONFIG_MPIC_U3_HT_IRQS
static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic)
{
irq_hw_number_t hwirq;
- struct irq_host_ops *ops = mpic->irqhost->ops;
+ const struct irq_domain_ops *ops = mpic->irqhost->ops;
struct device_node *np;
int flags, index, i;
- struct of_irq oirq;
+ struct of_phandle_args oirq;
pr_debug("mpic: found U3, guessing msi allocator setup\n");
- /* Reserve source numbers we know are reserved in the HW */
+ /* Reserve source numbers we know are reserved in the HW.
+ *
+ * This is a bit of a mix of U3 and U4 reserves but that's going
+ * to work fine, we have plenty enugh numbers left so let's just
+ * mark anything we don't like reserved.
+ */
for (i = 0; i < 8; i++)
- __mpic_msi_reserve_hwirq(mpic, i);
+ msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
for (i = 42; i < 46; i++)
- __mpic_msi_reserve_hwirq(mpic, i);
+ msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
for (i = 100; i < 105; i++)
- __mpic_msi_reserve_hwirq(mpic, i);
+ msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
+
+ for (i = 124; i < mpic->num_sources; i++)
+ msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
+
np = NULL;
while ((np = of_find_all_nodes(np))) {
pr_debug("mpic: mapping hwirqs for %s\n", np->full_name);
index = 0;
- while (of_irq_map_one(np, index++, &oirq) == 0) {
- ops->xlate(mpic->irqhost, NULL, oirq.specifier,
- oirq.size, &hwirq, &flags);
- __mpic_msi_reserve_hwirq(mpic, hwirq);
+ while (of_irq_parse_one(np, index++, &oirq) == 0) {
+ ops->xlate(mpic->irqhost, NULL, oirq.args,
+ oirq.args_count, &hwirq, &flags);
+ msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, hwirq);
}
}
@@ -111,70 +79,25 @@ static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic)
}
#endif
-static int mpic_msi_reserve_dt_hwirqs(struct mpic *mpic)
-{
- int i, len;
- const u32 *p;
-
- p = of_get_property(mpic->irqhost->of_node,
- "msi-available-ranges", &len);
- if (!p) {
- pr_debug("mpic: no msi-available-ranges property found on %s\n",
- mpic->irqhost->of_node->full_name);
- return -ENODEV;
- }
-
- if (len % 8 != 0) {
- printk(KERN_WARNING "mpic: Malformed msi-available-ranges "
- "property on %s\n", mpic->irqhost->of_node->full_name);
- return -EINVAL;
- }
-
- bitmap_allocate_region(mpic->hwirq_bitmap, 0,
- get_count_order(mpic->irq_count));
-
- /* Format is: (<u32 start> <u32 count>)+ */
- len /= sizeof(u32);
- for (i = 0; i < len / 2; i++, p += 2)
- mpic_msi_free_hwirqs(mpic, *p, *(p + 1));
-
- return 0;
-}
-
int mpic_msi_init_allocator(struct mpic *mpic)
{
- int rc, size;
-
- BUG_ON(mpic->hwirq_bitmap);
- spin_lock_init(&mpic->bitmap_lock);
-
- size = BITS_TO_LONGS(mpic->irq_count) * sizeof(long);
- pr_debug("mpic: allocator bitmap size is 0x%x bytes\n", size);
+ int rc;
- mpic->hwirq_bitmap = alloc_maybe_bootmem(size, GFP_KERNEL);
-
- if (!mpic->hwirq_bitmap) {
- pr_debug("mpic: ENOMEM allocating allocator bitmap!\n");
- return -ENOMEM;
- }
+ rc = msi_bitmap_alloc(&mpic->msi_bitmap, mpic->num_sources,
+ mpic->irqhost->of_node);
+ if (rc)
+ return rc;
- memset(mpic->hwirq_bitmap, 0, size);
-
- rc = mpic_msi_reserve_dt_hwirqs(mpic);
- if (rc) {
+ rc = msi_bitmap_reserve_dt_hwirqs(&mpic->msi_bitmap);
+ if (rc > 0) {
if (mpic->flags & MPIC_U3_HT_IRQS)
rc = mpic_msi_reserve_u3_hwirqs(mpic);
- if (rc)
- goto out_free;
+ if (rc) {
+ msi_bitmap_free(&mpic->msi_bitmap);
+ return rc;
+ }
}
return 0;
-
- out_free:
- if (mem_init_done)
- kfree(mpic->hwirq_bitmap);
-
- mpic->hwirq_bitmap = NULL;
- return rc;
}
diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c
index 33cbfb22ce3..38e62382070 100644
--- a/arch/powerpc/sysdev/mpic_pasemi_msi.c
+++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c
@@ -22,6 +22,7 @@
#include <asm/prom.h>
#include <asm/hw_irq.h>
#include <asm/ppc-pci.h>
+#include <asm/msi_bitmap.h>
#include "mpic.h"
@@ -38,28 +39,28 @@
static struct mpic *msi_mpic;
-static void mpic_pasemi_msi_mask_irq(unsigned int irq)
+static void mpic_pasemi_msi_mask_irq(struct irq_data *data)
{
- pr_debug("mpic_pasemi_msi_mask_irq %d\n", irq);
- mask_msi_irq(irq);
- mpic_mask_irq(irq);
+ pr_debug("mpic_pasemi_msi_mask_irq %d\n", data->irq);
+ mask_msi_irq(data);
+ mpic_mask_irq(data);
}
-static void mpic_pasemi_msi_unmask_irq(unsigned int irq)
+static void mpic_pasemi_msi_unmask_irq(struct irq_data *data)
{
- pr_debug("mpic_pasemi_msi_unmask_irq %d\n", irq);
- mpic_unmask_irq(irq);
- unmask_msi_irq(irq);
+ pr_debug("mpic_pasemi_msi_unmask_irq %d\n", data->irq);
+ mpic_unmask_irq(data);
+ unmask_msi_irq(data);
}
static struct irq_chip mpic_pasemi_msi_chip = {
- .shutdown = mpic_pasemi_msi_mask_irq,
- .mask = mpic_pasemi_msi_mask_irq,
- .unmask = mpic_pasemi_msi_unmask_irq,
- .eoi = mpic_end_irq,
- .set_type = mpic_set_irq_type,
- .set_affinity = mpic_set_affinity,
- .typename = "PASEMI-MSI ",
+ .irq_shutdown = mpic_pasemi_msi_mask_irq,
+ .irq_mask = mpic_pasemi_msi_mask_irq,
+ .irq_unmask = mpic_pasemi_msi_unmask_irq,
+ .irq_eoi = mpic_end_irq,
+ .irq_set_type = mpic_set_irq_type,
+ .irq_set_affinity = mpic_set_affinity,
+ .name = "PASEMI-MSI",
};
static int pasemi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
@@ -80,9 +81,9 @@ static void pasemi_msi_teardown_msi_irqs(struct pci_dev *pdev)
if (entry->irq == NO_IRQ)
continue;
- set_irq_msi(entry->irq, NULL);
- mpic_msi_free_hwirqs(msi_mpic, virq_to_hw(entry->irq),
- ALLOC_CHUNK);
+ irq_set_msi_desc(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap,
+ virq_to_hw(entry->irq), ALLOC_CHUNK);
irq_dispose_mapping(entry->irq);
}
@@ -91,10 +92,10 @@ static void pasemi_msi_teardown_msi_irqs(struct pci_dev *pdev)
static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
{
- irq_hw_number_t hwirq;
unsigned int virq;
struct msi_desc *entry;
struct msi_msg msg;
+ int hwirq;
pr_debug("pasemi_msi_setup_msi_irqs, pdev %p nvec %d type %d\n",
pdev, nvec, type);
@@ -108,7 +109,8 @@ static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
* few MSIs for someone, but restrictions will apply to how the
* sources can be changed independently.
*/
- hwirq = mpic_msi_alloc_hwirqs(msi_mpic, ALLOC_CHUNK);
+ hwirq = msi_bitmap_alloc_hwirqs(&msi_mpic->msi_bitmap,
+ ALLOC_CHUNK);
if (hwirq < 0) {
pr_debug("pasemi_msi: failed allocating hwirq\n");
return hwirq;
@@ -116,8 +118,10 @@ static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
virq = irq_create_mapping(msi_mpic->irqhost, hwirq);
if (virq == NO_IRQ) {
- pr_debug("pasemi_msi: failed mapping hwirq 0x%lx\n", hwirq);
- mpic_msi_free_hwirqs(msi_mpic, hwirq, ALLOC_CHUNK);
+ pr_debug("pasemi_msi: failed mapping hwirq 0x%x\n",
+ hwirq);
+ msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, hwirq,
+ ALLOC_CHUNK);
return -ENOSPC;
}
@@ -127,12 +131,12 @@ static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
*/
mpic_set_vector(virq, 0);
- set_irq_msi(virq, entry);
- set_irq_chip(virq, &mpic_pasemi_msi_chip);
- set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
+ irq_set_msi_desc(virq, entry);
+ irq_set_chip(virq, &mpic_pasemi_msi_chip);
+ irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
- pr_debug("pasemi_msi: allocated virq 0x%x (hw 0x%lx) addr 0x%x\n",
- virq, hwirq, msg.address_lo);
+ pr_debug("pasemi_msi: allocated virq 0x%x (hw 0x%x) " \
+ "addr 0x%x\n", virq, hwirq, msg.address_lo);
/* Likewise, the device writes [0...511] into the target
* register to generate MSI [512...1023]
diff --git a/arch/powerpc/sysdev/mpic_timer.c b/arch/powerpc/sysdev/mpic_timer.c
new file mode 100644
index 00000000000..9d9b06217f8
--- /dev/null
+++ b/arch/powerpc/sysdev/mpic_timer.c
@@ -0,0 +1,601 @@
+/*
+ * MPIC timer driver
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ * Author: Dongsheng Wang <Dongsheng.Wang@freescale.com>
+ * Li Yang <leoli@freescale.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 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/syscore_ops.h>
+#include <sysdev/fsl_soc.h>
+#include <asm/io.h>
+
+#include <asm/mpic_timer.h>
+
+#define FSL_GLOBAL_TIMER 0x1
+
+/* Clock Ratio
+ * Divide by 64 0x00000300
+ * Divide by 32 0x00000200
+ * Divide by 16 0x00000100
+ * Divide by 8 0x00000000 (Hardware default div)
+ */
+#define MPIC_TIMER_TCR_CLKDIV 0x00000300
+
+#define MPIC_TIMER_TCR_ROVR_OFFSET 24
+
+#define TIMER_STOP 0x80000000
+#define GTCCR_TOG 0x80000000
+#define TIMERS_PER_GROUP 4
+#define MAX_TICKS (~0U >> 1)
+#define MAX_TICKS_CASCADE (~0U)
+#define TIMER_OFFSET(num) (1 << (TIMERS_PER_GROUP - 1 - num))
+
+/* tv_usec should be less than ONE_SECOND, otherwise use tv_sec */
+#define ONE_SECOND 1000000
+
+struct timer_regs {
+ u32 gtccr;
+ u32 res0[3];
+ u32 gtbcr;
+ u32 res1[3];
+ u32 gtvpr;
+ u32 res2[3];
+ u32 gtdr;
+ u32 res3[3];
+};
+
+struct cascade_priv {
+ u32 tcr_value; /* TCR register: CASC & ROVR value */
+ unsigned int cascade_map; /* cascade map */
+ unsigned int timer_num; /* cascade control timer */
+};
+
+struct timer_group_priv {
+ struct timer_regs __iomem *regs;
+ struct mpic_timer timer[TIMERS_PER_GROUP];
+ struct list_head node;
+ unsigned int timerfreq;
+ unsigned int idle;
+ unsigned int flags;
+ spinlock_t lock;
+ void __iomem *group_tcr;
+};
+
+static struct cascade_priv cascade_timer[] = {
+ /* cascade timer 0 and 1 */
+ {0x1, 0xc, 0x1},
+ /* cascade timer 1 and 2 */
+ {0x2, 0x6, 0x2},
+ /* cascade timer 2 and 3 */
+ {0x4, 0x3, 0x3}
+};
+
+static LIST_HEAD(timer_group_list);
+
+static void convert_ticks_to_time(struct timer_group_priv *priv,
+ const u64 ticks, struct timeval *time)
+{
+ u64 tmp_sec;
+
+ time->tv_sec = (__kernel_time_t)div_u64(ticks, priv->timerfreq);
+ tmp_sec = (u64)time->tv_sec * (u64)priv->timerfreq;
+
+ time->tv_usec = 0;
+
+ if (tmp_sec <= ticks)
+ time->tv_usec = (__kernel_suseconds_t)
+ div_u64((ticks - tmp_sec) * 1000000, priv->timerfreq);
+
+ return;
+}
+
+/* the time set by the user is converted to "ticks" */
+static int convert_time_to_ticks(struct timer_group_priv *priv,
+ const struct timeval *time, u64 *ticks)
+{
+ u64 max_value; /* prevent u64 overflow */
+ u64 tmp = 0;
+
+ u64 tmp_sec;
+ u64 tmp_ms;
+ u64 tmp_us;
+
+ max_value = div_u64(ULLONG_MAX, priv->timerfreq);
+
+ if (time->tv_sec > max_value ||
+ (time->tv_sec == max_value && time->tv_usec > 0))
+ return -EINVAL;
+
+ tmp_sec = (u64)time->tv_sec * (u64)priv->timerfreq;
+ tmp += tmp_sec;
+
+ tmp_ms = time->tv_usec / 1000;
+ tmp_ms = div_u64((u64)tmp_ms * (u64)priv->timerfreq, 1000);
+ tmp += tmp_ms;
+
+ tmp_us = time->tv_usec % 1000;
+ tmp_us = div_u64((u64)tmp_us * (u64)priv->timerfreq, 1000000);
+ tmp += tmp_us;
+
+ *ticks = tmp;
+
+ return 0;
+}
+
+/* detect whether there is a cascade timer available */
+static struct mpic_timer *detect_idle_cascade_timer(
+ struct timer_group_priv *priv)
+{
+ struct cascade_priv *casc_priv;
+ unsigned int map;
+ unsigned int array_size = ARRAY_SIZE(cascade_timer);
+ unsigned int num;
+ unsigned int i;
+ unsigned long flags;
+
+ casc_priv = cascade_timer;
+ for (i = 0; i < array_size; i++) {
+ spin_lock_irqsave(&priv->lock, flags);
+ map = casc_priv->cascade_map & priv->idle;
+ if (map == casc_priv->cascade_map) {
+ num = casc_priv->timer_num;
+ priv->timer[num].cascade_handle = casc_priv;
+
+ /* set timer busy */
+ priv->idle &= ~casc_priv->cascade_map;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return &priv->timer[num];
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ casc_priv++;
+ }
+
+ return NULL;
+}
+
+static int set_cascade_timer(struct timer_group_priv *priv, u64 ticks,
+ unsigned int num)
+{
+ struct cascade_priv *casc_priv;
+ u32 tcr;
+ u32 tmp_ticks;
+ u32 rem_ticks;
+
+ /* set group tcr reg for cascade */
+ casc_priv = priv->timer[num].cascade_handle;
+ if (!casc_priv)
+ return -EINVAL;
+
+ tcr = casc_priv->tcr_value |
+ (casc_priv->tcr_value << MPIC_TIMER_TCR_ROVR_OFFSET);
+ setbits32(priv->group_tcr, tcr);
+
+ tmp_ticks = div_u64_rem(ticks, MAX_TICKS_CASCADE, &rem_ticks);
+
+ out_be32(&priv->regs[num].gtccr, 0);
+ out_be32(&priv->regs[num].gtbcr, tmp_ticks | TIMER_STOP);
+
+ out_be32(&priv->regs[num - 1].gtccr, 0);
+ out_be32(&priv->regs[num - 1].gtbcr, rem_ticks);
+
+ return 0;
+}
+
+static struct mpic_timer *get_cascade_timer(struct timer_group_priv *priv,
+ u64 ticks)
+{
+ struct mpic_timer *allocated_timer;
+
+ /* Two cascade timers: Support the maximum time */
+ const u64 max_ticks = (u64)MAX_TICKS * (u64)MAX_TICKS_CASCADE;
+ int ret;
+
+ if (ticks > max_ticks)
+ return NULL;
+
+ /* detect idle timer */
+ allocated_timer = detect_idle_cascade_timer(priv);
+ if (!allocated_timer)
+ return NULL;
+
+ /* set ticks to timer */
+ ret = set_cascade_timer(priv, ticks, allocated_timer->num);
+ if (ret < 0)
+ return NULL;
+
+ return allocated_timer;
+}
+
+static struct mpic_timer *get_timer(const struct timeval *time)
+{
+ struct timer_group_priv *priv;
+ struct mpic_timer *timer;
+
+ u64 ticks;
+ unsigned int num;
+ unsigned int i;
+ unsigned long flags;
+ int ret;
+
+ list_for_each_entry(priv, &timer_group_list, node) {
+ ret = convert_time_to_ticks(priv, time, &ticks);
+ if (ret < 0)
+ return NULL;
+
+ if (ticks > MAX_TICKS) {
+ if (!(priv->flags & FSL_GLOBAL_TIMER))
+ return NULL;
+
+ timer = get_cascade_timer(priv, ticks);
+ if (!timer)
+ continue;
+
+ return timer;
+ }
+
+ for (i = 0; i < TIMERS_PER_GROUP; i++) {
+ /* one timer: Reverse allocation */
+ num = TIMERS_PER_GROUP - 1 - i;
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->idle & (1 << i)) {
+ /* set timer busy */
+ priv->idle &= ~(1 << i);
+ /* set ticks & stop timer */
+ out_be32(&priv->regs[num].gtbcr,
+ ticks | TIMER_STOP);
+ out_be32(&priv->regs[num].gtccr, 0);
+ priv->timer[num].cascade_handle = NULL;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return &priv->timer[num];
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * mpic_start_timer - start hardware timer
+ * @handle: the timer to be started.
+ *
+ * It will do ->fn(->dev) callback from the hardware interrupt at
+ * the ->timeval point in the future.
+ */
+void mpic_start_timer(struct mpic_timer *handle)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+
+ clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP);
+}
+EXPORT_SYMBOL(mpic_start_timer);
+
+/**
+ * mpic_stop_timer - stop hardware timer
+ * @handle: the timer to be stoped
+ *
+ * The timer periodically generates an interrupt. Unless user stops the timer.
+ */
+void mpic_stop_timer(struct mpic_timer *handle)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+ struct cascade_priv *casc_priv;
+
+ setbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP);
+
+ casc_priv = priv->timer[handle->num].cascade_handle;
+ if (casc_priv) {
+ out_be32(&priv->regs[handle->num].gtccr, 0);
+ out_be32(&priv->regs[handle->num - 1].gtccr, 0);
+ } else {
+ out_be32(&priv->regs[handle->num].gtccr, 0);
+ }
+}
+EXPORT_SYMBOL(mpic_stop_timer);
+
+/**
+ * mpic_get_remain_time - get timer time
+ * @handle: the timer to be selected.
+ * @time: time for timer
+ *
+ * Query timer remaining time.
+ */
+void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+ struct cascade_priv *casc_priv;
+
+ u64 ticks;
+ u32 tmp_ticks;
+
+ casc_priv = priv->timer[handle->num].cascade_handle;
+ if (casc_priv) {
+ tmp_ticks = in_be32(&priv->regs[handle->num].gtccr);
+ tmp_ticks &= ~GTCCR_TOG;
+ ticks = ((u64)tmp_ticks & UINT_MAX) * (u64)MAX_TICKS_CASCADE;
+ tmp_ticks = in_be32(&priv->regs[handle->num - 1].gtccr);
+ ticks += tmp_ticks;
+ } else {
+ ticks = in_be32(&priv->regs[handle->num].gtccr);
+ ticks &= ~GTCCR_TOG;
+ }
+
+ convert_ticks_to_time(priv, ticks, time);
+}
+EXPORT_SYMBOL(mpic_get_remain_time);
+
+/**
+ * mpic_free_timer - free hardware timer
+ * @handle: the timer to be removed.
+ *
+ * Free the timer.
+ *
+ * Note: can not be used in interrupt context.
+ */
+void mpic_free_timer(struct mpic_timer *handle)
+{
+ struct timer_group_priv *priv = container_of(handle,
+ struct timer_group_priv, timer[handle->num]);
+
+ struct cascade_priv *casc_priv;
+ unsigned long flags;
+
+ mpic_stop_timer(handle);
+
+ casc_priv = priv->timer[handle->num].cascade_handle;
+
+ free_irq(priv->timer[handle->num].irq, priv->timer[handle->num].dev);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (casc_priv) {
+ u32 tcr;
+ tcr = casc_priv->tcr_value | (casc_priv->tcr_value <<
+ MPIC_TIMER_TCR_ROVR_OFFSET);
+ clrbits32(priv->group_tcr, tcr);
+ priv->idle |= casc_priv->cascade_map;
+ priv->timer[handle->num].cascade_handle = NULL;
+ } else {
+ priv->idle |= TIMER_OFFSET(handle->num);
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+EXPORT_SYMBOL(mpic_free_timer);
+
+/**
+ * mpic_request_timer - get a hardware timer
+ * @fn: interrupt handler function
+ * @dev: callback function of the data
+ * @time: time for timer
+ *
+ * This executes the "request_irq", returning NULL
+ * else "handle" on success.
+ */
+struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev,
+ const struct timeval *time)
+{
+ struct mpic_timer *allocated_timer;
+ int ret;
+
+ if (list_empty(&timer_group_list))
+ return NULL;
+
+ if (!(time->tv_sec + time->tv_usec) ||
+ time->tv_sec < 0 || time->tv_usec < 0)
+ return NULL;
+
+ if (time->tv_usec > ONE_SECOND)
+ return NULL;
+
+ allocated_timer = get_timer(time);
+ if (!allocated_timer)
+ return NULL;
+
+ ret = request_irq(allocated_timer->irq, fn,
+ IRQF_TRIGGER_LOW, "global-timer", dev);
+ if (ret) {
+ mpic_free_timer(allocated_timer);
+ return NULL;
+ }
+
+ allocated_timer->dev = dev;
+
+ return allocated_timer;
+}
+EXPORT_SYMBOL(mpic_request_timer);
+
+static int timer_group_get_freq(struct device_node *np,
+ struct timer_group_priv *priv)
+{
+ u32 div;
+
+ if (priv->flags & FSL_GLOBAL_TIMER) {
+ struct device_node *dn;
+
+ dn = of_find_compatible_node(NULL, NULL, "fsl,mpic");
+ if (dn) {
+ of_property_read_u32(dn, "clock-frequency",
+ &priv->timerfreq);
+ of_node_put(dn);
+ }
+ }
+
+ if (priv->timerfreq <= 0)
+ return -EINVAL;
+
+ if (priv->flags & FSL_GLOBAL_TIMER) {
+ div = (1 << (MPIC_TIMER_TCR_CLKDIV >> 8)) * 8;
+ priv->timerfreq /= div;
+ }
+
+ return 0;
+}
+
+static int timer_group_get_irq(struct device_node *np,
+ struct timer_group_priv *priv)
+{
+ const u32 all_timer[] = { 0, TIMERS_PER_GROUP };
+ const u32 *p;
+ u32 offset;
+ u32 count;
+
+ unsigned int i;
+ unsigned int j;
+ unsigned int irq_index = 0;
+ unsigned int irq;
+ int len;
+
+ p = of_get_property(np, "fsl,available-ranges", &len);
+ if (p && len % (2 * sizeof(u32)) != 0) {
+ pr_err("%s: malformed available-ranges property.\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ if (!p) {
+ p = all_timer;
+ len = sizeof(all_timer);
+ }
+
+ len /= 2 * sizeof(u32);
+
+ for (i = 0; i < len; i++) {
+ offset = p[i * 2];
+ count = p[i * 2 + 1];
+ for (j = 0; j < count; j++) {
+ irq = irq_of_parse_and_map(np, irq_index);
+ if (!irq) {
+ pr_err("%s: irq parse and map failed.\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ /* Set timer idle */
+ priv->idle |= TIMER_OFFSET((offset + j));
+ priv->timer[offset + j].irq = irq;
+ priv->timer[offset + j].num = offset + j;
+ irq_index++;
+ }
+ }
+
+ return 0;
+}
+
+static void timer_group_init(struct device_node *np)
+{
+ struct timer_group_priv *priv;
+ unsigned int i = 0;
+ int ret;
+
+ priv = kzalloc(sizeof(struct timer_group_priv), GFP_KERNEL);
+ if (!priv) {
+ pr_err("%s: cannot allocate memory for group.\n",
+ np->full_name);
+ return;
+ }
+
+ if (of_device_is_compatible(np, "fsl,mpic-global-timer"))
+ priv->flags |= FSL_GLOBAL_TIMER;
+
+ priv->regs = of_iomap(np, i++);
+ if (!priv->regs) {
+ pr_err("%s: cannot ioremap timer register address.\n",
+ np->full_name);
+ goto out;
+ }
+
+ if (priv->flags & FSL_GLOBAL_TIMER) {
+ priv->group_tcr = of_iomap(np, i++);
+ if (!priv->group_tcr) {
+ pr_err("%s: cannot ioremap tcr address.\n",
+ np->full_name);
+ goto out;
+ }
+ }
+
+ ret = timer_group_get_freq(np, priv);
+ if (ret < 0) {
+ pr_err("%s: cannot get timer frequency.\n", np->full_name);
+ goto out;
+ }
+
+ ret = timer_group_get_irq(np, priv);
+ if (ret < 0) {
+ pr_err("%s: cannot get timer irqs.\n", np->full_name);
+ goto out;
+ }
+
+ spin_lock_init(&priv->lock);
+
+ /* Init FSL timer hardware */
+ if (priv->flags & FSL_GLOBAL_TIMER)
+ setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV);
+
+ list_add_tail(&priv->node, &timer_group_list);
+
+ return;
+
+out:
+ if (priv->regs)
+ iounmap(priv->regs);
+
+ if (priv->group_tcr)
+ iounmap(priv->group_tcr);
+
+ kfree(priv);
+}
+
+static void mpic_timer_resume(void)
+{
+ struct timer_group_priv *priv;
+
+ list_for_each_entry(priv, &timer_group_list, node) {
+ /* Init FSL timer hardware */
+ if (priv->flags & FSL_GLOBAL_TIMER)
+ setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV);
+ }
+}
+
+static const struct of_device_id mpic_timer_ids[] = {
+ { .compatible = "fsl,mpic-global-timer", },
+ {},
+};
+
+static struct syscore_ops mpic_timer_syscore_ops = {
+ .resume = mpic_timer_resume,
+};
+
+static int __init mpic_timer_init(void)
+{
+ struct device_node *np = NULL;
+
+ for_each_matching_node(np, mpic_timer_ids)
+ timer_group_init(np);
+
+ register_syscore_ops(&mpic_timer_syscore_ops);
+
+ if (list_empty(&timer_group_list))
+ return -ENODEV;
+
+ return 0;
+}
+subsys_initcall(mpic_timer_init);
diff --git a/arch/powerpc/sysdev/mpic_u3msi.c b/arch/powerpc/sysdev/mpic_u3msi.c
index 1d5a40899b7..9a7aa0ed9c1 100644
--- a/arch/powerpc/sysdev/mpic_u3msi.c
+++ b/arch/powerpc/sysdev/mpic_u3msi.c
@@ -16,32 +16,33 @@
#include <asm/prom.h>
#include <asm/hw_irq.h>
#include <asm/ppc-pci.h>
+#include <asm/msi_bitmap.h>
#include "mpic.h"
/* A bit ugly, can we get this from the pci_dev somehow? */
static struct mpic *msi_mpic;
-static void mpic_u3msi_mask_irq(unsigned int irq)
+static void mpic_u3msi_mask_irq(struct irq_data *data)
{
- mask_msi_irq(irq);
- mpic_mask_irq(irq);
+ mask_msi_irq(data);
+ mpic_mask_irq(data);
}
-static void mpic_u3msi_unmask_irq(unsigned int irq)
+static void mpic_u3msi_unmask_irq(struct irq_data *data)
{
- mpic_unmask_irq(irq);
- unmask_msi_irq(irq);
+ mpic_unmask_irq(data);
+ unmask_msi_irq(data);
}
static struct irq_chip mpic_u3msi_chip = {
- .shutdown = mpic_u3msi_mask_irq,
- .mask = mpic_u3msi_mask_irq,
- .unmask = mpic_u3msi_unmask_irq,
- .eoi = mpic_end_irq,
- .set_type = mpic_set_irq_type,
- .set_affinity = mpic_set_affinity,
- .typename = "MPIC-U3MSI",
+ .irq_shutdown = mpic_u3msi_mask_irq,
+ .irq_mask = mpic_u3msi_mask_irq,
+ .irq_unmask = mpic_u3msi_unmask_irq,
+ .irq_eoi = mpic_end_irq,
+ .irq_set_type = mpic_set_irq_type,
+ .irq_set_affinity = mpic_set_affinity,
+ .name = "MPIC-U3MSI",
};
static u64 read_ht_magic_addr(struct pci_dev *pdev, unsigned int pos)
@@ -63,12 +64,12 @@ static u64 read_ht_magic_addr(struct pci_dev *pdev, unsigned int pos)
return addr;
}
-static u64 find_ht_magic_addr(struct pci_dev *pdev)
+static u64 find_ht_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
{
struct pci_bus *bus;
unsigned int pos;
- for (bus = pdev->bus; bus; bus = bus->parent) {
+ for (bus = pdev->bus; bus && bus->self; bus = bus->parent) {
pos = pci_find_ht_capability(bus->self, HT_CAPTYPE_MSI_MAPPING);
if (pos)
return read_ht_magic_addr(bus->self, pos);
@@ -77,13 +78,41 @@ static u64 find_ht_magic_addr(struct pci_dev *pdev)
return 0;
}
+static u64 find_u4_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
+{
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+
+ /* U4 PCIe MSIs need to write to the special register in
+ * the bridge that generates interrupts. There should be
+ * theorically a register at 0xf8005000 where you just write
+ * the MSI number and that triggers the right interrupt, but
+ * unfortunately, this is busted in HW, the bridge endian swaps
+ * the value and hits the wrong nibble in the register.
+ *
+ * So instead we use another register set which is used normally
+ * for converting HT interrupts to MPIC interrupts, which decodes
+ * the interrupt number as part of the low address bits
+ *
+ * This will not work if we ever use more than one legacy MSI in
+ * a block but we never do. For one MSI or multiple MSI-X where
+ * each interrupt address can be specified separately, it works
+ * just fine.
+ */
+ if (of_device_is_compatible(hose->dn, "u4-pcie") ||
+ of_device_is_compatible(hose->dn, "U4-pcie"))
+ return 0xf8004000 | (hwirq << 4);
+
+ return 0;
+}
+
static int u3msi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
{
if (type == PCI_CAP_ID_MSIX)
pr_debug("u3msi: MSI-X untested, trying anyway.\n");
/* If we can't find a magic address then MSI ain't gonna work */
- if (find_ht_magic_addr(pdev) == 0) {
+ if (find_ht_magic_addr(pdev, 0) == 0 &&
+ find_u4_magic_addr(pdev, 0) == 0) {
pr_debug("u3msi: no magic address found for %s\n",
pci_name(pdev));
return -ENXIO;
@@ -100,8 +129,9 @@ static void u3msi_teardown_msi_irqs(struct pci_dev *pdev)
if (entry->irq == NO_IRQ)
continue;
- set_irq_msi(entry->irq, NULL);
- mpic_msi_free_hwirqs(msi_mpic, virq_to_hw(entry->irq), 1);
+ irq_set_msi_desc(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap,
+ virq_to_hw(entry->irq), 1);
irq_dispose_mapping(entry->irq);
}
@@ -110,37 +140,41 @@ static void u3msi_teardown_msi_irqs(struct pci_dev *pdev)
static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
{
- irq_hw_number_t hwirq;
unsigned int virq;
struct msi_desc *entry;
struct msi_msg msg;
u64 addr;
-
- addr = find_ht_magic_addr(pdev);
- msg.address_lo = addr & 0xFFFFFFFF;
- msg.address_hi = addr >> 32;
+ int hwirq;
list_for_each_entry(entry, &pdev->msi_list, list) {
- hwirq = mpic_msi_alloc_hwirqs(msi_mpic, 1);
+ hwirq = msi_bitmap_alloc_hwirqs(&msi_mpic->msi_bitmap, 1);
if (hwirq < 0) {
pr_debug("u3msi: failed allocating hwirq\n");
return hwirq;
}
+ addr = find_ht_magic_addr(pdev, hwirq);
+ if (addr == 0)
+ addr = find_u4_magic_addr(pdev, hwirq);
+ msg.address_lo = addr & 0xFFFFFFFF;
+ msg.address_hi = addr >> 32;
+
virq = irq_create_mapping(msi_mpic->irqhost, hwirq);
if (virq == NO_IRQ) {
- pr_debug("u3msi: failed mapping hwirq 0x%lx\n", hwirq);
- mpic_msi_free_hwirqs(msi_mpic, hwirq, 1);
+ pr_debug("u3msi: failed mapping hwirq 0x%x\n", hwirq);
+ msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, hwirq, 1);
return -ENOSPC;
}
- set_irq_msi(virq, entry);
- set_irq_chip(virq, &mpic_u3msi_chip);
- set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
+ irq_set_msi_desc(virq, entry);
+ irq_set_chip(virq, &mpic_u3msi_chip);
+ irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
- pr_debug("u3msi: allocated virq 0x%x (hw 0x%lx) addr 0x%lx\n",
- virq, hwirq, addr);
+ pr_debug("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n",
+ virq, hwirq, (unsigned long)addr);
+ printk("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n",
+ virq, hwirq, (unsigned long)addr);
msg.data = hwirq;
write_msi_msg(virq, &msg);
diff --git a/arch/powerpc/sysdev/msi_bitmap.c b/arch/powerpc/sysdev/msi_bitmap.c
new file mode 100644
index 00000000000..2ff630267e9
--- /dev/null
+++ b/arch/powerpc/sysdev/msi_bitmap.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2006-2008, Michael Ellerman, 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; version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <asm/msi_bitmap.h>
+#include <asm/setup.h>
+
+int msi_bitmap_alloc_hwirqs(struct msi_bitmap *bmp, int num)
+{
+ unsigned long flags;
+ int offset, order = get_count_order(num);
+
+ spin_lock_irqsave(&bmp->lock, flags);
+ /*
+ * This is fast, but stricter than we need. We might want to add
+ * a fallback routine which does a linear search with no alignment.
+ */
+ offset = bitmap_find_free_region(bmp->bitmap, bmp->irq_count, order);
+ spin_unlock_irqrestore(&bmp->lock, flags);
+
+ pr_debug("msi_bitmap: allocated 0x%x (2^%d) at offset 0x%x\n",
+ num, order, offset);
+
+ return offset;
+}
+
+void msi_bitmap_free_hwirqs(struct msi_bitmap *bmp, unsigned int offset,
+ unsigned int num)
+{
+ unsigned long flags;
+ int order = get_count_order(num);
+
+ pr_debug("msi_bitmap: freeing 0x%x (2^%d) at offset 0x%x\n",
+ num, order, offset);
+
+ spin_lock_irqsave(&bmp->lock, flags);
+ bitmap_release_region(bmp->bitmap, offset, order);
+ spin_unlock_irqrestore(&bmp->lock, flags);
+}
+
+void msi_bitmap_reserve_hwirq(struct msi_bitmap *bmp, unsigned int hwirq)
+{
+ unsigned long flags;
+
+ pr_debug("msi_bitmap: reserving hwirq 0x%x\n", hwirq);
+
+ spin_lock_irqsave(&bmp->lock, flags);
+ bitmap_allocate_region(bmp->bitmap, hwirq, 0);
+ spin_unlock_irqrestore(&bmp->lock, flags);
+}
+
+/**
+ * msi_bitmap_reserve_dt_hwirqs - Reserve irqs specified in the device tree.
+ * @bmp: pointer to the MSI bitmap.
+ *
+ * Looks in the device tree to see if there is a property specifying which
+ * irqs can be used for MSI. If found those irqs reserved in the device tree
+ * are reserved in the bitmap.
+ *
+ * Returns 0 for success, < 0 if there was an error, and > 0 if no property
+ * was found in the device tree.
+ **/
+int msi_bitmap_reserve_dt_hwirqs(struct msi_bitmap *bmp)
+{
+ int i, j, len;
+ const u32 *p;
+
+ if (!bmp->of_node)
+ return 1;
+
+ p = of_get_property(bmp->of_node, "msi-available-ranges", &len);
+ if (!p) {
+ pr_debug("msi_bitmap: no msi-available-ranges property " \
+ "found on %s\n", bmp->of_node->full_name);
+ return 1;
+ }
+
+ if (len % (2 * sizeof(u32)) != 0) {
+ printk(KERN_WARNING "msi_bitmap: Malformed msi-available-ranges"
+ " property on %s\n", bmp->of_node->full_name);
+ return -EINVAL;
+ }
+
+ bitmap_allocate_region(bmp->bitmap, 0, get_count_order(bmp->irq_count));
+
+ spin_lock(&bmp->lock);
+
+ /* Format is: (<u32 start> <u32 count>)+ */
+ len /= 2 * sizeof(u32);
+ for (i = 0; i < len; i++, p += 2) {
+ for (j = 0; j < *(p + 1); j++)
+ bitmap_release_region(bmp->bitmap, *p + j, 0);
+ }
+
+ spin_unlock(&bmp->lock);
+
+ return 0;
+}
+
+int msi_bitmap_alloc(struct msi_bitmap *bmp, unsigned int irq_count,
+ struct device_node *of_node)
+{
+ int size;
+
+ if (!irq_count)
+ return -EINVAL;
+
+ size = BITS_TO_LONGS(irq_count) * sizeof(long);
+ pr_debug("msi_bitmap: allocator bitmap size is 0x%x bytes\n", size);
+
+ bmp->bitmap = zalloc_maybe_bootmem(size, GFP_KERNEL);
+ if (!bmp->bitmap) {
+ pr_debug("msi_bitmap: ENOMEM allocating allocator bitmap!\n");
+ return -ENOMEM;
+ }
+
+ /* We zalloc'ed the bitmap, so all irqs are free by default */
+ spin_lock_init(&bmp->lock);
+ bmp->of_node = of_node_get(of_node);
+ bmp->irq_count = irq_count;
+
+ return 0;
+}
+
+void msi_bitmap_free(struct msi_bitmap *bmp)
+{
+ /* we can't free the bitmap we don't know if it's bootmem etc. */
+ of_node_put(bmp->of_node);
+ bmp->bitmap = NULL;
+}
+
+#ifdef CONFIG_MSI_BITMAP_SELFTEST
+
+#define check(x) \
+ if (!(x)) printk("msi_bitmap: test failed at line %d\n", __LINE__);
+
+void __init test_basics(void)
+{
+ struct msi_bitmap bmp;
+ int i, size = 512;
+
+ /* Can't allocate a bitmap of 0 irqs */
+ check(msi_bitmap_alloc(&bmp, 0, NULL) != 0);
+
+ /* of_node may be NULL */
+ check(0 == msi_bitmap_alloc(&bmp, size, NULL));
+
+ /* Should all be free by default */
+ check(0 == bitmap_find_free_region(bmp.bitmap, size,
+ get_count_order(size)));
+ bitmap_release_region(bmp.bitmap, 0, get_count_order(size));
+
+ /* With no node, there's no msi-available-ranges, so expect > 0 */
+ check(msi_bitmap_reserve_dt_hwirqs(&bmp) > 0);
+
+ /* Should all still be free */
+ check(0 == bitmap_find_free_region(bmp.bitmap, size,
+ get_count_order(size)));
+ bitmap_release_region(bmp.bitmap, 0, get_count_order(size));
+
+ /* Check we can fill it up and then no more */
+ for (i = 0; i < size; i++)
+ check(msi_bitmap_alloc_hwirqs(&bmp, 1) >= 0);
+
+ check(msi_bitmap_alloc_hwirqs(&bmp, 1) < 0);
+
+ /* Should all be allocated */
+ check(bitmap_find_free_region(bmp.bitmap, size, 0) < 0);
+
+ /* And if we free one we can then allocate another */
+ msi_bitmap_free_hwirqs(&bmp, size / 2, 1);
+ check(msi_bitmap_alloc_hwirqs(&bmp, 1) == size / 2);
+
+ msi_bitmap_free(&bmp);
+
+ /* Clients may check bitmap == NULL for "not-allocated" */
+ check(bmp.bitmap == NULL);
+
+ kfree(bmp.bitmap);
+}
+
+void __init test_of_node(void)
+{
+ u32 prop_data[] = { 10, 10, 25, 3, 40, 1, 100, 100, 200, 20 };
+ const char *expected_str = "0-9,20-24,28-39,41-99,220-255";
+ char *prop_name = "msi-available-ranges";
+ char *node_name = "/fakenode";
+ struct device_node of_node;
+ struct property prop;
+ struct msi_bitmap bmp;
+ int size = 256;
+ DECLARE_BITMAP(expected, size);
+
+ /* There should really be a struct device_node allocator */
+ memset(&of_node, 0, sizeof(of_node));
+ of_node_init(&of_node);
+ of_node.full_name = node_name;
+
+ check(0 == msi_bitmap_alloc(&bmp, size, &of_node));
+
+ /* No msi-available-ranges, so expect > 0 */
+ check(msi_bitmap_reserve_dt_hwirqs(&bmp) > 0);
+
+ /* Should all still be free */
+ check(0 == bitmap_find_free_region(bmp.bitmap, size,
+ get_count_order(size)));
+ bitmap_release_region(bmp.bitmap, 0, get_count_order(size));
+
+ /* Now create a fake msi-available-ranges property */
+
+ /* There should really .. oh whatever */
+ memset(&prop, 0, sizeof(prop));
+ prop.name = prop_name;
+ prop.value = &prop_data;
+ prop.length = sizeof(prop_data);
+
+ of_node.properties = &prop;
+
+ /* msi-available-ranges, so expect == 0 */
+ check(msi_bitmap_reserve_dt_hwirqs(&bmp) == 0);
+
+ /* Check we got the expected result */
+ check(0 == bitmap_parselist(expected_str, expected, size));
+ check(bitmap_equal(expected, bmp.bitmap, size));
+
+ msi_bitmap_free(&bmp);
+ kfree(bmp.bitmap);
+}
+
+int __init msi_bitmap_selftest(void)
+{
+ printk(KERN_DEBUG "Running MSI bitmap self-tests ...\n");
+
+ test_basics();
+ test_of_node();
+
+ return 0;
+}
+late_initcall(msi_bitmap_selftest);
+#endif /* CONFIG_MSI_BITMAP_SELFTEST */
diff --git a/arch/powerpc/sysdev/mv64x60_dev.c b/arch/powerpc/sysdev/mv64x60_dev.c
index efda0028909..c2dba7db71a 100644
--- a/arch/powerpc/sysdev/mv64x60_dev.c
+++ b/arch/powerpc/sysdev/mv64x60_dev.c
@@ -15,15 +15,18 @@
#include <linux/console.h>
#include <linux/mv643xx.h>
#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_net.h>
+#include <linux/dma-mapping.h>
#include <asm/prom.h>
-/*
- * These functions provide the necessary setup for the mv64x60 drivers.
- * These drivers are unusual in that they work on both the MIPS and PowerPC
- * architectures. Because of that, the drivers do not support the normal
- * PowerPC of_platform_bus_type. They support platform_bus_type instead.
- */
+/* These functions provide the necessary setup for the mv64x60 drivers. */
+
+static struct of_device_id __initdata of_mv64x60_devices[] = {
+ { .compatible = "marvell,mv64306-devctrl", },
+ {}
+};
/*
* Create MPSC platform devices
@@ -127,7 +130,7 @@ static int __init mv64x60_mpsc_device_setup(struct device_node *np, int id)
if (err)
return err;
- prop = of_get_property(np, "block-index", NULL);
+ prop = of_get_property(np, "cell-index", NULL);
if (!prop)
return -ENODEV;
port_number = *(int *)prop;
@@ -136,6 +139,7 @@ static int __init mv64x60_mpsc_device_setup(struct device_node *np, int id)
pdata.cache_mgmt = 1; /* All current revs need this set */
+ pdata.max_idle = 40; /* default */
prop = of_get_property(np, "max_idle", NULL);
if (prop)
pdata.max_idle = *prop;
@@ -182,6 +186,7 @@ static int __init mv64x60_mpsc_device_setup(struct device_node *np, int id)
pdev = platform_device_alloc(MPSC_CTLR_NAME, port_number);
if (!pdev)
return -ENOMEM;
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
err = platform_device_add_resources(pdev, r, 5);
if (err)
@@ -205,30 +210,36 @@ error:
/*
* Create mv64x60_eth platform devices
*/
-static int __init eth_register_shared_pdev(struct device_node *np)
+static struct platform_device * __init mv64x60_eth_register_shared_pdev(
+ struct device_node *np, int id)
{
struct platform_device *pdev;
- struct resource r[1];
+ struct resource r[2];
int err;
- np = of_get_parent(np);
- if (!np)
- return -ENODEV;
-
err = of_address_to_resource(np, 0, &r[0]);
- of_node_put(np);
if (err)
- return err;
+ return ERR_PTR(err);
- pdev = platform_device_register_simple(MV643XX_ETH_SHARED_NAME, 0,
- r, 1);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
+ /* register an orion mdio bus driver */
+ r[1].start = r[0].start + 0x4;
+ r[1].end = r[0].start + 0x84 - 1;
+ r[1].flags = IORESOURCE_MEM;
- return 0;
+ if (id == 0) {
+ pdev = platform_device_register_simple("orion-mdio", -1, &r[1], 1);
+ if (IS_ERR(pdev))
+ return pdev;
+ }
+
+ pdev = platform_device_register_simple(MV643XX_ETH_SHARED_NAME, id,
+ &r[0], 1);
+
+ return pdev;
}
-static int __init mv64x60_eth_device_setup(struct device_node *np, int id)
+static int __init mv64x60_eth_device_setup(struct device_node *np, int id,
+ struct platform_device *shared_pdev)
{
struct resource r[1];
struct mv643xx_eth_platform_data pdata;
@@ -239,16 +250,14 @@ static int __init mv64x60_eth_device_setup(struct device_node *np, int id)
const phandle *ph;
int err;
- /* only register the shared platform device the first time through */
- if (id == 0 && (err = eth_register_shared_pdev(np)))
- return err;
-
memset(r, 0, sizeof(r));
of_irq_to_resource(np, 0, &r[0]);
memset(&pdata, 0, sizeof(pdata));
- prop = of_get_property(np, "block-index", NULL);
+ pdata.shared = shared_pdev;
+
+ prop = of_get_property(np, "reg", NULL);
if (!prop)
return -ENODEV;
pdata.port_number = *prop;
@@ -294,17 +303,16 @@ static int __init mv64x60_eth_device_setup(struct device_node *np, int id)
return -ENODEV;
prop = of_get_property(phy, "reg", NULL);
- if (prop) {
- pdata.force_phy_addr = 1;
- pdata.phy_addr = *prop;
- }
+ if (prop)
+ pdata.phy_addr = MV643XX_ETH_PHY_ADDR(*prop);
of_node_put(phy);
- pdev = platform_device_alloc(MV643XX_ETH_NAME, pdata.port_number);
+ pdev = platform_device_alloc(MV643XX_ETH_NAME, id);
if (!pdev)
return -ENOMEM;
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
err = platform_device_add_resources(pdev, r, 1);
if (err)
goto error;
@@ -345,21 +353,17 @@ static int __init mv64x60_i2c_device_setup(struct device_node *np, int id)
memset(&pdata, 0, sizeof(pdata));
+ pdata.freq_m = 8; /* default */
prop = of_get_property(np, "freq_m", NULL);
- if (!prop)
- return -ENODEV;
- pdata.freq_m = *prop;
+ if (prop)
+ pdata.freq_m = *prop;
+ pdata.freq_n = 3; /* default */
prop = of_get_property(np, "freq_n", NULL);
- if (!prop)
- return -ENODEV;
- pdata.freq_n = *prop;
-
- prop = of_get_property(np, "timeout", NULL);
if (prop)
- pdata.timeout = *prop;
- else
- pdata.timeout = 1000; /* 1 second */
+ pdata.freq_n = *prop;
+
+ pdata.timeout = 1000; /* default: 1 second */
pdev = platform_device_alloc(MV64XXX_I2C_CTLR_NAME, id);
if (!pdev)
@@ -401,10 +405,7 @@ static int __init mv64x60_wdt_device_setup(struct device_node *np, int id)
memset(&pdata, 0, sizeof(pdata));
- prop = of_get_property(np, "timeout", NULL);
- if (!prop)
- return -ENODEV;
- pdata.timeout = *prop;
+ pdata.timeout = 10; /* Default: 10 seconds */
np = of_get_parent(np);
if (!np)
@@ -441,38 +442,68 @@ error:
static int __init mv64x60_device_setup(void)
{
- struct device_node *np = NULL;
- int id;
+ struct device_node *np, *np2;
+ struct platform_device *pdev;
+ int id, id2;
int err;
id = 0;
- for_each_compatible_node(np, "serial", "marvell,mpsc")
- if ((err = mv64x60_mpsc_device_setup(np, id++)))
- goto error;
+ for_each_compatible_node(np, NULL, "marvell,mv64360-mpsc") {
+ err = mv64x60_mpsc_device_setup(np, id++);
+ if (err)
+ printk(KERN_ERR "Failed to initialize MV64x60 "
+ "serial device %s: error %d.\n",
+ np->full_name, err);
+ }
id = 0;
- for_each_compatible_node(np, "network", "marvell,mv64x60-eth")
- if ((err = mv64x60_eth_device_setup(np, id++)))
- goto error;
+ id2 = 0;
+ for_each_compatible_node(np, NULL, "marvell,mv64360-eth-group") {
+ pdev = mv64x60_eth_register_shared_pdev(np, id++);
+ if (IS_ERR(pdev)) {
+ err = PTR_ERR(pdev);
+ printk(KERN_ERR "Failed to initialize MV64x60 "
+ "network block %s: error %d.\n",
+ np->full_name, err);
+ continue;
+ }
+ for_each_child_of_node(np, np2) {
+ if (!of_device_is_compatible(np2,
+ "marvell,mv64360-eth"))
+ continue;
+ err = mv64x60_eth_device_setup(np2, id2++, pdev);
+ if (err)
+ printk(KERN_ERR "Failed to initialize "
+ "MV64x60 network device %s: "
+ "error %d.\n",
+ np2->full_name, err);
+ }
+ }
id = 0;
- for_each_compatible_node(np, "i2c", "marvell,mv64x60-i2c")
- if ((err = mv64x60_i2c_device_setup(np, id++)))
- goto error;
+ for_each_compatible_node(np, "i2c", "marvell,mv64360-i2c") {
+ err = mv64x60_i2c_device_setup(np, id++);
+ if (err)
+ printk(KERN_ERR "Failed to initialize MV64x60 I2C "
+ "bus %s: error %d.\n",
+ np->full_name, err);
+ }
/* support up to one watchdog timer */
- np = of_find_compatible_node(np, NULL, "marvell,mv64x60-wdt");
+ np = of_find_compatible_node(np, NULL, "marvell,mv64360-wdt");
if (np) {
if ((err = mv64x60_wdt_device_setup(np, id)))
- goto error;
+ printk(KERN_ERR "Failed to initialize MV64x60 "
+ "Watchdog %s: error %d.\n",
+ np->full_name, err);
of_node_put(np);
}
- return 0;
+ /* Now add every node that is on the device bus */
+ for_each_compatible_node(np, NULL, "marvell,mv64360")
+ of_platform_bus_probe(np, of_mv64x60_devices, NULL);
-error:
- of_node_put(np);
- return err;
+ return 0;
}
arch_initcall(mv64x60_device_setup);
@@ -489,10 +520,10 @@ static int __init mv64x60_add_mpsc_console(void)
if (!np)
goto not_mpsc;
- if (!of_device_is_compatible(np, "marvell,mpsc"))
+ if (!of_device_is_compatible(np, "marvell,mv64360-mpsc"))
goto not_mpsc;
- prop = of_get_property(np, "block-index", NULL);
+ prop = of_get_property(np, "cell-index", NULL);
if (!prop)
goto not_mpsc;
diff --git a/arch/powerpc/sysdev/mv64x60_pci.c b/arch/powerpc/sysdev/mv64x60_pci.c
index d21ab8fa499..330d56613c5 100644
--- a/arch/powerpc/sysdev/mv64x60_pci.c
+++ b/arch/powerpc/sysdev/mv64x60_pci.c
@@ -12,6 +12,7 @@
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/stat.h>
#include <linux/pci.h>
#include <asm/prom.h>
@@ -24,7 +25,7 @@
#define MV64X60_VAL_LEN_MAX 11
#define MV64X60_PCICFG_CPCI_HOTSWAP 0x68
-static ssize_t mv64x60_hs_reg_read(struct kobject *kobj,
+static ssize_t mv64x60_hs_reg_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
@@ -45,7 +46,7 @@ static ssize_t mv64x60_hs_reg_read(struct kobject *kobj,
return sprintf(buf, "0x%08x\n", v);
}
-static ssize_t mv64x60_hs_reg_write(struct kobject *kobj,
+static ssize_t mv64x60_hs_reg_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
@@ -73,7 +74,6 @@ static struct bin_attribute mv64x60_hs_reg_attr = { /* Hotswap register */
.attr = {
.name = "hs_reg",
.mode = S_IRUGO | S_IWUSR,
- .owner = THIS_MODULE,
},
.size = MV64X60_VAL_LEN_MAX,
.read = mv64x60_hs_reg_read,
@@ -86,14 +86,14 @@ static int __init mv64x60_sysfs_init(void)
struct platform_device *pdev;
const unsigned int *prop;
- np = of_find_compatible_node(NULL, NULL, "marvell,mv64x60");
+ np = of_find_compatible_node(NULL, NULL, "marvell,mv64360");
if (!np)
return 0;
prop = of_get_property(np, "hs_reg_valid", NULL);
of_node_put(np);
- pdev = platform_device_register_simple("marvell,mv64x60", 0, NULL, 0);
+ pdev = platform_device_register_simple("marvell,mv64360", 0, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
@@ -104,7 +104,7 @@ subsys_initcall(mv64x60_sysfs_init);
#endif /* CONFIG_SYSFS */
-static void __init mv64x60_pci_fixup_early(struct pci_dev *dev)
+static void mv64x60_pci_fixup_early(struct pci_dev *dev)
{
/*
* Set the host bridge hdr_type to an invalid value so that
@@ -166,6 +166,6 @@ void __init mv64x60_pci_init(void)
{
struct device_node *np;
- for_each_compatible_node(np, "pci", "marvell,mv64x60-pci")
+ for_each_compatible_node(np, "pci", "marvell,mv64360-pci")
mv64x60_add_bridge(np);
}
diff --git a/arch/powerpc/sysdev/mv64x60_pic.c b/arch/powerpc/sysdev/mv64x60_pic.c
index 19e6ef26379..8848e99a83f 100644
--- a/arch/powerpc/sysdev/mv64x60_pic.c
+++ b/arch/powerpc/sysdev/mv64x60_pic.c
@@ -70,15 +70,15 @@ static u32 mv64x60_cached_low_mask;
static u32 mv64x60_cached_high_mask = MV64X60_HIGH_GPP_GROUPS;
static u32 mv64x60_cached_gpp_mask;
-static struct irq_host *mv64x60_irq_host;
+static struct irq_domain *mv64x60_irq_host;
/*
* mv64x60_chip_low functions
*/
-static void mv64x60_mask_low(unsigned int virq)
+static void mv64x60_mask_low(struct irq_data *d)
{
- int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
+ int level2 = irqd_to_hwirq(d) & MV64x60_LEVEL2_MASK;
unsigned long flags;
spin_lock_irqsave(&mv64x60_lock, flags);
@@ -89,9 +89,9 @@ static void mv64x60_mask_low(unsigned int virq)
(void)in_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_LO);
}
-static void mv64x60_unmask_low(unsigned int virq)
+static void mv64x60_unmask_low(struct irq_data *d)
{
- int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
+ int level2 = irqd_to_hwirq(d) & MV64x60_LEVEL2_MASK;
unsigned long flags;
spin_lock_irqsave(&mv64x60_lock, flags);
@@ -104,18 +104,18 @@ static void mv64x60_unmask_low(unsigned int virq)
static struct irq_chip mv64x60_chip_low = {
.name = "mv64x60_low",
- .mask = mv64x60_mask_low,
- .mask_ack = mv64x60_mask_low,
- .unmask = mv64x60_unmask_low,
+ .irq_mask = mv64x60_mask_low,
+ .irq_mask_ack = mv64x60_mask_low,
+ .irq_unmask = mv64x60_unmask_low,
};
/*
* mv64x60_chip_high functions
*/
-static void mv64x60_mask_high(unsigned int virq)
+static void mv64x60_mask_high(struct irq_data *d)
{
- int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
+ int level2 = irqd_to_hwirq(d) & MV64x60_LEVEL2_MASK;
unsigned long flags;
spin_lock_irqsave(&mv64x60_lock, flags);
@@ -126,9 +126,9 @@ static void mv64x60_mask_high(unsigned int virq)
(void)in_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_HI);
}
-static void mv64x60_unmask_high(unsigned int virq)
+static void mv64x60_unmask_high(struct irq_data *d)
{
- int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
+ int level2 = irqd_to_hwirq(d) & MV64x60_LEVEL2_MASK;
unsigned long flags;
spin_lock_irqsave(&mv64x60_lock, flags);
@@ -141,18 +141,18 @@ static void mv64x60_unmask_high(unsigned int virq)
static struct irq_chip mv64x60_chip_high = {
.name = "mv64x60_high",
- .mask = mv64x60_mask_high,
- .mask_ack = mv64x60_mask_high,
- .unmask = mv64x60_unmask_high,
+ .irq_mask = mv64x60_mask_high,
+ .irq_mask_ack = mv64x60_mask_high,
+ .irq_unmask = mv64x60_unmask_high,
};
/*
* mv64x60_chip_gpp functions
*/
-static void mv64x60_mask_gpp(unsigned int virq)
+static void mv64x60_mask_gpp(struct irq_data *d)
{
- int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
+ int level2 = irqd_to_hwirq(d) & MV64x60_LEVEL2_MASK;
unsigned long flags;
spin_lock_irqsave(&mv64x60_lock, flags);
@@ -163,9 +163,9 @@ static void mv64x60_mask_gpp(unsigned int virq)
(void)in_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_MASK);
}
-static void mv64x60_mask_ack_gpp(unsigned int virq)
+static void mv64x60_mask_ack_gpp(struct irq_data *d)
{
- int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
+ int level2 = irqd_to_hwirq(d) & MV64x60_LEVEL2_MASK;
unsigned long flags;
spin_lock_irqsave(&mv64x60_lock, flags);
@@ -178,9 +178,9 @@ static void mv64x60_mask_ack_gpp(unsigned int virq)
(void)in_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_CAUSE);
}
-static void mv64x60_unmask_gpp(unsigned int virq)
+static void mv64x60_unmask_gpp(struct irq_data *d)
{
- int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
+ int level2 = irqd_to_hwirq(d) & MV64x60_LEVEL2_MASK;
unsigned long flags;
spin_lock_irqsave(&mv64x60_lock, flags);
@@ -193,9 +193,9 @@ static void mv64x60_unmask_gpp(unsigned int virq)
static struct irq_chip mv64x60_chip_gpp = {
.name = "mv64x60_gpp",
- .mask = mv64x60_mask_gpp,
- .mask_ack = mv64x60_mask_ack_gpp,
- .unmask = mv64x60_unmask_gpp,
+ .irq_mask = mv64x60_mask_gpp,
+ .irq_mask_ack = mv64x60_mask_ack_gpp,
+ .irq_unmask = mv64x60_unmask_gpp,
};
/*
@@ -208,21 +208,22 @@ static struct irq_chip *mv64x60_chips[] = {
[MV64x60_LEVEL1_GPP] = &mv64x60_chip_gpp,
};
-static int mv64x60_host_map(struct irq_host *h, unsigned int virq,
+static int mv64x60_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hwirq)
{
int level1;
- get_irq_desc(virq)->status |= IRQ_LEVEL;
+ irq_set_status_flags(virq, IRQ_LEVEL);
level1 = (hwirq & MV64x60_LEVEL1_MASK) >> MV64x60_LEVEL1_OFFSET;
BUG_ON(level1 > MV64x60_LEVEL1_GPP);
- set_irq_chip_and_handler(virq, mv64x60_chips[level1], handle_level_irq);
+ irq_set_chip_and_handler(virq, mv64x60_chips[level1],
+ handle_level_irq);
return 0;
}
-static struct irq_host_ops mv64x60_host_ops = {
+static struct irq_domain_ops mv64x60_host_ops = {
.map = mv64x60_host_map,
};
@@ -238,20 +239,19 @@ void __init mv64x60_init_irq(void)
const unsigned int *reg;
unsigned long flags;
- np = of_find_compatible_node(NULL, NULL, "marvell,mv64x60-gpp");
+ np = of_find_compatible_node(NULL, NULL, "marvell,mv64360-gpp");
reg = of_get_property(np, "reg", &size);
paddr = of_translate_address(np, reg);
mv64x60_gpp_reg_base = ioremap(paddr, reg[1]);
of_node_put(np);
- np = of_find_compatible_node(NULL, NULL, "marvell,mv64x60-pic");
+ np = of_find_compatible_node(NULL, NULL, "marvell,mv64360-pic");
reg = of_get_property(np, "reg", &size);
paddr = of_translate_address(np, reg);
mv64x60_irq_reg_base = ioremap(paddr, reg[1]);
- mv64x60_irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR,
- MV64x60_NUM_IRQS,
- &mv64x60_host_ops, MV64x60_NUM_IRQS);
+ mv64x60_irq_host = irq_domain_add_linear(np, MV64x60_NUM_IRQS,
+ &mv64x60_host_ops, NULL);
spin_lock_irqsave(&mv64x60_lock, flags);
out_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_MASK,
diff --git a/arch/powerpc/sysdev/mv64x60_udbg.c b/arch/powerpc/sysdev/mv64x60_udbg.c
index 35c77c7d061..3b8734b870e 100644
--- a/arch/powerpc/sysdev/mv64x60_udbg.c
+++ b/arch/powerpc/sysdev/mv64x60_udbg.c
@@ -85,7 +85,7 @@ static void mv64x60_udbg_init(void)
if (!stdout)
return;
- for_each_compatible_node(np, "serial", "marvell,mpsc") {
+ for_each_compatible_node(np, NULL, "marvell,mv64360-mpsc") {
if (np == stdout)
break;
}
@@ -94,7 +94,7 @@ static void mv64x60_udbg_init(void)
if (!np)
return;
- block_index = of_get_property(np, "block-index", NULL);
+ block_index = of_get_property(np, "cell-index", NULL);
if (!block_index)
goto error;
@@ -125,11 +125,11 @@ static void mv64x60_udbg_init(void)
of_node_put(np);
- mpsc_base = ioremap(r[0].start, r[0].end - r[0].start + 1);
+ mpsc_base = ioremap(r[0].start, resource_size(&r[0]));
if (!mpsc_base)
return;
- mpsc_intr_cause = ioremap(r[1].start, r[1].end - r[1].start + 1);
+ mpsc_intr_cause = ioremap(r[1].start, resource_size(&r[1]));
if (!mpsc_intr_cause) {
iounmap(mpsc_base);
return;
diff --git a/arch/powerpc/sysdev/of_rtc.c b/arch/powerpc/sysdev/of_rtc.c
index 3d54450640c..6f54b54b132 100644
--- a/arch/powerpc/sysdev/of_rtc.c
+++ b/arch/powerpc/sysdev/of_rtc.c
@@ -11,7 +11,9 @@
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/init.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
+#include <linux/slab.h>
static __initdata struct {
const char *compatible;
diff --git a/arch/powerpc/sysdev/pmi.c b/arch/powerpc/sysdev/pmi.c
index c858749263e..5aaf86c0389 100644
--- a/arch/powerpc/sysdev/pmi.c
+++ b/arch/powerpc/sysdev/pmi.c
@@ -25,8 +25,10 @@
*/
#include <linux/interrupt.h>
+#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
+#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
@@ -42,7 +44,7 @@ struct pmi_data {
struct mutex msg_mutex;
pmi_message_t msg;
struct completion *completion;
- struct of_device *dev;
+ struct platform_device *dev;
int irq;
u8 __iomem *pmi_reg;
struct work_struct work;
@@ -50,7 +52,7 @@ struct pmi_data {
static struct pmi_data *data;
-static int pmi_irq_handler(int irq, void *dev_id)
+static irqreturn_t pmi_irq_handler(int irq, void *dev_id)
{
u8 type;
int rc;
@@ -113,17 +115,16 @@ static void pmi_notify_handlers(struct work_struct *work)
spin_lock(&data->handler_spinlock);
list_for_each_entry(handler, &data->handler, node) {
- pr_debug(KERN_INFO "pmi: notifying handler %p\n", handler);
+ pr_debug("pmi: notifying handler %p\n", handler);
if (handler->type == data->msg.type)
handler->handle_pmi_message(data->msg);
}
spin_unlock(&data->handler_spinlock);
}
-static int pmi_of_probe(struct of_device *dev,
- const struct of_device_id *match)
+static int pmi_of_probe(struct platform_device *dev)
{
- struct device_node *np = dev->node;
+ struct device_node *np = dev->dev.of_node;
int rc;
if (data) {
@@ -184,7 +185,7 @@ out:
return rc;
}
-static int pmi_of_remove(struct of_device *dev)
+static int pmi_of_remove(struct platform_device *dev)
{
struct pmi_handler *handler, *tmp;
@@ -204,26 +205,16 @@ static int pmi_of_remove(struct of_device *dev)
return 0;
}
-static struct of_platform_driver pmi_of_platform_driver = {
- .match_table = pmi_match,
+static struct platform_driver pmi_of_platform_driver = {
.probe = pmi_of_probe,
.remove = pmi_of_remove,
- .driver = {
- .name = "pmi",
+ .driver = {
+ .name = "pmi",
+ .owner = THIS_MODULE,
+ .of_match_table = pmi_match,
},
};
-
-static int __init pmi_module_init(void)
-{
- return of_register_platform_driver(&pmi_of_platform_driver);
-}
-module_init(pmi_module_init);
-
-static void __exit pmi_module_exit(void)
-{
- of_unregister_platform_driver(&pmi_of_platform_driver);
-}
-module_exit(pmi_module_exit);
+module_platform_driver(pmi_of_platform_driver);
int pmi_send_message(pmi_message_t msg)
{
diff --git a/arch/powerpc/sysdev/ppc4xx_cpm.c b/arch/powerpc/sysdev/ppc4xx_cpm.c
new file mode 100644
index 00000000000..82e2cfe35c6
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_cpm.c
@@ -0,0 +1,346 @@
+/*
+ * PowerPC 4xx Clock and Power Management
+ *
+ * Copyright (C) 2010, Applied Micro Circuits Corporation
+ * Victor Gallardo (vgallardo@apm.com)
+ *
+ * Based on arch/powerpc/platforms/44x/idle.c:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * Copyright 2008 IBM Corp.
+ *
+ * Based on arch/powerpc/sysdev/fsl_pmc.c:
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ * Copyright 2009 MontaVista Software, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/sysfs.h>
+#include <linux/cpu.h>
+#include <linux/suspend.h>
+#include <asm/dcr.h>
+#include <asm/dcr-native.h>
+#include <asm/machdep.h>
+
+#define CPM_ER 0
+#define CPM_FR 1
+#define CPM_SR 2
+
+#define CPM_IDLE_WAIT 0
+#define CPM_IDLE_DOZE 1
+
+struct cpm {
+ dcr_host_t dcr_host;
+ unsigned int dcr_offset[3];
+ unsigned int powersave_off;
+ unsigned int unused;
+ unsigned int idle_doze;
+ unsigned int standby;
+ unsigned int suspend;
+};
+
+static struct cpm cpm;
+
+struct cpm_idle_mode {
+ unsigned int enabled;
+ const char *name;
+};
+
+static struct cpm_idle_mode idle_mode[] = {
+ [CPM_IDLE_WAIT] = { 1, "wait" }, /* default */
+ [CPM_IDLE_DOZE] = { 0, "doze" },
+};
+
+static unsigned int cpm_set(unsigned int cpm_reg, unsigned int mask)
+{
+ unsigned int value;
+
+ /* CPM controller supports 3 different types of sleep interface
+ * known as class 1, 2 and 3. For class 1 units, they are
+ * unconditionally put to sleep when the corresponding CPM bit is
+ * set. For class 2 and 3 units this is not case; if they can be
+ * put to to sleep, they will. Here we do not verify, we just
+ * set them and expect them to eventually go off when they can.
+ */
+ value = dcr_read(cpm.dcr_host, cpm.dcr_offset[cpm_reg]);
+ dcr_write(cpm.dcr_host, cpm.dcr_offset[cpm_reg], value | mask);
+
+ /* return old state, to restore later if needed */
+ return value;
+}
+
+static void cpm_idle_wait(void)
+{
+ unsigned long msr_save;
+
+ /* save off initial state */
+ msr_save = mfmsr();
+ /* sync required when CPM0_ER[CPU] is set */
+ mb();
+ /* set wait state MSR */
+ mtmsr(msr_save|MSR_WE|MSR_EE|MSR_CE|MSR_DE);
+ isync();
+ /* return to initial state */
+ mtmsr(msr_save);
+ isync();
+}
+
+static void cpm_idle_sleep(unsigned int mask)
+{
+ unsigned int er_save;
+
+ /* update CPM_ER state */
+ er_save = cpm_set(CPM_ER, mask);
+
+ /* go to wait state so that CPM0_ER[CPU] can take effect */
+ cpm_idle_wait();
+
+ /* restore CPM_ER state */
+ dcr_write(cpm.dcr_host, cpm.dcr_offset[CPM_ER], er_save);
+}
+
+static void cpm_idle_doze(void)
+{
+ cpm_idle_sleep(cpm.idle_doze);
+}
+
+static void cpm_idle_config(int mode)
+{
+ int i;
+
+ if (idle_mode[mode].enabled)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(idle_mode); i++)
+ idle_mode[i].enabled = 0;
+
+ idle_mode[mode].enabled = 1;
+}
+
+static ssize_t cpm_idle_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ char *s = buf;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(idle_mode); i++) {
+ if (idle_mode[i].enabled)
+ s += sprintf(s, "[%s] ", idle_mode[i].name);
+ else
+ s += sprintf(s, "%s ", idle_mode[i].name);
+ }
+
+ *(s-1) = '\n'; /* convert the last space to a newline */
+
+ return s - buf;
+}
+
+static ssize_t cpm_idle_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ int i;
+ char *p;
+ int len;
+
+ p = memchr(buf, '\n', n);
+ len = p ? p - buf : n;
+
+ for (i = 0; i < ARRAY_SIZE(idle_mode); i++) {
+ if (strncmp(buf, idle_mode[i].name, len) == 0) {
+ cpm_idle_config(i);
+ return n;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static struct kobj_attribute cpm_idle_attr =
+ __ATTR(idle, 0644, cpm_idle_show, cpm_idle_store);
+
+static void cpm_idle_config_sysfs(void)
+{
+ struct device *dev;
+ unsigned long ret;
+
+ dev = get_cpu_device(0);
+
+ ret = sysfs_create_file(&dev->kobj,
+ &cpm_idle_attr.attr);
+ if (ret)
+ printk(KERN_WARNING
+ "cpm: failed to create idle sysfs entry\n");
+}
+
+static void cpm_idle(void)
+{
+ if (idle_mode[CPM_IDLE_DOZE].enabled)
+ cpm_idle_doze();
+ else
+ cpm_idle_wait();
+}
+
+static int cpm_suspend_valid(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ return !!cpm.standby;
+ case PM_SUSPEND_MEM:
+ return !!cpm.suspend;
+ default:
+ return 0;
+ }
+}
+
+static void cpm_suspend_standby(unsigned int mask)
+{
+ unsigned long tcr_save;
+
+ /* disable decrement interrupt */
+ tcr_save = mfspr(SPRN_TCR);
+ mtspr(SPRN_TCR, tcr_save & ~TCR_DIE);
+
+ /* go to sleep state */
+ cpm_idle_sleep(mask);
+
+ /* restore decrement interrupt */
+ mtspr(SPRN_TCR, tcr_save);
+}
+
+static int cpm_suspend_enter(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ cpm_suspend_standby(cpm.standby);
+ break;
+ case PM_SUSPEND_MEM:
+ cpm_suspend_standby(cpm.suspend);
+ break;
+ }
+
+ return 0;
+}
+
+static struct platform_suspend_ops cpm_suspend_ops = {
+ .valid = cpm_suspend_valid,
+ .enter = cpm_suspend_enter,
+};
+
+static int cpm_get_uint_property(struct device_node *np,
+ const char *name)
+{
+ int len;
+ const unsigned int *prop = of_get_property(np, name, &len);
+
+ if (prop == NULL || len < sizeof(u32))
+ return 0;
+
+ return *prop;
+}
+
+static int __init cpm_init(void)
+{
+ struct device_node *np;
+ int dcr_base, dcr_len;
+ int ret = 0;
+
+ if (!cpm.powersave_off) {
+ cpm_idle_config(CPM_IDLE_WAIT);
+ ppc_md.power_save = &cpm_idle;
+ }
+
+ np = of_find_compatible_node(NULL, NULL, "ibm,cpm");
+ if (!np) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dcr_base = dcr_resource_start(np, 0);
+ dcr_len = dcr_resource_len(np, 0);
+
+ if (dcr_base == 0 || dcr_len == 0) {
+ printk(KERN_ERR "cpm: could not parse dcr property for %s\n",
+ np->full_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cpm.dcr_host = dcr_map(np, dcr_base, dcr_len);
+
+ if (!DCR_MAP_OK(cpm.dcr_host)) {
+ printk(KERN_ERR "cpm: failed to map dcr property for %s\n",
+ np->full_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* All 4xx SoCs with a CPM controller have one of two
+ * different order for the CPM registers. Some have the
+ * CPM registers in the following order (ER,FR,SR). The
+ * others have them in the following order (SR,ER,FR).
+ */
+
+ if (cpm_get_uint_property(np, "er-offset") == 0) {
+ cpm.dcr_offset[CPM_ER] = 0;
+ cpm.dcr_offset[CPM_FR] = 1;
+ cpm.dcr_offset[CPM_SR] = 2;
+ } else {
+ cpm.dcr_offset[CPM_ER] = 1;
+ cpm.dcr_offset[CPM_FR] = 2;
+ cpm.dcr_offset[CPM_SR] = 0;
+ }
+
+ /* Now let's see what IPs to turn off for the following modes */
+
+ cpm.unused = cpm_get_uint_property(np, "unused-units");
+ cpm.idle_doze = cpm_get_uint_property(np, "idle-doze");
+ cpm.standby = cpm_get_uint_property(np, "standby");
+ cpm.suspend = cpm_get_uint_property(np, "suspend");
+
+ /* If some IPs are unused let's turn them off now */
+
+ if (cpm.unused) {
+ cpm_set(CPM_ER, cpm.unused);
+ cpm_set(CPM_FR, cpm.unused);
+ }
+
+ /* Now let's export interfaces */
+
+ if (!cpm.powersave_off && cpm.idle_doze)
+ cpm_idle_config_sysfs();
+
+ if (cpm.standby || cpm.suspend)
+ suspend_set_ops(&cpm_suspend_ops);
+out:
+ if (np)
+ of_node_put(np);
+ return ret;
+}
+
+late_initcall(cpm_init);
+
+static int __init cpm_powersave_off(char *arg)
+{
+ cpm.powersave_off = 1;
+ return 0;
+}
+__setup("powersave=off", cpm_powersave_off);
diff --git a/arch/powerpc/sysdev/ppc4xx_gpio.c b/arch/powerpc/sysdev/ppc4xx_gpio.c
new file mode 100644
index 00000000000..fc65ad1b329
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_gpio.c
@@ -0,0 +1,215 @@
+/*
+ * PPC4xx gpio driver
+ *
+ * Copyright (c) 2008 Harris Corporation
+ * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *
+ * Author: Steve Falco <sfalco@harris.com>
+ *
+ * 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.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+#define GPIO_MASK(gpio) (0x80000000 >> (gpio))
+#define GPIO_MASK2(gpio) (0xc0000000 >> ((gpio) * 2))
+
+/* Physical GPIO register layout */
+struct ppc4xx_gpio {
+ __be32 or;
+ __be32 tcr;
+ __be32 osrl;
+ __be32 osrh;
+ __be32 tsrl;
+ __be32 tsrh;
+ __be32 odr;
+ __be32 ir;
+ __be32 rr1;
+ __be32 rr2;
+ __be32 rr3;
+ __be32 reserved1;
+ __be32 isr1l;
+ __be32 isr1h;
+ __be32 isr2l;
+ __be32 isr2h;
+ __be32 isr3l;
+ __be32 isr3h;
+};
+
+struct ppc4xx_gpio_chip {
+ struct of_mm_gpio_chip mm_gc;
+ spinlock_t lock;
+};
+
+/*
+ * GPIO LIB API implementation for GPIOs
+ *
+ * There are a maximum of 32 gpios in each gpio controller.
+ */
+
+static inline struct ppc4xx_gpio_chip *
+to_ppc4xx_gpiochip(struct of_mm_gpio_chip *mm_gc)
+{
+ return container_of(mm_gc, struct ppc4xx_gpio_chip, mm_gc);
+}
+
+static int ppc4xx_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct ppc4xx_gpio __iomem *regs = mm_gc->regs;
+
+ return in_be32(&regs->ir) & GPIO_MASK(gpio);
+}
+
+static inline void
+__ppc4xx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct ppc4xx_gpio __iomem *regs = mm_gc->regs;
+
+ if (val)
+ setbits32(&regs->or, GPIO_MASK(gpio));
+ else
+ clrbits32(&regs->or, GPIO_MASK(gpio));
+}
+
+static void
+ppc4xx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct ppc4xx_gpio_chip *chip = to_ppc4xx_gpiochip(mm_gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ __ppc4xx_gpio_set(gc, gpio, val);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+}
+
+static int ppc4xx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct ppc4xx_gpio_chip *chip = to_ppc4xx_gpiochip(mm_gc);
+ struct ppc4xx_gpio __iomem *regs = mm_gc->regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ /* Disable open-drain function */
+ clrbits32(&regs->odr, GPIO_MASK(gpio));
+
+ /* Float the pin */
+ clrbits32(&regs->tcr, GPIO_MASK(gpio));
+
+ /* Bits 0-15 use TSRL/OSRL, bits 16-31 use TSRH/OSRH */
+ if (gpio < 16) {
+ clrbits32(&regs->osrl, GPIO_MASK2(gpio));
+ clrbits32(&regs->tsrl, GPIO_MASK2(gpio));
+ } else {
+ clrbits32(&regs->osrh, GPIO_MASK2(gpio));
+ clrbits32(&regs->tsrh, GPIO_MASK2(gpio));
+ }
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int
+ppc4xx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct ppc4xx_gpio_chip *chip = to_ppc4xx_gpiochip(mm_gc);
+ struct ppc4xx_gpio __iomem *regs = mm_gc->regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ /* First set initial value */
+ __ppc4xx_gpio_set(gc, gpio, val);
+
+ /* Disable open-drain function */
+ clrbits32(&regs->odr, GPIO_MASK(gpio));
+
+ /* Drive the pin */
+ setbits32(&regs->tcr, GPIO_MASK(gpio));
+
+ /* Bits 0-15 use TSRL, bits 16-31 use TSRH */
+ if (gpio < 16) {
+ clrbits32(&regs->osrl, GPIO_MASK2(gpio));
+ clrbits32(&regs->tsrl, GPIO_MASK2(gpio));
+ } else {
+ clrbits32(&regs->osrh, GPIO_MASK2(gpio));
+ clrbits32(&regs->tsrh, GPIO_MASK2(gpio));
+ }
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
+
+ return 0;
+}
+
+static int __init ppc4xx_add_gpiochips(void)
+{
+ struct device_node *np;
+
+ for_each_compatible_node(np, NULL, "ibm,ppc4xx-gpio") {
+ int ret;
+ struct ppc4xx_gpio_chip *ppc4xx_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ struct gpio_chip *gc;
+
+ ppc4xx_gc = kzalloc(sizeof(*ppc4xx_gc), GFP_KERNEL);
+ if (!ppc4xx_gc) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ spin_lock_init(&ppc4xx_gc->lock);
+
+ mm_gc = &ppc4xx_gc->mm_gc;
+ gc = &mm_gc->gc;
+
+ gc->ngpio = 32;
+ gc->direction_input = ppc4xx_gpio_dir_in;
+ gc->direction_output = ppc4xx_gpio_dir_out;
+ gc->get = ppc4xx_gpio_get;
+ gc->set = ppc4xx_gpio_set;
+
+ ret = of_mm_gpiochip_add(np, mm_gc);
+ if (ret)
+ goto err;
+ continue;
+err:
+ pr_err("%s: registration failed with status %d\n",
+ np->full_name, ret);
+ kfree(ppc4xx_gc);
+ /* try others anyway */
+ }
+ return 0;
+}
+arch_initcall(ppc4xx_add_gpiochips);
diff --git a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c
new file mode 100644
index 00000000000..11c888416f0
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c
@@ -0,0 +1,215 @@
+/*
+ * MSI support for PPC4xx SoCs using High Speed Transfer Assist (HSTA) for
+ * generation of the interrupt.
+ *
+ * Copyright © 2013 Alistair Popple <alistair@popple.id.au> 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/semaphore.h>
+#include <asm/msi_bitmap.h>
+
+struct ppc4xx_hsta_msi {
+ struct device *dev;
+
+ /* The ioremapped HSTA MSI IO space */
+ u32 __iomem *data;
+
+ /* Physical address of HSTA MSI IO space */
+ u64 address;
+ struct msi_bitmap bmp;
+
+ /* An array mapping offsets to hardware IRQs */
+ int *irq_map;
+
+ /* Number of hwirqs supported */
+ int irq_count;
+};
+static struct ppc4xx_hsta_msi ppc4xx_hsta_msi;
+
+static int hsta_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+ struct msi_msg msg;
+ struct msi_desc *entry;
+ int irq, hwirq;
+ u64 addr;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ irq = msi_bitmap_alloc_hwirqs(&ppc4xx_hsta_msi.bmp, 1);
+ if (irq < 0) {
+ pr_debug("%s: Failed to allocate msi interrupt\n",
+ __func__);
+ return irq;
+ }
+
+ hwirq = ppc4xx_hsta_msi.irq_map[irq];
+ if (hwirq == NO_IRQ) {
+ pr_err("%s: Failed mapping irq %d\n", __func__, irq);
+ return -EINVAL;
+ }
+
+ /*
+ * HSTA generates interrupts on writes to 128-bit aligned
+ * addresses.
+ */
+ addr = ppc4xx_hsta_msi.address + irq*0x10;
+ msg.address_hi = upper_32_bits(addr);
+ msg.address_lo = lower_32_bits(addr);
+
+ /* Data is not used by the HSTA. */
+ msg.data = 0;
+
+ pr_debug("%s: Setup irq %d (0x%0llx)\n", __func__, hwirq,
+ (((u64) msg.address_hi) << 32) | msg.address_lo);
+
+ if (irq_set_msi_desc(hwirq, entry)) {
+ pr_err(
+ "%s: Invalid hwirq %d specified in device tree\n",
+ __func__, hwirq);
+ msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1);
+ return -EINVAL;
+ }
+ write_msi_msg(hwirq, &msg);
+ }
+
+ return 0;
+}
+
+static int hsta_find_hwirq_offset(int hwirq)
+{
+ int irq;
+
+ /* Find the offset given the hwirq */
+ for (irq = 0; irq < ppc4xx_hsta_msi.irq_count; irq++)
+ if (ppc4xx_hsta_msi.irq_map[irq] == hwirq)
+ return irq;
+
+ return -EINVAL;
+}
+
+static void hsta_teardown_msi_irqs(struct pci_dev *dev)
+{
+ struct msi_desc *entry;
+ int irq;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (entry->irq == NO_IRQ)
+ continue;
+
+ irq = hsta_find_hwirq_offset(entry->irq);
+
+ /* entry->irq should always be in irq_map */
+ BUG_ON(irq < 0);
+ irq_set_msi_desc(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1);
+ pr_debug("%s: Teardown IRQ %u (index %u)\n", __func__,
+ entry->irq, irq);
+ }
+}
+
+static int hsta_msi_check_device(struct pci_dev *pdev, int nvec, int type)
+{
+ /* We don't support MSI-X */
+ if (type == PCI_CAP_ID_MSIX) {
+ pr_debug("%s: MSI-X not supported.\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hsta_msi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *mem;
+ int irq, ret, irq_count;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(mem)) {
+ dev_err(dev, "Unable to get mmio space\n");
+ return -EINVAL;
+ }
+
+ irq_count = of_irq_count(dev->of_node);
+ if (!irq_count) {
+ dev_err(dev, "Unable to find IRQ range\n");
+ return -EINVAL;
+ }
+
+ ppc4xx_hsta_msi.dev = dev;
+ ppc4xx_hsta_msi.address = mem->start;
+ ppc4xx_hsta_msi.data = ioremap(mem->start, resource_size(mem));
+ ppc4xx_hsta_msi.irq_count = irq_count;
+ if (IS_ERR(ppc4xx_hsta_msi.data)) {
+ dev_err(dev, "Unable to map memory\n");
+ return -ENOMEM;
+ }
+
+ ret = msi_bitmap_alloc(&ppc4xx_hsta_msi.bmp, irq_count, dev->of_node);
+ if (ret)
+ goto out;
+
+ ppc4xx_hsta_msi.irq_map = kmalloc(sizeof(int) * irq_count, GFP_KERNEL);
+ if (IS_ERR(ppc4xx_hsta_msi.irq_map)) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ /* Setup a mapping from irq offsets to hardware irq numbers */
+ for (irq = 0; irq < irq_count; irq++) {
+ ppc4xx_hsta_msi.irq_map[irq] =
+ irq_of_parse_and_map(dev->of_node, irq);
+ if (ppc4xx_hsta_msi.irq_map[irq] == NO_IRQ) {
+ dev_err(dev, "Unable to map IRQ\n");
+ ret = -EINVAL;
+ goto out2;
+ }
+ }
+
+ ppc_md.setup_msi_irqs = hsta_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = hsta_teardown_msi_irqs;
+ ppc_md.msi_check_device = hsta_msi_check_device;
+ return 0;
+
+out2:
+ kfree(ppc4xx_hsta_msi.irq_map);
+
+out1:
+ msi_bitmap_free(&ppc4xx_hsta_msi.bmp);
+
+out:
+ iounmap(ppc4xx_hsta_msi.data);
+ return ret;
+}
+
+static const struct of_device_id hsta_msi_ids[] = {
+ {
+ .compatible = "ibm,hsta-msi",
+ },
+ {}
+};
+
+static struct platform_driver hsta_msi_driver = {
+ .probe = hsta_msi_probe,
+ .driver = {
+ .name = "hsta-msi",
+ .owner = THIS_MODULE,
+ .of_match_table = hsta_msi_ids,
+ },
+};
+
+static int hsta_msi_init(void)
+{
+ return platform_driver_register(&hsta_msi_driver);
+}
+subsys_initcall(hsta_msi_init);
diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c
new file mode 100644
index 00000000000..43948da837a
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_msi.c
@@ -0,0 +1,291 @@
+/*
+ * Adding PCI-E MSI support for PPC4XX SoCs.
+ *
+ * Copyright (c) 2010, Applied Micro Circuits Corporation
+ * Authors: Tirumala R Marri <tmarri@apm.com>
+ * Feng Kan <fkan@apm.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 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/irq.h>
+#include <linux/bootmem.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <asm/prom.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+#include <asm/msi_bitmap.h>
+
+#define PEIH_TERMADH 0x00
+#define PEIH_TERMADL 0x08
+#define PEIH_MSIED 0x10
+#define PEIH_MSIMK 0x18
+#define PEIH_MSIASS 0x20
+#define PEIH_FLUSH0 0x30
+#define PEIH_FLUSH1 0x38
+#define PEIH_CNTRST 0x48
+
+static int msi_irqs;
+
+struct ppc4xx_msi {
+ u32 msi_addr_lo;
+ u32 msi_addr_hi;
+ void __iomem *msi_regs;
+ int *msi_virqs;
+ struct msi_bitmap bitmap;
+ struct device_node *msi_dev;
+};
+
+static struct ppc4xx_msi ppc4xx_msi;
+
+static int ppc4xx_msi_init_allocator(struct platform_device *dev,
+ struct ppc4xx_msi *msi_data)
+{
+ int err;
+
+ err = msi_bitmap_alloc(&msi_data->bitmap, msi_irqs,
+ dev->dev.of_node);
+ if (err)
+ return err;
+
+ err = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap);
+ if (err < 0) {
+ msi_bitmap_free(&msi_data->bitmap);
+ return err;
+ }
+
+ return 0;
+}
+
+static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+ int int_no = -ENOMEM;
+ unsigned int virq;
+ struct msi_msg msg;
+ struct msi_desc *entry;
+ struct ppc4xx_msi *msi_data = &ppc4xx_msi;
+
+ msi_data->msi_virqs = kmalloc((msi_irqs) * sizeof(int),
+ GFP_KERNEL);
+ if (!msi_data->msi_virqs)
+ return -ENOMEM;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ int_no = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
+ if (int_no >= 0)
+ break;
+ if (int_no < 0) {
+ pr_debug("%s: fail allocating msi interrupt\n",
+ __func__);
+ }
+ virq = irq_of_parse_and_map(msi_data->msi_dev, int_no);
+ if (virq == NO_IRQ) {
+ dev_err(&dev->dev, "%s: fail mapping irq\n", __func__);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap, int_no, 1);
+ return -ENOSPC;
+ }
+ dev_dbg(&dev->dev, "%s: virq = %d\n", __func__, virq);
+
+ /* Setup msi address space */
+ msg.address_hi = msi_data->msi_addr_hi;
+ msg.address_lo = msi_data->msi_addr_lo;
+
+ irq_set_msi_desc(virq, entry);
+ msg.data = int_no;
+ write_msi_msg(virq, &msg);
+ }
+ return 0;
+}
+
+void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
+{
+ struct msi_desc *entry;
+ struct ppc4xx_msi *msi_data = &ppc4xx_msi;
+
+ dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n");
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (entry->irq == NO_IRQ)
+ continue;
+ irq_set_msi_desc(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap,
+ virq_to_hw(entry->irq), 1);
+ irq_dispose_mapping(entry->irq);
+ }
+}
+
+static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type)
+{
+ dev_dbg(&pdev->dev, "PCIE-MSI:%s called. vec %x type %d\n",
+ __func__, nvec, type);
+ if (type == PCI_CAP_ID_MSIX)
+ pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n");
+
+ return 0;
+}
+
+static int ppc4xx_setup_pcieh_hw(struct platform_device *dev,
+ struct resource res, struct ppc4xx_msi *msi)
+{
+ const u32 *msi_data;
+ const u32 *msi_mask;
+ const u32 *sdr_addr;
+ dma_addr_t msi_phys;
+ void *msi_virt;
+
+ sdr_addr = of_get_property(dev->dev.of_node, "sdr-base", NULL);
+ if (!sdr_addr)
+ return -1;
+
+ mtdcri(SDR0, *sdr_addr, upper_32_bits(res.start)); /*HIGH addr */
+ mtdcri(SDR0, *sdr_addr + 1, lower_32_bits(res.start)); /* Low addr */
+
+ msi->msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi");
+ if (!msi->msi_dev)
+ return -ENODEV;
+
+ msi->msi_regs = of_iomap(msi->msi_dev, 0);
+ if (!msi->msi_regs) {
+ dev_err(&dev->dev, "of_iomap problem failed\n");
+ return -ENOMEM;
+ }
+ dev_dbg(&dev->dev, "PCIE-MSI: msi register mapped 0x%x 0x%x\n",
+ (u32) (msi->msi_regs + PEIH_TERMADH), (u32) (msi->msi_regs));
+
+ msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL);
+ if (!msi_virt)
+ return -ENOMEM;
+ msi->msi_addr_hi = upper_32_bits(msi_phys);
+ msi->msi_addr_lo = lower_32_bits(msi_phys & 0xffffffff);
+ dev_dbg(&dev->dev, "PCIE-MSI: msi address high 0x%x, low 0x%x\n",
+ msi->msi_addr_hi, msi->msi_addr_lo);
+
+ /* Progam the Interrupt handler Termination addr registers */
+ out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi);
+ out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo);
+
+ msi_data = of_get_property(dev->dev.of_node, "msi-data", NULL);
+ if (!msi_data)
+ return -1;
+ msi_mask = of_get_property(dev->dev.of_node, "msi-mask", NULL);
+ if (!msi_mask)
+ return -1;
+ /* Program MSI Expected data and Mask bits */
+ out_be32(msi->msi_regs + PEIH_MSIED, *msi_data);
+ out_be32(msi->msi_regs + PEIH_MSIMK, *msi_mask);
+
+ dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys);
+
+ return 0;
+}
+
+static int ppc4xx_of_msi_remove(struct platform_device *dev)
+{
+ struct ppc4xx_msi *msi = dev->dev.platform_data;
+ int i;
+ int virq;
+
+ for (i = 0; i < msi_irqs; i++) {
+ virq = msi->msi_virqs[i];
+ if (virq != NO_IRQ)
+ irq_dispose_mapping(virq);
+ }
+
+ if (msi->bitmap.bitmap)
+ msi_bitmap_free(&msi->bitmap);
+ iounmap(msi->msi_regs);
+ of_node_put(msi->msi_dev);
+ kfree(msi);
+
+ return 0;
+}
+
+static int ppc4xx_msi_probe(struct platform_device *dev)
+{
+ struct ppc4xx_msi *msi;
+ struct resource res;
+ int err = 0;
+
+ dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n");
+
+ msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL);
+ if (!msi) {
+ dev_err(&dev->dev, "No memory for MSI structure\n");
+ return -ENOMEM;
+ }
+ dev->dev.platform_data = msi;
+
+ /* Get MSI ranges */
+ err = of_address_to_resource(dev->dev.of_node, 0, &res);
+ if (err) {
+ dev_err(&dev->dev, "%s resource error!\n",
+ dev->dev.of_node->full_name);
+ goto error_out;
+ }
+
+ msi_irqs = of_irq_count(dev->dev.of_node);
+ if (!msi_irqs)
+ return -ENODEV;
+
+ if (ppc4xx_setup_pcieh_hw(dev, res, msi))
+ goto error_out;
+
+ err = ppc4xx_msi_init_allocator(dev, msi);
+ if (err) {
+ dev_err(&dev->dev, "Error allocating MSI bitmap\n");
+ goto error_out;
+ }
+ ppc4xx_msi = *msi;
+
+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
+ ppc_md.msi_check_device = ppc4xx_msi_check_device;
+ return err;
+
+error_out:
+ ppc4xx_of_msi_remove(dev);
+ return err;
+}
+static const struct of_device_id ppc4xx_msi_ids[] = {
+ {
+ .compatible = "amcc,ppc4xx-msi",
+ },
+ {}
+};
+static struct platform_driver ppc4xx_msi_driver = {
+ .probe = ppc4xx_msi_probe,
+ .remove = ppc4xx_of_msi_remove,
+ .driver = {
+ .name = "ppc4xx-msi",
+ .owner = THIS_MODULE,
+ .of_match_table = ppc4xx_msi_ids,
+ },
+
+};
+
+static __init int ppc4xx_msi_init(void)
+{
+ return platform_driver_register(&ppc4xx_msi_driver);
+}
+
+subsys_initcall(ppc4xx_msi_init);
diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c b/arch/powerpc/sysdev/ppc4xx_ocm.c
new file mode 100644
index 00000000000..85d9e37f5cc
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_ocm.c
@@ -0,0 +1,416 @@
+/*
+ * PowerPC 4xx OCM memory allocation support
+ *
+ * (C) Copyright 2009, Applied Micro Circuits Corporation
+ * Victor Gallardo (vgallardo@amcc.com)
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/rheap.h>
+#include <asm/ppc4xx_ocm.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+
+#define OCM_DISABLED 0
+#define OCM_ENABLED 1
+
+struct ocm_block {
+ struct list_head list;
+ void __iomem *addr;
+ int size;
+ const char *owner;
+};
+
+/* non-cached or cached region */
+struct ocm_region {
+ phys_addr_t phys;
+ void __iomem *virt;
+
+ int memtotal;
+ int memfree;
+
+ rh_info_t *rh;
+ struct list_head list;
+};
+
+struct ocm_info {
+ int index;
+ int status;
+ int ready;
+
+ phys_addr_t phys;
+
+ int alignment;
+ int memtotal;
+ int cache_size;
+
+ struct ocm_region nc; /* non-cached region */
+ struct ocm_region c; /* cached region */
+};
+
+static struct ocm_info *ocm_nodes;
+static int ocm_count;
+
+static struct ocm_info *ocm_get_node(unsigned int index)
+{
+ if (index >= ocm_count) {
+ printk(KERN_ERR "PPC4XX OCM: invalid index");
+ return NULL;
+ }
+
+ return &ocm_nodes[index];
+}
+
+static int ocm_free_region(struct ocm_region *ocm_reg, const void *addr)
+{
+ struct ocm_block *blk, *tmp;
+ unsigned long offset;
+
+ if (!ocm_reg->virt)
+ return 0;
+
+ list_for_each_entry_safe(blk, tmp, &ocm_reg->list, list) {
+ if (blk->addr == addr) {
+ offset = addr - ocm_reg->virt;
+ ocm_reg->memfree += blk->size;
+ rh_free(ocm_reg->rh, offset);
+ list_del(&blk->list);
+ kfree(blk);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void __init ocm_init_node(int count, struct device_node *node)
+{
+ struct ocm_info *ocm;
+
+ const unsigned int *cell_index;
+ const unsigned int *cache_size;
+ int len;
+
+ struct resource rsrc;
+ int ioflags;
+
+ ocm = ocm_get_node(count);
+
+ cell_index = of_get_property(node, "cell-index", &len);
+ if (!cell_index) {
+ printk(KERN_ERR "PPC4XX OCM: missing cell-index property");
+ return;
+ }
+ ocm->index = *cell_index;
+
+ if (of_device_is_available(node))
+ ocm->status = OCM_ENABLED;
+
+ cache_size = of_get_property(node, "cached-region-size", &len);
+ if (cache_size)
+ ocm->cache_size = *cache_size;
+
+ if (of_address_to_resource(node, 0, &rsrc)) {
+ printk(KERN_ERR "PPC4XX OCM%d: could not get resource address\n",
+ ocm->index);
+ return;
+ }
+
+ ocm->phys = rsrc.start;
+ ocm->memtotal = (rsrc.end - rsrc.start + 1);
+
+ printk(KERN_INFO "PPC4XX OCM%d: %d Bytes (%s)\n",
+ ocm->index, ocm->memtotal,
+ (ocm->status == OCM_DISABLED) ? "disabled" : "enabled");
+
+ if (ocm->status == OCM_DISABLED)
+ return;
+
+ /* request region */
+
+ if (!request_mem_region(ocm->phys, ocm->memtotal, "ppc4xx_ocm")) {
+ printk(KERN_ERR "PPC4XX OCM%d: could not request region\n",
+ ocm->index);
+ return;
+ }
+
+ /* Configure non-cached and cached regions */
+
+ ocm->nc.phys = ocm->phys;
+ ocm->nc.memtotal = ocm->memtotal - ocm->cache_size;
+ ocm->nc.memfree = ocm->nc.memtotal;
+
+ ocm->c.phys = ocm->phys + ocm->nc.memtotal;
+ ocm->c.memtotal = ocm->cache_size;
+ ocm->c.memfree = ocm->c.memtotal;
+
+ if (ocm->nc.memtotal == 0)
+ ocm->nc.phys = 0;
+
+ if (ocm->c.memtotal == 0)
+ ocm->c.phys = 0;
+
+ printk(KERN_INFO "PPC4XX OCM%d: %d Bytes (non-cached)\n",
+ ocm->index, ocm->nc.memtotal);
+
+ printk(KERN_INFO "PPC4XX OCM%d: %d Bytes (cached)\n",
+ ocm->index, ocm->c.memtotal);
+
+ /* ioremap the non-cached region */
+ if (ocm->nc.memtotal) {
+ ioflags = _PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_EXEC;
+ ocm->nc.virt = __ioremap(ocm->nc.phys, ocm->nc.memtotal,
+ ioflags);
+
+ if (!ocm->nc.virt) {
+ printk(KERN_ERR
+ "PPC4XX OCM%d: failed to ioremap non-cached memory\n",
+ ocm->index);
+ ocm->nc.memfree = 0;
+ return;
+ }
+ }
+
+ /* ioremap the cached region */
+
+ if (ocm->c.memtotal) {
+ ioflags = _PAGE_EXEC;
+ ocm->c.virt = __ioremap(ocm->c.phys, ocm->c.memtotal,
+ ioflags);
+
+ if (!ocm->c.virt) {
+ printk(KERN_ERR
+ "PPC4XX OCM%d: failed to ioremap cached memory\n",
+ ocm->index);
+ ocm->c.memfree = 0;
+ return;
+ }
+ }
+
+ /* Create Remote Heaps */
+
+ ocm->alignment = 4; /* default 4 byte alignment */
+
+ if (ocm->nc.virt) {
+ ocm->nc.rh = rh_create(ocm->alignment);
+ rh_attach_region(ocm->nc.rh, 0, ocm->nc.memtotal);
+ }
+
+ if (ocm->c.virt) {
+ ocm->c.rh = rh_create(ocm->alignment);
+ rh_attach_region(ocm->c.rh, 0, ocm->c.memtotal);
+ }
+
+ INIT_LIST_HEAD(&ocm->nc.list);
+ INIT_LIST_HEAD(&ocm->c.list);
+
+ ocm->ready = 1;
+
+ return;
+}
+
+static int ocm_debugfs_show(struct seq_file *m, void *v)
+{
+ struct ocm_block *blk, *tmp;
+ unsigned int i;
+
+ for (i = 0; i < ocm_count; i++) {
+ struct ocm_info *ocm = ocm_get_node(i);
+
+ if (!ocm || !ocm->ready)
+ continue;
+
+ seq_printf(m, "PPC4XX OCM : %d\n", ocm->index);
+ seq_printf(m, "PhysAddr : 0x%llx\n", ocm->phys);
+ seq_printf(m, "MemTotal : %d Bytes\n", ocm->memtotal);
+ seq_printf(m, "MemTotal(NC) : %d Bytes\n", ocm->nc.memtotal);
+ seq_printf(m, "MemTotal(C) : %d Bytes\n", ocm->c.memtotal);
+
+ seq_printf(m, "\n");
+
+ seq_printf(m, "NC.PhysAddr : 0x%llx\n", ocm->nc.phys);
+ seq_printf(m, "NC.VirtAddr : 0x%p\n", ocm->nc.virt);
+ seq_printf(m, "NC.MemTotal : %d Bytes\n", ocm->nc.memtotal);
+ seq_printf(m, "NC.MemFree : %d Bytes\n", ocm->nc.memfree);
+
+ list_for_each_entry_safe(blk, tmp, &ocm->nc.list, list) {
+ seq_printf(m, "NC.MemUsed : %d Bytes (%s)\n",
+ blk->size, blk->owner);
+ }
+
+ seq_printf(m, "\n");
+
+ seq_printf(m, "C.PhysAddr : 0x%llx\n", ocm->c.phys);
+ seq_printf(m, "C.VirtAddr : 0x%p\n", ocm->c.virt);
+ seq_printf(m, "C.MemTotal : %d Bytes\n", ocm->c.memtotal);
+ seq_printf(m, "C.MemFree : %d Bytes\n", ocm->c.memfree);
+
+ list_for_each_entry_safe(blk, tmp, &ocm->c.list, list) {
+ seq_printf(m, "C.MemUsed : %d Bytes (%s)\n",
+ blk->size, blk->owner);
+ }
+
+ seq_printf(m, "\n");
+ }
+
+ return 0;
+}
+
+static int ocm_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ocm_debugfs_show, NULL);
+}
+
+static const struct file_operations ocm_debugfs_fops = {
+ .open = ocm_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ocm_debugfs_init(void)
+{
+ struct dentry *junk;
+
+ junk = debugfs_create_dir("ppc4xx_ocm", 0);
+ if (!junk) {
+ printk(KERN_ALERT "debugfs ppc4xx ocm: failed to create dir\n");
+ return -1;
+ }
+
+ if (debugfs_create_file("info", 0644, junk, NULL, &ocm_debugfs_fops)) {
+ printk(KERN_ALERT "debugfs ppc4xx ocm: failed to create file\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void *ppc4xx_ocm_alloc(phys_addr_t *phys, int size, int align,
+ int flags, const char *owner)
+{
+ void __iomem *addr = NULL;
+ unsigned long offset;
+ struct ocm_info *ocm;
+ struct ocm_region *ocm_reg;
+ struct ocm_block *ocm_blk;
+ int i;
+
+ for (i = 0; i < ocm_count; i++) {
+ ocm = ocm_get_node(i);
+
+ if (!ocm || !ocm->ready)
+ continue;
+
+ if (flags == PPC4XX_OCM_NON_CACHED)
+ ocm_reg = &ocm->nc;
+ else
+ ocm_reg = &ocm->c;
+
+ if (!ocm_reg->virt)
+ continue;
+
+ if (align < ocm->alignment)
+ align = ocm->alignment;
+
+ offset = rh_alloc_align(ocm_reg->rh, size, align, NULL);
+
+ if (IS_ERR_VALUE(offset))
+ continue;
+
+ ocm_blk = kzalloc(sizeof(struct ocm_block), GFP_KERNEL);
+ if (!ocm_blk) {
+ printk(KERN_ERR "PPC4XX OCM: could not allocate ocm block");
+ rh_free(ocm_reg->rh, offset);
+ break;
+ }
+
+ *phys = ocm_reg->phys + offset;
+ addr = ocm_reg->virt + offset;
+ size = ALIGN(size, align);
+
+ ocm_blk->addr = addr;
+ ocm_blk->size = size;
+ ocm_blk->owner = owner;
+ list_add_tail(&ocm_blk->list, &ocm_reg->list);
+
+ ocm_reg->memfree -= size;
+
+ break;
+ }
+
+ return addr;
+}
+
+void ppc4xx_ocm_free(const void *addr)
+{
+ int i;
+
+ if (!addr)
+ return;
+
+ for (i = 0; i < ocm_count; i++) {
+ struct ocm_info *ocm = ocm_get_node(i);
+
+ if (!ocm || !ocm->ready)
+ continue;
+
+ if (ocm_free_region(&ocm->nc, addr) ||
+ ocm_free_region(&ocm->c, addr))
+ return;
+ }
+}
+
+static int __init ppc4xx_ocm_init(void)
+{
+ struct device_node *np;
+ int count;
+
+ count = 0;
+ for_each_compatible_node(np, NULL, "ibm,ocm")
+ count++;
+
+ if (!count)
+ return 0;
+
+ ocm_nodes = kzalloc((count * sizeof(struct ocm_info)), GFP_KERNEL);
+ if (!ocm_nodes) {
+ printk(KERN_ERR "PPC4XX OCM: failed to allocate OCM nodes!\n");
+ return -ENOMEM;
+ }
+
+ ocm_count = count;
+ count = 0;
+
+ for_each_compatible_node(np, NULL, "ibm,ocm") {
+ ocm_init_node(count, np);
+ count++;
+ }
+
+ ocm_debugfs_init();
+
+ return 0;
+}
+
+arch_initcall(ppc4xx_ocm_init);
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index 5abfcd15748..df6e2fc4ff9 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -24,30 +24,26 @@
#include <linux/of.h>
#include <linux/bootmem.h>
#include <linux/delay.h>
+#include <linux/slab.h>
#include <asm/io.h>
#include <asm/pci-bridge.h>
#include <asm/machdep.h>
#include <asm/dcr.h>
#include <asm/dcr-regs.h>
+#include <mm/mmu_decl.h>
#include "ppc4xx_pci.h"
static int dma_offset_set;
-/* Move that to a useable header */
-extern unsigned long total_memory;
-
#define U64_TO_U32_LOW(val) ((u32)((val) & 0x00000000ffffffffULL))
#define U64_TO_U32_HIGH(val) ((u32)((val) >> 32))
-#ifdef CONFIG_RESOURCES_64BIT
-#define RES_TO_U32_LOW(val) U64_TO_U32_LOW(val)
-#define RES_TO_U32_HIGH(val) U64_TO_U32_HIGH(val)
-#else
-#define RES_TO_U32_LOW(val) (val)
-#define RES_TO_U32_HIGH(val) (0)
-#endif
+#define RES_TO_U32_LOW(val) \
+ ((sizeof(resource_size_t) > sizeof(u32)) ? U64_TO_U32_LOW(val) : (val))
+#define RES_TO_U32_HIGH(val) \
+ ((sizeof(resource_size_t) > sizeof(u32)) ? U64_TO_U32_HIGH(val) : (0))
static inline int ppc440spe_revA(void)
{
@@ -75,6 +71,11 @@ static void fixup_ppc4xx_pci_bridge(struct pci_dev *dev)
!of_device_is_compatible(hose->dn, "ibm,plb-pci"))
return;
+ if (of_device_is_compatible(hose->dn, "ibm,plb440epx-pci") ||
+ of_device_is_compatible(hose->dn, "ibm,plb440grx-pci")) {
+ hose->indirect_type |= PPC_INDIRECT_TYPE_BROKEN_MRM;
+ }
+
/* Hide the PCI host BARs from the kernel as their content doesn't
* fit well in the resource management
*/
@@ -100,7 +101,8 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
/* Default */
res->start = 0;
- res->end = size = 0x80000000;
+ size = 0x80000000;
+ res->end = size - 1;
res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
/* Get dma-ranges property */
@@ -140,12 +142,11 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
/* Use that */
res->start = pci_addr;
-#ifndef CONFIG_RESOURCES_64BIT
/* Beware of 32 bits resources */
- if ((pci_addr + size) > 0x100000000ull)
+ if (sizeof(resource_size_t) == sizeof(u32) &&
+ (pci_addr + size) > 0x100000000ull)
res->end = 0xffffffff;
else
-#endif
res->end = res->start + size - 1;
break;
}
@@ -162,21 +163,25 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
*/
if (size < total_memory) {
printk(KERN_ERR "%s: dma-ranges too small "
- "(size=%llx total_memory=%lx)\n",
- hose->dn->full_name, size, total_memory);
+ "(size=%llx total_memory=%llx)\n",
+ hose->dn->full_name, size, (u64)total_memory);
return -ENXIO;
}
/* Check we are a power of 2 size and that base is a multiple of size*/
- if (!is_power_of_2(size) ||
+ if ((size & (size - 1)) != 0 ||
(res->start & (size - 1)) != 0) {
printk(KERN_ERR "%s: dma-ranges unaligned\n",
hose->dn->full_name);
return -ENXIO;
}
- /* Check that we are fully contained within 32 bits space */
- if (res->end > 0xffffffff) {
+ /* Check that we are fully contained within 32 bits space if we are not
+ * running on a 460sx or 476fpe which have 64 bit bus addresses.
+ */
+ if (res->end > 0xffffffff &&
+ !(of_device_is_compatible(hose->dn, "ibm,plb-pciex-460sx")
+ || of_device_is_compatible(hose->dn, "ibm,plb-pciex-476fpe"))) {
printk(KERN_ERR "%s: dma-ranges outside of 32 bits space\n",
hose->dn->full_name);
return -ENXIO;
@@ -184,9 +189,15 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
out:
dma_offset_set = 1;
pci_dram_offset = res->start;
+ hose->dma_window_base_cur = res->start;
+ hose->dma_window_size = resource_size(res);
printk(KERN_INFO "4xx PCI DMA offset set to 0x%08lx\n",
pci_dram_offset);
+ printk(KERN_INFO "4xx PCI DMA window base to 0x%016llx\n",
+ (unsigned long long)hose->dma_window_base_cur);
+ printk(KERN_INFO "DMA window size 0x%016llx\n",
+ (unsigned long long)hose->dma_window_size);
return 0;
}
@@ -194,15 +205,63 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
* 4xx PCI 2.x part
*/
+static int __init ppc4xx_setup_one_pci_PMM(struct pci_controller *hose,
+ void __iomem *reg,
+ u64 plb_addr,
+ u64 pci_addr,
+ u64 size,
+ unsigned int flags,
+ int index)
+{
+ u32 ma, pcila, pciha;
+
+ /* Hack warning ! The "old" PCI 2.x cell only let us configure the low
+ * 32-bit of incoming PLB addresses. The top 4 bits of the 36-bit
+ * address are actually hard wired to a value that appears to depend
+ * on the specific SoC. For example, it's 0 on 440EP and 1 on 440EPx.
+ *
+ * The trick here is we just crop those top bits and ignore them when
+ * programming the chip. That means the device-tree has to be right
+ * for the specific part used (we don't print a warning if it's wrong
+ * but on the other hand, you'll crash quickly enough), but at least
+ * this code should work whatever the hard coded value is
+ */
+ plb_addr &= 0xffffffffull;
+
+ /* Note: Due to the above hack, the test below doesn't actually test
+ * if you address is above 4G, but it tests that address and
+ * (address + size) are both contained in the same 4G
+ */
+ if ((plb_addr + size) > 0xffffffffull || !is_power_of_2(size) ||
+ size < 0x1000 || (plb_addr & (size - 1)) != 0) {
+ printk(KERN_WARNING "%s: Resource out of range\n",
+ hose->dn->full_name);
+ return -1;
+ }
+ ma = (0xffffffffu << ilog2(size)) | 1;
+ if (flags & IORESOURCE_PREFETCH)
+ ma |= 2;
+
+ pciha = RES_TO_U32_HIGH(pci_addr);
+ pcila = RES_TO_U32_LOW(pci_addr);
+
+ writel(plb_addr, reg + PCIL0_PMM0LA + (0x10 * index));
+ writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * index));
+ writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * index));
+ writel(ma, reg + PCIL0_PMM0MA + (0x10 * index));
+
+ return 0;
+}
+
static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
void __iomem *reg)
{
- u32 la, ma, pcila, pciha;
- int i, j;
+ int i, j, found_isa_hole = 0;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
+ resource_size_t offset = hose->mem_offset[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
@@ -213,35 +272,36 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
break;
}
- /* Calculate register values */
- la = res->start;
- pciha = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
- pcila = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
-
- ma = res->end + 1 - res->start;
- if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) {
- printk(KERN_WARNING "%s: Resource out of range\n",
- hose->dn->full_name);
- continue;
+ /* Configure the resource */
+ if (ppc4xx_setup_one_pci_PMM(hose, reg,
+ res->start,
+ res->start - offset,
+ resource_size(res),
+ res->flags,
+ j) == 0) {
+ j++;
+
+ /* If the resource PCI address is 0 then we have our
+ * ISA memory hole
+ */
+ if (res->start == offset)
+ found_isa_hole = 1;
}
- ma = (0xffffffffu << ilog2(ma)) | 0x1;
- if (res->flags & IORESOURCE_PREFETCH)
- ma |= 0x2;
-
- /* Program register values */
- writel(la, reg + PCIL0_PMM0LA + (0x10 * j));
- writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * j));
- writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * j));
- writel(ma, reg + PCIL0_PMM0MA + (0x10 * j));
- j++;
}
+
+ /* Handle ISA memory hole if not already covered */
+ if (j <= 2 && !found_isa_hole && hose->isa_mem_size)
+ if (ppc4xx_setup_one_pci_PMM(hose, reg, hose->isa_mem_phys, 0,
+ hose->isa_mem_size, 0, j) == 0)
+ printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
+ hose->dn->full_name);
}
static void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose,
void __iomem *reg,
const struct resource *res)
{
- resource_size_t size = res->end - res->start + 1;
+ resource_size_t size = resource_size(res);
u32 sa;
/* Calculate window size */
@@ -272,9 +332,16 @@ static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
const int *bus_range;
int primary = 0;
+ /* Check if device is enabled */
+ if (!of_device_is_available(np)) {
+ printk(KERN_INFO "%s: Port disabled via device-tree\n",
+ np->full_name);
+ return;
+ }
+
/* Fetch config space registers address */
if (of_address_to_resource(np, 0, &rsrc_cfg)) {
- printk(KERN_ERR "%s:Can't get PCI config register base !",
+ printk(KERN_ERR "%s: Can't get PCI config register base !",
np->full_name);
return;
}
@@ -293,7 +360,7 @@ static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
bus_range = of_get_property(np, "bus-range", NULL);
/* Map registers */
- reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start);
+ reg = ioremap(rsrc_reg.start, resource_size(&rsrc_reg));
if (reg == NULL) {
printk(KERN_ERR "%s: Can't map registers !", np->full_name);
goto fail;
@@ -345,15 +412,57 @@ static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
* 4xx PCI-X part
*/
+static int __init ppc4xx_setup_one_pcix_POM(struct pci_controller *hose,
+ void __iomem *reg,
+ u64 plb_addr,
+ u64 pci_addr,
+ u64 size,
+ unsigned int flags,
+ int index)
+{
+ u32 lah, lal, pciah, pcial, sa;
+
+ if (!is_power_of_2(size) || size < 0x1000 ||
+ (plb_addr & (size - 1)) != 0) {
+ printk(KERN_WARNING "%s: Resource out of range\n",
+ hose->dn->full_name);
+ return -1;
+ }
+
+ /* Calculate register values */
+ lah = RES_TO_U32_HIGH(plb_addr);
+ lal = RES_TO_U32_LOW(plb_addr);
+ pciah = RES_TO_U32_HIGH(pci_addr);
+ pcial = RES_TO_U32_LOW(pci_addr);
+ sa = (0xffffffffu << ilog2(size)) | 0x1;
+
+ /* Program register values */
+ if (index == 0) {
+ writel(lah, reg + PCIX0_POM0LAH);
+ writel(lal, reg + PCIX0_POM0LAL);
+ writel(pciah, reg + PCIX0_POM0PCIAH);
+ writel(pcial, reg + PCIX0_POM0PCIAL);
+ writel(sa, reg + PCIX0_POM0SA);
+ } else {
+ writel(lah, reg + PCIX0_POM1LAH);
+ writel(lal, reg + PCIX0_POM1LAL);
+ writel(pciah, reg + PCIX0_POM1PCIAH);
+ writel(pcial, reg + PCIX0_POM1PCIAL);
+ writel(sa, reg + PCIX0_POM1SA);
+ }
+
+ return 0;
+}
+
static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
void __iomem *reg)
{
- u32 lah, lal, pciah, pcial, sa;
- int i, j;
+ int i, j, found_isa_hole = 0;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
+ resource_size_t offset = hose->mem_offset[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
@@ -364,36 +473,29 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
break;
}
- /* Calculate register values */
- lah = RES_TO_U32_HIGH(res->start);
- lal = RES_TO_U32_LOW(res->start);
- pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
- pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
- sa = res->end + 1 - res->start;
- if (!is_power_of_2(sa) || sa < 0x100000 ||
- sa > 0xffffffffu) {
- printk(KERN_WARNING "%s: Resource out of range\n",
- hose->dn->full_name);
- continue;
+ /* Configure the resource */
+ if (ppc4xx_setup_one_pcix_POM(hose, reg,
+ res->start,
+ res->start - offset,
+ resource_size(res),
+ res->flags,
+ j) == 0) {
+ j++;
+
+ /* If the resource PCI address is 0 then we have our
+ * ISA memory hole
+ */
+ if (res->start == offset)
+ found_isa_hole = 1;
}
- sa = (0xffffffffu << ilog2(sa)) | 0x1;
-
- /* Program register values */
- if (j == 0) {
- writel(lah, reg + PCIX0_POM0LAH);
- writel(lal, reg + PCIX0_POM0LAL);
- writel(pciah, reg + PCIX0_POM0PCIAH);
- writel(pcial, reg + PCIX0_POM0PCIAL);
- writel(sa, reg + PCIX0_POM0SA);
- } else {
- writel(lah, reg + PCIX0_POM1LAH);
- writel(lal, reg + PCIX0_POM1LAL);
- writel(pciah, reg + PCIX0_POM1PCIAH);
- writel(pcial, reg + PCIX0_POM1PCIAL);
- writel(sa, reg + PCIX0_POM1SA);
- }
- j++;
}
+
+ /* Handle ISA memory hole if not already covered */
+ if (j <= 1 && !found_isa_hole && hose->isa_mem_size)
+ if (ppc4xx_setup_one_pcix_POM(hose, reg, hose->isa_mem_phys, 0,
+ hose->isa_mem_size, 0, j) == 0)
+ printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
+ hose->dn->full_name);
}
static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose,
@@ -402,7 +504,7 @@ static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose,
int big_pim,
int enable_msi_hole)
{
- resource_size_t size = res->end - res->start + 1;
+ resource_size_t size = resource_size(res);
u32 sa;
/* RAM is always at 0 */
@@ -465,7 +567,7 @@ static void __init ppc4xx_probe_pcix_bridge(struct device_node *np)
bus_range = of_get_property(np, "bus-range", NULL);
/* Map registers */
- reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start);
+ reg = ioremap(rsrc_reg.start, resource_size(&rsrc_reg));
if (reg == NULL) {
printk(KERN_ERR "%s: Can't map registers !", np->full_name);
goto fail;
@@ -480,7 +582,8 @@ static void __init ppc4xx_probe_pcix_bridge(struct device_node *np)
hose->last_busno = bus_range ? bus_range[1] : 0xff;
/* Setup config space */
- setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0);
+ setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4,
+ PPC_INDIRECT_TYPE_SET_CFG_TYPE);
/* Disable all windows */
writel(0, reg + PCIX0_POM0SA);
@@ -527,6 +630,7 @@ static void __init ppc4xx_probe_pcix_bridge(struct device_node *np)
*
* ibm,plb-pciex-440spe
* ibm,plb-pciex-405ex
+ * ibm,plb-pciex-460ex
*
* Anything else will be rejected for now as they are all subtly
* different unfortunately.
@@ -555,13 +659,77 @@ static unsigned int ppc4xx_pciex_port_count;
struct ppc4xx_pciex_hwops
{
+ bool want_sdr;
int (*core_init)(struct device_node *np);
int (*port_init_hw)(struct ppc4xx_pciex_port *port);
int (*setup_utl)(struct ppc4xx_pciex_port *port);
+ void (*check_link)(struct ppc4xx_pciex_port *port);
};
static struct ppc4xx_pciex_hwops *ppc4xx_pciex_hwops;
+static int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port,
+ unsigned int sdr_offset,
+ unsigned int mask,
+ unsigned int value,
+ int timeout_ms)
+{
+ u32 val;
+
+ while(timeout_ms--) {
+ val = mfdcri(SDR0, port->sdr_base + sdr_offset);
+ if ((val & mask) == value) {
+ pr_debug("PCIE%d: Wait on SDR %x success with tm %d (%08x)\n",
+ port->index, sdr_offset, timeout_ms, val);
+ return 0;
+ }
+ msleep(1);
+ }
+ return -1;
+}
+
+static int __init ppc4xx_pciex_port_reset_sdr(struct ppc4xx_pciex_port *port)
+{
+ /* Wait for reset to complete */
+ if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) {
+ printk(KERN_WARNING "PCIE%d: PGRST failed\n",
+ port->index);
+ return -1;
+ }
+ return 0;
+}
+
+
+static void __init ppc4xx_pciex_check_link_sdr(struct ppc4xx_pciex_port *port)
+{
+ printk(KERN_INFO "PCIE%d: Checking link...\n", port->index);
+
+ /* Check for card presence detect if supported, if not, just wait for
+ * link unconditionally.
+ *
+ * note that we don't fail if there is no link, we just filter out
+ * config space accesses. That way, it will be easier to implement
+ * hotplug later on.
+ */
+ if (!port->has_ibpre ||
+ !ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
+ 1 << 28, 1 << 28, 100)) {
+ printk(KERN_INFO
+ "PCIE%d: Device detected, waiting for link...\n",
+ port->index);
+ if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
+ 0x1000, 0x1000, 2000))
+ printk(KERN_WARNING
+ "PCIE%d: Link up failed\n", port->index);
+ else {
+ printk(KERN_INFO
+ "PCIE%d: link is up !\n", port->index);
+ port->link = 1;
+ }
+ } else
+ printk(KERN_INFO "PCIE%d: No device detected.\n", port->index);
+}
+
#ifdef CONFIG_44x
/* Check various reset bits of the 440SPe PCIe core */
@@ -645,7 +813,7 @@ static int __init ppc440spe_pciex_core_init(struct device_node *np)
int time_out = 20;
/* Set PLL clock receiver to LVPECL */
- mtdcri(SDR0, PESDR0_PLLLCT1, mfdcri(SDR0, PESDR0_PLLLCT1) | 1 << 28);
+ dcri_clrset(SDR0, PESDR0_PLLLCT1, 0, 1 << 28);
/* Shouldn't we do all the calibration stuff etc... here ? */
if (ppc440spe_pciex_check_reset(np))
@@ -659,8 +827,7 @@ static int __init ppc440spe_pciex_core_init(struct device_node *np)
}
/* De-assert reset of PCIe PLL, wait for lock */
- mtdcri(SDR0, PESDR0_PLLLCT1,
- mfdcri(SDR0, PESDR0_PLLLCT1) & ~(1 << 24));
+ dcri_clrset(SDR0, PESDR0_PLLLCT1, 1 << 24, 0);
udelay(3);
while (time_out) {
@@ -680,7 +847,7 @@ static int __init ppc440spe_pciex_core_init(struct device_node *np)
return 3;
}
-static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+static int __init ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
{
u32 val = 1 << 24;
@@ -712,19 +879,18 @@ static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL7SET1,
0x35000000);
}
- val = mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET);
- mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
- (val & ~(1 << 24 | 1 << 16)) | 1 << 12);
+ dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET,
+ (1 << 24) | (1 << 16), 1 << 12);
- return 0;
+ return ppc4xx_pciex_port_reset_sdr(port);
}
-static int ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+static int __init ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
{
return ppc440spe_pciex_init_port_hw(port);
}
-static int ppc440speB_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+static int __init ppc440speB_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
{
int rc = ppc440spe_pciex_init_port_hw(port);
@@ -763,16 +929,347 @@ static int ppc440speB_pciex_init_utl(struct ppc4xx_pciex_port *port)
static struct ppc4xx_pciex_hwops ppc440speA_pcie_hwops __initdata =
{
+ .want_sdr = true,
.core_init = ppc440spe_pciex_core_init,
.port_init_hw = ppc440speA_pciex_init_port_hw,
.setup_utl = ppc440speA_pciex_init_utl,
+ .check_link = ppc4xx_pciex_check_link_sdr,
};
static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata =
{
+ .want_sdr = true,
.core_init = ppc440spe_pciex_core_init,
.port_init_hw = ppc440speB_pciex_init_port_hw,
.setup_utl = ppc440speB_pciex_init_utl,
+ .check_link = ppc4xx_pciex_check_link_sdr,
+};
+
+static int __init ppc460ex_pciex_core_init(struct device_node *np)
+{
+ /* Nothing to do, return 2 ports */
+ return 2;
+}
+
+static int __init ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+{
+ u32 val;
+ u32 utlset1;
+
+ if (port->endpoint)
+ val = PTYPE_LEGACY_ENDPOINT << 20;
+ else
+ val = PTYPE_ROOT_PORT << 20;
+
+ if (port->index == 0) {
+ val |= LNKW_X1 << 12;
+ utlset1 = 0x20000000;
+ } else {
+ val |= LNKW_X4 << 12;
+ utlset1 = 0x20101101;
+ }
+
+ mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val);
+ mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, utlset1);
+ mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01210000);
+
+ switch (port->index) {
+ case 0:
+ mtdcri(SDR0, PESDR0_460EX_L0CDRCTL, 0x00003230);
+ mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130);
+ mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006);
+
+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST,0x10000000);
+ break;
+
+ case 1:
+ mtdcri(SDR0, PESDR1_460EX_L0CDRCTL, 0x00003230);
+ mtdcri(SDR0, PESDR1_460EX_L1CDRCTL, 0x00003230);
+ mtdcri(SDR0, PESDR1_460EX_L2CDRCTL, 0x00003230);
+ mtdcri(SDR0, PESDR1_460EX_L3CDRCTL, 0x00003230);
+ mtdcri(SDR0, PESDR1_460EX_L0DRV, 0x00000130);
+ mtdcri(SDR0, PESDR1_460EX_L1DRV, 0x00000130);
+ mtdcri(SDR0, PESDR1_460EX_L2DRV, 0x00000130);
+ mtdcri(SDR0, PESDR1_460EX_L3DRV, 0x00000130);
+ mtdcri(SDR0, PESDR1_460EX_L0CLK, 0x00000006);
+ mtdcri(SDR0, PESDR1_460EX_L1CLK, 0x00000006);
+ mtdcri(SDR0, PESDR1_460EX_L2CLK, 0x00000006);
+ mtdcri(SDR0, PESDR1_460EX_L3CLK, 0x00000006);
+
+ mtdcri(SDR0, PESDR1_460EX_PHY_CTL_RST,0x10000000);
+ break;
+ }
+
+ mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
+ mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) |
+ (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTPYN));
+
+ /* Poll for PHY reset */
+ /* XXX FIXME add timeout */
+ switch (port->index) {
+ case 0:
+ while (!(mfdcri(SDR0, PESDR0_460EX_RSTSTA) & 0x1))
+ udelay(10);
+ break;
+ case 1:
+ while (!(mfdcri(SDR0, PESDR1_460EX_RSTSTA) & 0x1))
+ udelay(10);
+ break;
+ }
+
+ mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
+ (mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) &
+ ~(PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL)) |
+ PESDRx_RCSSET_RSTPYN);
+
+ port->has_ibpre = 1;
+
+ return ppc4xx_pciex_port_reset_sdr(port);
+}
+
+static int ppc460ex_pciex_init_utl(struct ppc4xx_pciex_port *port)
+{
+ dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x0);
+
+ /*
+ * Set buffer allocations and then assert VRB and TXE.
+ */
+ out_be32(port->utl_base + PEUTL_PBCTL, 0x0800000c);
+ out_be32(port->utl_base + PEUTL_OUTTR, 0x08000000);
+ out_be32(port->utl_base + PEUTL_INTR, 0x02000000);
+ out_be32(port->utl_base + PEUTL_OPDBSZ, 0x04000000);
+ out_be32(port->utl_base + PEUTL_PBBSZ, 0x00000000);
+ out_be32(port->utl_base + PEUTL_IPHBSZ, 0x02000000);
+ out_be32(port->utl_base + PEUTL_IPDBSZ, 0x04000000);
+ out_be32(port->utl_base + PEUTL_RCIRQEN,0x00f00000);
+ out_be32(port->utl_base + PEUTL_PCTL, 0x80800066);
+
+ return 0;
+}
+
+static struct ppc4xx_pciex_hwops ppc460ex_pcie_hwops __initdata =
+{
+ .want_sdr = true,
+ .core_init = ppc460ex_pciex_core_init,
+ .port_init_hw = ppc460ex_pciex_init_port_hw,
+ .setup_utl = ppc460ex_pciex_init_utl,
+ .check_link = ppc4xx_pciex_check_link_sdr,
+};
+
+static int __init apm821xx_pciex_core_init(struct device_node *np)
+{
+ /* Return the number of pcie port */
+ return 1;
+}
+
+static int __init apm821xx_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+{
+ u32 val;
+
+ /*
+ * Do a software reset on PCIe ports.
+ * This code is to fix the issue that pci drivers doesn't re-assign
+ * bus number for PCIE devices after Uboot
+ * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000
+ * PT quad port, SAS LSI 1064E)
+ */
+
+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0);
+ mdelay(10);
+
+ if (port->endpoint)
+ val = PTYPE_LEGACY_ENDPOINT << 20;
+ else
+ val = PTYPE_ROOT_PORT << 20;
+
+ val |= LNKW_X1 << 12;
+
+ mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val);
+ mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x00000000);
+ mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01010000);
+
+ mtdcri(SDR0, PESDR0_460EX_L0CDRCTL, 0x00003230);
+ mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130);
+ mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006);
+
+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000);
+ mdelay(50);
+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000);
+
+ mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
+ mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) |
+ (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTPYN));
+
+ /* Poll for PHY reset */
+ val = PESDR0_460EX_RSTSTA - port->sdr_base;
+ if (ppc4xx_pciex_wait_on_sdr(port, val, 0x1, 1, 100)) {
+ printk(KERN_WARNING "%s: PCIE: Can't reset PHY\n", __func__);
+ return -EBUSY;
+ } else {
+ mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
+ (mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) &
+ ~(PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL)) |
+ PESDRx_RCSSET_RSTPYN);
+
+ port->has_ibpre = 1;
+ return 0;
+ }
+}
+
+static struct ppc4xx_pciex_hwops apm821xx_pcie_hwops __initdata = {
+ .want_sdr = true,
+ .core_init = apm821xx_pciex_core_init,
+ .port_init_hw = apm821xx_pciex_init_port_hw,
+ .setup_utl = ppc460ex_pciex_init_utl,
+ .check_link = ppc4xx_pciex_check_link_sdr,
+};
+
+static int __init ppc460sx_pciex_core_init(struct device_node *np)
+{
+ /* HSS drive amplitude */
+ mtdcri(SDR0, PESDR0_460SX_HSSL0DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL1DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL2DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL3DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL4DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL5DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL6DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL7DAMP, 0xB9843211);
+
+ mtdcri(SDR0, PESDR1_460SX_HSSL0DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR1_460SX_HSSL1DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR1_460SX_HSSL2DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR1_460SX_HSSL3DAMP, 0xB9843211);
+
+ mtdcri(SDR0, PESDR2_460SX_HSSL0DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR2_460SX_HSSL1DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR2_460SX_HSSL2DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR2_460SX_HSSL3DAMP, 0xB9843211);
+
+ /* HSS TX pre-emphasis */
+ mtdcri(SDR0, PESDR0_460SX_HSSL0COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL1COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL2COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL3COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL4COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL5COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL6COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL7COEFA, 0xDCB98987);
+
+ mtdcri(SDR0, PESDR1_460SX_HSSL0COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR1_460SX_HSSL1COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR1_460SX_HSSL2COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR1_460SX_HSSL3COEFA, 0xDCB98987);
+
+ mtdcri(SDR0, PESDR2_460SX_HSSL0COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR2_460SX_HSSL1COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR2_460SX_HSSL2COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR2_460SX_HSSL3COEFA, 0xDCB98987);
+
+ /* HSS TX calibration control */
+ mtdcri(SDR0, PESDR0_460SX_HSSL1CALDRV, 0x22222222);
+ mtdcri(SDR0, PESDR1_460SX_HSSL1CALDRV, 0x22220000);
+ mtdcri(SDR0, PESDR2_460SX_HSSL1CALDRV, 0x22220000);
+
+ /* HSS TX slew control */
+ mtdcri(SDR0, PESDR0_460SX_HSSSLEW, 0xFFFFFFFF);
+ mtdcri(SDR0, PESDR1_460SX_HSSSLEW, 0xFFFF0000);
+ mtdcri(SDR0, PESDR2_460SX_HSSSLEW, 0xFFFF0000);
+
+ /* Set HSS PRBS enabled */
+ mtdcri(SDR0, PESDR0_460SX_HSSCTLSET, 0x00001130);
+ mtdcri(SDR0, PESDR2_460SX_HSSCTLSET, 0x00001130);
+
+ udelay(100);
+
+ /* De-assert PLLRESET */
+ dcri_clrset(SDR0, PESDR0_PLLLCT2, 0x00000100, 0);
+
+ /* Reset DL, UTL, GPL before configuration */
+ mtdcri(SDR0, PESDR0_460SX_RCSSET,
+ PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU);
+ mtdcri(SDR0, PESDR1_460SX_RCSSET,
+ PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU);
+ mtdcri(SDR0, PESDR2_460SX_RCSSET,
+ PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU);
+
+ udelay(100);
+
+ /*
+ * If bifurcation is not enabled, u-boot would have disabled the
+ * third PCIe port
+ */
+ if (((mfdcri(SDR0, PESDR1_460SX_HSSCTLSET) & 0x00000001) ==
+ 0x00000001)) {
+ printk(KERN_INFO "PCI: PCIE bifurcation setup successfully.\n");
+ printk(KERN_INFO "PCI: Total 3 PCIE ports are present\n");
+ return 3;
+ }
+
+ printk(KERN_INFO "PCI: Total 2 PCIE ports are present\n");
+ return 2;
+}
+
+static int __init ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+{
+
+ if (port->endpoint)
+ dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2,
+ 0x01000000, 0);
+ else
+ dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2,
+ 0, 0x01000000);
+
+ dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET,
+ (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL),
+ PESDRx_RCSSET_RSTPYN);
+
+ port->has_ibpre = 1;
+
+ return ppc4xx_pciex_port_reset_sdr(port);
+}
+
+static int ppc460sx_pciex_init_utl(struct ppc4xx_pciex_port *port)
+{
+ /* Max 128 Bytes */
+ out_be32 (port->utl_base + PEUTL_PBBSZ, 0x00000000);
+ /* Assert VRB and TXE - per datasheet turn off addr validation */
+ out_be32(port->utl_base + PEUTL_PCTL, 0x80800000);
+ return 0;
+}
+
+static void __init ppc460sx_pciex_check_link(struct ppc4xx_pciex_port *port)
+{
+ void __iomem *mbase;
+ int attempt = 50;
+
+ port->link = 0;
+
+ mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000);
+ if (mbase == NULL) {
+ printk(KERN_ERR "%s: Can't map internal config space !",
+ port->node->full_name);
+ goto done;
+ }
+
+ while (attempt && (0 == (in_le32(mbase + PECFG_460SX_DLLSTA)
+ & PECFG_460SX_DLLSTA_LINKUP))) {
+ attempt--;
+ mdelay(10);
+ }
+ if (attempt)
+ port->link = 1;
+done:
+ iounmap(mbase);
+
+}
+
+static struct ppc4xx_pciex_hwops ppc460sx_pcie_hwops __initdata = {
+ .want_sdr = true,
+ .core_init = ppc460sx_pciex_core_init,
+ .port_init_hw = ppc460sx_pciex_init_port_hw,
+ .setup_utl = ppc460sx_pciex_init_utl,
+ .check_link = ppc460sx_pciex_check_link,
};
#endif /* CONFIG_44x */
@@ -806,7 +1303,7 @@ static void ppc405ex_pcie_phy_reset(struct ppc4xx_pciex_port *port)
mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x00101000);
}
-static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+static int __init ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
{
u32 val;
@@ -830,23 +1327,15 @@ static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
* PCIe boards don't show this problem.
* This has to be re-tested and fixed in a later release!
*/
-#if 0 /* XXX FIXME: Not resetting the PHY will leave all resources
- * configured as done previously by U-Boot. Then Linux will currently
- * not reassign them. So the PHY reset is now done always. This will
- * lead to problems with the Atheros PCIe board again.
- */
val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP);
if (!(val & 0x00001000))
ppc405ex_pcie_phy_reset(port);
-#else
- ppc405ex_pcie_phy_reset(port);
-#endif
dcr_write(port->dcrs, DCRO_PEGPL_CFG, 0x10000000); /* guarded on */
port->has_ibpre = 1;
- return 0;
+ return ppc4xx_pciex_port_reset_sdr(port);
}
static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port)
@@ -872,13 +1361,60 @@ static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port)
static struct ppc4xx_pciex_hwops ppc405ex_pcie_hwops __initdata =
{
+ .want_sdr = true,
.core_init = ppc405ex_pciex_core_init,
.port_init_hw = ppc405ex_pciex_init_port_hw,
.setup_utl = ppc405ex_pciex_init_utl,
+ .check_link = ppc4xx_pciex_check_link_sdr,
};
#endif /* CONFIG_40x */
+#ifdef CONFIG_476FPE
+static int __init ppc_476fpe_pciex_core_init(struct device_node *np)
+{
+ return 4;
+}
+
+static void __init ppc_476fpe_pciex_check_link(struct ppc4xx_pciex_port *port)
+{
+ u32 timeout_ms = 20;
+ u32 val = 0, mask = (PECFG_TLDLP_LNKUP|PECFG_TLDLP_PRESENT);
+ void __iomem *mbase = ioremap(port->cfg_space.start + 0x10000000,
+ 0x1000);
+
+ printk(KERN_INFO "PCIE%d: Checking link...\n", port->index);
+
+ if (mbase == NULL) {
+ printk(KERN_WARNING "PCIE%d: failed to get cfg space\n",
+ port->index);
+ return;
+ }
+
+ while (timeout_ms--) {
+ val = in_le32(mbase + PECFG_TLDLP);
+
+ if ((val & mask) == mask)
+ break;
+ msleep(10);
+ }
+
+ if (val & PECFG_TLDLP_PRESENT) {
+ printk(KERN_INFO "PCIE%d: link is up !\n", port->index);
+ port->link = 1;
+ } else
+ printk(KERN_WARNING "PCIE%d: Link up failed\n", port->index);
+
+ iounmap(mbase);
+ return;
+}
+
+static struct ppc4xx_pciex_hwops ppc_476fpe_pcie_hwops __initdata =
+{
+ .core_init = ppc_476fpe_pciex_core_init,
+ .check_link = ppc_476fpe_pciex_check_link,
+};
+#endif /* CONFIG_476FPE */
/* Check that the core has been initied and if not, do it */
static int __init ppc4xx_pciex_check_core_init(struct device_node *np)
@@ -896,11 +1432,22 @@ static int __init ppc4xx_pciex_check_core_init(struct device_node *np)
else
ppc4xx_pciex_hwops = &ppc440speB_pcie_hwops;
}
+ if (of_device_is_compatible(np, "ibm,plb-pciex-460ex"))
+ ppc4xx_pciex_hwops = &ppc460ex_pcie_hwops;
+ if (of_device_is_compatible(np, "ibm,plb-pciex-460sx"))
+ ppc4xx_pciex_hwops = &ppc460sx_pcie_hwops;
+ if (of_device_is_compatible(np, "ibm,plb-pciex-apm821xx"))
+ ppc4xx_pciex_hwops = &apm821xx_pcie_hwops;
#endif /* CONFIG_44x */
#ifdef CONFIG_40x
if (of_device_is_compatible(np, "ibm,plb-pciex-405ex"))
ppc4xx_pciex_hwops = &ppc405ex_pcie_hwops;
#endif
+#ifdef CONFIG_476FPE
+ if (of_device_is_compatible(np, "ibm,plb-pciex-476fpe")
+ || of_device_is_compatible(np, "ibm,plb-pciex-476gtr"))
+ ppc4xx_pciex_hwops = &ppc_476fpe_pcie_hwops;
+#endif
if (ppc4xx_pciex_hwops == NULL) {
printk(KERN_WARNING "PCIE: unknown host type %s\n",
np->full_name);
@@ -949,26 +1496,6 @@ static void __init ppc4xx_pciex_port_init_mapping(struct ppc4xx_pciex_port *port
dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0);
}
-static int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port,
- unsigned int sdr_offset,
- unsigned int mask,
- unsigned int value,
- int timeout_ms)
-{
- u32 val;
-
- while(timeout_ms--) {
- val = mfdcri(SDR0, port->sdr_base + sdr_offset);
- if ((val & mask) == value) {
- pr_debug("PCIE%d: Wait on SDR %x success with tm %d (%08x)\n",
- port->index, sdr_offset, timeout_ms, val);
- return 0;
- }
- msleep(1);
- }
- return -1;
-}
-
static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
{
int rc = 0;
@@ -979,47 +1506,15 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
if (rc != 0)
return rc;
- printk(KERN_INFO "PCIE%d: Checking link...\n",
- port->index);
-
- /* Wait for reset to complete */
- if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) {
- printk(KERN_WARNING "PCIE%d: PGRST failed\n",
- port->index);
- return -1;
- }
-
- /* Check for card presence detect if supported, if not, just wait for
- * link unconditionally.
- *
- * note that we don't fail if there is no link, we just filter out
- * config space accesses. That way, it will be easier to implement
- * hotplug later on.
- */
- if (!port->has_ibpre ||
- !ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
- 1 << 28, 1 << 28, 100)) {
- printk(KERN_INFO
- "PCIE%d: Device detected, waiting for link...\n",
- port->index);
- if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
- 0x1000, 0x1000, 2000))
- printk(KERN_WARNING
- "PCIE%d: Link up failed\n", port->index);
- else {
- printk(KERN_INFO
- "PCIE%d: link is up !\n", port->index);
- port->link = 1;
- }
- } else
- printk(KERN_INFO "PCIE%d: No device detected.\n", port->index);
-
/*
* Initialize mapping: disable all regions and configure
* CFG and REG regions based on resources in the device tree
*/
ppc4xx_pciex_port_init_mapping(port);
+ if (ppc4xx_pciex_hwops->check_link)
+ ppc4xx_pciex_hwops->check_link(port);
+
/*
* Map UTL
*/
@@ -1033,17 +1528,29 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
ppc4xx_pciex_hwops->setup_utl(port);
/*
- * Check for VC0 active and assert RDY.
+ * Check for VC0 active or PLL Locked and assert RDY.
*/
- if (port->link &&
- ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS,
- 1 << 16, 1 << 16, 5000)) {
- printk(KERN_INFO "PCIE%d: VC0 not active\n", port->index);
- port->link = 0;
+ if (port->sdr_base) {
+ if (of_device_is_compatible(port->node,
+ "ibm,plb-pciex-460sx")){
+ if (port->link && ppc4xx_pciex_wait_on_sdr(port,
+ PESDRn_RCSSTS,
+ 1 << 12, 1 << 12, 5000)) {
+ printk(KERN_INFO "PCIE%d: PLL not locked\n",
+ port->index);
+ port->link = 0;
+ }
+ } else if (port->link &&
+ ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS,
+ 1 << 16, 1 << 16, 5000)) {
+ printk(KERN_INFO "PCIE%d: VC0 not active\n",
+ port->index);
+ port->link = 0;
+ }
+
+ dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, 0, 1 << 20);
}
- mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
- mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | 1 << 20);
msleep(100);
return 0;
@@ -1105,7 +1612,7 @@ static void __iomem *ppc4xx_pciex_get_config_base(struct ppc4xx_pciex_port *port
static int ppc4xx_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 *val)
{
- struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+ struct pci_controller *hose = pci_bus_to_host(bus);
struct ppc4xx_pciex_port *port =
&ppc4xx_pciex_ports[hose->indirect_type];
void __iomem *addr;
@@ -1162,7 +1669,7 @@ static int ppc4xx_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
static int ppc4xx_pciex_write_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 val)
{
- struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+ struct pci_controller *hose = pci_bus_to_host(bus);
struct ppc4xx_pciex_port *port =
&ppc4xx_pciex_ports[hose->indirect_type];
void __iomem *addr;
@@ -1209,16 +1716,93 @@ static struct pci_ops ppc4xx_pciex_pci_ops =
.write = ppc4xx_pciex_write_config,
};
+static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port,
+ struct pci_controller *hose,
+ void __iomem *mbase,
+ u64 plb_addr,
+ u64 pci_addr,
+ u64 size,
+ unsigned int flags,
+ int index)
+{
+ u32 lah, lal, pciah, pcial, sa;
+
+ if (!is_power_of_2(size) ||
+ (index < 2 && size < 0x100000) ||
+ (index == 2 && size < 0x100) ||
+ (plb_addr & (size - 1)) != 0) {
+ printk(KERN_WARNING "%s: Resource out of range\n",
+ hose->dn->full_name);
+ return -1;
+ }
+
+ /* Calculate register values */
+ lah = RES_TO_U32_HIGH(plb_addr);
+ lal = RES_TO_U32_LOW(plb_addr);
+ pciah = RES_TO_U32_HIGH(pci_addr);
+ pcial = RES_TO_U32_LOW(pci_addr);
+ sa = (0xffffffffu << ilog2(size)) | 0x1;
+
+ /* Program register values */
+ switch (index) {
+ case 0:
+ out_le32(mbase + PECFG_POM0LAH, pciah);
+ out_le32(mbase + PECFG_POM0LAL, pcial);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff);
+ /*Enabled and single region */
+ if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx"))
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL,
+ sa | DCRO_PEGPL_460SX_OMR1MSKL_UOT
+ | DCRO_PEGPL_OMRxMSKL_VAL);
+ else if (of_device_is_compatible(
+ port->node, "ibm,plb-pciex-476fpe") ||
+ of_device_is_compatible(
+ port->node, "ibm,plb-pciex-476gtr"))
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL,
+ sa | DCRO_PEGPL_476FPE_OMR1MSKL_UOT
+ | DCRO_PEGPL_OMRxMSKL_VAL);
+ else
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL,
+ sa | DCRO_PEGPL_OMR1MSKL_UOT
+ | DCRO_PEGPL_OMRxMSKL_VAL);
+ break;
+ case 1:
+ out_le32(mbase + PECFG_POM1LAH, pciah);
+ out_le32(mbase + PECFG_POM1LAL, pcial);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL,
+ sa | DCRO_PEGPL_OMRxMSKL_VAL);
+ break;
+ case 2:
+ out_le32(mbase + PECFG_POM2LAH, pciah);
+ out_le32(mbase + PECFG_POM2LAL, pcial);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff);
+ /* Note that 3 here means enabled | IO space !!! */
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL,
+ sa | DCRO_PEGPL_OMR3MSKL_IO
+ | DCRO_PEGPL_OMRxMSKL_VAL);
+ break;
+ }
+
+ return 0;
+}
+
static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
struct pci_controller *hose,
void __iomem *mbase)
{
- u32 lah, lal, pciah, pcial, sa;
- int i, j;
+ int i, j, found_isa_hole = 0;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
+ resource_size_t offset = hose->mem_offset[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
@@ -1229,53 +1813,38 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
break;
}
- /* Calculate register values */
- lah = RES_TO_U32_HIGH(res->start);
- lal = RES_TO_U32_LOW(res->start);
- pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
- pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
- sa = res->end + 1 - res->start;
- if (!is_power_of_2(sa) || sa < 0x100000 ||
- sa > 0xffffffffu) {
- printk(KERN_WARNING "%s: Resource out of range\n",
- port->node->full_name);
- continue;
- }
- sa = (0xffffffffu << ilog2(sa)) | 0x1;
-
- /* Program register values */
- switch (j) {
- case 0:
- out_le32(mbase + PECFG_POM0LAH, pciah);
- out_le32(mbase + PECFG_POM0LAL, pcial);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3);
- break;
- case 1:
- out_le32(mbase + PECFG_POM1LAH, pciah);
- out_le32(mbase + PECFG_POM1LAL, pcial);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3);
- break;
+ /* Configure the resource */
+ if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
+ res->start,
+ res->start - offset,
+ resource_size(res),
+ res->flags,
+ j) == 0) {
+ j++;
+
+ /* If the resource PCI address is 0 then we have our
+ * ISA memory hole
+ */
+ if (res->start == offset)
+ found_isa_hole = 1;
}
- j++;
}
- /* Configure IO, always 64K starting at 0 */
- if (hose->io_resource.flags & IORESOURCE_IO) {
- lah = RES_TO_U32_HIGH(hose->io_base_phys);
- lal = RES_TO_U32_LOW(hose->io_base_phys);
- out_le32(mbase + PECFG_POM2LAH, 0);
- out_le32(mbase + PECFG_POM2LAL, 0);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0xffff0000 | 3);
- }
+ /* Handle ISA memory hole if not already covered */
+ if (j <= 1 && !found_isa_hole && hose->isa_mem_size)
+ if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
+ hose->isa_mem_phys, 0,
+ hose->isa_mem_size, 0, j) == 0)
+ printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
+ hose->dn->full_name);
+
+ /* Configure IO, always 64K starting at 0. We hard wire it to 64K !
+ * Note also that it -has- to be region index 2 on this HW
+ */
+ if (hose->io_resource.flags & IORESOURCE_IO)
+ ppc4xx_setup_one_pciex_POM(port, hose, mbase,
+ hose->io_base_phys, 0,
+ 0x10000, IORESOURCE_IO, 2);
}
static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port,
@@ -1283,31 +1852,69 @@ static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port,
void __iomem *mbase,
struct resource *res)
{
- resource_size_t size = res->end - res->start + 1;
+ resource_size_t size = resource_size(res);
u64 sa;
- /* Calculate window size */
- sa = (0xffffffffffffffffull << ilog2(size));;
- if (res->flags & IORESOURCE_PREFETCH)
- sa |= 0x8;
+ if (port->endpoint) {
+ resource_size_t ep_addr = 0;
+ resource_size_t ep_size = 32 << 20;
+
+ /* Currently we map a fixed 64MByte window to PLB address
+ * 0 (SDRAM). This should probably be configurable via a dts
+ * property.
+ */
+
+ /* Calculate window size */
+ sa = (0xffffffffffffffffull << ilog2(ep_size));
+
+ /* Setup BAR0 */
+ out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
+ out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa) |
+ PCI_BASE_ADDRESS_MEM_TYPE_64);
- out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
- out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa));
+ /* Disable BAR1 & BAR2 */
+ out_le32(mbase + PECFG_BAR1MPA, 0);
+ out_le32(mbase + PECFG_BAR2HMPA, 0);
+ out_le32(mbase + PECFG_BAR2LMPA, 0);
- /* The setup of the split looks weird to me ... let's see if it works */
- out_le32(mbase + PECFG_PIM0LAL, 0x00000000);
- out_le32(mbase + PECFG_PIM0LAH, 0x00000000);
- out_le32(mbase + PECFG_PIM1LAL, 0x00000000);
- out_le32(mbase + PECFG_PIM1LAH, 0x00000000);
- out_le32(mbase + PECFG_PIM01SAH, 0xffff0000);
- out_le32(mbase + PECFG_PIM01SAL, 0x00000000);
+ out_le32(mbase + PECFG_PIM01SAH, RES_TO_U32_HIGH(sa));
+ out_le32(mbase + PECFG_PIM01SAL, RES_TO_U32_LOW(sa));
+
+ out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(ep_addr));
+ out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(ep_addr));
+ } else {
+ /* Calculate window size */
+ sa = (0xffffffffffffffffull << ilog2(size));
+ if (res->flags & IORESOURCE_PREFETCH)
+ sa |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+ if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx") ||
+ of_device_is_compatible(
+ port->node, "ibm,plb-pciex-476fpe") ||
+ of_device_is_compatible(
+ port->node, "ibm,plb-pciex-476gtr"))
+ sa |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+
+ out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
+ out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa));
+
+ /* The setup of the split looks weird to me ... let's see
+ * if it works
+ */
+ out_le32(mbase + PECFG_PIM0LAL, 0x00000000);
+ out_le32(mbase + PECFG_PIM0LAH, 0x00000000);
+ out_le32(mbase + PECFG_PIM1LAL, 0x00000000);
+ out_le32(mbase + PECFG_PIM1LAH, 0x00000000);
+ out_le32(mbase + PECFG_PIM01SAH, 0xffff0000);
+ out_le32(mbase + PECFG_PIM01SAL, 0x00000000);
+
+ out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(res->start));
+ out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(res->start));
+ }
/* Enable inbound mapping */
out_le32(mbase + PECFG_PIMEN, 0x1);
- out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(res->start));
- out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(res->start));
-
/* Enable I/O, Mem, and Busmaster cycles */
out_le16(mbase + PCI_COMMAND,
in_le16(mbase + PCI_COMMAND) |
@@ -1321,13 +1928,8 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
const int *bus_range;
int primary = 0, busses;
void __iomem *mbase = NULL, *cfg_data = NULL;
-
- /* XXX FIXME: Handle endpoint mode properly */
- if (port->endpoint) {
- printk(KERN_WARNING "PCIE%d: Port in endpoint mode !\n",
- port->index);
- return;
- }
+ const u32 *pval;
+ u32 val;
/* Check if primary bridge */
if (of_get_property(port->node, "primary", NULL))
@@ -1361,21 +1963,30 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
hose->last_busno = hose->first_busno + busses;
}
- /* We map the external config space in cfg_data and the host config
- * space in cfg_addr. External space is 1M per bus, internal space
- * is 4K
+ if (!port->endpoint) {
+ /* Only map the external config space in cfg_data for
+ * PCIe root-complexes. External space is 1M per bus
+ */
+ cfg_data = ioremap(port->cfg_space.start +
+ (hose->first_busno + 1) * 0x100000,
+ busses * 0x100000);
+ if (cfg_data == NULL) {
+ printk(KERN_ERR "%s: Can't map external config space !",
+ port->node->full_name);
+ goto fail;
+ }
+ hose->cfg_data = cfg_data;
+ }
+
+ /* Always map the host config space in cfg_addr.
+ * Internal space is 4K
*/
- cfg_data = ioremap(port->cfg_space.start +
- (hose->first_busno + 1) * 0x100000,
- busses * 0x100000);
mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000);
- if (cfg_data == NULL || mbase == NULL) {
- printk(KERN_ERR "%s: Can't map config space !",
+ if (mbase == NULL) {
+ printk(KERN_ERR "%s: Can't map internal config space !",
port->node->full_name);
goto fail;
}
-
- hose->cfg_data = cfg_data;
hose->cfg_addr = mbase;
pr_debug("PCIE %s, bus %d..%d\n", port->node->full_name,
@@ -1388,12 +1999,14 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
port->hose = hose;
mbase = (void __iomem *)hose->cfg_addr;
- /*
- * Set bus numbers on our root port
- */
- out_8(mbase + PCI_PRIMARY_BUS, hose->first_busno);
- out_8(mbase + PCI_SECONDARY_BUS, hose->first_busno + 1);
- out_8(mbase + PCI_SUBORDINATE_BUS, hose->last_busno);
+ if (!port->endpoint) {
+ /*
+ * Set bus numbers on our root port
+ */
+ out_8(mbase + PCI_PRIMARY_BUS, hose->first_busno);
+ out_8(mbase + PCI_SECONDARY_BUS, hose->first_busno + 1);
+ out_8(mbase + PCI_SUBORDINATE_BUS, hose->last_busno);
+ }
/*
* OMRs are already reset, also disable PIMs
@@ -1414,17 +2027,53 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
ppc4xx_configure_pciex_PIMs(port, hose, mbase, &dma_window);
/* The root complex doesn't show up if we don't set some vendor
- * and device IDs into it. Those are the same bogus one that the
- * initial code in arch/ppc add. We might want to change that.
+ * and device IDs into it. The defaults below are the same bogus
+ * one that the initial code in arch/ppc had. This can be
+ * overwritten by setting the "vendor-id/device-id" properties
+ * in the pciex node.
*/
- out_le16(mbase + 0x200, 0xaaa0 + port->index);
- out_le16(mbase + 0x202, 0xbed0 + port->index);
- /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */
- out_le32(mbase + 0x208, 0x06040001);
+ /* Get the (optional) vendor-/device-id from the device-tree */
+ pval = of_get_property(port->node, "vendor-id", NULL);
+ if (pval) {
+ val = *pval;
+ } else {
+ if (!port->endpoint)
+ val = 0xaaa0 + port->index;
+ else
+ val = 0xeee0 + port->index;
+ }
+ out_le16(mbase + 0x200, val);
+
+ pval = of_get_property(port->node, "device-id", NULL);
+ if (pval) {
+ val = *pval;
+ } else {
+ if (!port->endpoint)
+ val = 0xbed0 + port->index;
+ else
+ val = 0xfed0 + port->index;
+ }
+ out_le16(mbase + 0x202, val);
+
+ /* Enable Bus master, memory, and io space */
+ if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx"))
+ out_le16(mbase + 0x204, 0x7);
+
+ if (!port->endpoint) {
+ /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */
+ out_le32(mbase + 0x208, 0x06040001);
+
+ printk(KERN_INFO "PCIE%d: successfully set as root-complex\n",
+ port->index);
+ } else {
+ /* Set Class Code to Processor/PPC */
+ out_le32(mbase + 0x208, 0x0b200001);
+
+ printk(KERN_INFO "PCIE%d: successfully set as endpoint\n",
+ port->index);
+ }
- printk(KERN_INFO "PCIE%d: successfully set as root-complex\n",
- port->index);
return;
fail:
if (hose)
@@ -1441,6 +2090,7 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
const u32 *pval;
int portno;
unsigned int dcrs;
+ const char *val;
/* First, proceed to core initialization as we assume there's
* only one PCIe core in the system
@@ -1463,17 +2113,40 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
}
port = &ppc4xx_pciex_ports[portno];
port->index = portno;
+
+ /*
+ * Check if device is enabled
+ */
+ if (!of_device_is_available(np)) {
+ printk(KERN_INFO "PCIE%d: Port disabled via device-tree\n", port->index);
+ return;
+ }
+
port->node = of_node_get(np);
- pval = of_get_property(np, "sdr-base", NULL);
- if (pval == NULL) {
- printk(KERN_ERR "PCIE: missing sdr-base for %s\n",
+ if (ppc4xx_pciex_hwops->want_sdr) {
+ pval = of_get_property(np, "sdr-base", NULL);
+ if (pval == NULL) {
+ printk(KERN_ERR "PCIE: missing sdr-base for %s\n",
+ np->full_name);
+ return;
+ }
+ port->sdr_base = *pval;
+ }
+
+ /* Check if device_type property is set to "pci" or "pci-endpoint".
+ * Resulting from this setup this PCIe port will be configured
+ * as root-complex or as endpoint.
+ */
+ val = of_get_property(port->node, "device_type", NULL);
+ if (!strcmp(val, "pci-endpoint")) {
+ port->endpoint = 1;
+ } else if (!strcmp(val, "pci")) {
+ port->endpoint = 0;
+ } else {
+ printk(KERN_ERR "PCIE: missing or incorrect device_type for %s\n",
np->full_name);
return;
}
- port->sdr_base = *pval;
-
- /* XXX Currently, we only support root complex mode */
- port->endpoint = 0;
/* Fetch config space registers address */
if (of_address_to_resource(np, 0, &port->cfg_space)) {
@@ -1513,6 +2186,8 @@ static int __init ppc4xx_pci_find_bridges(void)
{
struct device_node *np;
+ pci_add_flags(PCI_ENABLE_PROC_DOMAINS | PCI_COMPAT_DOMAIN_0);
+
#ifdef CONFIG_PPC4xx_PCI_EXPRESS
for_each_compatible_node(np, NULL, "ibm,plb-pciex")
ppc4xx_probe_pciex_bridge(np);
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.h b/arch/powerpc/sysdev/ppc4xx_pci.h
index 1c07908dc6e..bb4821938ab 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.h
+++ b/arch/powerpc/sysdev/ppc4xx_pci.h
@@ -271,6 +271,117 @@
#define PESDR1_405EX_PHYSTA 0x044C
/*
+ * 460EX additional DCRs
+ */
+#define PESDR0_460EX_L0BIST 0x0308
+#define PESDR0_460EX_L0BISTSTS 0x0309
+#define PESDR0_460EX_L0CDRCTL 0x030A
+#define PESDR0_460EX_L0DRV 0x030B
+#define PESDR0_460EX_L0REC 0x030C
+#define PESDR0_460EX_L0LPB 0x030D
+#define PESDR0_460EX_L0CLK 0x030E
+#define PESDR0_460EX_PHY_CTL_RST 0x030F
+#define PESDR0_460EX_RSTSTA 0x0310
+#define PESDR0_460EX_OBS 0x0311
+#define PESDR0_460EX_L0ERRC 0x0320
+
+#define PESDR1_460EX_L0BIST 0x0348
+#define PESDR1_460EX_L1BIST 0x0349
+#define PESDR1_460EX_L2BIST 0x034A
+#define PESDR1_460EX_L3BIST 0x034B
+#define PESDR1_460EX_L0BISTSTS 0x034C
+#define PESDR1_460EX_L1BISTSTS 0x034D
+#define PESDR1_460EX_L2BISTSTS 0x034E
+#define PESDR1_460EX_L3BISTSTS 0x034F
+#define PESDR1_460EX_L0CDRCTL 0x0350
+#define PESDR1_460EX_L1CDRCTL 0x0351
+#define PESDR1_460EX_L2CDRCTL 0x0352
+#define PESDR1_460EX_L3CDRCTL 0x0353
+#define PESDR1_460EX_L0DRV 0x0354
+#define PESDR1_460EX_L1DRV 0x0355
+#define PESDR1_460EX_L2DRV 0x0356
+#define PESDR1_460EX_L3DRV 0x0357
+#define PESDR1_460EX_L0REC 0x0358
+#define PESDR1_460EX_L1REC 0x0359
+#define PESDR1_460EX_L2REC 0x035A
+#define PESDR1_460EX_L3REC 0x035B
+#define PESDR1_460EX_L0LPB 0x035C
+#define PESDR1_460EX_L1LPB 0x035D
+#define PESDR1_460EX_L2LPB 0x035E
+#define PESDR1_460EX_L3LPB 0x035F
+#define PESDR1_460EX_L0CLK 0x0360
+#define PESDR1_460EX_L1CLK 0x0361
+#define PESDR1_460EX_L2CLK 0x0362
+#define PESDR1_460EX_L3CLK 0x0363
+#define PESDR1_460EX_PHY_CTL_RST 0x0364
+#define PESDR1_460EX_RSTSTA 0x0365
+#define PESDR1_460EX_OBS 0x0366
+#define PESDR1_460EX_L0ERRC 0x0368
+#define PESDR1_460EX_L1ERRC 0x0369
+#define PESDR1_460EX_L2ERRC 0x036A
+#define PESDR1_460EX_L3ERRC 0x036B
+#define PESDR0_460EX_IHS1 0x036C
+#define PESDR0_460EX_IHS2 0x036D
+
+/*
+ * 460SX additional DCRs
+ */
+#define PESDRn_460SX_RCEI 0x02
+
+#define PESDR0_460SX_HSSL0DAMP 0x320
+#define PESDR0_460SX_HSSL1DAMP 0x321
+#define PESDR0_460SX_HSSL2DAMP 0x322
+#define PESDR0_460SX_HSSL3DAMP 0x323
+#define PESDR0_460SX_HSSL4DAMP 0x324
+#define PESDR0_460SX_HSSL5DAMP 0x325
+#define PESDR0_460SX_HSSL6DAMP 0x326
+#define PESDR0_460SX_HSSL7DAMP 0x327
+
+#define PESDR1_460SX_HSSL0DAMP 0x354
+#define PESDR1_460SX_HSSL1DAMP 0x355
+#define PESDR1_460SX_HSSL2DAMP 0x356
+#define PESDR1_460SX_HSSL3DAMP 0x357
+
+#define PESDR2_460SX_HSSL0DAMP 0x384
+#define PESDR2_460SX_HSSL1DAMP 0x385
+#define PESDR2_460SX_HSSL2DAMP 0x386
+#define PESDR2_460SX_HSSL3DAMP 0x387
+
+#define PESDR0_460SX_HSSL0COEFA 0x328
+#define PESDR0_460SX_HSSL1COEFA 0x329
+#define PESDR0_460SX_HSSL2COEFA 0x32A
+#define PESDR0_460SX_HSSL3COEFA 0x32B
+#define PESDR0_460SX_HSSL4COEFA 0x32C
+#define PESDR0_460SX_HSSL5COEFA 0x32D
+#define PESDR0_460SX_HSSL6COEFA 0x32E
+#define PESDR0_460SX_HSSL7COEFA 0x32F
+
+#define PESDR1_460SX_HSSL0COEFA 0x358
+#define PESDR1_460SX_HSSL1COEFA 0x359
+#define PESDR1_460SX_HSSL2COEFA 0x35A
+#define PESDR1_460SX_HSSL3COEFA 0x35B
+
+#define PESDR2_460SX_HSSL0COEFA 0x388
+#define PESDR2_460SX_HSSL1COEFA 0x389
+#define PESDR2_460SX_HSSL2COEFA 0x38A
+#define PESDR2_460SX_HSSL3COEFA 0x38B
+
+#define PESDR0_460SX_HSSL1CALDRV 0x339
+#define PESDR1_460SX_HSSL1CALDRV 0x361
+#define PESDR2_460SX_HSSL1CALDRV 0x391
+
+#define PESDR0_460SX_HSSSLEW 0x338
+#define PESDR1_460SX_HSSSLEW 0x360
+#define PESDR2_460SX_HSSSLEW 0x390
+
+#define PESDR0_460SX_HSSCTLSET 0x31E
+#define PESDR1_460SX_HSSCTLSET 0x352
+#define PESDR2_460SX_HSSCTLSET 0x382
+
+#define PESDR0_460SX_RCSSET 0x304
+#define PESDR1_460SX_RCSSET 0x344
+#define PESDR2_460SX_RCSSET 0x374
+/*
* Of the above, some are common offsets from the base
*/
#define PESDRn_UTLSET1 0x00
@@ -353,6 +464,31 @@
#define PECFG_POM2LAL 0x390
#define PECFG_POM2LAH 0x394
+/* 460sx only */
+#define PECFG_460SX_DLLSTA 0x3f8
+
+/* 460sx Bit Mappings */
+#define PECFG_460SX_DLLSTA_LINKUP 0x00000010
+#define DCRO_PEGPL_460SX_OMR1MSKL_UOT 0x00000004
+
+/* PEGPL Bit Mappings */
+#define DCRO_PEGPL_OMRxMSKL_VAL 0x00000001
+#define DCRO_PEGPL_OMR1MSKL_UOT 0x00000002
+#define DCRO_PEGPL_OMR3MSKL_IO 0x00000002
+
+/* 476FPE */
+#define PCCFG_LCPA 0x270
+#define PECFG_TLDLP 0x3F8
+#define PECFG_TLDLP_LNKUP 0x00000008
+#define PECFG_TLDLP_PRESENT 0x00000010
+#define DCRO_PEGPL_476FPE_OMR1MSKL_UOT 0x00000004
+
+/* SDR Bit Mappings */
+#define PESDRx_RCSSET_HLDPLB 0x10000000
+#define PESDRx_RCSSET_RSTGU 0x01000000
+#define PESDRx_RCSSET_RDY 0x00100000
+#define PESDRx_RCSSET_RSTDL 0x00010000
+#define PESDRx_RCSSET_RSTPYN 0x00001000
enum
{
diff --git a/arch/powerpc/sysdev/ppc4xx_soc.c b/arch/powerpc/sysdev/ppc4xx_soc.c
new file mode 100644
index 00000000000..5c77c9ba33a
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_soc.c
@@ -0,0 +1,222 @@
+/*
+ * IBM/AMCC PPC4xx SoC setup code
+ *
+ * Copyright 2008 DENX Software Engineering, Stefan Roese <sr@denx.de>
+ *
+ * L2 cache routines cloned from arch/ppc/syslib/ibm440gx_common.c which is:
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003 - 2006 Zultys Technologies
+ *
+ * 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/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+#include <asm/reg.h>
+
+static u32 dcrbase_l2c;
+
+/*
+ * L2-cache
+ */
+
+/* Issue L2C diagnostic command */
+static inline u32 l2c_diag(u32 addr)
+{
+ mtdcr(dcrbase_l2c + DCRN_L2C0_ADDR, addr);
+ mtdcr(dcrbase_l2c + DCRN_L2C0_CMD, L2C_CMD_DIAG);
+ while (!(mfdcr(dcrbase_l2c + DCRN_L2C0_SR) & L2C_SR_CC))
+ ;
+
+ return mfdcr(dcrbase_l2c + DCRN_L2C0_DATA);
+}
+
+static irqreturn_t l2c_error_handler(int irq, void *dev)
+{
+ u32 sr = mfdcr(dcrbase_l2c + DCRN_L2C0_SR);
+
+ if (sr & L2C_SR_CPE) {
+ /* Read cache trapped address */
+ u32 addr = l2c_diag(0x42000000);
+ printk(KERN_EMERG "L2C: Cache Parity Error, addr[16:26] = 0x%08x\n",
+ addr);
+ }
+ if (sr & L2C_SR_TPE) {
+ /* Read tag trapped address */
+ u32 addr = l2c_diag(0x82000000) >> 16;
+ printk(KERN_EMERG "L2C: Tag Parity Error, addr[16:26] = 0x%08x\n",
+ addr);
+ }
+
+ /* Clear parity errors */
+ if (sr & (L2C_SR_CPE | L2C_SR_TPE)){
+ mtdcr(dcrbase_l2c + DCRN_L2C0_ADDR, 0);
+ mtdcr(dcrbase_l2c + DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE);
+ } else {
+ printk(KERN_EMERG "L2C: LRU error\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __init ppc4xx_l2c_probe(void)
+{
+ struct device_node *np;
+ u32 r;
+ unsigned long flags;
+ int irq;
+ const u32 *dcrreg;
+ u32 dcrbase_isram;
+ int len;
+ const u32 *prop;
+ u32 l2_size;
+
+ np = of_find_compatible_node(NULL, NULL, "ibm,l2-cache");
+ if (!np)
+ return 0;
+
+ /* Get l2 cache size */
+ prop = of_get_property(np, "cache-size", NULL);
+ if (prop == NULL) {
+ printk(KERN_ERR "%s: Can't get cache-size!\n", np->full_name);
+ of_node_put(np);
+ return -ENODEV;
+ }
+ l2_size = prop[0];
+
+ /* Map DCRs */
+ dcrreg = of_get_property(np, "dcr-reg", &len);
+ if (!dcrreg || (len != 4 * sizeof(u32))) {
+ printk(KERN_ERR "%s: Can't get DCR register base !",
+ np->full_name);
+ of_node_put(np);
+ return -ENODEV;
+ }
+ dcrbase_isram = dcrreg[0];
+ dcrbase_l2c = dcrreg[2];
+
+ /* Get and map irq number from device tree */
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq == NO_IRQ) {
+ printk(KERN_ERR "irq_of_parse_and_map failed\n");
+ of_node_put(np);
+ return -ENODEV;
+ }
+
+ /* Install error handler */
+ if (request_irq(irq, l2c_error_handler, 0, "L2C", 0) < 0) {
+ printk(KERN_ERR "Cannot install L2C error handler"
+ ", cache is not enabled\n");
+ of_node_put(np);
+ return -ENODEV;
+ }
+
+ local_irq_save(flags);
+ asm volatile ("sync" ::: "memory");
+
+ /* Disable SRAM */
+ mtdcr(dcrbase_isram + DCRN_SRAM0_DPC,
+ mfdcr(dcrbase_isram + DCRN_SRAM0_DPC) & ~SRAM_DPC_ENABLE);
+ mtdcr(dcrbase_isram + DCRN_SRAM0_SB0CR,
+ mfdcr(dcrbase_isram + DCRN_SRAM0_SB0CR) & ~SRAM_SBCR_BU_MASK);
+ mtdcr(dcrbase_isram + DCRN_SRAM0_SB1CR,
+ mfdcr(dcrbase_isram + DCRN_SRAM0_SB1CR) & ~SRAM_SBCR_BU_MASK);
+ mtdcr(dcrbase_isram + DCRN_SRAM0_SB2CR,
+ mfdcr(dcrbase_isram + DCRN_SRAM0_SB2CR) & ~SRAM_SBCR_BU_MASK);
+ mtdcr(dcrbase_isram + DCRN_SRAM0_SB3CR,
+ mfdcr(dcrbase_isram + DCRN_SRAM0_SB3CR) & ~SRAM_SBCR_BU_MASK);
+
+ /* Enable L2_MODE without ICU/DCU */
+ r = mfdcr(dcrbase_l2c + DCRN_L2C0_CFG) &
+ ~(L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_SS_MASK);
+ r |= L2C_CFG_L2M | L2C_CFG_SS_256;
+ mtdcr(dcrbase_l2c + DCRN_L2C0_CFG, r);
+
+ mtdcr(dcrbase_l2c + DCRN_L2C0_ADDR, 0);
+
+ /* Hardware Clear Command */
+ mtdcr(dcrbase_l2c + DCRN_L2C0_CMD, L2C_CMD_HCC);
+ while (!(mfdcr(dcrbase_l2c + DCRN_L2C0_SR) & L2C_SR_CC))
+ ;
+
+ /* Clear Cache Parity and Tag Errors */
+ mtdcr(dcrbase_l2c + DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE);
+
+ /* Enable 64G snoop region starting at 0 */
+ r = mfdcr(dcrbase_l2c + DCRN_L2C0_SNP0) &
+ ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK);
+ r |= L2C_SNP_SSR_32G | L2C_SNP_ESR;
+ mtdcr(dcrbase_l2c + DCRN_L2C0_SNP0, r);
+
+ r = mfdcr(dcrbase_l2c + DCRN_L2C0_SNP1) &
+ ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK);
+ r |= 0x80000000 | L2C_SNP_SSR_32G | L2C_SNP_ESR;
+ mtdcr(dcrbase_l2c + DCRN_L2C0_SNP1, r);
+
+ asm volatile ("sync" ::: "memory");
+
+ /* Enable ICU/DCU ports */
+ r = mfdcr(dcrbase_l2c + DCRN_L2C0_CFG);
+ r &= ~(L2C_CFG_DCW_MASK | L2C_CFG_PMUX_MASK | L2C_CFG_PMIM
+ | L2C_CFG_TPEI | L2C_CFG_CPEI | L2C_CFG_NAM | L2C_CFG_NBRM);
+ r |= L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_TPC | L2C_CFG_CPC | L2C_CFG_FRAN
+ | L2C_CFG_CPIM | L2C_CFG_TPIM | L2C_CFG_LIM | L2C_CFG_SMCM;
+
+ /* Check for 460EX/GT special handling */
+ if (of_device_is_compatible(np, "ibm,l2-cache-460ex") ||
+ of_device_is_compatible(np, "ibm,l2-cache-460gt"))
+ r |= L2C_CFG_RDBW;
+
+ mtdcr(dcrbase_l2c + DCRN_L2C0_CFG, r);
+
+ asm volatile ("sync; isync" ::: "memory");
+ local_irq_restore(flags);
+
+ printk(KERN_INFO "%dk L2-cache enabled\n", l2_size >> 10);
+
+ of_node_put(np);
+ return 0;
+}
+arch_initcall(ppc4xx_l2c_probe);
+
+/*
+ * Apply a system reset. Alternatively a board specific value may be
+ * provided via the "reset-type" property in the cpu node.
+ */
+void ppc4xx_reset_system(char *cmd)
+{
+ struct device_node *np;
+ u32 reset_type = DBCR0_RST_SYSTEM;
+ const u32 *prop;
+
+ np = of_find_node_by_type(NULL, "cpu");
+ if (np) {
+ prop = of_get_property(np, "reset-type", NULL);
+
+ /*
+ * Check if property exists and if it is in range:
+ * 1 - PPC4xx core reset
+ * 2 - PPC4xx chip reset
+ * 3 - PPC4xx system reset (default)
+ */
+ if ((prop) && ((prop[0] >= 1) && (prop[0] <= 3)))
+ reset_type = prop[0] << 28;
+ }
+
+ mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | reset_type);
+
+ while (1)
+ ; /* Just in case the reset doesn't work */
+}
diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig
index adc66212a41..3c251993bac 100644
--- a/arch/powerpc/sysdev/qe_lib/Kconfig
+++ b/arch/powerpc/sysdev/qe_lib/Kconfig
@@ -20,3 +20,8 @@ config UCC
bool
default y if UCC_FAST || UCC_SLOW
+config QE_USB
+ bool
+ default y if USB_FSL_QE
+ help
+ QE USB Controller support
diff --git a/arch/powerpc/sysdev/qe_lib/Makefile b/arch/powerpc/sysdev/qe_lib/Makefile
index 874fe1a5b1c..f1855c18529 100644
--- a/arch/powerpc/sysdev/qe_lib/Makefile
+++ b/arch/powerpc/sysdev/qe_lib/Makefile
@@ -6,3 +6,5 @@ obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_ic.o qe_io.o
obj-$(CONFIG_UCC) += ucc.o
obj-$(CONFIG_UCC_SLOW) += ucc_slow.o
obj-$(CONFIG_UCC_FAST) += ucc_fast.o
+obj-$(CONFIG_QE_USB) += usb.o
+obj-$(CONFIG_QE_GPIO) += gpio.o
diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c
new file mode 100644
index 00000000000..521e67a49dc
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/gpio.c
@@ -0,0 +1,317 @@
+/*
+ * QUICC Engine GPIOs
+ *
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.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 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <asm/qe.h>
+
+struct qe_gpio_chip {
+ struct of_mm_gpio_chip mm_gc;
+ spinlock_t lock;
+
+ unsigned long pin_flags[QE_PIO_PINS];
+#define QE_PIN_REQUESTED 0
+
+ /* shadowed data register to clear/set bits safely */
+ u32 cpdata;
+
+ /* saved_regs used to restore dedicated functions */
+ struct qe_pio_regs saved_regs;
+};
+
+static inline struct qe_gpio_chip *
+to_qe_gpio_chip(struct of_mm_gpio_chip *mm_gc)
+{
+ return container_of(mm_gc, struct qe_gpio_chip, mm_gc);
+}
+
+static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+ struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+ struct qe_pio_regs __iomem *regs = mm_gc->regs;
+
+ qe_gc->cpdata = in_be32(&regs->cpdata);
+ qe_gc->saved_regs.cpdata = qe_gc->cpdata;
+ qe_gc->saved_regs.cpdir1 = in_be32(&regs->cpdir1);
+ qe_gc->saved_regs.cpdir2 = in_be32(&regs->cpdir2);
+ qe_gc->saved_regs.cppar1 = in_be32(&regs->cppar1);
+ qe_gc->saved_regs.cppar2 = in_be32(&regs->cppar2);
+ qe_gc->saved_regs.cpodr = in_be32(&regs->cpodr);
+}
+
+static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct qe_pio_regs __iomem *regs = mm_gc->regs;
+ u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio);
+
+ return in_be32(&regs->cpdata) & pin_mask;
+}
+
+static void qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+ struct qe_pio_regs __iomem *regs = mm_gc->regs;
+ unsigned long flags;
+ u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio);
+
+ spin_lock_irqsave(&qe_gc->lock, flags);
+
+ if (val)
+ qe_gc->cpdata |= pin_mask;
+ else
+ qe_gc->cpdata &= ~pin_mask;
+
+ out_be32(&regs->cpdata, qe_gc->cpdata);
+
+ spin_unlock_irqrestore(&qe_gc->lock, flags);
+}
+
+static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&qe_gc->lock, flags);
+
+ __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_IN, 0, 0, 0);
+
+ spin_unlock_irqrestore(&qe_gc->lock, flags);
+
+ return 0;
+}
+
+static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+ unsigned long flags;
+
+ qe_gpio_set(gc, gpio, val);
+
+ spin_lock_irqsave(&qe_gc->lock, flags);
+
+ __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_OUT, 0, 0, 0);
+
+ spin_unlock_irqrestore(&qe_gc->lock, flags);
+
+ return 0;
+}
+
+struct qe_pin {
+ /*
+ * The qe_gpio_chip name is unfortunate, we should change that to
+ * something like qe_pio_controller. Someday.
+ */
+ struct qe_gpio_chip *controller;
+ int num;
+};
+
+/**
+ * qe_pin_request - Request a QE pin
+ * @np: device node to get a pin from
+ * @index: index of a pin in the device tree
+ * Context: non-atomic
+ *
+ * This function return qe_pin so that you could use it with the rest of
+ * the QE Pin Multiplexing API.
+ */
+struct qe_pin *qe_pin_request(struct device_node *np, int index)
+{
+ struct qe_pin *qe_pin;
+ struct gpio_chip *gc;
+ struct of_mm_gpio_chip *mm_gc;
+ struct qe_gpio_chip *qe_gc;
+ int err;
+ unsigned long flags;
+
+ qe_pin = kzalloc(sizeof(*qe_pin), GFP_KERNEL);
+ if (!qe_pin) {
+ pr_debug("%s: can't allocate memory\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ err = of_get_gpio(np, index);
+ if (err < 0)
+ goto err0;
+ gc = gpio_to_chip(err);
+ if (WARN_ON(!gc))
+ goto err0;
+
+ if (!of_device_is_compatible(gc->of_node, "fsl,mpc8323-qe-pario-bank")) {
+ pr_debug("%s: tried to get a non-qe pin\n", __func__);
+ err = -EINVAL;
+ goto err0;
+ }
+
+ mm_gc = to_of_mm_gpio_chip(gc);
+ qe_gc = to_qe_gpio_chip(mm_gc);
+
+ spin_lock_irqsave(&qe_gc->lock, flags);
+
+ err -= gc->base;
+ if (test_and_set_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[err]) == 0) {
+ qe_pin->controller = qe_gc;
+ qe_pin->num = err;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irqrestore(&qe_gc->lock, flags);
+
+ if (!err)
+ return qe_pin;
+err0:
+ kfree(qe_pin);
+ pr_debug("%s failed with status %d\n", __func__, err);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(qe_pin_request);
+
+/**
+ * qe_pin_free - Free a pin
+ * @qe_pin: pointer to the qe_pin structure
+ * Context: any
+ *
+ * This function frees the qe_pin structure and makes a pin available
+ * for further qe_pin_request() calls.
+ */
+void qe_pin_free(struct qe_pin *qe_pin)
+{
+ struct qe_gpio_chip *qe_gc = qe_pin->controller;
+ unsigned long flags;
+ const int pin = qe_pin->num;
+
+ spin_lock_irqsave(&qe_gc->lock, flags);
+ test_and_clear_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[pin]);
+ spin_unlock_irqrestore(&qe_gc->lock, flags);
+
+ kfree(qe_pin);
+}
+EXPORT_SYMBOL(qe_pin_free);
+
+/**
+ * qe_pin_set_dedicated - Revert a pin to a dedicated peripheral function mode
+ * @qe_pin: pointer to the qe_pin structure
+ * Context: any
+ *
+ * This function resets a pin to a dedicated peripheral function that
+ * has been set up by the firmware.
+ */
+void qe_pin_set_dedicated(struct qe_pin *qe_pin)
+{
+ struct qe_gpio_chip *qe_gc = qe_pin->controller;
+ struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs;
+ struct qe_pio_regs *sregs = &qe_gc->saved_regs;
+ int pin = qe_pin->num;
+ u32 mask1 = 1 << (QE_PIO_PINS - (pin + 1));
+ u32 mask2 = 0x3 << (QE_PIO_PINS - (pin % (QE_PIO_PINS / 2) + 1) * 2);
+ bool second_reg = pin > (QE_PIO_PINS / 2) - 1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&qe_gc->lock, flags);
+
+ if (second_reg) {
+ clrsetbits_be32(&regs->cpdir2, mask2, sregs->cpdir2 & mask2);
+ clrsetbits_be32(&regs->cppar2, mask2, sregs->cppar2 & mask2);
+ } else {
+ clrsetbits_be32(&regs->cpdir1, mask2, sregs->cpdir1 & mask2);
+ clrsetbits_be32(&regs->cppar1, mask2, sregs->cppar1 & mask2);
+ }
+
+ if (sregs->cpdata & mask1)
+ qe_gc->cpdata |= mask1;
+ else
+ qe_gc->cpdata &= ~mask1;
+
+ out_be32(&regs->cpdata, qe_gc->cpdata);
+ clrsetbits_be32(&regs->cpodr, mask1, sregs->cpodr & mask1);
+
+ spin_unlock_irqrestore(&qe_gc->lock, flags);
+}
+EXPORT_SYMBOL(qe_pin_set_dedicated);
+
+/**
+ * qe_pin_set_gpio - Set a pin to the GPIO mode
+ * @qe_pin: pointer to the qe_pin structure
+ * Context: any
+ *
+ * This function sets a pin to the GPIO mode.
+ */
+void qe_pin_set_gpio(struct qe_pin *qe_pin)
+{
+ struct qe_gpio_chip *qe_gc = qe_pin->controller;
+ struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&qe_gc->lock, flags);
+
+ /* Let's make it input by default, GPIO API is able to change that. */
+ __par_io_config_pin(regs, qe_pin->num, QE_PIO_DIR_IN, 0, 0, 0);
+
+ spin_unlock_irqrestore(&qe_gc->lock, flags);
+}
+EXPORT_SYMBOL(qe_pin_set_gpio);
+
+static int __init qe_add_gpiochips(void)
+{
+ struct device_node *np;
+
+ for_each_compatible_node(np, NULL, "fsl,mpc8323-qe-pario-bank") {
+ int ret;
+ struct qe_gpio_chip *qe_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ struct gpio_chip *gc;
+
+ qe_gc = kzalloc(sizeof(*qe_gc), GFP_KERNEL);
+ if (!qe_gc) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ spin_lock_init(&qe_gc->lock);
+
+ mm_gc = &qe_gc->mm_gc;
+ gc = &mm_gc->gc;
+
+ mm_gc->save_regs = qe_gpio_save_regs;
+ gc->ngpio = QE_PIO_PINS;
+ gc->direction_input = qe_gpio_dir_in;
+ gc->direction_output = qe_gpio_dir_out;
+ gc->get = qe_gpio_get;
+ gc->set = qe_gpio_set;
+
+ ret = of_mm_gpiochip_add(np, mm_gc);
+ if (ret)
+ goto err;
+ continue;
+err:
+ pr_err("%s: registration failed with status %d\n",
+ np->full_name, ret);
+ kfree(qe_gc);
+ /* try others anyway */
+ }
+ return 0;
+}
+arch_initcall(qe_add_gpiochips);
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index cc81fd1141b..238a07b97f2 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved.
+ * Copyright (C) 2006-2010 Freescale Semiconductor, Inc. All rights reserved.
*
* Authors: Shlomi Gridish <gridish@freescale.com>
* Li Yang <leoli@freescale.com>
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
+#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/bootmem.h>
@@ -26,6 +27,8 @@
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/crc32.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_platform.h>
#include <asm/irq.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -35,10 +38,11 @@
#include <asm/rheap.h>
static void qe_snums_init(void);
-static void qe_muram_init(void);
static int qe_sdma_init(void);
static DEFINE_SPINLOCK(qe_lock);
+DEFINE_SPINLOCK(cmxgcr_lock);
+EXPORT_SYMBOL(cmxgcr_lock);
/* QE snum state */
enum qe_snum_state {
@@ -55,17 +59,18 @@ struct qe_snum {
/* We allocate this here because it is used almost exclusively for
* the communication processor devices.
*/
-struct qe_immap *qe_immr = NULL;
+struct qe_immap __iomem *qe_immr;
EXPORT_SYMBOL(qe_immr);
static struct qe_snum snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */
+static unsigned int qe_num_of_snum;
static phys_addr_t qebase = -1;
phys_addr_t get_qe_base(void)
{
struct device_node *qe;
- unsigned int size;
+ int size;
const u32 *prop;
if (qebase != -1)
@@ -109,6 +114,7 @@ int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input)
{
unsigned long flags;
u8 mcn_shift = 0, dev_shift = 0;
+ u32 ret;
spin_lock_irqsave(&qe_lock, flags);
if (cmd == QE_RESET) {
@@ -136,11 +142,13 @@ int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input)
}
/* wait for the QE_CR_FLG to clear */
- while(in_be32(&qe_immr->cp.cecr) & QE_CR_FLG)
- cpu_relax();
+ ret = spin_event_timeout((in_be32(&qe_immr->cp.cecr) & QE_CR_FLG) == 0,
+ 100, 0);
+ /* On timeout (e.g. failure), the expression will be false (ret == 0),
+ otherwise it will be true (ret == 1). */
spin_unlock_irqrestore(&qe_lock, flags);
- return 0;
+ return ret == 1;
}
EXPORT_SYMBOL(qe_issue_cmd);
@@ -156,10 +164,10 @@ EXPORT_SYMBOL(qe_issue_cmd);
*/
static unsigned int brg_clk = 0;
-unsigned int get_brg_clk(void)
+unsigned int qe_get_brg_clk(void)
{
struct device_node *qe;
- unsigned int size;
+ int size;
const u32 *prop;
if (brg_clk)
@@ -180,6 +188,7 @@ unsigned int get_brg_clk(void)
return brg_clk;
}
+EXPORT_SYMBOL(qe_get_brg_clk);
/* Program the BRG to the given sampling rate and multiplier
*
@@ -197,7 +206,7 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
if ((brg < QE_BRG1) || (brg > QE_BRG16))
return -EINVAL;
- divisor = get_brg_clk() / (rate * multiplier);
+ divisor = qe_get_brg_clk() / (rate * multiplier);
if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
div16 = QE_BRGC_DIV16;
@@ -207,7 +216,7 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
/* Errata QE_General4, which affects some MPC832x and MPC836x SOCs, says
that the BRG divisor must be even if you're not using divide-by-16
mode. */
- if (!div16 && (divisor & 1))
+ if (!div16 && (divisor & 1) && (divisor > 3))
divisor++;
tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) |
@@ -257,14 +266,36 @@ EXPORT_SYMBOL(qe_clock_source);
static void qe_snums_init(void)
{
int i;
- static const u8 snum_init[] = {
+ static const u8 snum_init_76[] = {
+ 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
+ 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89,
+ 0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9,
+ 0xD8, 0xD9, 0xE8, 0xE9, 0x44, 0x45, 0x4C, 0x4D,
+ 0x54, 0x55, 0x5C, 0x5D, 0x64, 0x65, 0x6C, 0x6D,
+ 0x74, 0x75, 0x7C, 0x7D, 0x84, 0x85, 0x8C, 0x8D,
+ 0x94, 0x95, 0x9C, 0x9D, 0xA4, 0xA5, 0xAC, 0xAD,
+ 0xB4, 0xB5, 0xBC, 0xBD, 0xC4, 0xC5, 0xCC, 0xCD,
+ 0xD4, 0xD5, 0xDC, 0xDD, 0xE4, 0xE5, 0xEC, 0xED,
+ 0xF4, 0xF5, 0xFC, 0xFD,
+ };
+ static const u8 snum_init_46[] = {
0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89,
0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9,
- 0xD8, 0xD9, 0xE8, 0xE9,
+ 0xD8, 0xD9, 0xE8, 0xE9, 0x08, 0x09, 0x18, 0x19,
+ 0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59,
+ 0x68, 0x69, 0x78, 0x79, 0x80, 0x81,
};
+ static const u8 *snum_init;
+
+ qe_num_of_snum = qe_get_num_of_snums();
- for (i = 0; i < QE_NUM_OF_SNUM; i++) {
+ if (qe_num_of_snum == 76)
+ snum_init = snum_init_76;
+ else
+ snum_init = snum_init_46;
+
+ for (i = 0; i < qe_num_of_snum; i++) {
snums[i].num = snum_init[i];
snums[i].state = QE_SNUM_STATE_FREE;
}
@@ -277,7 +308,7 @@ int qe_get_snum(void)
int i;
spin_lock_irqsave(&qe_lock, flags);
- for (i = 0; i < QE_NUM_OF_SNUM; i++) {
+ for (i = 0; i < qe_num_of_snum; i++) {
if (snums[i].state == QE_SNUM_STATE_FREE) {
snums[i].state = QE_SNUM_STATE_USED;
snum = snums[i].num;
@@ -294,7 +325,7 @@ void qe_put_snum(u8 snum)
{
int i;
- for (i = 0; i < QE_NUM_OF_SNUM; i++) {
+ for (i = 0; i < qe_num_of_snum; i++) {
if (snums[i].num == snum) {
snums[i].state = QE_SNUM_STATE_FREE;
break;
@@ -305,17 +336,19 @@ EXPORT_SYMBOL(qe_put_snum);
static int qe_sdma_init(void)
{
- struct sdma *sdma = &qe_immr->sdma;
- unsigned long sdma_buf_offset;
+ struct sdma __iomem *sdma = &qe_immr->sdma;
+ static unsigned long sdma_buf_offset = (unsigned long)-ENOMEM;
if (!sdma)
return -ENODEV;
/* allocate 2 internal temporary buffers (512 bytes size each) for
* the SDMA */
- sdma_buf_offset = qe_muram_alloc(512 * 2, 4096);
- if (IS_ERR_VALUE(sdma_buf_offset))
- return -ENOMEM;
+ if (IS_ERR_VALUE(sdma_buf_offset)) {
+ sdma_buf_offset = qe_muram_alloc(512 * 2, 4096);
+ if (IS_ERR_VALUE(sdma_buf_offset))
+ return -ENOMEM;
+ }
out_be32(&sdma->sdebcr, (u32) sdma_buf_offset & QE_SDEBCR_BA_MASK);
out_be32(&sdma->sdmr, (QE_SDMR_GLB_1_MSK |
@@ -324,105 +357,8 @@ static int qe_sdma_init(void)
return 0;
}
-/*
- * muram_alloc / muram_free bits.
- */
-static DEFINE_SPINLOCK(qe_muram_lock);
-
-/* 16 blocks should be enough to satisfy all requests
- * until the memory subsystem goes up... */
-static rh_block_t qe_boot_muram_rh_block[16];
-static rh_info_t qe_muram_info;
-
-static void qe_muram_init(void)
-{
- struct device_node *np;
- const u32 *address;
- u64 size;
- unsigned int flags;
-
- /* initialize the info header */
- rh_init(&qe_muram_info, 1,
- sizeof(qe_boot_muram_rh_block) /
- sizeof(qe_boot_muram_rh_block[0]), qe_boot_muram_rh_block);
-
- /* Attach the usable muram area */
- /* XXX: This is a subset of the available muram. It
- * varies with the processor and the microcode patches activated.
- */
- np = of_find_compatible_node(NULL, NULL, "fsl,qe-muram-data");
- if (!np) {
- np = of_find_node_by_name(NULL, "data-only");
- if (!np) {
- WARN_ON(1);
- return;
- }
- }
-
- address = of_get_address(np, 0, &size, &flags);
- WARN_ON(!address);
-
- of_node_put(np);
- if (address)
- rh_attach_region(&qe_muram_info, *address, (int)size);
-}
-
-/* This function returns an index into the MURAM area.
- */
-unsigned long qe_muram_alloc(int size, int align)
-{
- unsigned long start;
- unsigned long flags;
-
- spin_lock_irqsave(&qe_muram_lock, flags);
- start = rh_alloc_align(&qe_muram_info, size, align, "QE");
- spin_unlock_irqrestore(&qe_muram_lock, flags);
-
- return start;
-}
-EXPORT_SYMBOL(qe_muram_alloc);
-
-int qe_muram_free(unsigned long offset)
-{
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&qe_muram_lock, flags);
- ret = rh_free(&qe_muram_info, offset);
- spin_unlock_irqrestore(&qe_muram_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(qe_muram_free);
-
-/* not sure if this is ever needed */
-unsigned long qe_muram_alloc_fixed(unsigned long offset, int size)
-{
- unsigned long start;
- unsigned long flags;
-
- spin_lock_irqsave(&qe_muram_lock, flags);
- start = rh_alloc_fixed(&qe_muram_info, offset, size, "commproc");
- spin_unlock_irqrestore(&qe_muram_lock, flags);
-
- return start;
-}
-EXPORT_SYMBOL(qe_muram_alloc_fixed);
-
-void qe_muram_dump(void)
-{
- rh_dump(&qe_muram_info);
-}
-EXPORT_SYMBOL(qe_muram_dump);
-
-void *qe_muram_addr(unsigned long offset)
-{
- return (void *)&qe_immr->muram[offset];
-}
-EXPORT_SYMBOL(qe_muram_addr);
-
/* The maximum number of RISCs we support */
-#define MAX_QE_RISC 2
+#define MAX_QE_RISC 4
/* Firmware information stored here for qe_get_firmware_info() */
static struct qe_firmware_info qe_firmware_info;
@@ -459,12 +395,15 @@ static void qe_upload_microcode(const void *base,
for (i = 0; i < be32_to_cpu(ucode->count); i++)
out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i]));
+
+ /* Set I-RAM Ready Register */
+ out_be32(&qe_immr->iram.iready, be32_to_cpu(QE_IRAM_READY));
}
/*
* Upload a microcode to the I-RAM at a specific address.
*
- * See Documentation/powerpc/qe-firmware.txt for information on QE microcode
+ * See Documentation/powerpc/qe_firmware.txt for information on QE microcode
* uploading.
*
* Currently, only version 1 is supported, so the 'version' field must be
@@ -669,3 +608,101 @@ struct qe_firmware_info *qe_get_firmware_info(void)
}
EXPORT_SYMBOL(qe_get_firmware_info);
+unsigned int qe_get_num_of_risc(void)
+{
+ struct device_node *qe;
+ int size;
+ unsigned int num_of_risc = 0;
+ const u32 *prop;
+
+ qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
+ if (!qe) {
+ /* Older devices trees did not have an "fsl,qe"
+ * compatible property, so we need to look for
+ * the QE node by name.
+ */
+ qe = of_find_node_by_type(NULL, "qe");
+ if (!qe)
+ return num_of_risc;
+ }
+
+ prop = of_get_property(qe, "fsl,qe-num-riscs", &size);
+ if (prop && size == sizeof(*prop))
+ num_of_risc = *prop;
+
+ of_node_put(qe);
+
+ return num_of_risc;
+}
+EXPORT_SYMBOL(qe_get_num_of_risc);
+
+unsigned int qe_get_num_of_snums(void)
+{
+ struct device_node *qe;
+ int size;
+ unsigned int num_of_snums;
+ const u32 *prop;
+
+ num_of_snums = 28; /* The default number of snum for threads is 28 */
+ qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
+ if (!qe) {
+ /* Older devices trees did not have an "fsl,qe"
+ * compatible property, so we need to look for
+ * the QE node by name.
+ */
+ qe = of_find_node_by_type(NULL, "qe");
+ if (!qe)
+ return num_of_snums;
+ }
+
+ prop = of_get_property(qe, "fsl,qe-num-snums", &size);
+ if (prop && size == sizeof(*prop)) {
+ num_of_snums = *prop;
+ if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) {
+ /* No QE ever has fewer than 28 SNUMs */
+ pr_err("QE: number of snum is invalid\n");
+ of_node_put(qe);
+ return -EINVAL;
+ }
+ }
+
+ of_node_put(qe);
+
+ return num_of_snums;
+}
+EXPORT_SYMBOL(qe_get_num_of_snums);
+
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx)
+static int qe_resume(struct platform_device *ofdev)
+{
+ if (!qe_alive_during_sleep())
+ qe_reset();
+ return 0;
+}
+
+static int qe_probe(struct platform_device *ofdev)
+{
+ return 0;
+}
+
+static const struct of_device_id qe_ids[] = {
+ { .compatible = "fsl,qe", },
+ { },
+};
+
+static struct platform_driver qe_driver = {
+ .driver = {
+ .name = "fsl-qe",
+ .owner = THIS_MODULE,
+ .of_match_table = qe_ids,
+ },
+ .probe = qe_probe,
+ .resume = qe_resume,
+};
+
+static int __init qe_drv_init(void)
+{
+ return platform_driver_register(&qe_driver);
+}
+device_initcall(qe_drv_init);
+#endif /* defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx) */
diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c
index f59444d3be7..b2b87c30e26 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_ic.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c
@@ -1,7 +1,7 @@
/*
* arch/powerpc/sysdev/qe_lib/qe_ic.c
*
- * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved.
+ * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved.
*
* Author: Li Yang <leoli@freescale.com>
* Based on code from Shlomi Gridish <gridish@freescale.com>
@@ -22,7 +22,6 @@
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/signal.h>
-#include <linux/sysdev.h>
#include <linux/device.h>
#include <linux/bootmem.h>
#include <linux/spinlock.h>
@@ -33,7 +32,7 @@
#include "qe_ic.h"
-static DEFINE_SPINLOCK(qe_ic_lock);
+static DEFINE_RAW_SPINLOCK(qe_ic_lock);
static struct qe_ic_info qe_ic_info[] = {
[1] = {
@@ -189,35 +188,38 @@ static inline void qe_ic_write(volatile __be32 __iomem * base, unsigned int reg
static inline struct qe_ic *qe_ic_from_irq(unsigned int virq)
{
- return irq_desc[virq].chip_data;
+ return irq_get_chip_data(virq);
}
-#define virq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq)
+static inline struct qe_ic *qe_ic_from_irq_data(struct irq_data *d)
+{
+ return irq_data_get_irq_chip_data(d);
+}
-static void qe_ic_unmask_irq(unsigned int virq)
+static void qe_ic_unmask_irq(struct irq_data *d)
{
- struct qe_ic *qe_ic = qe_ic_from_irq(virq);
- unsigned int src = virq_to_hw(virq);
+ struct qe_ic *qe_ic = qe_ic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
u32 temp;
- spin_lock_irqsave(&qe_ic_lock, flags);
+ raw_spin_lock_irqsave(&qe_ic_lock, flags);
temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
temp | qe_ic_info[src].mask);
- spin_unlock_irqrestore(&qe_ic_lock, flags);
+ raw_spin_unlock_irqrestore(&qe_ic_lock, flags);
}
-static void qe_ic_mask_irq(unsigned int virq)
+static void qe_ic_mask_irq(struct irq_data *d)
{
- struct qe_ic *qe_ic = qe_ic_from_irq(virq);
- unsigned int src = virq_to_hw(virq);
+ struct qe_ic *qe_ic = qe_ic_from_irq_data(d);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
u32 temp;
- spin_lock_irqsave(&qe_ic_lock, flags);
+ raw_spin_lock_irqsave(&qe_ic_lock, flags);
temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
@@ -233,60 +235,47 @@ static void qe_ic_mask_irq(unsigned int virq)
*/
mb();
- spin_unlock_irqrestore(&qe_ic_lock, flags);
+ raw_spin_unlock_irqrestore(&qe_ic_lock, flags);
}
static struct irq_chip qe_ic_irq_chip = {
- .typename = " QEIC ",
- .unmask = qe_ic_unmask_irq,
- .mask = qe_ic_mask_irq,
- .mask_ack = qe_ic_mask_irq,
+ .name = "QEIC",
+ .irq_unmask = qe_ic_unmask_irq,
+ .irq_mask = qe_ic_mask_irq,
+ .irq_mask_ack = qe_ic_mask_irq,
};
-static int qe_ic_host_match(struct irq_host *h, struct device_node *node)
+static int qe_ic_host_match(struct irq_domain *h, struct device_node *node)
{
/* Exact match, unless qe_ic node is NULL */
return h->of_node == NULL || h->of_node == node;
}
-static int qe_ic_host_map(struct irq_host *h, unsigned int virq,
+static int qe_ic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct qe_ic *qe_ic = h->host_data;
struct irq_chip *chip;
if (qe_ic_info[hw].mask == 0) {
- printk(KERN_ERR "Can't map reserved IRQ \n");
+ printk(KERN_ERR "Can't map reserved IRQ\n");
return -EINVAL;
}
/* Default chip */
chip = &qe_ic->hc_irq;
- set_irq_chip_data(virq, qe_ic);
- get_irq_desc(virq)->status |= IRQ_LEVEL;
+ irq_set_chip_data(virq, qe_ic);
+ irq_set_status_flags(virq, IRQ_LEVEL);
- set_irq_chip_and_handler(virq, chip, handle_level_irq);
+ irq_set_chip_and_handler(virq, chip, handle_level_irq);
return 0;
}
-static int qe_ic_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 * intspec, unsigned int intsize,
- irq_hw_number_t * out_hwirq,
- unsigned int *out_flags)
-{
- *out_hwirq = intspec[0];
- if (intsize > 1)
- *out_flags = intspec[1];
- else
- *out_flags = IRQ_TYPE_NONE;
- return 0;
-}
-
-static struct irq_host_ops qe_ic_host_ops = {
+static struct irq_domain_ops qe_ic_host_ops = {
.match = qe_ic_host_match,
.map = qe_ic_host_map,
- .xlate = qe_ic_host_xlate,
+ .xlate = irq_domain_xlate_onetwocell,
};
/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */
@@ -329,26 +318,23 @@ void __init qe_ic_init(struct device_node *node, unsigned int flags,
struct resource res;
u32 temp = 0, ret, high_active = 0;
- qe_ic = alloc_bootmem(sizeof(struct qe_ic));
- if (qe_ic == NULL)
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret)
return;
- memset(qe_ic, 0, sizeof(struct qe_ic));
+ qe_ic = kzalloc(sizeof(*qe_ic), GFP_KERNEL);
+ if (qe_ic == NULL)
+ return;
- qe_ic->irqhost = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LINEAR,
- NR_QE_IC_INTS, &qe_ic_host_ops, 0);
+ qe_ic->irqhost = irq_domain_add_linear(node, NR_QE_IC_INTS,
+ &qe_ic_host_ops, qe_ic);
if (qe_ic->irqhost == NULL) {
- of_node_put(node);
+ kfree(qe_ic);
return;
}
- ret = of_address_to_resource(node, 0, &res);
- if (ret)
- return;
-
- qe_ic->regs = ioremap(res.start, res.end - res.start + 1);
+ qe_ic->regs = ioremap(res.start, resource_size(&res));
- qe_ic->irqhost->host_data = qe_ic;
qe_ic->hc_irq = qe_ic_irq_chip;
qe_ic->virq_high = irq_of_parse_and_map(node, 0);
@@ -356,6 +342,7 @@ void __init qe_ic_init(struct device_node *node, unsigned int flags,
if (qe_ic->virq_low == NO_IRQ) {
printk(KERN_ERR "Failed to map QE_IC low IRQ\n");
+ kfree(qe_ic);
return;
}
@@ -382,13 +369,13 @@ void __init qe_ic_init(struct device_node *node, unsigned int flags,
qe_ic_write(qe_ic->regs, QEIC_CICR, temp);
- set_irq_data(qe_ic->virq_low, qe_ic);
- set_irq_chained_handler(qe_ic->virq_low, low_handler);
+ irq_set_handler_data(qe_ic->virq_low, qe_ic);
+ irq_set_chained_handler(qe_ic->virq_low, low_handler);
if (qe_ic->virq_high != NO_IRQ &&
qe_ic->virq_high != qe_ic->virq_low) {
- set_irq_data(qe_ic->virq_high, qe_ic);
- set_irq_chained_handler(qe_ic->virq_high, high_handler);
+ irq_set_handler_data(qe_ic->virq_high, qe_ic);
+ irq_set_chained_handler(qe_ic->virq_high, high_handler);
}
}
@@ -482,13 +469,14 @@ int qe_ic_set_high_priority(unsigned int virq, unsigned int priority, int high)
return 0;
}
-static struct sysdev_class qe_ic_sysclass = {
+static struct bus_type qe_ic_subsys = {
.name = "qe_ic",
+ .dev_name = "qe_ic",
};
-static struct sys_device device_qe_ic = {
+static struct device device_qe_ic = {
.id = 0,
- .cls = &qe_ic_sysclass,
+ .bus = &qe_ic_subsys,
};
static int __init init_qe_ic_sysfs(void)
@@ -497,12 +485,12 @@ static int __init init_qe_ic_sysfs(void)
printk(KERN_DEBUG "Registering qe_ic with sysfs...\n");
- rc = sysdev_class_register(&qe_ic_sysclass);
+ rc = subsys_system_register(&qe_ic_subsys, NULL);
if (rc) {
printk(KERN_ERR "Failed registering qe_ic sys class\n");
return -ENODEV;
}
- rc = sysdev_register(&device_qe_ic);
+ rc = device_register(&device_qe_ic);
if (rc) {
printk(KERN_ERR "Failed registering qe_ic sys device\n");
return -ENODEV;
diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.h b/arch/powerpc/sysdev/qe_lib/qe_ic.h
index c1361d005a8..efef7ab9b75 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_ic.h
+++ b/arch/powerpc/sysdev/qe_lib/qe_ic.h
@@ -3,7 +3,7 @@
*
* QUICC ENGINE Interrupt Controller Header
*
- * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved.
+ * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved.
*
* Author: Li Yang <leoli@freescale.com>
* Based on code from Shlomi Gridish <gridish@freescale.com>
@@ -79,7 +79,7 @@ struct qe_ic {
volatile u32 __iomem *regs;
/* The remapper for this QEIC */
- struct irq_host *irqhost;
+ struct irq_domain *irqhost;
/* The "linux" controller struct */
struct irq_chip hc_irq;
diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c
index e53ea4d374a..d09994164da 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -3,7 +3,7 @@
*
* QE Parallel I/O ports configuration routines
*
- * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
+ * Copyright 2006 Freescale Semiconductor, Inc. All rights reserved.
*
* Author: Li Yang <LeoLi@freescale.com>
* Based on code from Shlomi Gridish <gridish@freescale.com>
@@ -16,32 +16,18 @@
#include <linux/stddef.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <asm/io.h>
+#include <asm/qe.h>
#include <asm/prom.h>
#include <sysdev/fsl_soc.h>
#undef DEBUG
-#define NUM_OF_PINS 32
-
-struct port_regs {
- __be32 cpodr; /* Open drain register */
- __be32 cpdata; /* Data register */
- __be32 cpdir1; /* Direction register */
- __be32 cpdir2; /* Direction register */
- __be32 cppar1; /* Pin assignment register */
- __be32 cppar2; /* Pin assignment register */
-#ifdef CONFIG_PPC_85xx
- u8 pad[8];
-#endif
-};
-
-static struct port_regs *par_io = NULL;
+static struct qe_pio_regs __iomem *par_io;
static int num_par_io_ports = 0;
int par_io_init(struct device_node *np)
@@ -54,7 +40,7 @@ int par_io_init(struct device_node *np)
ret = of_address_to_resource(np, 0, &res);
if (ret)
return ret;
- par_io = ioremap(res.start, res.end - res.start + 1);
+ par_io = ioremap(res.start, resource_size(&res));
num_ports = of_get_property(np, "num-ports", NULL);
if (num_ports)
@@ -63,69 +49,79 @@ int par_io_init(struct device_node *np)
return 0;
}
-int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
- int assignment, int has_irq)
+void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, int dir,
+ int open_drain, int assignment, int has_irq)
{
- u32 pin_mask1bit, pin_mask2bits, new_mask2bits, tmp_val;
-
- if (!par_io)
- return -1;
+ u32 pin_mask1bit;
+ u32 pin_mask2bits;
+ u32 new_mask2bits;
+ u32 tmp_val;
/* calculate pin location for single and 2 bits information */
- pin_mask1bit = (u32) (1 << (NUM_OF_PINS - (pin + 1)));
+ pin_mask1bit = (u32) (1 << (QE_PIO_PINS - (pin + 1)));
/* Set open drain, if required */
- tmp_val = in_be32(&par_io[port].cpodr);
+ tmp_val = in_be32(&par_io->cpodr);
if (open_drain)
- out_be32(&par_io[port].cpodr, pin_mask1bit | tmp_val);
+ out_be32(&par_io->cpodr, pin_mask1bit | tmp_val);
else
- out_be32(&par_io[port].cpodr, ~pin_mask1bit & tmp_val);
+ out_be32(&par_io->cpodr, ~pin_mask1bit & tmp_val);
/* define direction */
- tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ?
- in_be32(&par_io[port].cpdir2) :
- in_be32(&par_io[port].cpdir1);
+ tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ?
+ in_be32(&par_io->cpdir2) :
+ in_be32(&par_io->cpdir1);
/* get all bits mask for 2 bit per port */
- pin_mask2bits = (u32) (0x3 << (NUM_OF_PINS -
- (pin % (NUM_OF_PINS / 2) + 1) * 2));
+ pin_mask2bits = (u32) (0x3 << (QE_PIO_PINS -
+ (pin % (QE_PIO_PINS / 2) + 1) * 2));
/* Get the final mask we need for the right definition */
- new_mask2bits = (u32) (dir << (NUM_OF_PINS -
- (pin % (NUM_OF_PINS / 2) + 1) * 2));
+ new_mask2bits = (u32) (dir << (QE_PIO_PINS -
+ (pin % (QE_PIO_PINS / 2) + 1) * 2));
/* clear and set 2 bits mask */
- if (pin > (NUM_OF_PINS / 2) - 1) {
- out_be32(&par_io[port].cpdir2,
+ if (pin > (QE_PIO_PINS / 2) - 1) {
+ out_be32(&par_io->cpdir2,
~pin_mask2bits & tmp_val);
tmp_val &= ~pin_mask2bits;
- out_be32(&par_io[port].cpdir2, new_mask2bits | tmp_val);
+ out_be32(&par_io->cpdir2, new_mask2bits | tmp_val);
} else {
- out_be32(&par_io[port].cpdir1,
+ out_be32(&par_io->cpdir1,
~pin_mask2bits & tmp_val);
tmp_val &= ~pin_mask2bits;
- out_be32(&par_io[port].cpdir1, new_mask2bits | tmp_val);
+ out_be32(&par_io->cpdir1, new_mask2bits | tmp_val);
}
/* define pin assignment */
- tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ?
- in_be32(&par_io[port].cppar2) :
- in_be32(&par_io[port].cppar1);
+ tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ?
+ in_be32(&par_io->cppar2) :
+ in_be32(&par_io->cppar1);
- new_mask2bits = (u32) (assignment << (NUM_OF_PINS -
- (pin % (NUM_OF_PINS / 2) + 1) * 2));
+ new_mask2bits = (u32) (assignment << (QE_PIO_PINS -
+ (pin % (QE_PIO_PINS / 2) + 1) * 2));
/* clear and set 2 bits mask */
- if (pin > (NUM_OF_PINS / 2) - 1) {
- out_be32(&par_io[port].cppar2,
+ if (pin > (QE_PIO_PINS / 2) - 1) {
+ out_be32(&par_io->cppar2,
~pin_mask2bits & tmp_val);
tmp_val &= ~pin_mask2bits;
- out_be32(&par_io[port].cppar2, new_mask2bits | tmp_val);
+ out_be32(&par_io->cppar2, new_mask2bits | tmp_val);
} else {
- out_be32(&par_io[port].cppar1,
+ out_be32(&par_io->cppar1,
~pin_mask2bits & tmp_val);
tmp_val &= ~pin_mask2bits;
- out_be32(&par_io[port].cppar1, new_mask2bits | tmp_val);
+ out_be32(&par_io->cppar1, new_mask2bits | tmp_val);
}
+}
+EXPORT_SYMBOL(__par_io_config_pin);
+
+int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
+ int assignment, int has_irq)
+{
+ if (!par_io || port >= num_par_io_ports)
+ return -EINVAL;
+ __par_io_config_pin(&par_io[port], pin, dir, open_drain, assignment,
+ has_irq);
return 0;
}
EXPORT_SYMBOL(par_io_config_pin);
@@ -136,10 +132,10 @@ int par_io_data_set(u8 port, u8 pin, u8 val)
if (port >= num_par_io_ports)
return -EINVAL;
- if (pin >= NUM_OF_PINS)
+ if (pin >= QE_PIO_PINS)
return -EINVAL;
/* calculate pin location */
- pin_mask = (u32) (1 << (NUM_OF_PINS - 1 - pin));
+ pin_mask = (u32) (1 << (QE_PIO_PINS - 1 - pin));
tmp_val = in_be32(&par_io[port].cpdata);
@@ -160,13 +156,13 @@ int par_io_of_config(struct device_node *np)
const unsigned int *pio_map;
if (par_io == NULL) {
- printk(KERN_ERR "par_io not initialized \n");
+ printk(KERN_ERR "par_io not initialized\n");
return -1;
}
ph = of_get_property(np, "pio-handle", NULL);
- if (ph == 0) {
- printk(KERN_ERR "pio-handle not available \n");
+ if (ph == NULL) {
+ printk(KERN_ERR "pio-handle not available\n");
return -1;
}
@@ -174,12 +170,12 @@ int par_io_of_config(struct device_node *np)
pio_map = of_get_property(pio, "pio-map", &pio_map_len);
if (pio_map == NULL) {
- printk(KERN_ERR "pio-map is not set! \n");
+ printk(KERN_ERR "pio-map is not set!\n");
return -1;
}
pio_map_len /= sizeof(unsigned int);
if ((pio_map_len % 6) != 0) {
- printk(KERN_ERR "pio-map format wrong! \n");
+ printk(KERN_ERR "pio-map format wrong!\n");
return -1;
}
@@ -200,7 +196,7 @@ static void dump_par_io(void)
{
unsigned int i;
- printk(KERN_INFO "%s: par_io=%p\n", __FUNCTION__, par_io);
+ printk(KERN_INFO "%s: par_io=%p\n", __func__, par_io);
for (i = 0; i < num_par_io_ports; i++) {
printk(KERN_INFO " cpodr[%u]=%08x\n", i,
in_be32(&par_io[i].cpodr));
diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c
index 0e348d9af8a..621575b7e84 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc.c
@@ -3,7 +3,7 @@
*
* QE UCC API Set - UCC specific routines implementations.
*
- * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved.
+ * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved.
*
* Authors: Shlomi Gridish <gridish@freescale.com>
* Li Yang <leoli@freescale.com>
@@ -14,11 +14,10 @@
* option) any later version.
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/errno.h>
-#include <linux/slab.h>
#include <linux/stddef.h>
-#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/export.h>
#include <asm/irq.h>
#include <asm/io.h>
@@ -26,8 +25,6 @@
#include <asm/qe.h>
#include <asm/ucc.h>
-static DEFINE_SPINLOCK(ucc_lock);
-
int ucc_set_qe_mux_mii_mng(unsigned int ucc_num)
{
unsigned long flags;
@@ -35,10 +32,10 @@ int ucc_set_qe_mux_mii_mng(unsigned int ucc_num)
if (ucc_num > UCC_MAX_NUM - 1)
return -EINVAL;
- spin_lock_irqsave(&ucc_lock, flags);
+ spin_lock_irqsave(&cmxgcr_lock, flags);
clrsetbits_be32(&qe_immr->qmx.cmxgcr, QE_CMXGCR_MII_ENET_MNG,
ucc_num << QE_CMXGCR_MII_ENET_MNG_SHIFT);
- spin_unlock_irqrestore(&ucc_lock, flags);
+ spin_unlock_irqrestore(&cmxgcr_lock, flags);
return 0;
}
@@ -87,7 +84,7 @@ int ucc_set_type(unsigned int ucc_num, enum ucc_speed_type speed)
return 0;
}
-static void get_cmxucr_reg(unsigned int ucc_num, __be32 **cmxucr,
+static void get_cmxucr_reg(unsigned int ucc_num, __be32 __iomem **cmxucr,
unsigned int *reg_num, unsigned int *shift)
{
unsigned int cmx = ((ucc_num & 1) << 1) + (ucc_num > 3);
@@ -99,7 +96,7 @@ static void get_cmxucr_reg(unsigned int ucc_num, __be32 **cmxucr,
int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask)
{
- __be32 *cmxucr;
+ __be32 __iomem *cmxucr;
unsigned int reg_num;
unsigned int shift;
@@ -120,7 +117,7 @@ int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask)
int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
enum comm_dir mode)
{
- __be32 *cmxucr;
+ __be32 __iomem *cmxucr;
unsigned int reg_num;
unsigned int shift;
u32 clock_bits = 0;
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
index 3223acbc39e..65aaf15032a 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved.
+ * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved.
*
* Authors: Shlomi Gridish <gridish@freescale.com>
* Li Yang <leoli@freescale.com>
@@ -13,13 +13,12 @@
* option) any later version.
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/interrupt.h>
#include <linux/err.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <asm/io.h>
#include <asm/immap_qe.h>
@@ -46,7 +45,7 @@ void ucc_fast_dump_regs(struct ucc_fast_private * uccf)
printk(KERN_INFO "uccm : addr=0x%p, val=0x%08x\n",
&uccf->uf_regs->uccm, in_be32(&uccf->uf_regs->uccm));
printk(KERN_INFO "uccs : addr=0x%p, val=0x%02x\n",
- &uccf->uf_regs->uccs, uccf->uf_regs->uccs);
+ &uccf->uf_regs->uccs, in_8(&uccf->uf_regs->uccs));
printk(KERN_INFO "urfb : addr=0x%p, val=0x%08x\n",
&uccf->uf_regs->urfb, in_be32(&uccf->uf_regs->urfb));
printk(KERN_INFO "urfs : addr=0x%p, val=0x%04x\n",
@@ -68,7 +67,7 @@ void ucc_fast_dump_regs(struct ucc_fast_private * uccf)
printk(KERN_INFO "urtry : addr=0x%p, val=0x%08x\n",
&uccf->uf_regs->urtry, in_be32(&uccf->uf_regs->urtry));
printk(KERN_INFO "guemr : addr=0x%p, val=0x%02x\n",
- &uccf->uf_regs->guemr, uccf->uf_regs->guemr);
+ &uccf->uf_regs->guemr, in_8(&uccf->uf_regs->guemr));
}
EXPORT_SYMBOL(ucc_fast_dump_regs);
@@ -96,7 +95,7 @@ EXPORT_SYMBOL(ucc_fast_transmit_on_demand);
void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode)
{
- struct ucc_fast *uf_regs;
+ struct ucc_fast __iomem *uf_regs;
u32 gumr;
uf_regs = uccf->uf_regs;
@@ -117,7 +116,7 @@ EXPORT_SYMBOL(ucc_fast_enable);
void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode)
{
- struct ucc_fast *uf_regs;
+ struct ucc_fast __iomem *uf_regs;
u32 gumr;
uf_regs = uccf->uf_regs;
@@ -139,7 +138,7 @@ EXPORT_SYMBOL(ucc_fast_disable);
int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret)
{
struct ucc_fast_private *uccf;
- struct ucc_fast *uf_regs;
+ struct ucc_fast __iomem *uf_regs;
u32 gumr;
int ret;
@@ -148,57 +147,57 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
/* check if the UCC port number is in range. */
if ((uf_info->ucc_num < 0) || (uf_info->ucc_num > UCC_MAX_NUM - 1)) {
- printk(KERN_ERR "%s: illegal UCC number\n", __FUNCTION__);
+ printk(KERN_ERR "%s: illegal UCC number\n", __func__);
return -EINVAL;
}
/* Check that 'max_rx_buf_length' is properly aligned (4). */
if (uf_info->max_rx_buf_length & (UCC_FAST_MRBLR_ALIGNMENT - 1)) {
printk(KERN_ERR "%s: max_rx_buf_length not aligned\n",
- __FUNCTION__);
+ __func__);
return -EINVAL;
}
/* Validate Virtual Fifo register values */
if (uf_info->urfs < UCC_FAST_URFS_MIN_VAL) {
- printk(KERN_ERR "%s: urfs is too small\n", __FUNCTION__);
+ printk(KERN_ERR "%s: urfs is too small\n", __func__);
return -EINVAL;
}
if (uf_info->urfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
- printk(KERN_ERR "%s: urfs is not aligned\n", __FUNCTION__);
+ printk(KERN_ERR "%s: urfs is not aligned\n", __func__);
return -EINVAL;
}
if (uf_info->urfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
- printk(KERN_ERR "%s: urfet is not aligned.\n", __FUNCTION__);
+ printk(KERN_ERR "%s: urfet is not aligned.\n", __func__);
return -EINVAL;
}
if (uf_info->urfset & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
- printk(KERN_ERR "%s: urfset is not aligned\n", __FUNCTION__);
+ printk(KERN_ERR "%s: urfset is not aligned\n", __func__);
return -EINVAL;
}
if (uf_info->utfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
- printk(KERN_ERR "%s: utfs is not aligned\n", __FUNCTION__);
+ printk(KERN_ERR "%s: utfs is not aligned\n", __func__);
return -EINVAL;
}
if (uf_info->utfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
- printk(KERN_ERR "%s: utfet is not aligned\n", __FUNCTION__);
+ printk(KERN_ERR "%s: utfet is not aligned\n", __func__);
return -EINVAL;
}
if (uf_info->utftt & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) {
- printk(KERN_ERR "%s: utftt is not aligned\n", __FUNCTION__);
+ printk(KERN_ERR "%s: utftt is not aligned\n", __func__);
return -EINVAL;
}
uccf = kzalloc(sizeof(struct ucc_fast_private), GFP_KERNEL);
if (!uccf) {
printk(KERN_ERR "%s: Cannot allocate private data\n",
- __FUNCTION__);
+ __func__);
return -ENOMEM;
}
@@ -207,7 +206,8 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
/* Set the PHY base address */
uccf->uf_regs = ioremap(uf_info->regs, sizeof(struct ucc_fast));
if (uccf->uf_regs == NULL) {
- printk(KERN_ERR "%s: Cannot map UCC registers\n", __FUNCTION__);
+ printk(KERN_ERR "%s: Cannot map UCC registers\n", __func__);
+ kfree(uccf);
return -ENOMEM;
}
@@ -216,10 +216,10 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
uccf->stopped_tx = 0;
uccf->stopped_rx = 0;
uf_regs = uccf->uf_regs;
- uccf->p_ucce = (u32 *) & (uf_regs->ucce);
- uccf->p_uccm = (u32 *) & (uf_regs->uccm);
+ uccf->p_ucce = &uf_regs->ucce;
+ uccf->p_uccm = &uf_regs->uccm;
#ifdef CONFIG_UGETH_TX_ON_DEMAND
- uccf->p_utodr = (u16 *) & (uf_regs->utodr);
+ uccf->p_utodr = &uf_regs->utodr;
#endif
#ifdef STATISTICS
uccf->tx_frames = 0;
@@ -230,7 +230,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
/* Set UCC to fast type */
ret = ucc_set_type(uf_info->ucc_num, UCC_SPEED_TYPE_FAST);
if (ret) {
- printk(KERN_ERR "%s: cannot set UCC type\n", __FUNCTION__);
+ printk(KERN_ERR "%s: cannot set UCC type\n", __func__);
ucc_fast_free(uccf);
return ret;
}
@@ -270,7 +270,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
qe_muram_alloc(uf_info->utfs, UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT);
if (IS_ERR_VALUE(uccf->ucc_fast_tx_virtual_fifo_base_offset)) {
printk(KERN_ERR "%s: cannot allocate MURAM for TX FIFO\n",
- __FUNCTION__);
+ __func__);
uccf->ucc_fast_tx_virtual_fifo_base_offset = 0;
ucc_fast_free(uccf);
return -ENOMEM;
@@ -283,7 +283,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT);
if (IS_ERR_VALUE(uccf->ucc_fast_rx_virtual_fifo_base_offset)) {
printk(KERN_ERR "%s: cannot allocate MURAM for RX FIFO\n",
- __FUNCTION__);
+ __func__);
uccf->ucc_fast_rx_virtual_fifo_base_offset = 0;
ucc_fast_free(uccf);
return -ENOMEM;
@@ -314,7 +314,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
ucc_set_qe_mux_rxtx(uf_info->ucc_num, uf_info->rx_clock,
COMM_DIR_RX)) {
printk(KERN_ERR "%s: illegal value for RX clock\n",
- __FUNCTION__);
+ __func__);
ucc_fast_free(uccf);
return -EINVAL;
}
@@ -323,7 +323,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
ucc_set_qe_mux_rxtx(uf_info->ucc_num, uf_info->tx_clock,
COMM_DIR_TX)) {
printk(KERN_ERR "%s: illegal value for TX clock\n",
- __FUNCTION__);
+ __func__);
ucc_fast_free(uccf);
return -EINVAL;
}
@@ -355,6 +355,9 @@ void ucc_fast_free(struct ucc_fast_private * uccf)
if (uccf->ucc_fast_rx_virtual_fifo_base_offset)
qe_muram_free(uccf->ucc_fast_rx_virtual_fifo_base_offset);
+ if (uccf->uf_regs)
+ iounmap(uccf->uf_regs);
+
kfree(uccf);
}
EXPORT_SYMBOL(ucc_fast_free);
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_slow.c b/arch/powerpc/sysdev/qe_lib/ucc_slow.c
index b2870b208dd..befaf1123f7 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_slow.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc_slow.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved.
+ * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved.
*
* Authors: Shlomi Gridish <gridish@freescale.com>
* Li Yang <leoli@freescale.com>
@@ -13,13 +13,12 @@
* option) any later version.
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/interrupt.h>
#include <linux/err.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <asm/io.h>
#include <asm/immap_qe.h>
@@ -142,7 +141,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
/* check if the UCC port number is in range. */
if ((us_info->ucc_num < 0) || (us_info->ucc_num > UCC_MAX_NUM - 1)) {
- printk(KERN_ERR "%s: illegal UCC number\n", __FUNCTION__);
+ printk(KERN_ERR "%s: illegal UCC number\n", __func__);
return -EINVAL;
}
@@ -161,7 +160,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
uccs = kzalloc(sizeof(struct ucc_slow_private), GFP_KERNEL);
if (!uccs) {
printk(KERN_ERR "%s: Cannot allocate private data\n",
- __FUNCTION__);
+ __func__);
return -ENOMEM;
}
@@ -170,7 +169,8 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
/* Set the PHY base address */
uccs->us_regs = ioremap(us_info->regs, sizeof(struct ucc_slow));
if (uccs->us_regs == NULL) {
- printk(KERN_ERR "%s: Cannot map UCC registers\n", __FUNCTION__);
+ printk(KERN_ERR "%s: Cannot map UCC registers\n", __func__);
+ kfree(uccs);
return -ENOMEM;
}
@@ -189,7 +189,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
uccs->us_pram_offset =
qe_muram_alloc(UCC_SLOW_PRAM_SIZE, ALIGNMENT_OF_UCC_SLOW_PRAM);
if (IS_ERR_VALUE(uccs->us_pram_offset)) {
- printk(KERN_ERR "%s: cannot allocate MURAM for PRAM", __FUNCTION__);
+ printk(KERN_ERR "%s: cannot allocate MURAM for PRAM", __func__);
ucc_slow_free(uccs);
return -ENOMEM;
}
@@ -202,7 +202,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
/* Set UCC to slow type */
ret = ucc_set_type(us_info->ucc_num, UCC_SPEED_TYPE_SLOW);
if (ret) {
- printk(KERN_ERR "%s: cannot set UCC type", __FUNCTION__);
+ printk(KERN_ERR "%s: cannot set UCC type", __func__);
ucc_slow_free(uccs);
return ret;
}
@@ -216,7 +216,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
qe_muram_alloc(us_info->rx_bd_ring_len * sizeof(struct qe_bd),
QE_ALIGNMENT_OF_BD);
if (IS_ERR_VALUE(uccs->rx_base_offset)) {
- printk(KERN_ERR "%s: cannot allocate %u RX BDs\n", __FUNCTION__,
+ printk(KERN_ERR "%s: cannot allocate %u RX BDs\n", __func__,
us_info->rx_bd_ring_len);
uccs->rx_base_offset = 0;
ucc_slow_free(uccs);
@@ -227,7 +227,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
qe_muram_alloc(us_info->tx_bd_ring_len * sizeof(struct qe_bd),
QE_ALIGNMENT_OF_BD);
if (IS_ERR_VALUE(uccs->tx_base_offset)) {
- printk(KERN_ERR "%s: cannot allocate TX BDs", __FUNCTION__);
+ printk(KERN_ERR "%s: cannot allocate TX BDs", __func__);
uccs->tx_base_offset = 0;
ucc_slow_free(uccs);
return -ENOMEM;
@@ -317,7 +317,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->rx_clock,
COMM_DIR_RX)) {
printk(KERN_ERR "%s: illegal value for RX clock\n",
- __FUNCTION__);
+ __func__);
ucc_slow_free(uccs);
return -EINVAL;
}
@@ -325,7 +325,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->tx_clock,
COMM_DIR_TX)) {
printk(KERN_ERR "%s: illegal value for TX clock\n",
- __FUNCTION__);
+ __func__);
ucc_slow_free(uccs);
return -EINVAL;
}
@@ -367,10 +367,11 @@ void ucc_slow_free(struct ucc_slow_private * uccs)
if (uccs->tx_base_offset)
qe_muram_free(uccs->tx_base_offset);
- if (uccs->us_pram) {
+ if (uccs->us_pram)
qe_muram_free(uccs->us_pram_offset);
- uccs->us_pram = NULL;
- }
+
+ if (uccs->us_regs)
+ iounmap(uccs->us_regs);
kfree(uccs);
}
diff --git a/arch/powerpc/sysdev/qe_lib/usb.c b/arch/powerpc/sysdev/qe_lib/usb.c
new file mode 100644
index 00000000000..27f23bd15eb
--- /dev/null
+++ b/arch/powerpc/sysdev/qe_lib/usb.c
@@ -0,0 +1,56 @@
+/*
+ * QE USB routines
+ *
+ * Copyright 2006 Freescale Semiconductor, Inc.
+ * Shlomi Gridish <gridish@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ * Anton Vorontsov <avorontsov@ru.mvista.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 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <asm/immap_qe.h>
+#include <asm/qe.h>
+
+int qe_usb_clock_set(enum qe_clock clk, int rate)
+{
+ struct qe_mux __iomem *mux = &qe_immr->qmx;
+ unsigned long flags;
+ u32 val;
+
+ switch (clk) {
+ case QE_CLK3: val = QE_CMXGCR_USBCS_CLK3; break;
+ case QE_CLK5: val = QE_CMXGCR_USBCS_CLK5; break;
+ case QE_CLK7: val = QE_CMXGCR_USBCS_CLK7; break;
+ case QE_CLK9: val = QE_CMXGCR_USBCS_CLK9; break;
+ case QE_CLK13: val = QE_CMXGCR_USBCS_CLK13; break;
+ case QE_CLK17: val = QE_CMXGCR_USBCS_CLK17; break;
+ case QE_CLK19: val = QE_CMXGCR_USBCS_CLK19; break;
+ case QE_CLK21: val = QE_CMXGCR_USBCS_CLK21; break;
+ case QE_BRG9: val = QE_CMXGCR_USBCS_BRG9; break;
+ case QE_BRG10: val = QE_CMXGCR_USBCS_BRG10; break;
+ default:
+ pr_err("%s: requested unknown clock %d\n", __func__, clk);
+ return -EINVAL;
+ }
+
+ if (qe_clock_is_brg(clk))
+ qe_setbrg(clk, rate, 1);
+
+ spin_lock_irqsave(&cmxgcr_lock, flags);
+
+ clrsetbits_be32(&mux->cmxgcr, QE_CMXGCR_USBCS, val);
+
+ spin_unlock_irqrestore(&cmxgcr_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(qe_usb_clock_set);
diff --git a/arch/powerpc/sysdev/rtc_cmos_setup.c b/arch/powerpc/sysdev/rtc_cmos_setup.c
index 0c9ac7ee08f..af0f9beddca 100644
--- a/arch/powerpc/sysdev/rtc_cmos_setup.c
+++ b/arch/powerpc/sysdev/rtc_cmos_setup.c
@@ -12,6 +12,7 @@
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/mc146818rtc.h>
#include <asm/prom.h>
@@ -21,6 +22,7 @@ static int __init add_rtc(void)
struct device_node *np;
struct platform_device *pd;
struct resource res[2];
+ unsigned int num_res = 1;
int ret;
memset(&res, 0, sizeof(res));
@@ -41,18 +43,27 @@ static int __init add_rtc(void)
if (res[0].start != RTC_PORT(0))
return -EINVAL;
- /* Use a fixed interrupt value of 8 since on PPC if we are using this
- * its off an i8259 which we ensure has interrupt numbers 0..15. */
- res[1].start = 8;
- res[1].end = 8;
- res[1].flags = IORESOURCE_IRQ;
+ np = of_find_compatible_node(NULL, NULL, "chrp,iic");
+ if (!np)
+ np = of_find_compatible_node(NULL, NULL, "pnpPNP,000");
+ if (np) {
+ of_node_put(np);
+ /*
+ * Use a fixed interrupt value of 8 since on PPC if we are
+ * using this its off an i8259 which we ensure has interrupt
+ * numbers 0..15.
+ */
+ res[1].start = 8;
+ res[1].end = 8;
+ res[1].flags = IORESOURCE_IRQ;
+ num_res++;
+ }
pd = platform_device_register_simple("rtc_cmos", -1,
- &res[0], 2);
-
- if (IS_ERR(pd))
- return PTR_ERR(pd);
+ &res[0], num_res);
- return 0;
+ return PTR_ERR_OR_ZERO(pd);
}
fs_initcall(add_rtc);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/powerpc/sysdev/scom.c b/arch/powerpc/sysdev/scom.c
new file mode 100644
index 00000000000..6f5a8d177c4
--- /dev/null
+++ b/arch/powerpc/sysdev/scom.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
+ * <benh@kernel.crashing.org>
+ * and David Gibson, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <asm/debug.h>
+#include <asm/prom.h>
+#include <asm/scom.h>
+#include <asm/uaccess.h>
+
+const struct scom_controller *scom_controller;
+EXPORT_SYMBOL_GPL(scom_controller);
+
+struct device_node *scom_find_parent(struct device_node *node)
+{
+ struct device_node *par, *tmp;
+ const u32 *p;
+
+ for (par = of_node_get(node); par;) {
+ if (of_get_property(par, "scom-controller", NULL))
+ break;
+ p = of_get_property(par, "scom-parent", NULL);
+ tmp = par;
+ if (p == NULL)
+ par = of_get_parent(par);
+ else
+ par = of_find_node_by_phandle(*p);
+ of_node_put(tmp);
+ }
+ return par;
+}
+EXPORT_SYMBOL_GPL(scom_find_parent);
+
+scom_map_t scom_map_device(struct device_node *dev, int index)
+{
+ struct device_node *parent;
+ unsigned int cells, size;
+ const __be32 *prop, *sprop;
+ u64 reg, cnt;
+ scom_map_t ret;
+
+ parent = scom_find_parent(dev);
+
+ if (parent == NULL)
+ return 0;
+
+ /*
+ * We support "scom-reg" properties for adding scom registers
+ * to a random device-tree node with an explicit scom-parent
+ *
+ * We also support the simple "reg" property if the device is
+ * a direct child of a scom controller.
+ *
+ * In case both exist, "scom-reg" takes precedence.
+ */
+ prop = of_get_property(dev, "scom-reg", &size);
+ sprop = of_get_property(parent, "#scom-cells", NULL);
+ if (!prop && parent == dev->parent) {
+ prop = of_get_property(dev, "reg", &size);
+ sprop = of_get_property(parent, "#address-cells", NULL);
+ }
+ if (!prop)
+ return NULL;
+ cells = sprop ? be32_to_cpup(sprop) : 1;
+ size >>= 2;
+
+ if (index >= (size / (2*cells)))
+ return 0;
+
+ reg = of_read_number(&prop[index * cells * 2], cells);
+ cnt = of_read_number(&prop[index * cells * 2 + cells], cells);
+
+ ret = scom_map(parent, reg, cnt);
+ of_node_put(parent);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scom_map_device);
+
+#ifdef CONFIG_SCOM_DEBUGFS
+struct scom_debug_entry {
+ struct device_node *dn;
+ struct debugfs_blob_wrapper path;
+ char name[16];
+};
+
+static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct scom_debug_entry *ent = filp->private_data;
+ u64 __user *ubuf64 = (u64 __user *)ubuf;
+ loff_t off = *ppos;
+ ssize_t done = 0;
+ u64 reg, reg_cnt, val;
+ scom_map_t map;
+ int rc;
+
+ if (off < 0 || (off & 7) || (count & 7))
+ return -EINVAL;
+ reg = off >> 3;
+ reg_cnt = count >> 3;
+
+ map = scom_map(ent->dn, reg, reg_cnt);
+ if (!scom_map_ok(map))
+ return -ENXIO;
+
+ for (reg = 0; reg < reg_cnt; reg++) {
+ rc = scom_read(map, reg, &val);
+ if (!rc)
+ rc = put_user(val, ubuf64);
+ if (rc) {
+ if (!done)
+ done = rc;
+ break;
+ }
+ ubuf64++;
+ *ppos += 8;
+ done += 8;
+ }
+ scom_unmap(map);
+ return done;
+}
+
+static ssize_t scom_debug_write(struct file* filp, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct scom_debug_entry *ent = filp->private_data;
+ u64 __user *ubuf64 = (u64 __user *)ubuf;
+ loff_t off = *ppos;
+ ssize_t done = 0;
+ u64 reg, reg_cnt, val;
+ scom_map_t map;
+ int rc;
+
+ if (off < 0 || (off & 7) || (count & 7))
+ return -EINVAL;
+ reg = off >> 3;
+ reg_cnt = count >> 3;
+
+ map = scom_map(ent->dn, reg, reg_cnt);
+ if (!scom_map_ok(map))
+ return -ENXIO;
+
+ for (reg = 0; reg < reg_cnt; reg++) {
+ rc = get_user(val, ubuf64);
+ if (!rc)
+ rc = scom_write(map, reg, val);
+ if (rc) {
+ if (!done)
+ done = rc;
+ break;
+ }
+ ubuf64++;
+ done += 8;
+ }
+ scom_unmap(map);
+ return done;
+}
+
+static const struct file_operations scom_debug_fops = {
+ .read = scom_debug_read,
+ .write = scom_debug_write,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
+ int i)
+{
+ struct scom_debug_entry *ent;
+ struct dentry *dir;
+
+ ent = kzalloc(sizeof(*ent), GFP_KERNEL);
+ if (!ent)
+ return -ENOMEM;
+
+ ent->dn = of_node_get(dn);
+ snprintf(ent->name, 16, "%08x", i);
+ ent->path.data = (void*) dn->full_name;
+ ent->path.size = strlen(dn->full_name);
+
+ dir = debugfs_create_dir(ent->name, root);
+ if (!dir) {
+ of_node_put(dn);
+ kfree(ent);
+ return -1;
+ }
+
+ debugfs_create_blob("devspec", 0400, dir, &ent->path);
+ debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
+
+ return 0;
+}
+
+static int scom_debug_init(void)
+{
+ struct device_node *dn;
+ struct dentry *root;
+ int i, rc;
+
+ root = debugfs_create_dir("scom", powerpc_debugfs_root);
+ if (!root)
+ return -1;
+
+ i = rc = 0;
+ for_each_node_with_property(dn, "scom-controller") {
+ int id = of_get_ibm_chip_id(dn);
+ if (id == -1)
+ id = i;
+ rc |= scom_debug_init_one(root, dn, id);
+ i++;
+ }
+
+ return rc;
+}
+device_initcall(scom_debug_init);
+#endif /* CONFIG_SCOM_DEBUGFS */
diff --git a/arch/powerpc/sysdev/simple_gpio.c b/arch/powerpc/sysdev/simple_gpio.c
new file mode 100644
index 00000000000..ff5e73230a3
--- /dev/null
+++ b/arch/powerpc/sysdev/simple_gpio.c
@@ -0,0 +1,152 @@
+/*
+ * Simple Memory-Mapped GPIOs
+ *
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.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 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <asm/prom.h>
+#include "simple_gpio.h"
+
+struct u8_gpio_chip {
+ struct of_mm_gpio_chip mm_gc;
+ spinlock_t lock;
+
+ /* shadowed data register to clear/set bits safely */
+ u8 data;
+};
+
+static struct u8_gpio_chip *to_u8_gpio_chip(struct of_mm_gpio_chip *mm_gc)
+{
+ return container_of(mm_gc, struct u8_gpio_chip, mm_gc);
+}
+
+static u8 u8_pin2mask(unsigned int pin)
+{
+ return 1 << (8 - 1 - pin);
+}
+
+static int u8_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+
+ return in_8(mm_gc->regs) & u8_pin2mask(gpio);
+}
+
+static void u8_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&u8_gc->lock, flags);
+
+ if (val)
+ u8_gc->data |= u8_pin2mask(gpio);
+ else
+ u8_gc->data &= ~u8_pin2mask(gpio);
+
+ out_8(mm_gc->regs, u8_gc->data);
+
+ spin_unlock_irqrestore(&u8_gc->lock, flags);
+}
+
+static int u8_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ return 0;
+}
+
+static int u8_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ u8_gpio_set(gc, gpio, val);
+ return 0;
+}
+
+static void u8_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+ struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc);
+
+ u8_gc->data = in_8(mm_gc->regs);
+}
+
+static int __init u8_simple_gpiochip_add(struct device_node *np)
+{
+ int ret;
+ struct u8_gpio_chip *u8_gc;
+ struct of_mm_gpio_chip *mm_gc;
+ struct gpio_chip *gc;
+
+ u8_gc = kzalloc(sizeof(*u8_gc), GFP_KERNEL);
+ if (!u8_gc)
+ return -ENOMEM;
+
+ spin_lock_init(&u8_gc->lock);
+
+ mm_gc = &u8_gc->mm_gc;
+ gc = &mm_gc->gc;
+
+ mm_gc->save_regs = u8_gpio_save_regs;
+ gc->ngpio = 8;
+ gc->direction_input = u8_gpio_dir_in;
+ gc->direction_output = u8_gpio_dir_out;
+ gc->get = u8_gpio_get;
+ gc->set = u8_gpio_set;
+
+ ret = of_mm_gpiochip_add(np, mm_gc);
+ if (ret)
+ goto err;
+ return 0;
+err:
+ kfree(u8_gc);
+ return ret;
+}
+
+void __init simple_gpiochip_init(const char *compatible)
+{
+ struct device_node *np;
+
+ for_each_compatible_node(np, NULL, compatible) {
+ int ret;
+ struct resource r;
+
+ ret = of_address_to_resource(np, 0, &r);
+ if (ret)
+ goto err;
+
+ switch (resource_size(&r)) {
+ case 1:
+ ret = u8_simple_gpiochip_add(np);
+ if (ret)
+ goto err;
+ break;
+ default:
+ /*
+ * Whenever you need support for GPIO bank width > 1,
+ * please just turn u8_ code into huge macros, and
+ * construct needed uX_ code with it.
+ */
+ ret = -ENOSYS;
+ goto err;
+ }
+ continue;
+err:
+ pr_err("%s: registration failed, status %d\n",
+ np->full_name, ret);
+ }
+}
diff --git a/arch/powerpc/sysdev/simple_gpio.h b/arch/powerpc/sysdev/simple_gpio.h
new file mode 100644
index 00000000000..3a7b0c513c7
--- /dev/null
+++ b/arch/powerpc/sysdev/simple_gpio.h
@@ -0,0 +1,12 @@
+#ifndef __SYSDEV_SIMPLE_GPIO_H
+#define __SYSDEV_SIMPLE_GPIO_H
+
+#include <linux/errno.h>
+
+#ifdef CONFIG_SIMPLE_GPIO
+extern void simple_gpiochip_init(const char *compatible);
+#else
+static inline void simple_gpiochip_init(const char *compatible) {}
+#endif /* CONFIG_SIMPLE_GPIO */
+
+#endif /* __SYSDEV_SIMPLE_GPIO_H */
diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c
index be2808a292f..1fd0717ade0 100644
--- a/arch/powerpc/sysdev/tsi108_dev.c
+++ b/arch/powerpc/sysdev/tsi108_dev.c
@@ -16,13 +16,13 @@
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/irq.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/of_net.h>
#include <asm/tsi108.h>
-#include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/prom.h>
@@ -83,8 +83,8 @@ static int __init tsi108_eth_of_init(void)
memset(&tsi_eth_data, 0, sizeof(tsi_eth_data));
ret = of_address_to_resource(np, 0, &r[0]);
- DBG("%s: name:start->end = %s:0x%lx-> 0x%lx\n",
- __FUNCTION__,r[0].name, r[0].start, r[0].end);
+ DBG("%s: name:start->end = %s:%pR\n",
+ __func__, r[0].name, &r[0]);
if (ret)
goto err;
@@ -92,8 +92,8 @@ static int __init tsi108_eth_of_init(void)
r[1].start = irq_of_parse_and_map(np, 0);
r[1].end = irq_of_parse_and_map(np, 0);
r[1].flags = IORESOURCE_IRQ;
- DBG("%s: name:start->end = %s:0x%lx-> 0x%lx\n",
- __FUNCTION__,r[1].name, r[1].start, r[1].end);
+ DBG("%s: name:start->end = %s:%pR\n",
+ __func__, r[1].name, &r[1]);
tsi_eth_dev =
platform_device_register_simple("tsi-ethernet", i++, &r[0],
diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c
index 31d3d33d91f..188012c58f7 100644
--- a/arch/powerpc/sysdev/tsi108_pci.c
+++ b/arch/powerpc/sysdev/tsi108_pci.c
@@ -24,7 +24,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
-#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
@@ -52,7 +51,7 @@
u32 tsi108_pci_cfg_base;
static u32 tsi108_pci_cfg_phys;
u32 tsi108_csr_vir_base;
-static struct irq_host *pci_irq_host;
+static struct irq_domain *pci_irq_host;
extern u32 get_vir_csrbase(void);
extern u32 tsi108_read_reg(u32 reg_offset);
@@ -63,7 +62,7 @@ tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfunc,
int offset, int len, u32 val)
{
volatile unsigned char *cfg_addr;
- struct pci_controller *hose = bus->sysdata;
+ struct pci_controller *hose = pci_bus_to_host(bus);
if (ppc_md.pci_exclude_device)
if (ppc_md.pci_exclude_device(hose, bus->number, devfunc))
@@ -149,7 +148,7 @@ tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
int len, u32 * val)
{
volatile unsigned char *cfg_addr;
- struct pci_controller *hose = bus->sysdata;
+ struct pci_controller *hose = pci_bus_to_host(bus);
u32 temp;
if (ppc_md.pci_exclude_device)
@@ -207,7 +206,7 @@ int __init tsi108_setup_pci(struct device_node *dev, u32 cfg_phys, int primary)
/* PCI Config mapping */
tsi108_pci_cfg_base = (u32)ioremap(cfg_phys, TSI108_PCI_CFG_SIZE);
tsi108_pci_cfg_phys = cfg_phys;
- DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __FUNCTION__,
+ DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __func__,
tsi108_pci_cfg_base);
/* Fetch host bridge registers address */
@@ -344,24 +343,9 @@ static inline unsigned int get_pci_source(void)
* Linux descriptor level callbacks
*/
-static void tsi108_pci_irq_enable(u_int irq)
+static void tsi108_pci_irq_unmask(struct irq_data *d)
{
- tsi108_pci_int_unmask(irq);
-}
-
-static void tsi108_pci_irq_disable(u_int irq)
-{
- tsi108_pci_int_mask(irq);
-}
-
-static void tsi108_pci_irq_ack(u_int irq)
-{
- tsi108_pci_int_mask(irq);
-}
-
-static void tsi108_pci_irq_end(u_int irq)
-{
- tsi108_pci_int_unmask(irq);
+ tsi108_pci_int_unmask(d->irq);
/* Enable interrupts from PCI block */
tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
@@ -371,20 +355,29 @@ static void tsi108_pci_irq_end(u_int irq)
mb();
}
+static void tsi108_pci_irq_mask(struct irq_data *d)
+{
+ tsi108_pci_int_mask(d->irq);
+}
+
+static void tsi108_pci_irq_ack(struct irq_data *d)
+{
+ tsi108_pci_int_mask(d->irq);
+}
+
/*
* Interrupt controller descriptor for cascaded PCI interrupt controller.
*/
static struct irq_chip tsi108_pci_irq = {
- .typename = "tsi108_PCI_int",
- .mask = tsi108_pci_irq_disable,
- .ack = tsi108_pci_irq_ack,
- .end = tsi108_pci_irq_end,
- .unmask = tsi108_pci_irq_enable,
+ .name = "tsi108_PCI_int",
+ .irq_mask = tsi108_pci_irq_mask,
+ .irq_ack = tsi108_pci_irq_ack,
+ .irq_unmask = tsi108_pci_irq_unmask,
};
-static int pci_irq_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
+static int pci_irq_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)
{
*out_hwirq = intspec[0];
@@ -392,19 +385,19 @@ static int pci_irq_host_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
-static int pci_irq_host_map(struct irq_host *h, unsigned int virq,
+static int pci_irq_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{ unsigned int irq;
- DBG("%s(%d, 0x%lx)\n", __FUNCTION__, virq, hw);
+ DBG("%s(%d, 0x%lx)\n", __func__, virq, hw);
if ((virq >= 1) && (virq <= 4)){
irq = virq + IRQ_PCI_INTAD_BASE - 1;
- get_irq_desc(irq)->status |= IRQ_LEVEL;
- set_irq_chip(irq, &tsi108_pci_irq);
+ irq_set_status_flags(irq, IRQ_LEVEL);
+ irq_set_chip(irq, &tsi108_pci_irq);
}
return 0;
}
-static struct irq_host_ops pci_irq_host_ops = {
+static struct irq_domain_ops pci_irq_domain_ops = {
.map = pci_irq_host_map,
.xlate = pci_irq_host_xlate,
};
@@ -426,11 +419,9 @@ void __init tsi108_pci_int_init(struct device_node *node)
{
DBG("Tsi108_pci_int_init: initializing PCI interrupts\n");
- pci_irq_host = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LEGACY,
- 0, &pci_irq_host_ops, 0);
+ pci_irq_host = irq_domain_add_legacy_isa(node, &pci_irq_domain_ops, NULL);
if (pci_irq_host == NULL) {
- printk(KERN_ERR "pci_irq_host: failed to allocate irq host !\n");
- of_node_put(node);
+ printk(KERN_ERR "pci_irq_host: failed to allocate irq domain!\n");
return;
}
@@ -439,8 +430,11 @@ void __init tsi108_pci_int_init(struct device_node *node)
void tsi108_irq_cascade(unsigned int irq, struct irq_desc *desc)
{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int cascade_irq = get_pci_source();
+
if (cascade_irq != NO_IRQ)
generic_handle_irq(cascade_irq);
- desc->chip->eoi(irq);
+
+ chip->irq_eoi(&desc->irq_data);
}
diff --git a/arch/powerpc/sysdev/udbg_memcons.c b/arch/powerpc/sysdev/udbg_memcons.c
new file mode 100644
index 00000000000..9998c0de12d
--- /dev/null
+++ b/arch/powerpc/sysdev/udbg_memcons.c
@@ -0,0 +1,104 @@
+/*
+ * A udbg backend which logs messages and reads input from in memory
+ * buffers.
+ *
+ * The console output can be read from memcons_output which is a
+ * circular buffer whose next write position is stored in memcons.output_pos.
+ *
+ * Input may be passed by writing into the memcons_input buffer when it is
+ * empty. The input buffer is empty when both input_pos == input_start and
+ * *input_start == '\0'.
+ *
+ * Copyright (C) 2003-2005 Anton Blanchard and Milton Miller, IBM Corp
+ * Copyright (C) 2013 Alistair Popple, 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/kernel.h>
+#include <asm/barrier.h>
+#include <asm/page.h>
+#include <asm/processor.h>
+#include <asm/udbg.h>
+
+struct memcons {
+ char *output_start;
+ char *output_pos;
+ char *output_end;
+ char *input_start;
+ char *input_pos;
+ char *input_end;
+};
+
+static char memcons_output[CONFIG_PPC_MEMCONS_OUTPUT_SIZE];
+static char memcons_input[CONFIG_PPC_MEMCONS_INPUT_SIZE];
+
+struct memcons memcons = {
+ .output_start = memcons_output,
+ .output_pos = memcons_output,
+ .output_end = &memcons_output[CONFIG_PPC_MEMCONS_OUTPUT_SIZE],
+ .input_start = memcons_input,
+ .input_pos = memcons_input,
+ .input_end = &memcons_input[CONFIG_PPC_MEMCONS_INPUT_SIZE],
+};
+
+void memcons_putc(char c)
+{
+ char *new_output_pos;
+
+ *memcons.output_pos = c;
+ wmb();
+ new_output_pos = memcons.output_pos + 1;
+ if (new_output_pos >= memcons.output_end)
+ new_output_pos = memcons.output_start;
+
+ memcons.output_pos = new_output_pos;
+}
+
+int memcons_getc_poll(void)
+{
+ char c;
+ char *new_input_pos;
+
+ if (*memcons.input_pos) {
+ c = *memcons.input_pos;
+
+ new_input_pos = memcons.input_pos + 1;
+ if (new_input_pos >= memcons.input_end)
+ new_input_pos = memcons.input_start;
+ else if (*new_input_pos == '\0')
+ new_input_pos = memcons.input_start;
+
+ *memcons.input_pos = '\0';
+ wmb();
+ memcons.input_pos = new_input_pos;
+ return c;
+ }
+
+ return -1;
+}
+
+int memcons_getc(void)
+{
+ int c;
+
+ while (1) {
+ c = memcons_getc_poll();
+ if (c == -1)
+ cpu_relax();
+ else
+ break;
+ }
+
+ return c;
+}
+
+void udbg_init_memcons(void)
+{
+ udbg_putc = memcons_putc;
+ udbg_getc = memcons_getc;
+ udbg_getc_poll = memcons_getc_poll;
+}
diff --git a/arch/powerpc/sysdev/uic.c b/arch/powerpc/sysdev/uic.c
index 625b275c379..92033936a8f 100644
--- a/arch/powerpc/sysdev/uic.c
+++ b/arch/powerpc/sysdev/uic.c
@@ -18,7 +18,6 @@
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/signal.h>
-#include <linux/sysdev.h>
#include <linux/device.h>
#include <linux/bootmem.h>
#include <linux/spinlock.h>
@@ -41,74 +40,70 @@
#define UIC_VR 0x7
#define UIC_VCR 0x8
-#define uic_irq_to_hw(virq) (irq_map[virq].hwirq)
-
struct uic *primary_uic;
struct uic {
int index;
int dcrbase;
- spinlock_t lock;
+ raw_spinlock_t lock;
/* The remapper for this UIC */
- struct irq_host *irqhost;
+ struct irq_domain *irqhost;
};
-static void uic_unmask_irq(unsigned int virq)
+static void uic_unmask_irq(struct irq_data *d)
{
- struct irq_desc *desc = get_irq_desc(virq);
- struct uic *uic = get_irq_chip_data(virq);
- unsigned int src = uic_irq_to_hw(virq);
+ struct uic *uic = irq_data_get_irq_chip_data(d);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
u32 er, sr;
sr = 1 << (31-src);
- spin_lock_irqsave(&uic->lock, flags);
+ raw_spin_lock_irqsave(&uic->lock, flags);
/* ack level-triggered interrupts here */
- if (desc->status & IRQ_LEVEL)
+ if (irqd_is_level_type(d))
mtdcr(uic->dcrbase + UIC_SR, sr);
er = mfdcr(uic->dcrbase + UIC_ER);
er |= sr;
mtdcr(uic->dcrbase + UIC_ER, er);
- spin_unlock_irqrestore(&uic->lock, flags);
+ raw_spin_unlock_irqrestore(&uic->lock, flags);
}
-static void uic_mask_irq(unsigned int virq)
+static void uic_mask_irq(struct irq_data *d)
{
- struct uic *uic = get_irq_chip_data(virq);
- unsigned int src = uic_irq_to_hw(virq);
+ struct uic *uic = irq_data_get_irq_chip_data(d);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
u32 er;
- spin_lock_irqsave(&uic->lock, flags);
+ raw_spin_lock_irqsave(&uic->lock, flags);
er = mfdcr(uic->dcrbase + UIC_ER);
er &= ~(1 << (31 - src));
mtdcr(uic->dcrbase + UIC_ER, er);
- spin_unlock_irqrestore(&uic->lock, flags);
+ raw_spin_unlock_irqrestore(&uic->lock, flags);
}
-static void uic_ack_irq(unsigned int virq)
+static void uic_ack_irq(struct irq_data *d)
{
- struct uic *uic = get_irq_chip_data(virq);
- unsigned int src = uic_irq_to_hw(virq);
+ struct uic *uic = irq_data_get_irq_chip_data(d);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
- spin_lock_irqsave(&uic->lock, flags);
+ raw_spin_lock_irqsave(&uic->lock, flags);
mtdcr(uic->dcrbase + UIC_SR, 1 << (31-src));
- spin_unlock_irqrestore(&uic->lock, flags);
+ raw_spin_unlock_irqrestore(&uic->lock, flags);
}
-static void uic_mask_ack_irq(unsigned int virq)
+static void uic_mask_ack_irq(struct irq_data *d)
{
- struct irq_desc *desc = get_irq_desc(virq);
- struct uic *uic = get_irq_chip_data(virq);
- unsigned int src = uic_irq_to_hw(virq);
+ struct uic *uic = irq_data_get_irq_chip_data(d);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
u32 er, sr;
sr = 1 << (31-src);
- spin_lock_irqsave(&uic->lock, flags);
+ raw_spin_lock_irqsave(&uic->lock, flags);
er = mfdcr(uic->dcrbase + UIC_ER);
er &= ~sr;
mtdcr(uic->dcrbase + UIC_ER, er);
@@ -120,23 +115,22 @@ static void uic_mask_ack_irq(unsigned int virq)
* level interrupts are ack'ed after the actual
* isr call in the uic_unmask_irq()
*/
- if (!(desc->status & IRQ_LEVEL))
+ if (!irqd_is_level_type(d))
mtdcr(uic->dcrbase + UIC_SR, sr);
- spin_unlock_irqrestore(&uic->lock, flags);
+ raw_spin_unlock_irqrestore(&uic->lock, flags);
}
-static int uic_set_irq_type(unsigned int virq, unsigned int flow_type)
+static int uic_set_irq_type(struct irq_data *d, unsigned int flow_type)
{
- struct uic *uic = get_irq_chip_data(virq);
- unsigned int src = uic_irq_to_hw(virq);
- struct irq_desc *desc = get_irq_desc(virq);
+ struct uic *uic = irq_data_get_irq_chip_data(d);
+ unsigned int src = irqd_to_hwirq(d);
unsigned long flags;
int trigger, polarity;
u32 tr, pr, mask;
switch (flow_type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_NONE:
- uic_mask_irq(virq);
+ uic_mask_irq(d);
return 0;
case IRQ_TYPE_EDGE_RISING:
@@ -157,7 +151,7 @@ static int uic_set_irq_type(unsigned int virq, unsigned int flow_type)
mask = ~(1 << (31 - src));
- spin_lock_irqsave(&uic->lock, flags);
+ raw_spin_lock_irqsave(&uic->lock, flags);
tr = mfdcr(uic->dcrbase + UIC_TR);
pr = mfdcr(uic->dcrbase + UIC_PR);
tr = (tr & mask) | (trigger << (31-src));
@@ -166,71 +160,56 @@ static int uic_set_irq_type(unsigned int virq, unsigned int flow_type)
mtdcr(uic->dcrbase + UIC_PR, pr);
mtdcr(uic->dcrbase + UIC_TR, tr);
- desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
- desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
- if (!trigger)
- desc->status |= IRQ_LEVEL;
-
- spin_unlock_irqrestore(&uic->lock, flags);
+ raw_spin_unlock_irqrestore(&uic->lock, flags);
return 0;
}
static struct irq_chip uic_irq_chip = {
- .typename = " UIC ",
- .unmask = uic_unmask_irq,
- .mask = uic_mask_irq,
- .mask_ack = uic_mask_ack_irq,
- .ack = uic_ack_irq,
- .set_type = uic_set_irq_type,
+ .name = "UIC",
+ .irq_unmask = uic_unmask_irq,
+ .irq_mask = uic_mask_irq,
+ .irq_mask_ack = uic_mask_ack_irq,
+ .irq_ack = uic_ack_irq,
+ .irq_set_type = uic_set_irq_type,
};
-static int uic_host_map(struct irq_host *h, unsigned int virq,
+static int uic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct uic *uic = h->host_data;
- set_irq_chip_data(virq, uic);
+ irq_set_chip_data(virq, uic);
/* Despite the name, handle_level_irq() works for both level
* and edge irqs on UIC. FIXME: check this is correct */
- set_irq_chip_and_handler(virq, &uic_irq_chip, handle_level_irq);
+ irq_set_chip_and_handler(virq, &uic_irq_chip, 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 uic_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
- irq_hw_number_t *out_hwirq, unsigned int *out_type)
-
-{
- /* UIC intspecs must have 2 cells */
- BUG_ON(intsize != 2);
- *out_hwirq = intspec[0];
- *out_type = intspec[1];
- return 0;
-}
-
-static struct irq_host_ops uic_host_ops = {
+static struct irq_domain_ops uic_host_ops = {
.map = uic_host_map,
- .xlate = uic_host_xlate,
+ .xlate = irq_domain_xlate_twocell,
};
void uic_irq_cascade(unsigned int virq, struct irq_desc *desc)
{
- struct uic *uic = get_irq_data(virq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_data *idata = irq_desc_get_irq_data(desc);
+ struct uic *uic = irq_get_handler_data(virq);
u32 msr;
int src;
int subvirq;
- spin_lock(&desc->lock);
- if (desc->status & IRQ_LEVEL)
- desc->chip->mask(virq);
+ raw_spin_lock(&desc->lock);
+ if (irqd_is_level_type(idata))
+ chip->irq_mask(idata);
else
- desc->chip->mask_ack(virq);
- spin_unlock(&desc->lock);
+ chip->irq_mask_ack(idata);
+ raw_spin_unlock(&desc->lock);
msr = mfdcr(uic->dcrbase + UIC_MSR);
if (!msr) /* spurious interrupt */
@@ -242,12 +221,12 @@ void uic_irq_cascade(unsigned int virq, struct irq_desc *desc)
generic_handle_irq(subvirq);
uic_irq_ret:
- spin_lock(&desc->lock);
- if (desc->status & IRQ_LEVEL)
- desc->chip->ack(virq);
- if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
- desc->chip->unmask(virq);
- spin_unlock(&desc->lock);
+ raw_spin_lock(&desc->lock);
+ if (irqd_is_level_type(idata))
+ chip->irq_ack(idata);
+ if (!irqd_irq_disabled(idata) && chip->irq_unmask)
+ chip->irq_unmask(idata);
+ raw_spin_unlock(&desc->lock);
}
static struct uic * __init uic_init_one(struct device_node *node)
@@ -258,12 +237,11 @@ static struct uic * __init uic_init_one(struct device_node *node)
BUG_ON(! of_device_is_compatible(node, "ibm,uic"));
- uic = alloc_bootmem(sizeof(*uic));
+ uic = kzalloc(sizeof(*uic), GFP_KERNEL);
if (! uic)
return NULL; /* FIXME: panic? */
- memset(uic, 0, sizeof(*uic));
- spin_lock_init(&uic->lock);
+ raw_spin_lock_init(&uic->lock);
indexp = of_get_property(node, "cell-index", &len);
if (!indexp || (len != sizeof(u32))) {
printk(KERN_ERR "uic: Device node %s has missing or invalid "
@@ -280,14 +258,10 @@ static struct uic * __init uic_init_one(struct device_node *node)
}
uic->dcrbase = *dcrreg;
- uic->irqhost = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LINEAR,
- NR_UIC_INTS, &uic_host_ops, -1);
- if (! uic->irqhost) {
- of_node_put(node);
+ uic->irqhost = irq_domain_add_linear(node, NR_UIC_INTS, &uic_host_ops,
+ uic);
+ if (! uic->irqhost)
return NULL; /* FIXME: panic? */
- }
-
- uic->irqhost->host_data = uic;
/* Start with all interrupts disabled, level and non-critical */
mtdcr(uic->dcrbase + UIC_ER, 0);
@@ -338,8 +312,8 @@ void __init uic_init_tree(void)
cascade_virq = irq_of_parse_and_map(np, 0);
- set_irq_data(cascade_virq, uic);
- set_irq_chained_handler(cascade_virq, uic_irq_cascade);
+ irq_set_handler_data(cascade_virq, uic);
+ irq_set_chained_handler(cascade_virq, uic_irq_cascade);
/* FIXME: setup critical cascade?? */
}
diff --git a/arch/powerpc/sysdev/xics/Kconfig b/arch/powerpc/sysdev/xics/Kconfig
new file mode 100644
index 00000000000..0031eda320c
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/Kconfig
@@ -0,0 +1,13 @@
+config PPC_XICS
+ def_bool n
+ select PPC_SMP_MUXED_IPI
+
+config PPC_ICP_NATIVE
+ def_bool n
+
+config PPC_ICP_HV
+ def_bool n
+
+config PPC_ICS_RTAS
+ def_bool n
+
diff --git a/arch/powerpc/sysdev/xics/Makefile b/arch/powerpc/sysdev/xics/Makefile
new file mode 100644
index 00000000000..c606aa8ba60
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/Makefile
@@ -0,0 +1,7 @@
+subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
+
+obj-y += xics-common.o
+obj-$(CONFIG_PPC_ICP_NATIVE) += icp-native.o
+obj-$(CONFIG_PPC_ICP_HV) += icp-hv.o
+obj-$(CONFIG_PPC_ICS_RTAS) += ics-rtas.o
+obj-$(CONFIG_PPC_POWERNV) += ics-opal.o
diff --git a/arch/powerpc/sysdev/xics/icp-hv.c b/arch/powerpc/sysdev/xics/icp-hv.c
new file mode 100644
index 00000000000..c1917cf67c3
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/icp-hv.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2011 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.
+ *
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+
+#include <asm/smp.h>
+#include <asm/irq.h>
+#include <asm/errno.h>
+#include <asm/xics.h>
+#include <asm/io.h>
+#include <asm/hvcall.h>
+
+static inline unsigned int icp_hv_get_xirr(unsigned char cppr)
+{
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+ long rc;
+ unsigned int ret = XICS_IRQ_SPURIOUS;
+
+ rc = plpar_hcall(H_XIRR, retbuf, cppr);
+ if (rc == H_SUCCESS) {
+ ret = (unsigned int)retbuf[0];
+ } else {
+ pr_err("%s: bad return code xirr cppr=0x%x returned %ld\n",
+ __func__, cppr, rc);
+ WARN_ON_ONCE(1);
+ }
+
+ return ret;
+}
+
+static inline void icp_hv_set_cppr(u8 value)
+{
+ long rc = plpar_hcall_norets(H_CPPR, value);
+ if (rc != H_SUCCESS) {
+ pr_err("%s: bad return code cppr cppr=0x%x returned %ld\n",
+ __func__, value, rc);
+ WARN_ON_ONCE(1);
+ }
+}
+
+static inline void icp_hv_set_xirr(unsigned int value)
+{
+ long rc = plpar_hcall_norets(H_EOI, value);
+ if (rc != H_SUCCESS) {
+ pr_err("%s: bad return code eoi xirr=0x%x returned %ld\n",
+ __func__, value, rc);
+ WARN_ON_ONCE(1);
+ icp_hv_set_cppr(value >> 24);
+ }
+}
+
+static inline void icp_hv_set_qirr(int n_cpu , u8 value)
+{
+ int hw_cpu = get_hard_smp_processor_id(n_cpu);
+ long rc;
+
+ /* Make sure all previous accesses are ordered before IPI sending */
+ mb();
+ rc = plpar_hcall_norets(H_IPI, hw_cpu, value);
+ if (rc != H_SUCCESS) {
+ pr_err("%s: bad return code qirr cpu=%d hw_cpu=%d mfrr=0x%x "
+ "returned %ld\n", __func__, n_cpu, hw_cpu, value, rc);
+ WARN_ON_ONCE(1);
+ }
+}
+
+static void icp_hv_eoi(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+
+ iosync();
+ icp_hv_set_xirr((xics_pop_cppr() << 24) | hw_irq);
+}
+
+static void icp_hv_teardown_cpu(void)
+{
+ int cpu = smp_processor_id();
+
+ /* Clear any pending IPI */
+ icp_hv_set_qirr(cpu, 0xff);
+}
+
+static void icp_hv_flush_ipi(void)
+{
+ /* We take the ipi irq but and never return so we
+ * need to EOI the IPI, but want to leave our priority 0
+ *
+ * should we check all the other interrupts too?
+ * should we be flagging idle loop instead?
+ * or creating some task to be scheduled?
+ */
+
+ icp_hv_set_xirr((0x00 << 24) | XICS_IPI);
+}
+
+static unsigned int icp_hv_get_irq(void)
+{
+ unsigned int xirr = icp_hv_get_xirr(xics_cppr_top());
+ unsigned int vec = xirr & 0x00ffffff;
+ unsigned int irq;
+
+ if (vec == XICS_IRQ_SPURIOUS)
+ return NO_IRQ;
+
+ irq = irq_find_mapping(xics_host, vec);
+ if (likely(irq != NO_IRQ)) {
+ xics_push_cppr(vec);
+ return irq;
+ }
+
+ /* We don't have a linux mapping, so have rtas mask it. */
+ xics_mask_unknown_vec(vec);
+
+ /* We might learn about it later, so EOI it */
+ icp_hv_set_xirr(xirr);
+
+ return NO_IRQ;
+}
+
+static void icp_hv_set_cpu_priority(unsigned char cppr)
+{
+ xics_set_base_cppr(cppr);
+ icp_hv_set_cppr(cppr);
+ iosync();
+}
+
+#ifdef CONFIG_SMP
+
+static void icp_hv_cause_ipi(int cpu, unsigned long data)
+{
+ icp_hv_set_qirr(cpu, IPI_PRIORITY);
+}
+
+static irqreturn_t icp_hv_ipi_action(int irq, void *dev_id)
+{
+ int cpu = smp_processor_id();
+
+ icp_hv_set_qirr(cpu, 0xff);
+
+ return smp_ipi_demux();
+}
+
+#endif /* CONFIG_SMP */
+
+static const struct icp_ops icp_hv_ops = {
+ .get_irq = icp_hv_get_irq,
+ .eoi = icp_hv_eoi,
+ .set_priority = icp_hv_set_cpu_priority,
+ .teardown_cpu = icp_hv_teardown_cpu,
+ .flush_ipi = icp_hv_flush_ipi,
+#ifdef CONFIG_SMP
+ .ipi_action = icp_hv_ipi_action,
+ .cause_ipi = icp_hv_cause_ipi,
+#endif
+};
+
+int icp_hv_init(void)
+{
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "ibm,ppc-xicp");
+ if (!np)
+ np = of_find_node_by_type(NULL,
+ "PowerPC-External-Interrupt-Presentation");
+ if (!np)
+ return -ENODEV;
+
+ icp_ops = &icp_hv_ops;
+
+ return 0;
+}
+
diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c
new file mode 100644
index 00000000000..de8d9483bbe
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/icp-native.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2011 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.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/irq.h>
+#include <asm/errno.h>
+#include <asm/xics.h>
+#include <asm/kvm_ppc.h>
+#include <asm/dbell.h>
+
+struct icp_ipl {
+ union {
+ u32 word;
+ u8 bytes[4];
+ } xirr_poll;
+ union {
+ u32 word;
+ u8 bytes[4];
+ } xirr;
+ u32 dummy;
+ union {
+ u32 word;
+ u8 bytes[4];
+ } qirr;
+ u32 link_a;
+ u32 link_b;
+ u32 link_c;
+};
+
+static struct icp_ipl __iomem *icp_native_regs[NR_CPUS];
+
+static inline unsigned int icp_native_get_xirr(void)
+{
+ int cpu = smp_processor_id();
+ unsigned int xirr;
+
+ /* Handled an interrupt latched by KVM */
+ xirr = kvmppc_get_xics_latch();
+ if (xirr)
+ return xirr;
+
+ return in_be32(&icp_native_regs[cpu]->xirr.word);
+}
+
+static inline void icp_native_set_xirr(unsigned int value)
+{
+ int cpu = smp_processor_id();
+
+ out_be32(&icp_native_regs[cpu]->xirr.word, value);
+}
+
+static inline void icp_native_set_cppr(u8 value)
+{
+ int cpu = smp_processor_id();
+
+ out_8(&icp_native_regs[cpu]->xirr.bytes[0], value);
+}
+
+static inline void icp_native_set_qirr(int n_cpu, u8 value)
+{
+ out_8(&icp_native_regs[n_cpu]->qirr.bytes[0], value);
+}
+
+static void icp_native_set_cpu_priority(unsigned char cppr)
+{
+ xics_set_base_cppr(cppr);
+ icp_native_set_cppr(cppr);
+ iosync();
+}
+
+void icp_native_eoi(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+
+ iosync();
+ icp_native_set_xirr((xics_pop_cppr() << 24) | hw_irq);
+}
+
+static void icp_native_teardown_cpu(void)
+{
+ int cpu = smp_processor_id();
+
+ /* Clear any pending IPI */
+ icp_native_set_qirr(cpu, 0xff);
+}
+
+static void icp_native_flush_ipi(void)
+{
+ /* We take the ipi irq but and never return so we
+ * need to EOI the IPI, but want to leave our priority 0
+ *
+ * should we check all the other interrupts too?
+ * should we be flagging idle loop instead?
+ * or creating some task to be scheduled?
+ */
+
+ icp_native_set_xirr((0x00 << 24) | XICS_IPI);
+}
+
+static unsigned int icp_native_get_irq(void)
+{
+ unsigned int xirr = icp_native_get_xirr();
+ unsigned int vec = xirr & 0x00ffffff;
+ unsigned int irq;
+
+ if (vec == XICS_IRQ_SPURIOUS)
+ return NO_IRQ;
+
+ irq = irq_find_mapping(xics_host, vec);
+ if (likely(irq != NO_IRQ)) {
+ xics_push_cppr(vec);
+ return irq;
+ }
+
+ /* We don't have a linux mapping, so have rtas mask it. */
+ xics_mask_unknown_vec(vec);
+
+ /* We might learn about it later, so EOI it */
+ icp_native_set_xirr(xirr);
+
+ return NO_IRQ;
+}
+
+#ifdef CONFIG_SMP
+
+static void icp_native_cause_ipi(int cpu, unsigned long data)
+{
+ kvmppc_set_host_ipi(cpu, 1);
+#ifdef CONFIG_PPC_DOORBELL
+ if (cpu_has_feature(CPU_FTR_DBELL) &&
+ (cpumask_test_cpu(cpu, cpu_sibling_mask(smp_processor_id()))))
+ doorbell_cause_ipi(cpu, data);
+ else
+#endif
+ icp_native_set_qirr(cpu, IPI_PRIORITY);
+}
+
+void xics_wake_cpu(int cpu)
+{
+ icp_native_set_qirr(cpu, IPI_PRIORITY);
+}
+EXPORT_SYMBOL_GPL(xics_wake_cpu);
+
+static irqreturn_t icp_native_ipi_action(int irq, void *dev_id)
+{
+ int cpu = smp_processor_id();
+
+ kvmppc_set_host_ipi(cpu, 0);
+ icp_native_set_qirr(cpu, 0xff);
+
+ return smp_ipi_demux();
+}
+
+#endif /* CONFIG_SMP */
+
+static int __init icp_native_map_one_cpu(int hw_id, unsigned long addr,
+ unsigned long size)
+{
+ char *rname;
+ int i, cpu = -1;
+
+ /* This may look gross but it's good enough for now, we don't quite
+ * have a hard -> linux processor id matching.
+ */
+ for_each_possible_cpu(i) {
+ if (!cpu_present(i))
+ continue;
+ if (hw_id == get_hard_smp_processor_id(i)) {
+ cpu = i;
+ break;
+ }
+ }
+
+ /* Fail, skip that CPU. Don't print, it's normal, some XICS come up
+ * with way more entries in there than you have CPUs
+ */
+ if (cpu == -1)
+ return 0;
+
+ rname = kasprintf(GFP_KERNEL, "CPU %d [0x%x] Interrupt Presentation",
+ cpu, hw_id);
+
+ if (!request_mem_region(addr, size, rname)) {
+ pr_warning("icp_native: Could not reserve ICP MMIO"
+ " for CPU %d, interrupt server #0x%x\n",
+ cpu, hw_id);
+ return -EBUSY;
+ }
+
+ icp_native_regs[cpu] = ioremap(addr, size);
+ kvmppc_set_xics_phys(cpu, addr);
+ if (!icp_native_regs[cpu]) {
+ pr_warning("icp_native: Failed ioremap for CPU %d, "
+ "interrupt server #0x%x, addr %#lx\n",
+ cpu, hw_id, addr);
+ release_mem_region(addr, size);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int __init icp_native_init_one_node(struct device_node *np,
+ unsigned int *indx)
+{
+ unsigned int ilen;
+ const __be32 *ireg;
+ int i;
+ int reg_tuple_size;
+ int num_servers = 0;
+
+ /* This code does the theorically broken assumption that the interrupt
+ * server numbers are the same as the hard CPU numbers.
+ * This happens to be the case so far but we are playing with fire...
+ * should be fixed one of these days. -BenH.
+ */
+ ireg = of_get_property(np, "ibm,interrupt-server-ranges", &ilen);
+
+ /* Do that ever happen ? we'll know soon enough... but even good'old
+ * f80 does have that property ..
+ */
+ WARN_ON((ireg == NULL) || (ilen != 2*sizeof(u32)));
+
+ if (ireg) {
+ *indx = of_read_number(ireg, 1);
+ if (ilen >= 2*sizeof(u32))
+ num_servers = of_read_number(ireg + 1, 1);
+ }
+
+ ireg = of_get_property(np, "reg", &ilen);
+ if (!ireg) {
+ pr_err("icp_native: Can't find interrupt reg property");
+ return -1;
+ }
+
+ reg_tuple_size = (of_n_addr_cells(np) + of_n_size_cells(np)) * 4;
+ if (((ilen % reg_tuple_size) != 0)
+ || (num_servers && (num_servers != (ilen / reg_tuple_size)))) {
+ pr_err("icp_native: ICP reg len (%d) != num servers (%d)",
+ ilen / reg_tuple_size, num_servers);
+ return -1;
+ }
+
+ for (i = 0; i < (ilen / reg_tuple_size); i++) {
+ struct resource r;
+ int err;
+
+ err = of_address_to_resource(np, i, &r);
+ if (err) {
+ pr_err("icp_native: Could not translate ICP MMIO"
+ " for interrupt server 0x%x (%d)\n", *indx, err);
+ return -1;
+ }
+
+ if (icp_native_map_one_cpu(*indx, r.start, resource_size(&r)))
+ return -1;
+
+ (*indx)++;
+ }
+ return 0;
+}
+
+static const struct icp_ops icp_native_ops = {
+ .get_irq = icp_native_get_irq,
+ .eoi = icp_native_eoi,
+ .set_priority = icp_native_set_cpu_priority,
+ .teardown_cpu = icp_native_teardown_cpu,
+ .flush_ipi = icp_native_flush_ipi,
+#ifdef CONFIG_SMP
+ .ipi_action = icp_native_ipi_action,
+ .cause_ipi = icp_native_cause_ipi,
+#endif
+};
+
+int __init icp_native_init(void)
+{
+ struct device_node *np;
+ u32 indx = 0;
+ int found = 0;
+
+ for_each_compatible_node(np, NULL, "ibm,ppc-xicp")
+ if (icp_native_init_one_node(np, &indx) == 0)
+ found = 1;
+ if (!found) {
+ for_each_node_by_type(np,
+ "PowerPC-External-Interrupt-Presentation") {
+ if (icp_native_init_one_node(np, &indx) == 0)
+ found = 1;
+ }
+ }
+
+ if (found == 0)
+ return -ENODEV;
+
+ icp_ops = &icp_native_ops;
+
+ return 0;
+}
diff --git a/arch/powerpc/sysdev/xics/ics-opal.c b/arch/powerpc/sysdev/xics/ics-opal.c
new file mode 100644
index 00000000000..3c6ee1b64e5
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/ics-opal.c
@@ -0,0 +1,245 @@
+/*
+ * ICS backend for OPAL managed interrupts.
+ *
+ * Copyright 2011 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.
+ */
+
+#undef DEBUG
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/spinlock.h>
+#include <linux/msi.h>
+
+#include <asm/prom.h>
+#include <asm/smp.h>
+#include <asm/machdep.h>
+#include <asm/irq.h>
+#include <asm/errno.h>
+#include <asm/xics.h>
+#include <asm/opal.h>
+#include <asm/firmware.h>
+
+static int ics_opal_mangle_server(int server)
+{
+ /* No link for now */
+ return server << 2;
+}
+
+static int ics_opal_unmangle_server(int server)
+{
+ /* No link for now */
+ return server >> 2;
+}
+
+static void ics_opal_unmask_irq(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+ int64_t rc;
+ int server;
+
+ pr_devel("ics-hal: unmask virq %d [hw 0x%x]\n", d->irq, hw_irq);
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return;
+
+ server = xics_get_irq_server(d->irq, d->affinity, 0);
+ server = ics_opal_mangle_server(server);
+
+ rc = opal_set_xive(hw_irq, server, DEFAULT_PRIORITY);
+ if (rc != OPAL_SUCCESS)
+ pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)"
+ " error %lld\n",
+ __func__, d->irq, hw_irq, server, rc);
+}
+
+static unsigned int ics_opal_startup(struct irq_data *d)
+{
+#ifdef CONFIG_PCI_MSI
+ /*
+ * The generic MSI code returns with the interrupt disabled on the
+ * card, using the MSI mask bits. Firmware doesn't appear to unmask
+ * at that level, so we do it here by hand.
+ */
+ if (d->msi_desc)
+ unmask_msi_irq(d);
+#endif
+
+ /* unmask it */
+ ics_opal_unmask_irq(d);
+ return 0;
+}
+
+static void ics_opal_mask_real_irq(unsigned int hw_irq)
+{
+ int server = ics_opal_mangle_server(xics_default_server);
+ int64_t rc;
+
+ if (hw_irq == XICS_IPI)
+ return;
+
+ /* Have to set XIVE to 0xff to be able to remove a slot */
+ rc = opal_set_xive(hw_irq, server, 0xff);
+ if (rc != OPAL_SUCCESS)
+ pr_err("%s: opal_set_xive(0xff) irq=%u returned %lld\n",
+ __func__, hw_irq, rc);
+}
+
+static void ics_opal_mask_irq(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+
+ pr_devel("ics-hal: mask virq %d [hw 0x%x]\n", d->irq, hw_irq);
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return;
+ ics_opal_mask_real_irq(hw_irq);
+}
+
+static int ics_opal_set_affinity(struct irq_data *d,
+ const struct cpumask *cpumask,
+ bool force)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+ __be16 oserver;
+ int16_t server;
+ int8_t priority;
+ int64_t rc;
+ int wanted_server;
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return -1;
+
+ rc = opal_get_xive(hw_irq, &oserver, &priority);
+ if (rc != OPAL_SUCCESS) {
+ pr_err("%s: opal_get_xive(irq=%d [hw 0x%x]) error %lld\n",
+ __func__, d->irq, hw_irq, rc);
+ return -1;
+ }
+ server = be16_to_cpu(oserver);
+
+ wanted_server = xics_get_irq_server(d->irq, cpumask, 1);
+ if (wanted_server < 0) {
+ char cpulist[128];
+ cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
+ pr_warning("%s: No online cpus in the mask %s for irq %d\n",
+ __func__, cpulist, d->irq);
+ return -1;
+ }
+ server = ics_opal_mangle_server(wanted_server);
+
+ pr_devel("ics-hal: set-affinity irq %d [hw 0x%x] server: 0x%x/0x%x\n",
+ d->irq, hw_irq, wanted_server, server);
+
+ rc = opal_set_xive(hw_irq, server, priority);
+ if (rc != OPAL_SUCCESS) {
+ pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)"
+ " error %lld\n",
+ __func__, d->irq, hw_irq, server, rc);
+ return -1;
+ }
+ return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip ics_opal_irq_chip = {
+ .name = "OPAL ICS",
+ .irq_startup = ics_opal_startup,
+ .irq_mask = ics_opal_mask_irq,
+ .irq_unmask = ics_opal_unmask_irq,
+ .irq_eoi = NULL, /* Patched at init time */
+ .irq_set_affinity = ics_opal_set_affinity
+};
+
+static int ics_opal_map(struct ics *ics, unsigned int virq);
+static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec);
+static long ics_opal_get_server(struct ics *ics, unsigned long vec);
+
+static int ics_opal_host_match(struct ics *ics, struct device_node *node)
+{
+ return 1;
+}
+
+/* Only one global & state struct ics */
+static struct ics ics_hal = {
+ .map = ics_opal_map,
+ .mask_unknown = ics_opal_mask_unknown,
+ .get_server = ics_opal_get_server,
+ .host_match = ics_opal_host_match,
+};
+
+static int ics_opal_map(struct ics *ics, unsigned int virq)
+{
+ unsigned int hw_irq = (unsigned int)virq_to_hw(virq);
+ int64_t rc;
+ __be16 server;
+ int8_t priority;
+
+ if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS))
+ return -EINVAL;
+
+ /* Check if HAL knows about this interrupt */
+ rc = opal_get_xive(hw_irq, &server, &priority);
+ if (rc != OPAL_SUCCESS)
+ return -ENXIO;
+
+ irq_set_chip_and_handler(virq, &ics_opal_irq_chip, handle_fasteoi_irq);
+ irq_set_chip_data(virq, &ics_hal);
+
+ return 0;
+}
+
+static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec)
+{
+ int64_t rc;
+ __be16 server;
+ int8_t priority;
+
+ /* Check if HAL knows about this interrupt */
+ rc = opal_get_xive(vec, &server, &priority);
+ if (rc != OPAL_SUCCESS)
+ return;
+
+ ics_opal_mask_real_irq(vec);
+}
+
+static long ics_opal_get_server(struct ics *ics, unsigned long vec)
+{
+ int64_t rc;
+ __be16 server;
+ int8_t priority;
+
+ /* Check if HAL knows about this interrupt */
+ rc = opal_get_xive(vec, &server, &priority);
+ if (rc != OPAL_SUCCESS)
+ return -1;
+ return ics_opal_unmangle_server(be16_to_cpu(server));
+}
+
+int __init ics_opal_init(void)
+{
+ if (!firmware_has_feature(FW_FEATURE_OPAL))
+ return -ENODEV;
+
+ /* We need to patch our irq chip's EOI to point to the
+ * right ICP
+ */
+ ics_opal_irq_chip.irq_eoi = icp_ops->eoi;
+
+ /* Register ourselves */
+ xics_register_ics(&ics_hal);
+
+ pr_info("ICS OPAL backend registered\n");
+
+ return 0;
+}
diff --git a/arch/powerpc/sysdev/xics/ics-rtas.c b/arch/powerpc/sysdev/xics/ics-rtas.c
new file mode 100644
index 00000000000..936575d99c5
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/ics-rtas.c
@@ -0,0 +1,240 @@
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/spinlock.h>
+#include <linux/msi.h>
+
+#include <asm/prom.h>
+#include <asm/smp.h>
+#include <asm/machdep.h>
+#include <asm/irq.h>
+#include <asm/errno.h>
+#include <asm/xics.h>
+#include <asm/rtas.h>
+
+/* RTAS service tokens */
+static int ibm_get_xive;
+static int ibm_set_xive;
+static int ibm_int_on;
+static int ibm_int_off;
+
+static int ics_rtas_map(struct ics *ics, unsigned int virq);
+static void ics_rtas_mask_unknown(struct ics *ics, unsigned long vec);
+static long ics_rtas_get_server(struct ics *ics, unsigned long vec);
+static int ics_rtas_host_match(struct ics *ics, struct device_node *node);
+
+/* Only one global & state struct ics */
+static struct ics ics_rtas = {
+ .map = ics_rtas_map,
+ .mask_unknown = ics_rtas_mask_unknown,
+ .get_server = ics_rtas_get_server,
+ .host_match = ics_rtas_host_match,
+};
+
+static void ics_rtas_unmask_irq(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+ int call_status;
+ int server;
+
+ pr_devel("xics: unmask virq %d [hw 0x%x]\n", d->irq, hw_irq);
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return;
+
+ server = xics_get_irq_server(d->irq, d->affinity, 0);
+
+ call_status = rtas_call(ibm_set_xive, 3, 1, NULL, hw_irq, server,
+ DEFAULT_PRIORITY);
+ if (call_status != 0) {
+ printk(KERN_ERR
+ "%s: ibm_set_xive irq %u server %x returned %d\n",
+ __func__, hw_irq, server, call_status);
+ return;
+ }
+
+ /* Now unmask the interrupt (often a no-op) */
+ call_status = rtas_call(ibm_int_on, 1, 1, NULL, hw_irq);
+ if (call_status != 0) {
+ printk(KERN_ERR "%s: ibm_int_on irq=%u returned %d\n",
+ __func__, hw_irq, call_status);
+ return;
+ }
+}
+
+static unsigned int ics_rtas_startup(struct irq_data *d)
+{
+#ifdef CONFIG_PCI_MSI
+ /*
+ * The generic MSI code returns with the interrupt disabled on the
+ * card, using the MSI mask bits. Firmware doesn't appear to unmask
+ * at that level, so we do it here by hand.
+ */
+ if (d->msi_desc)
+ unmask_msi_irq(d);
+#endif
+ /* unmask it */
+ ics_rtas_unmask_irq(d);
+ return 0;
+}
+
+static void ics_rtas_mask_real_irq(unsigned int hw_irq)
+{
+ int call_status;
+
+ if (hw_irq == XICS_IPI)
+ return;
+
+ call_status = rtas_call(ibm_int_off, 1, 1, NULL, hw_irq);
+ if (call_status != 0) {
+ printk(KERN_ERR "%s: ibm_int_off irq=%u returned %d\n",
+ __func__, hw_irq, call_status);
+ return;
+ }
+
+ /* Have to set XIVE to 0xff to be able to remove a slot */
+ call_status = rtas_call(ibm_set_xive, 3, 1, NULL, hw_irq,
+ xics_default_server, 0xff);
+ if (call_status != 0) {
+ printk(KERN_ERR "%s: ibm_set_xive(0xff) irq=%u returned %d\n",
+ __func__, hw_irq, call_status);
+ return;
+ }
+}
+
+static void ics_rtas_mask_irq(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+
+ pr_devel("xics: mask virq %d [hw 0x%x]\n", d->irq, hw_irq);
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return;
+ ics_rtas_mask_real_irq(hw_irq);
+}
+
+static int ics_rtas_set_affinity(struct irq_data *d,
+ const struct cpumask *cpumask,
+ bool force)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+ int status;
+ int xics_status[2];
+ int irq_server;
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return -1;
+
+ status = rtas_call(ibm_get_xive, 1, 3, xics_status, hw_irq);
+
+ if (status) {
+ printk(KERN_ERR "%s: ibm,get-xive irq=%u returns %d\n",
+ __func__, hw_irq, status);
+ return -1;
+ }
+
+ irq_server = xics_get_irq_server(d->irq, cpumask, 1);
+ if (irq_server == -1) {
+ char cpulist[128];
+ cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
+ printk(KERN_WARNING
+ "%s: No online cpus in the mask %s for irq %d\n",
+ __func__, cpulist, d->irq);
+ return -1;
+ }
+
+ status = rtas_call(ibm_set_xive, 3, 1, NULL,
+ hw_irq, irq_server, xics_status[1]);
+
+ if (status) {
+ printk(KERN_ERR "%s: ibm,set-xive irq=%u returns %d\n",
+ __func__, hw_irq, status);
+ return -1;
+ }
+
+ return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip ics_rtas_irq_chip = {
+ .name = "XICS",
+ .irq_startup = ics_rtas_startup,
+ .irq_mask = ics_rtas_mask_irq,
+ .irq_unmask = ics_rtas_unmask_irq,
+ .irq_eoi = NULL, /* Patched at init time */
+ .irq_set_affinity = ics_rtas_set_affinity
+};
+
+static int ics_rtas_map(struct ics *ics, unsigned int virq)
+{
+ unsigned int hw_irq = (unsigned int)virq_to_hw(virq);
+ int status[2];
+ int rc;
+
+ if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS))
+ return -EINVAL;
+
+ /* Check if RTAS knows about this interrupt */
+ rc = rtas_call(ibm_get_xive, 1, 3, status, hw_irq);
+ if (rc)
+ return -ENXIO;
+
+ irq_set_chip_and_handler(virq, &ics_rtas_irq_chip, handle_fasteoi_irq);
+ irq_set_chip_data(virq, &ics_rtas);
+
+ return 0;
+}
+
+static void ics_rtas_mask_unknown(struct ics *ics, unsigned long vec)
+{
+ ics_rtas_mask_real_irq(vec);
+}
+
+static long ics_rtas_get_server(struct ics *ics, unsigned long vec)
+{
+ int rc, status[2];
+
+ rc = rtas_call(ibm_get_xive, 1, 3, status, vec);
+ if (rc)
+ return -1;
+ return status[0];
+}
+
+static int ics_rtas_host_match(struct ics *ics, struct device_node *node)
+{
+ /* IBM machines have interrupt parents of various funky types for things
+ * like vdevices, events, etc... The trick we use here is to match
+ * everything here except the legacy 8259 which is compatible "chrp,iic"
+ */
+ return !of_device_is_compatible(node, "chrp,iic");
+}
+
+__init int ics_rtas_init(void)
+{
+ ibm_get_xive = rtas_token("ibm,get-xive");
+ ibm_set_xive = rtas_token("ibm,set-xive");
+ ibm_int_on = rtas_token("ibm,int-on");
+ ibm_int_off = rtas_token("ibm,int-off");
+
+ /* We enable the RTAS "ICS" if RTAS is present with the
+ * appropriate tokens
+ */
+ if (ibm_get_xive == RTAS_UNKNOWN_SERVICE ||
+ ibm_set_xive == RTAS_UNKNOWN_SERVICE)
+ return -ENODEV;
+
+ /* We need to patch our irq chip's EOI to point to the
+ * right ICP
+ */
+ ics_rtas_irq_chip.irq_eoi = icp_ops->eoi;
+
+ /* Register ourselves */
+ xics_register_ics(&ics_rtas);
+
+ return 0;
+}
+
diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c
new file mode 100644
index 00000000000..fe0cca47716
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/xics-common.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2011 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.
+ *
+ */
+#include <linux/types.h>
+#include <linux/threads.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/debugfs.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/machdep.h>
+#include <asm/irq.h>
+#include <asm/errno.h>
+#include <asm/rtas.h>
+#include <asm/xics.h>
+#include <asm/firmware.h>
+
+/* Globals common to all ICP/ICS implementations */
+const struct icp_ops *icp_ops;
+
+unsigned int xics_default_server = 0xff;
+unsigned int xics_default_distrib_server = 0;
+unsigned int xics_interrupt_server_size = 8;
+
+DEFINE_PER_CPU(struct xics_cppr, xics_cppr);
+
+struct irq_domain *xics_host;
+
+static LIST_HEAD(ics_list);
+
+void xics_update_irq_servers(void)
+{
+ int i, j;
+ struct device_node *np;
+ u32 ilen;
+ const __be32 *ireg;
+ u32 hcpuid;
+
+ /* Find the server numbers for the boot cpu. */
+ np = of_get_cpu_node(boot_cpuid, NULL);
+ BUG_ON(!np);
+
+ hcpuid = get_hard_smp_processor_id(boot_cpuid);
+ xics_default_server = xics_default_distrib_server = hcpuid;
+
+ pr_devel("xics: xics_default_server = 0x%x\n", xics_default_server);
+
+ ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
+ if (!ireg) {
+ of_node_put(np);
+ return;
+ }
+
+ i = ilen / sizeof(int);
+
+ /* Global interrupt distribution server is specified in the last
+ * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last
+ * entry fom this property for current boot cpu id and use it as
+ * default distribution server
+ */
+ for (j = 0; j < i; j += 2) {
+ if (be32_to_cpu(ireg[j]) == hcpuid) {
+ xics_default_distrib_server = be32_to_cpu(ireg[j+1]);
+ break;
+ }
+ }
+ pr_devel("xics: xics_default_distrib_server = 0x%x\n",
+ xics_default_distrib_server);
+ of_node_put(np);
+}
+
+/* GIQ stuff, currently only supported on RTAS setups, will have
+ * to be sorted properly for bare metal
+ */
+void xics_set_cpu_giq(unsigned int gserver, unsigned int join)
+{
+#ifdef CONFIG_PPC_RTAS
+ int index;
+ int status;
+
+ if (!rtas_indicator_present(GLOBAL_INTERRUPT_QUEUE, NULL))
+ return;
+
+ index = (1UL << xics_interrupt_server_size) - 1 - gserver;
+
+ status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE, index, join);
+
+ WARN(status < 0, "set-indicator(%d, %d, %u) returned %d\n",
+ GLOBAL_INTERRUPT_QUEUE, index, join, status);
+#endif
+}
+
+void xics_setup_cpu(void)
+{
+ icp_ops->set_priority(LOWEST_PRIORITY);
+
+ xics_set_cpu_giq(xics_default_distrib_server, 1);
+}
+
+void xics_mask_unknown_vec(unsigned int vec)
+{
+ struct ics *ics;
+
+ pr_err("Interrupt 0x%x (real) is invalid, disabling it.\n", vec);
+
+ list_for_each_entry(ics, &ics_list, link)
+ ics->mask_unknown(ics, vec);
+}
+
+
+#ifdef CONFIG_SMP
+
+static void xics_request_ipi(void)
+{
+ unsigned int ipi;
+
+ ipi = irq_create_mapping(xics_host, XICS_IPI);
+ BUG_ON(ipi == NO_IRQ);
+
+ /*
+ * IPIs are marked IRQF_PERCPU. The handler was set in map.
+ */
+ BUG_ON(request_irq(ipi, icp_ops->ipi_action,
+ IRQF_PERCPU | IRQF_NO_THREAD, "IPI", NULL));
+}
+
+int __init xics_smp_probe(void)
+{
+ /* Setup cause_ipi callback based on which ICP is used */
+ smp_ops->cause_ipi = icp_ops->cause_ipi;
+
+ /* Register all the IPIs */
+ xics_request_ipi();
+
+ return cpumask_weight(cpu_possible_mask);
+}
+
+#endif /* CONFIG_SMP */
+
+void xics_teardown_cpu(void)
+{
+ struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
+
+ /*
+ * we have to reset the cppr index to 0 because we're
+ * not going to return from the IPI
+ */
+ os_cppr->index = 0;
+ icp_ops->set_priority(0);
+ icp_ops->teardown_cpu();
+}
+
+void xics_kexec_teardown_cpu(int secondary)
+{
+ xics_teardown_cpu();
+
+ icp_ops->flush_ipi();
+
+ /*
+ * Some machines need to have at least one cpu in the GIQ,
+ * so leave the master cpu in the group.
+ */
+ if (secondary)
+ xics_set_cpu_giq(xics_default_distrib_server, 0);
+}
+
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+/* Interrupts are disabled. */
+void xics_migrate_irqs_away(void)
+{
+ int cpu = smp_processor_id(), hw_cpu = hard_smp_processor_id();
+ unsigned int irq, virq;
+ struct irq_desc *desc;
+
+ /* If we used to be the default server, move to the new "boot_cpuid" */
+ if (hw_cpu == xics_default_server)
+ xics_update_irq_servers();
+
+ /* Reject any interrupt that was queued to us... */
+ icp_ops->set_priority(0);
+
+ /* Remove ourselves from the global interrupt queue */
+ xics_set_cpu_giq(xics_default_distrib_server, 0);
+
+ /* Allow IPIs again... */
+ icp_ops->set_priority(DEFAULT_PRIORITY);
+
+ for_each_irq_desc(virq, desc) {
+ struct irq_chip *chip;
+ long server;
+ unsigned long flags;
+ struct ics *ics;
+
+ /* We can't set affinity on ISA interrupts */
+ if (virq < NUM_ISA_INTERRUPTS)
+ continue;
+ /* We only need to migrate enabled IRQS */
+ if (!desc->action)
+ continue;
+ if (desc->irq_data.domain != xics_host)
+ continue;
+ irq = desc->irq_data.hwirq;
+ /* We need to get IPIs still. */
+ if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS)
+ continue;
+ chip = irq_desc_get_chip(desc);
+ if (!chip || !chip->irq_set_affinity)
+ continue;
+
+ raw_spin_lock_irqsave(&desc->lock, flags);
+
+ /* Locate interrupt server */
+ server = -1;
+ ics = irq_get_chip_data(virq);
+ if (ics)
+ server = ics->get_server(ics, irq);
+ if (server < 0) {
+ printk(KERN_ERR "%s: Can't find server for irq %d\n",
+ __func__, irq);
+ goto unlock;
+ }
+
+ /* We only support delivery to all cpus or to one cpu.
+ * The irq has to be migrated only in the single cpu
+ * case.
+ */
+ if (server != hw_cpu)
+ goto unlock;
+
+ /* This is expected during cpu offline. */
+ if (cpu_online(cpu))
+ pr_warning("IRQ %u affinity broken off cpu %u\n",
+ virq, cpu);
+
+ /* Reset affinity to all cpus */
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
+ irq_set_affinity(virq, cpu_all_mask);
+ continue;
+unlock:
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
+ }
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+#ifdef CONFIG_SMP
+/*
+ * For the moment we only implement delivery to all cpus or one cpu.
+ *
+ * If the requested affinity is cpu_all_mask, we set global affinity.
+ * If not we set it to the first cpu in the mask, even if multiple cpus
+ * are set. This is so things like irqbalance (which set core and package
+ * wide affinities) do the right thing.
+ *
+ * We need to fix this to implement support for the links
+ */
+int xics_get_irq_server(unsigned int virq, const struct cpumask *cpumask,
+ unsigned int strict_check)
+{
+
+ if (!distribute_irqs)
+ return xics_default_server;
+
+ if (!cpumask_subset(cpu_possible_mask, cpumask)) {
+ int server = cpumask_first_and(cpu_online_mask, cpumask);
+
+ if (server < nr_cpu_ids)
+ return get_hard_smp_processor_id(server);
+
+ if (strict_check)
+ return -1;
+ }
+
+ /*
+ * Workaround issue with some versions of JS20 firmware that
+ * deliver interrupts to cpus which haven't been started. This
+ * happens when using the maxcpus= boot option.
+ */
+ if (cpumask_equal(cpu_online_mask, cpu_present_mask))
+ return xics_default_distrib_server;
+
+ return xics_default_server;
+}
+#endif /* CONFIG_SMP */
+
+static int xics_host_match(struct irq_domain *h, struct device_node *node)
+{
+ struct ics *ics;
+
+ list_for_each_entry(ics, &ics_list, link)
+ if (ics->host_match(ics, node))
+ return 1;
+
+ return 0;
+}
+
+/* Dummies */
+static void xics_ipi_unmask(struct irq_data *d) { }
+static void xics_ipi_mask(struct irq_data *d) { }
+
+static struct irq_chip xics_ipi_chip = {
+ .name = "XICS",
+ .irq_eoi = NULL, /* Patched at init time */
+ .irq_mask = xics_ipi_mask,
+ .irq_unmask = xics_ipi_unmask,
+};
+
+static int xics_host_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct ics *ics;
+
+ pr_devel("xics: map virq %d, hwirq 0x%lx\n", virq, hw);
+
+ /* They aren't all level sensitive but we just don't really know */
+ irq_set_status_flags(virq, IRQ_LEVEL);
+
+ /* Don't call into ICS for IPIs */
+ if (hw == XICS_IPI) {
+ irq_set_chip_and_handler(virq, &xics_ipi_chip,
+ handle_percpu_irq);
+ return 0;
+ }
+
+ /* Let the ICS setup the chip data */
+ list_for_each_entry(ics, &ics_list, link)
+ if (ics->map(ics, virq) == 0)
+ return 0;
+
+ return -EINVAL;
+}
+
+static int xics_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)
+
+{
+ /* Current xics implementation translates everything
+ * to level. It is not technically right for MSIs but this
+ * is irrelevant at this point. We might get smarter in the future
+ */
+ *out_hwirq = intspec[0];
+ *out_flags = IRQ_TYPE_LEVEL_LOW;
+
+ return 0;
+}
+
+static struct irq_domain_ops xics_host_ops = {
+ .match = xics_host_match,
+ .map = xics_host_map,
+ .xlate = xics_host_xlate,
+};
+
+static void __init xics_init_host(void)
+{
+ xics_host = irq_domain_add_tree(NULL, &xics_host_ops, NULL);
+ BUG_ON(xics_host == NULL);
+ irq_set_default_host(xics_host);
+}
+
+void __init xics_register_ics(struct ics *ics)
+{
+ list_add(&ics->link, &ics_list);
+}
+
+static void __init xics_get_server_size(void)
+{
+ struct device_node *np;
+ const __be32 *isize;
+
+ /* We fetch the interrupt server size from the first ICS node
+ * we find if any
+ */
+ np = of_find_compatible_node(NULL, NULL, "ibm,ppc-xics");
+ if (!np)
+ return;
+ isize = of_get_property(np, "ibm,interrupt-server#-size", NULL);
+ if (!isize)
+ return;
+ xics_interrupt_server_size = be32_to_cpu(*isize);
+ of_node_put(np);
+}
+
+void __init xics_init(void)
+{
+ int rc = -1;
+
+ /* Fist locate ICP */
+ if (firmware_has_feature(FW_FEATURE_LPAR))
+ rc = icp_hv_init();
+ if (rc < 0)
+ rc = icp_native_init();
+ if (rc < 0) {
+ pr_warning("XICS: Cannot find a Presentation Controller !\n");
+ return;
+ }
+
+ /* Copy get_irq callback over to ppc_md */
+ ppc_md.get_irq = icp_ops->get_irq;
+
+ /* Patch up IPI chip EOI */
+ xics_ipi_chip.irq_eoi = icp_ops->eoi;
+
+ /* Now locate ICS */
+ rc = ics_rtas_init();
+ if (rc < 0)
+ rc = ics_opal_init();
+ if (rc < 0)
+ pr_warning("XICS: Cannot find a Source Controller !\n");
+
+ /* Initialize common bits */
+ xics_get_server_size();
+ xics_update_irq_servers();
+ xics_init_host();
+ xics_setup_cpu();
+}
diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c
index ba8eea2bcce..83f943a8e0d 100644
--- a/arch/powerpc/sysdev/xilinx_intc.c
+++ b/arch/powerpc/sysdev/xilinx_intc.c
@@ -23,8 +23,11 @@
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <asm/io.h>
#include <asm/processor.h>
+#include <asm/i8259.h>
#include <asm/irq.h>
/*
@@ -39,87 +42,172 @@
#define XINTC_IVR 24 /* Interrupt Vector */
#define XINTC_MER 28 /* Master Enable */
-static struct irq_host *master_irqhost;
+static struct irq_domain *master_irqhost;
+
+#define XILINX_INTC_MAXIRQS (32)
+
+/* The following table allows the interrupt type, edge or level,
+ * to be cached after being read from the device tree until the interrupt
+ * is mapped
+ */
+static int xilinx_intc_typetable[XILINX_INTC_MAXIRQS];
+
+/* Map the interrupt type from the device tree to the interrupt types
+ * used by the interrupt subsystem
+ */
+static unsigned char xilinx_intc_map_senses[] = {
+ IRQ_TYPE_EDGE_RISING,
+ IRQ_TYPE_EDGE_FALLING,
+ IRQ_TYPE_LEVEL_HIGH,
+ IRQ_TYPE_LEVEL_LOW,
+};
/*
- * IRQ Chip operations
+ * The interrupt controller is setup such that it doesn't work well with
+ * the level interrupt handler in the kernel because the handler acks the
+ * interrupt before calling the application interrupt handler. To deal with
+ * that, we use 2 different irq chips so that different functions can be
+ * used for level and edge type interrupts.
+ *
+ * IRQ Chip common (across level and edge) operations
*/
-static void xilinx_intc_mask(unsigned int virq)
+static void xilinx_intc_mask(struct irq_data *d)
{
- int irq = virq_to_hw(virq);
- void * regs = get_irq_chip_data(virq);
+ int irq = irqd_to_hwirq(d);
+ void * regs = irq_data_get_irq_chip_data(d);
pr_debug("mask: %d\n", irq);
out_be32(regs + XINTC_CIE, 1 << irq);
}
-static void xilinx_intc_unmask(unsigned int virq)
+static int xilinx_intc_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ return 0;
+}
+
+/*
+ * IRQ Chip level operations
+ */
+static void xilinx_intc_level_unmask(struct irq_data *d)
+{
+ int irq = irqd_to_hwirq(d);
+ void * regs = irq_data_get_irq_chip_data(d);
+ pr_debug("unmask: %d\n", irq);
+ out_be32(regs + XINTC_SIE, 1 << irq);
+
+ /* ack level irqs because they can't be acked during
+ * ack function since the handle_level_irq function
+ * acks the irq before calling the inerrupt handler
+ */
+ out_be32(regs + XINTC_IAR, 1 << irq);
+}
+
+static struct irq_chip xilinx_intc_level_irqchip = {
+ .name = "Xilinx Level INTC",
+ .irq_mask = xilinx_intc_mask,
+ .irq_mask_ack = xilinx_intc_mask,
+ .irq_unmask = xilinx_intc_level_unmask,
+ .irq_set_type = xilinx_intc_set_type,
+};
+
+/*
+ * IRQ Chip edge operations
+ */
+static void xilinx_intc_edge_unmask(struct irq_data *d)
{
- int irq = virq_to_hw(virq);
- void * regs = get_irq_chip_data(virq);
+ int irq = irqd_to_hwirq(d);
+ void *regs = irq_data_get_irq_chip_data(d);
pr_debug("unmask: %d\n", irq);
out_be32(regs + XINTC_SIE, 1 << irq);
}
-static void xilinx_intc_ack(unsigned int virq)
+static void xilinx_intc_edge_ack(struct irq_data *d)
{
- int irq = virq_to_hw(virq);
- void * regs = get_irq_chip_data(virq);
+ int irq = irqd_to_hwirq(d);
+ void * regs = irq_data_get_irq_chip_data(d);
pr_debug("ack: %d\n", irq);
out_be32(regs + XINTC_IAR, 1 << irq);
}
-static struct irq_chip xilinx_intc_irqchip = {
- .typename = "Xilinx INTC",
- .mask = xilinx_intc_mask,
- .unmask = xilinx_intc_unmask,
- .ack = xilinx_intc_ack,
+static struct irq_chip xilinx_intc_edge_irqchip = {
+ .name = "Xilinx Edge INTC",
+ .irq_mask = xilinx_intc_mask,
+ .irq_unmask = xilinx_intc_edge_unmask,
+ .irq_ack = xilinx_intc_edge_ack,
+ .irq_set_type = xilinx_intc_set_type,
};
/*
* IRQ Host operations
*/
-static int xilinx_intc_map(struct irq_host *h, unsigned int virq,
+
+/**
+ * xilinx_intc_xlate - translate virq# from device tree interrupts property
+ */
+static int xilinx_intc_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)
+{
+ if ((intsize < 2) || (intspec[0] >= XILINX_INTC_MAXIRQS))
+ return -EINVAL;
+
+ /* keep a copy of the interrupt type til the interrupt is mapped
+ */
+ xilinx_intc_typetable[intspec[0]] = xilinx_intc_map_senses[intspec[1]];
+
+ /* Xilinx uses 2 interrupt entries, the 1st being the h/w
+ * interrupt number, the 2nd being the interrupt type, edge or level
+ */
+ *out_hwirq = intspec[0];
+ *out_flags = xilinx_intc_map_senses[intspec[1]];
+
+ return 0;
+}
+static int xilinx_intc_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t irq)
{
- set_irq_chip_data(virq, h->host_data);
- set_irq_chip_and_handler(virq, &xilinx_intc_irqchip, handle_level_irq);
- set_irq_type(virq, IRQ_TYPE_NONE);
+ irq_set_chip_data(virq, h->host_data);
+
+ if (xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_HIGH ||
+ xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_LOW) {
+ irq_set_chip_and_handler(virq, &xilinx_intc_level_irqchip,
+ handle_level_irq);
+ } else {
+ irq_set_chip_and_handler(virq, &xilinx_intc_edge_irqchip,
+ handle_edge_irq);
+ }
return 0;
}
-static struct irq_host_ops xilinx_intc_ops = {
+static struct irq_domain_ops xilinx_intc_ops = {
.map = xilinx_intc_map,
+ .xlate = xilinx_intc_xlate,
};
-struct irq_host * __init
+struct irq_domain * __init
xilinx_intc_init(struct device_node *np)
{
- struct irq_host * irq;
- struct resource res;
+ struct irq_domain * irq;
void * regs;
- int rc;
/* Find and map the intc registers */
- rc = of_address_to_resource(np, 0, &res);
- if (rc) {
- printk(KERN_ERR __FILE__ ": of_address_to_resource() failed\n");
+ regs = of_iomap(np, 0);
+ if (!regs) {
+ pr_err("xilinx_intc: could not map registers\n");
return NULL;
}
- regs = ioremap(res.start, 32);
-
- printk(KERN_INFO "Xilinx intc at 0x%08X mapped to 0x%p\n",
- res.start, regs);
/* Setup interrupt controller */
out_be32(regs + XINTC_IER, 0); /* disable all irqs */
out_be32(regs + XINTC_IAR, ~(u32) 0); /* Acknowledge pending irqs */
out_be32(regs + XINTC_MER, 0x3UL); /* Turn on the Master Enable. */
- /* Allocate and initialize an irq_host structure. */
- irq = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, 32, &xilinx_intc_ops, -1);
+ /* Allocate and initialize an irq_domain structure. */
+ irq = irq_domain_add_linear(np, XILINX_INTC_MAXIRQS, &xilinx_intc_ops,
+ regs);
if (!irq)
panic(__FILE__ ": Cannot allocate IRQ host\n");
- irq->host_data = regs;
+
return irq;
}
@@ -130,23 +218,71 @@ int xilinx_intc_get_irq(void)
return irq_linear_revmap(master_irqhost, in_be32(regs + XINTC_IVR));
}
+#if defined(CONFIG_PPC_I8259)
+/*
+ * Support code for cascading to 8259 interrupt controllers
+ */
+static void xilinx_i8259_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned int cascade_irq = i8259_irq();
+
+ if (cascade_irq)
+ generic_handle_irq(cascade_irq);
+
+ /* Let xilinx_intc end the interrupt */
+ chip->irq_unmask(&desc->irq_data);
+}
+
+static void __init xilinx_i8259_setup_cascade(void)
+{
+ struct device_node *cascade_node;
+ int cascade_irq;
+
+ /* Initialize i8259 controller */
+ cascade_node = of_find_compatible_node(NULL, NULL, "chrp,iic");
+ if (!cascade_node)
+ return;
+
+ cascade_irq = irq_of_parse_and_map(cascade_node, 0);
+ if (!cascade_irq) {
+ pr_err("virtex_ml510: Failed to map cascade interrupt\n");
+ goto out;
+ }
+
+ i8259_init(cascade_node, 0);
+ irq_set_chained_handler(cascade_irq, xilinx_i8259_cascade);
+
+ /* Program irq 7 (usb/audio), 14/15 (ide) to level sensitive */
+ /* This looks like a dirty hack to me --gcl */
+ outb(0xc0, 0x4d0);
+ outb(0xc0, 0x4d1);
+
+ out:
+ of_node_put(cascade_node);
+}
+#else
+static inline void xilinx_i8259_setup_cascade(void) { return; }
+#endif /* defined(CONFIG_PPC_I8259) */
+
+static struct of_device_id xilinx_intc_match[] __initconst = {
+ { .compatible = "xlnx,opb-intc-1.00.c", },
+ { .compatible = "xlnx,xps-intc-1.00.a", },
+ {}
+};
+
+/*
+ * Initialize master Xilinx interrupt controller
+ */
void __init xilinx_intc_init_tree(void)
{
struct device_node *np;
/* find top level interrupt controller */
- for_each_compatible_node(np, NULL, "xlnx,opb-intc-1.00.c") {
+ for_each_matching_node(np, xilinx_intc_match) {
if (!of_get_property(np, "interrupts", NULL))
break;
}
- if (!np) {
- for_each_compatible_node(np, NULL, "xlnx,xps-intc-1.00.a") {
- if (!of_get_property(np, "interrupts", NULL))
- break;
- }
- }
-
- /* xilinx interrupt controller needs to be top level */
BUG_ON(!np);
master_irqhost = xilinx_intc_init(np);
@@ -154,4 +290,6 @@ void __init xilinx_intc_init_tree(void)
irq_set_default_host(master_irqhost);
of_node_put(np);
+
+ xilinx_i8259_setup_cascade();
}
diff --git a/arch/powerpc/sysdev/xilinx_pci.c b/arch/powerpc/sysdev/xilinx_pci.c
new file mode 100644
index 00000000000..1453b0eed22
--- /dev/null
+++ b/arch/powerpc/sysdev/xilinx_pci.c
@@ -0,0 +1,132 @@
+/*
+ * PCI support for Xilinx plbv46_pci soft-core which can be used on
+ * Xilinx Virtex ML410 / ML510 boards.
+ *
+ * Copyright 2009 Roderick Colenbrander
+ * Copyright 2009 Secret Lab Technologies Ltd.
+ *
+ * The pci bridge fixup code was copied from ppc4xx_pci.c and was written
+ * by Benjamin Herrenschmidt.
+ * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/ioport.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+#include <mm/mmu_decl.h>
+#include <asm/io.h>
+#include <asm/xilinx_pci.h>
+
+#define XPLB_PCI_ADDR 0x10c
+#define XPLB_PCI_DATA 0x110
+#define XPLB_PCI_BUS 0x114
+
+#define PCI_HOST_ENABLE_CMD PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY
+
+static struct of_device_id xilinx_pci_match[] = {
+ { .compatible = "xlnx,plbv46-pci-1.03.a", },
+ {}
+};
+
+/**
+ * xilinx_pci_fixup_bridge - Block Xilinx PHB configuration.
+ */
+static void xilinx_pci_fixup_bridge(struct pci_dev *dev)
+{
+ struct pci_controller *hose;
+ int i;
+
+ if (dev->devfn || dev->bus->self)
+ return;
+
+ hose = pci_bus_to_host(dev->bus);
+ if (!hose)
+ return;
+
+ if (!of_match_node(xilinx_pci_match, hose->dn))
+ return;
+
+ /* Hide the PCI host BARs from the kernel as their content doesn't
+ * fit well in the resource management
+ */
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ dev->resource[i].start = 0;
+ dev->resource[i].end = 0;
+ dev->resource[i].flags = 0;
+ }
+
+ dev_info(&dev->dev, "Hiding Xilinx plb-pci host bridge resources %s\n",
+ pci_name(dev));
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, xilinx_pci_fixup_bridge);
+
+/**
+ * xilinx_pci_exclude_device - Don't do config access for non-root bus
+ *
+ * This is a hack. Config access to any bus other than bus 0 does not
+ * currently work on the ML510 so we prevent it here.
+ */
+static int
+xilinx_pci_exclude_device(struct pci_controller *hose, u_char bus, u8 devfn)
+{
+ return (bus != 0);
+}
+
+/**
+ * xilinx_pci_init - Find and register a Xilinx PCI host bridge
+ */
+void __init xilinx_pci_init(void)
+{
+ struct pci_controller *hose;
+ struct resource r;
+ void __iomem *pci_reg;
+ struct device_node *pci_node;
+
+ pci_node = of_find_matching_node(NULL, xilinx_pci_match);
+ if(!pci_node)
+ return;
+
+ if (of_address_to_resource(pci_node, 0, &r)) {
+ pr_err("xilinx-pci: cannot resolve base address\n");
+ return;
+ }
+
+ hose = pcibios_alloc_controller(pci_node);
+ if (!hose) {
+ pr_err("xilinx-pci: pcibios_alloc_controller() failed\n");
+ return;
+ }
+
+ /* Setup config space */
+ setup_indirect_pci(hose, r.start + XPLB_PCI_ADDR,
+ r.start + XPLB_PCI_DATA,
+ PPC_INDIRECT_TYPE_SET_CFG_TYPE);
+
+ /* According to the xilinx plbv46_pci documentation the soft-core starts
+ * a self-init when the bus master enable bit is set. Without this bit
+ * set the pci bus can't be scanned.
+ */
+ early_write_config_word(hose, 0, 0, PCI_COMMAND, PCI_HOST_ENABLE_CMD);
+
+ /* Set the max latency timer to 255 */
+ early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0xff);
+
+ /* Set the max bus number to 255 */
+ pci_reg = of_iomap(pci_node, 0);
+ out_8(pci_reg + XPLB_PCI_BUS, 0xff);
+ iounmap(pci_reg);
+
+ /* Nothing past the root bridge is working right now. By default
+ * exclude config access to anything except bus 0 */
+ if (!ppc_md.pci_exclude_device)
+ ppc_md.pci_exclude_device = xilinx_pci_exclude_device;
+
+ /* Register the host bridge with the linux kernel! */
+ pci_process_bridge_OF_ranges(hose, pci_node, 1);
+
+ pr_info("xilinx-pci: Registered PCI host bridge\n");
+}