You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6
* 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6: (23 commits)
[SPARC64]: virt_to_real_irq_table --> virt_irq_table
[SPARC64]: virt_irq --> bucket mapping no longer necessary
[SPARC64]: Kill ugly __bucket() macro.
[SPARC64]: Kill ugly __irq_ino() macro.
[SPARC64]: Only use bypass accesses to INO buckets.
[SPARC64]: Update defconfig.
[SPARC64]: Use sun4v VIRQ interfaces as intended.
[SPARC64]: Allocate ivector_table dynamically.
[SPARC64]: Access ivector_table[] using physical addresses.
[SPARC64]: Make IVEC pointers 64-bit.
[SPARC64]: Fix register usage in xor_raid_4().
[SPARC64]: Kill pci_memspace_mask.
[SPARC64]: Consolidate MSI support code.
[SPARC/64]: Move of_platform_driver initialisations: arch/sparc{,64}.
[SPARC64]: Fix bugs in SYSV IPC handling in 64-bit processes.
[SPARC/64]: Prepare to remove of_platform_driver name.
[SPARC32]: Add irqflags.h to sparc32 and use it from generic code.
[SPARC64]: beautify vmlinux.lds
[SPARC]: beautify vmlinux.lds
[SPARC64]: Enable MSI on sun4u Fire PCI-E controllers.
...
This commit is contained in:
@@ -67,10 +67,12 @@ probe in an SBUS driver under Linux:
|
||||
MODULE_DEVICE_TABLE(of, mydevice_match);
|
||||
|
||||
static struct of_platform_driver mydevice_driver = {
|
||||
.name = "mydevice",
|
||||
.match_table = mydevice_match,
|
||||
.probe = mydevice_probe,
|
||||
.remove = __devexit_p(mydevice_remove),
|
||||
.driver = {
|
||||
.name = "mydevice",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mydevice_init(void)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
menu "Kernel hacking"
|
||||
|
||||
config TRACE_IRQFLAGS_SUPPORT
|
||||
bool
|
||||
default y
|
||||
|
||||
source "lib/Kconfig.debug"
|
||||
|
||||
config DEBUG_STACK_USAGE
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
#define SMP_NOP2
|
||||
#define SMP_NOP3
|
||||
#endif /* SMP */
|
||||
unsigned long __local_irq_save(void)
|
||||
unsigned long __raw_local_irq_save(void)
|
||||
{
|
||||
unsigned long retval;
|
||||
unsigned long tmp;
|
||||
@@ -74,7 +74,7 @@ unsigned long __local_irq_save(void)
|
||||
return retval;
|
||||
}
|
||||
|
||||
void local_irq_enable(void)
|
||||
void raw_local_irq_enable(void)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
@@ -89,7 +89,7 @@ void local_irq_enable(void)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
void local_irq_restore(unsigned long old_psr)
|
||||
void raw_local_irq_restore(unsigned long old_psr)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
@@ -105,9 +105,9 @@ void local_irq_restore(unsigned long old_psr)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__local_irq_save);
|
||||
EXPORT_SYMBOL(local_irq_enable);
|
||||
EXPORT_SYMBOL(local_irq_restore);
|
||||
EXPORT_SYMBOL(__raw_local_irq_save);
|
||||
EXPORT_SYMBOL(raw_local_irq_enable);
|
||||
EXPORT_SYMBOL(raw_local_irq_restore);
|
||||
|
||||
/*
|
||||
* Dave Redman (djhr@tadpole.co.uk)
|
||||
|
||||
@@ -588,7 +588,10 @@ __setup("of_debug=", of_debug);
|
||||
int of_register_driver(struct of_platform_driver *drv, struct bus_type *bus)
|
||||
{
|
||||
/* initialize common driver fields */
|
||||
if (!drv->driver.name)
|
||||
drv->driver.name = drv->name;
|
||||
if (!drv->driver.owner)
|
||||
drv->driver.owner = drv->owner;
|
||||
drv->driver.bus = bus;
|
||||
|
||||
/* register with core */
|
||||
|
||||
@@ -347,9 +347,11 @@ static struct of_device_id clock_match[] = {
|
||||
};
|
||||
|
||||
static struct of_platform_driver clock_driver = {
|
||||
.name = "clock",
|
||||
.match_table = clock_match,
|
||||
.probe = clock_probe,
|
||||
.driver = {
|
||||
.name = "clock",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* ld script to make SparcLinux kernel */
|
||||
|
||||
#include <asm-generic/vmlinux.lds.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
OUTPUT_FORMAT("elf32-sparc", "elf32-sparc", "elf32-sparc")
|
||||
OUTPUT_ARCH(sparc)
|
||||
@@ -20,72 +21,92 @@ SECTIONS
|
||||
_etext = .;
|
||||
PROVIDE (etext = .);
|
||||
RODATA
|
||||
.data :
|
||||
{
|
||||
.data : {
|
||||
DATA_DATA
|
||||
CONSTRUCTORS
|
||||
}
|
||||
.data1 : { *(.data1) }
|
||||
.data1 : {
|
||||
*(.data1)
|
||||
}
|
||||
_edata = .;
|
||||
PROVIDE (edata = .);
|
||||
|
||||
.fixup : {
|
||||
__start___fixup = .;
|
||||
.fixup : { *(.fixup) }
|
||||
*(.fixup)
|
||||
__stop___fixup = .;
|
||||
}
|
||||
__ex_table : {
|
||||
__start___ex_table = .;
|
||||
__ex_table : { *(__ex_table) }
|
||||
*(__ex_table)
|
||||
__stop___ex_table = .;
|
||||
}
|
||||
|
||||
NOTES
|
||||
|
||||
. = ALIGN(4096);
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
__init_begin = .;
|
||||
_sinittext = .;
|
||||
.init.text : {
|
||||
_sinittext = .;
|
||||
*(.init.text)
|
||||
}
|
||||
_einittext = .;
|
||||
__init_text_end = .;
|
||||
.init.data : { *(.init.data) }
|
||||
. = ALIGN(16);
|
||||
__setup_start = .;
|
||||
.init.setup : { *(.init.setup) }
|
||||
__setup_end = .;
|
||||
__initcall_start = .;
|
||||
.initcall.init : {
|
||||
INITCALLS
|
||||
}
|
||||
__init_text_end = .;
|
||||
.init.data : {
|
||||
*(.init.data)
|
||||
}
|
||||
. = ALIGN(16);
|
||||
.init.setup : {
|
||||
__setup_start = .;
|
||||
*(.init.setup)
|
||||
__setup_end = .;
|
||||
}
|
||||
.initcall.init : {
|
||||
__initcall_start = .;
|
||||
INITCALLS
|
||||
__initcall_end = .;
|
||||
}
|
||||
.con_initcall.init : {
|
||||
__con_initcall_start = .;
|
||||
.con_initcall.init : { *(.con_initcall.init) }
|
||||
*(.con_initcall.init)
|
||||
__con_initcall_end = .;
|
||||
}
|
||||
SECURITY_INIT
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
. = ALIGN(4096);
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
.init.ramfs : {
|
||||
__initramfs_start = .;
|
||||
.init.ramfs : { *(.init.ramfs) }
|
||||
*(.init.ramfs)
|
||||
__initramfs_end = .;
|
||||
}
|
||||
#endif
|
||||
|
||||
PERCPU(4096)
|
||||
. = ALIGN(4096);
|
||||
PERCPU(PAGE_SIZE)
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
__init_end = .;
|
||||
. = ALIGN(32);
|
||||
.data.cacheline_aligned : { *(.data.cacheline_aligned) }
|
||||
.data.cacheline_aligned : {
|
||||
*(.data.cacheline_aligned)
|
||||
}
|
||||
|
||||
__bss_start = .;
|
||||
.sbss : { *(.sbss) *(.scommon) }
|
||||
.bss :
|
||||
{
|
||||
.sbss : {
|
||||
*(.sbss)
|
||||
*(.scommon) }
|
||||
.bss : {
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
_end = . ;
|
||||
PROVIDE (end = .);
|
||||
/DISCARD/ : { *(.exit.text) *(.exit.data) *(.exitcall.exit) }
|
||||
/DISCARD/ : {
|
||||
*(.exit.text)
|
||||
*(.exit.data)
|
||||
*(.exitcall.exit)
|
||||
}
|
||||
|
||||
STABS_DEBUG
|
||||
|
||||
DWARF_DEBUG
|
||||
}
|
||||
|
||||
+33
-5
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Automatically generated make config: don't edit
|
||||
# Linux kernel version: 2.6.23-rc6
|
||||
# Sun Sep 16 09:52:11 2007
|
||||
# Linux kernel version: 2.6.23
|
||||
# Sat Oct 13 21:53:54 2007
|
||||
#
|
||||
CONFIG_SPARC=y
|
||||
CONFIG_SPARC64=y
|
||||
@@ -69,7 +69,6 @@ CONFIG_FUTEX=y
|
||||
CONFIG_ANON_INODES=y
|
||||
CONFIG_EPOLL=y
|
||||
CONFIG_SIGNALFD=y
|
||||
CONFIG_TIMERFD=y
|
||||
CONFIG_EVENTFD=y
|
||||
CONFIG_SHMEM=y
|
||||
CONFIG_VM_EVENT_COUNTERS=y
|
||||
@@ -89,6 +88,7 @@ CONFIG_KMOD=y
|
||||
CONFIG_BLOCK=y
|
||||
CONFIG_BLK_DEV_IO_TRACE=y
|
||||
CONFIG_BLK_DEV_BSG=y
|
||||
CONFIG_BLOCK_COMPAT=y
|
||||
|
||||
#
|
||||
# IO Schedulers
|
||||
@@ -111,6 +111,7 @@ CONFIG_GENERIC_HARDIRQS=y
|
||||
CONFIG_TICK_ONESHOT=y
|
||||
CONFIG_NO_HZ=y
|
||||
CONFIG_HIGH_RES_TIMERS=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
|
||||
# CONFIG_SMP is not set
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_TABLE=m
|
||||
@@ -119,6 +120,8 @@ CONFIG_CPU_FREQ_STAT=m
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
|
||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=m
|
||||
@@ -213,6 +216,7 @@ CONFIG_INET_TUNNEL=y
|
||||
CONFIG_INET_XFRM_MODE_TRANSPORT=y
|
||||
CONFIG_INET_XFRM_MODE_TUNNEL=y
|
||||
CONFIG_INET_XFRM_MODE_BEET=y
|
||||
CONFIG_INET_LRO=y
|
||||
CONFIG_INET_DIAG=y
|
||||
CONFIG_INET_TCP_DIAG=y
|
||||
# CONFIG_TCP_CONG_ADVANCED is not set
|
||||
@@ -304,6 +308,7 @@ CONFIG_NET_TCPPROBE=m
|
||||
#
|
||||
# Generic Driver Options
|
||||
#
|
||||
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
|
||||
CONFIG_STANDALONE=y
|
||||
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
|
||||
CONFIG_FW_LOADER=y
|
||||
@@ -355,6 +360,11 @@ CONFIG_IDE_PROC_FS=y
|
||||
# IDE chipset support/bugfixes
|
||||
#
|
||||
CONFIG_IDE_GENERIC=y
|
||||
# CONFIG_BLK_DEV_PLATFORM is not set
|
||||
|
||||
#
|
||||
# PCI IDE chipsets support
|
||||
#
|
||||
CONFIG_BLK_DEV_IDEPCI=y
|
||||
# CONFIG_IDEPCI_SHARE_IRQ is not set
|
||||
CONFIG_IDEPCI_PCIBUS_ORDER=y
|
||||
@@ -391,7 +401,6 @@ CONFIG_BLK_DEV_ALI15X3=y
|
||||
# CONFIG_BLK_DEV_TC86C001 is not set
|
||||
# CONFIG_IDE_ARM is not set
|
||||
CONFIG_BLK_DEV_IDEDMA=y
|
||||
# CONFIG_IDEDMA_IVB is not set
|
||||
# CONFIG_BLK_DEV_HD is not set
|
||||
|
||||
#
|
||||
@@ -505,6 +514,8 @@ CONFIG_DUMMY=m
|
||||
# CONFIG_MACVLAN is not set
|
||||
# CONFIG_EQUALIZER is not set
|
||||
# CONFIG_TUN is not set
|
||||
# CONFIG_VETH is not set
|
||||
# CONFIG_IP1000 is not set
|
||||
# CONFIG_ARCNET is not set
|
||||
# CONFIG_PHYLIB is not set
|
||||
CONFIG_NET_ETHERNET=y
|
||||
@@ -518,13 +529,16 @@ CONFIG_CASSINI=m
|
||||
# CONFIG_NET_VENDOR_3COM is not set
|
||||
# CONFIG_NET_TULIP is not set
|
||||
# CONFIG_HP100 is not set
|
||||
# CONFIG_IBM_NEW_EMAC_ZMII is not set
|
||||
# CONFIG_IBM_NEW_EMAC_RGMII is not set
|
||||
# CONFIG_IBM_NEW_EMAC_TAH is not set
|
||||
# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
|
||||
CONFIG_NET_PCI=y
|
||||
# CONFIG_PCNET32 is not set
|
||||
# CONFIG_AMD8111_ETH is not set
|
||||
# CONFIG_ADAPTEC_STARFIRE is not set
|
||||
# CONFIG_B44 is not set
|
||||
# CONFIG_FORCEDETH is not set
|
||||
# CONFIG_DGRS is not set
|
||||
# CONFIG_EEPRO100 is not set
|
||||
# CONFIG_E100 is not set
|
||||
# CONFIG_FEALNX is not set
|
||||
@@ -543,6 +557,7 @@ CONFIG_NETDEV_1000=y
|
||||
CONFIG_E1000=m
|
||||
CONFIG_E1000_NAPI=y
|
||||
# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set
|
||||
# CONFIG_E1000E is not set
|
||||
# CONFIG_MYRI_SBUS is not set
|
||||
# CONFIG_NS83820 is not set
|
||||
# CONFIG_HAMACHI is not set
|
||||
@@ -560,11 +575,14 @@ CONFIG_BNX2=m
|
||||
CONFIG_NETDEV_10000=y
|
||||
# CONFIG_CHELSIO_T1 is not set
|
||||
# CONFIG_CHELSIO_T3 is not set
|
||||
# CONFIG_IXGBE is not set
|
||||
# CONFIG_IXGB is not set
|
||||
# CONFIG_S2IO is not set
|
||||
# CONFIG_MYRI10GE is not set
|
||||
# CONFIG_NETXEN_NIC is not set
|
||||
# CONFIG_NIU is not set
|
||||
# CONFIG_MLX4_CORE is not set
|
||||
# CONFIG_TEHUTI is not set
|
||||
# CONFIG_TR is not set
|
||||
|
||||
#
|
||||
@@ -819,6 +837,12 @@ CONFIG_HWMON=y
|
||||
# CONFIG_SENSORS_W83627EHF is not set
|
||||
# CONFIG_HWMON_DEBUG_CHIP is not set
|
||||
|
||||
#
|
||||
# Sonics Silicon Backplane
|
||||
#
|
||||
CONFIG_SSB_POSSIBLE=y
|
||||
# CONFIG_SSB is not set
|
||||
|
||||
#
|
||||
# Multifunction device drivers
|
||||
#
|
||||
@@ -1399,6 +1423,7 @@ CONFIG_ASYNC_MEMCPY=m
|
||||
CONFIG_ASYNC_XOR=m
|
||||
CONFIG_CRYPTO=y
|
||||
CONFIG_CRYPTO_ALGAPI=y
|
||||
CONFIG_CRYPTO_AEAD=m
|
||||
CONFIG_CRYPTO_BLKCIPHER=y
|
||||
CONFIG_CRYPTO_HASH=y
|
||||
CONFIG_CRYPTO_MANAGER=y
|
||||
@@ -1417,6 +1442,7 @@ CONFIG_CRYPTO_ECB=m
|
||||
CONFIG_CRYPTO_CBC=y
|
||||
CONFIG_CRYPTO_PCBC=m
|
||||
CONFIG_CRYPTO_LRW=m
|
||||
CONFIG_CRYPTO_XTS=m
|
||||
# CONFIG_CRYPTO_CRYPTD is not set
|
||||
CONFIG_CRYPTO_DES=y
|
||||
CONFIG_CRYPTO_FCRYPT=m
|
||||
@@ -1431,11 +1457,13 @@ CONFIG_CRYPTO_TEA=m
|
||||
CONFIG_CRYPTO_ARC4=m
|
||||
CONFIG_CRYPTO_KHAZAD=m
|
||||
CONFIG_CRYPTO_ANUBIS=m
|
||||
CONFIG_CRYPTO_SEED=m
|
||||
CONFIG_CRYPTO_DEFLATE=y
|
||||
CONFIG_CRYPTO_MICHAEL_MIC=m
|
||||
CONFIG_CRYPTO_CRC32C=m
|
||||
CONFIG_CRYPTO_CAMELLIA=m
|
||||
CONFIG_CRYPTO_TEST=m
|
||||
CONFIG_CRYPTO_AUTHENC=m
|
||||
CONFIG_CRYPTO_HW=y
|
||||
|
||||
#
|
||||
|
||||
@@ -18,6 +18,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o
|
||||
obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o \
|
||||
pci_psycho.o pci_sabre.o pci_schizo.o \
|
||||
pci_sun4v.o pci_sun4v_asm.o pci_fire.o
|
||||
obj-$(CONFIG_PCI_MSI) += pci_msi.o
|
||||
obj-$(CONFIG_SMP) += smp.o trampoline.o hvtramp.o
|
||||
obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o
|
||||
obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o
|
||||
|
||||
@@ -148,9 +148,11 @@ static int __devinit auxio_probe(struct of_device *dev, const struct of_device_i
|
||||
}
|
||||
|
||||
static struct of_platform_driver auxio_driver = {
|
||||
.name = "auxio",
|
||||
.match_table = auxio_match,
|
||||
.probe = auxio_probe,
|
||||
.driver = {
|
||||
.name = "auxio",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init auxio_init(void)
|
||||
|
||||
@@ -429,16 +429,16 @@ do_ivec:
|
||||
stxa %g0, [%g0] ASI_INTR_RECEIVE
|
||||
membar #Sync
|
||||
|
||||
sethi %hi(ivector_table), %g2
|
||||
sllx %g3, 3, %g3
|
||||
or %g2, %lo(ivector_table), %g2
|
||||
sethi %hi(ivector_table_pa), %g2
|
||||
ldx [%g2 + %lo(ivector_table_pa)], %g2
|
||||
sllx %g3, 4, %g3
|
||||
add %g2, %g3, %g3
|
||||
|
||||
TRAP_LOAD_IRQ_WORK(%g6, %g1)
|
||||
TRAP_LOAD_IRQ_WORK_PA(%g6, %g1)
|
||||
|
||||
lduw [%g6], %g5 /* g5 = irq_work(cpu) */
|
||||
stw %g5, [%g3 + 0x00] /* bucket->irq_chain = g5 */
|
||||
stw %g3, [%g6] /* irq_work(cpu) = bucket */
|
||||
ldx [%g6], %g5
|
||||
stxa %g5, [%g3] ASI_PHYS_USE_EC
|
||||
stx %g3, [%g6]
|
||||
wr %g0, 1 << PIL_DEVICE_IRQ, %set_softint
|
||||
retry
|
||||
do_ivec_xcall:
|
||||
|
||||
+195
-271
File diff suppressed because it is too large
Load Diff
@@ -872,7 +872,10 @@ __setup("of_debug=", of_debug);
|
||||
int of_register_driver(struct of_platform_driver *drv, struct bus_type *bus)
|
||||
{
|
||||
/* initialize common driver fields */
|
||||
if (!drv->driver.name)
|
||||
drv->driver.name = drv->name;
|
||||
if (!drv->driver.owner)
|
||||
drv->driver.owner = drv->owner;
|
||||
drv->driver.bus = bus;
|
||||
|
||||
/* register with core */
|
||||
|
||||
+30
-30
@@ -29,8 +29,6 @@
|
||||
|
||||
#include "pci_impl.h"
|
||||
|
||||
unsigned long pci_memspace_mask = 0xffffffffUL;
|
||||
|
||||
#ifndef CONFIG_PCI
|
||||
/* A "nop" PCI implementation. */
|
||||
asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn,
|
||||
@@ -1066,8 +1064,8 @@ static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Adjust vm_pgoff of VMA such that it is the physical page offset corresponding
|
||||
* to the 32-bit pci bus offset for DEV requested by the user.
|
||||
/* Adjust vm_pgoff of VMA such that it is the physical page offset
|
||||
* corresponding to the 32-bit pci bus offset for DEV requested by the user.
|
||||
*
|
||||
* Basically, the user finds the base address for his device which he wishes
|
||||
* to mmap. They read the 32-bit value from the config space base register,
|
||||
@@ -1076,21 +1074,35 @@ static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struc
|
||||
*
|
||||
* Returns negative error code on failure, zero on success.
|
||||
*/
|
||||
static int __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
|
||||
static int __pci_mmap_make_offset(struct pci_dev *pdev,
|
||||
struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state)
|
||||
{
|
||||
unsigned long user_offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
unsigned long user32 = user_offset & pci_memspace_mask;
|
||||
unsigned long largest_base, this_base, addr32;
|
||||
int i;
|
||||
unsigned long user_paddr, user_size;
|
||||
int i, err;
|
||||
|
||||
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
|
||||
return __pci_mmap_make_offset_bus(dev, vma, mmap_state);
|
||||
/* First compute the physical address in vma->vm_pgoff,
|
||||
* making sure the user offset is within range in the
|
||||
* appropriate PCI space.
|
||||
*/
|
||||
err = __pci_mmap_make_offset_bus(pdev, vma, mmap_state);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* If this is a mapping on a host bridge, any address
|
||||
* is OK.
|
||||
*/
|
||||
if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
|
||||
return err;
|
||||
|
||||
/* Otherwise make sure it's in the range for one of the
|
||||
* device's resources.
|
||||
*/
|
||||
user_paddr = vma->vm_pgoff << PAGE_SHIFT;
|
||||
user_size = vma->vm_end - vma->vm_start;
|
||||
|
||||
/* Figure out which base address this is for. */
|
||||
largest_base = 0UL;
|
||||
for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
|
||||
struct resource *rp = &dev->resource[i];
|
||||
struct resource *rp = &pdev->resource[i];
|
||||
|
||||
/* Active? */
|
||||
if (!rp->flags)
|
||||
@@ -1108,26 +1120,14 @@ static int __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vm
|
||||
continue;
|
||||
}
|
||||
|
||||
this_base = rp->start;
|
||||
|
||||
addr32 = (this_base & PAGE_MASK) & pci_memspace_mask;
|
||||
|
||||
if (mmap_state == pci_mmap_io)
|
||||
addr32 &= 0xffffff;
|
||||
|
||||
if (addr32 <= user32 && this_base > largest_base)
|
||||
largest_base = this_base;
|
||||
if ((rp->start <= user_paddr) &&
|
||||
(user_paddr + user_size) <= (rp->end + 1UL))
|
||||
break;
|
||||
}
|
||||
|
||||
if (largest_base == 0UL)
|
||||
if (i > PCI_ROM_RESOURCE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Now construct the final physical address. */
|
||||
if (mmap_state == pci_mmap_io)
|
||||
vma->vm_pgoff = (((largest_base & ~0xffffffUL) | user32) >> PAGE_SHIFT);
|
||||
else
|
||||
vma->vm_pgoff = (((largest_base & ~(pci_memspace_mask)) | user32) >> PAGE_SHIFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/oplib.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include "pci_impl.h"
|
||||
|
||||
@@ -84,6 +87,266 @@ static int pci_fire_pbm_iommu_init(struct pci_pbm_info *pbm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
struct pci_msiq_entry {
|
||||
u64 word0;
|
||||
#define MSIQ_WORD0_RESV 0x8000000000000000UL
|
||||
#define MSIQ_WORD0_FMT_TYPE 0x7f00000000000000UL
|
||||
#define MSIQ_WORD0_FMT_TYPE_SHIFT 56
|
||||
#define MSIQ_WORD0_LEN 0x00ffc00000000000UL
|
||||
#define MSIQ_WORD0_LEN_SHIFT 46
|
||||
#define MSIQ_WORD0_ADDR0 0x00003fff00000000UL
|
||||
#define MSIQ_WORD0_ADDR0_SHIFT 32
|
||||
#define MSIQ_WORD0_RID 0x00000000ffff0000UL
|
||||
#define MSIQ_WORD0_RID_SHIFT 16
|
||||
#define MSIQ_WORD0_DATA0 0x000000000000ffffUL
|
||||
#define MSIQ_WORD0_DATA0_SHIFT 0
|
||||
|
||||
#define MSIQ_TYPE_MSG 0x6
|
||||
#define MSIQ_TYPE_MSI32 0xb
|
||||
#define MSIQ_TYPE_MSI64 0xf
|
||||
|
||||
u64 word1;
|
||||
#define MSIQ_WORD1_ADDR1 0xffffffffffff0000UL
|
||||
#define MSIQ_WORD1_ADDR1_SHIFT 16
|
||||
#define MSIQ_WORD1_DATA1 0x000000000000ffffUL
|
||||
#define MSIQ_WORD1_DATA1_SHIFT 0
|
||||
|
||||
u64 resv[6];
|
||||
};
|
||||
|
||||
/* All MSI registers are offset from pbm->pbm_regs */
|
||||
#define EVENT_QUEUE_BASE_ADDR_REG 0x010000UL
|
||||
#define EVENT_QUEUE_BASE_ADDR_ALL_ONES 0xfffc000000000000UL
|
||||
|
||||
#define EVENT_QUEUE_CONTROL_SET(EQ) (0x011000UL + (EQ) * 0x8UL)
|
||||
#define EVENT_QUEUE_CONTROL_SET_OFLOW 0x0200000000000000UL
|
||||
#define EVENT_QUEUE_CONTROL_SET_EN 0x0000100000000000UL
|
||||
|
||||
#define EVENT_QUEUE_CONTROL_CLEAR(EQ) (0x011200UL + (EQ) * 0x8UL)
|
||||
#define EVENT_QUEUE_CONTROL_CLEAR_OF 0x0200000000000000UL
|
||||
#define EVENT_QUEUE_CONTROL_CLEAR_E2I 0x0000800000000000UL
|
||||
#define EVENT_QUEUE_CONTROL_CLEAR_DIS 0x0000100000000000UL
|
||||
|
||||
#define EVENT_QUEUE_STATE(EQ) (0x011400UL + (EQ) * 0x8UL)
|
||||
#define EVENT_QUEUE_STATE_MASK 0x0000000000000007UL
|
||||
#define EVENT_QUEUE_STATE_IDLE 0x0000000000000001UL
|
||||
#define EVENT_QUEUE_STATE_ACTIVE 0x0000000000000002UL
|
||||
#define EVENT_QUEUE_STATE_ERROR 0x0000000000000004UL
|
||||
|
||||
#define EVENT_QUEUE_TAIL(EQ) (0x011600UL + (EQ) * 0x8UL)
|
||||
#define EVENT_QUEUE_TAIL_OFLOW 0x0200000000000000UL
|
||||
#define EVENT_QUEUE_TAIL_VAL 0x000000000000007fUL
|
||||
|
||||
#define EVENT_QUEUE_HEAD(EQ) (0x011800UL + (EQ) * 0x8UL)
|
||||
#define EVENT_QUEUE_HEAD_VAL 0x000000000000007fUL
|
||||
|
||||
#define MSI_MAP(MSI) (0x020000UL + (MSI) * 0x8UL)
|
||||
#define MSI_MAP_VALID 0x8000000000000000UL
|
||||
#define MSI_MAP_EQWR_N 0x4000000000000000UL
|
||||
#define MSI_MAP_EQNUM 0x000000000000003fUL
|
||||
|
||||
#define MSI_CLEAR(MSI) (0x028000UL + (MSI) * 0x8UL)
|
||||
#define MSI_CLEAR_EQWR_N 0x4000000000000000UL
|
||||
|
||||
#define IMONDO_DATA0 0x02C000UL
|
||||
#define IMONDO_DATA0_DATA 0xffffffffffffffc0UL
|
||||
|
||||
#define IMONDO_DATA1 0x02C008UL
|
||||
#define IMONDO_DATA1_DATA 0xffffffffffffffffUL
|
||||
|
||||
#define MSI_32BIT_ADDR 0x034000UL
|
||||
#define MSI_32BIT_ADDR_VAL 0x00000000ffff0000UL
|
||||
|
||||
#define MSI_64BIT_ADDR 0x034008UL
|
||||
#define MSI_64BIT_ADDR_VAL 0xffffffffffff0000UL
|
||||
|
||||
static int pci_fire_get_head(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long *head)
|
||||
{
|
||||
*head = fire_read(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_fire_dequeue_msi(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long *head, unsigned long *msi)
|
||||
{
|
||||
unsigned long type_fmt, type, msi_num;
|
||||
struct pci_msiq_entry *base, *ep;
|
||||
|
||||
base = (pbm->msi_queues + ((msiqid - pbm->msiq_first) * 8192));
|
||||
ep = &base[*head];
|
||||
|
||||
if ((ep->word0 & MSIQ_WORD0_FMT_TYPE) == 0)
|
||||
return 0;
|
||||
|
||||
type_fmt = ((ep->word0 & MSIQ_WORD0_FMT_TYPE) >>
|
||||
MSIQ_WORD0_FMT_TYPE_SHIFT);
|
||||
type = (type_fmt >> 3);
|
||||
if (unlikely(type != MSIQ_TYPE_MSI32 &&
|
||||
type != MSIQ_TYPE_MSI64))
|
||||
return -EINVAL;
|
||||
|
||||
*msi = msi_num = ((ep->word0 & MSIQ_WORD0_DATA0) >>
|
||||
MSIQ_WORD0_DATA0_SHIFT);
|
||||
|
||||
fire_write(pbm->pbm_regs + MSI_CLEAR(msi_num),
|
||||
MSI_CLEAR_EQWR_N);
|
||||
|
||||
/* Clear the entry. */
|
||||
ep->word0 &= ~MSIQ_WORD0_FMT_TYPE;
|
||||
|
||||
/* Go to next entry in ring. */
|
||||
(*head)++;
|
||||
if (*head >= pbm->msiq_ent_count)
|
||||
*head = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pci_fire_set_head(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long head)
|
||||
{
|
||||
fire_write(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid), head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_fire_msi_setup(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long msi, int is_msi64)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
val = fire_read(pbm->pbm_regs + MSI_MAP(msi));
|
||||
val &= ~(MSI_MAP_EQNUM);
|
||||
val |= msiqid;
|
||||
fire_write(pbm->pbm_regs + MSI_MAP(msi), val);
|
||||
|
||||
fire_write(pbm->pbm_regs + MSI_CLEAR(msi),
|
||||
MSI_CLEAR_EQWR_N);
|
||||
|
||||
val = fire_read(pbm->pbm_regs + MSI_MAP(msi));
|
||||
val |= MSI_MAP_VALID;
|
||||
fire_write(pbm->pbm_regs + MSI_MAP(msi), val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_fire_msi_teardown(struct pci_pbm_info *pbm, unsigned long msi)
|
||||
{
|
||||
unsigned long msiqid;
|
||||
u64 val;
|
||||
|
||||
val = fire_read(pbm->pbm_regs + MSI_MAP(msi));
|
||||
msiqid = (val & MSI_MAP_EQNUM);
|
||||
|
||||
val &= ~MSI_MAP_VALID;
|
||||
|
||||
fire_write(pbm->pbm_regs + MSI_MAP(msi), val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_fire_msiq_alloc(struct pci_pbm_info *pbm)
|
||||
{
|
||||
unsigned long pages, order, i;
|
||||
|
||||
order = get_order(512 * 1024);
|
||||
pages = __get_free_pages(GFP_KERNEL | __GFP_COMP, order);
|
||||
if (pages == 0UL) {
|
||||
printk(KERN_ERR "MSI: Cannot allocate MSI queues (o=%lu).\n",
|
||||
order);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset((char *)pages, 0, PAGE_SIZE << order);
|
||||
pbm->msi_queues = (void *) pages;
|
||||
|
||||
fire_write(pbm->pbm_regs + EVENT_QUEUE_BASE_ADDR_REG,
|
||||
(EVENT_QUEUE_BASE_ADDR_ALL_ONES |
|
||||
__pa(pbm->msi_queues)));
|
||||
|
||||
fire_write(pbm->pbm_regs + IMONDO_DATA0,
|
||||
pbm->portid << 6);
|
||||
fire_write(pbm->pbm_regs + IMONDO_DATA1, 0);
|
||||
|
||||
fire_write(pbm->pbm_regs + MSI_32BIT_ADDR,
|
||||
pbm->msi32_start);
|
||||
fire_write(pbm->pbm_regs + MSI_64BIT_ADDR,
|
||||
pbm->msi64_start);
|
||||
|
||||
for (i = 0; i < pbm->msiq_num; i++) {
|
||||
fire_write(pbm->pbm_regs + EVENT_QUEUE_HEAD(i), 0);
|
||||
fire_write(pbm->pbm_regs + EVENT_QUEUE_TAIL(i), 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_fire_msiq_free(struct pci_pbm_info *pbm)
|
||||
{
|
||||
unsigned long pages, order;
|
||||
|
||||
order = get_order(512 * 1024);
|
||||
pages = (unsigned long) pbm->msi_queues;
|
||||
|
||||
free_pages(pages, order);
|
||||
|
||||
pbm->msi_queues = NULL;
|
||||
}
|
||||
|
||||
static int pci_fire_msiq_build_irq(struct pci_pbm_info *pbm,
|
||||
unsigned long msiqid,
|
||||
unsigned long devino)
|
||||
{
|
||||
unsigned long cregs = (unsigned long) pbm->pbm_regs;
|
||||
unsigned long imap_reg, iclr_reg, int_ctrlr;
|
||||
unsigned int virt_irq;
|
||||
int fixup;
|
||||
u64 val;
|
||||
|
||||
imap_reg = cregs + (0x001000UL + (devino * 0x08UL));
|
||||
iclr_reg = cregs + (0x001400UL + (devino * 0x08UL));
|
||||
|
||||
/* XXX iterate amongst the 4 IRQ controllers XXX */
|
||||
int_ctrlr = (1UL << 6);
|
||||
|
||||
val = fire_read(imap_reg);
|
||||
val |= (1UL << 63) | int_ctrlr;
|
||||
fire_write(imap_reg, val);
|
||||
|
||||
fixup = ((pbm->portid << 6) | devino) - int_ctrlr;
|
||||
|
||||
virt_irq = build_irq(fixup, iclr_reg, imap_reg);
|
||||
if (!virt_irq)
|
||||
return -ENOMEM;
|
||||
|
||||
fire_write(pbm->pbm_regs +
|
||||
EVENT_QUEUE_CONTROL_SET(msiqid),
|
||||
EVENT_QUEUE_CONTROL_SET_EN);
|
||||
|
||||
return virt_irq;
|
||||
}
|
||||
|
||||
static const struct sparc64_msiq_ops pci_fire_msiq_ops = {
|
||||
.get_head = pci_fire_get_head,
|
||||
.dequeue_msi = pci_fire_dequeue_msi,
|
||||
.set_head = pci_fire_set_head,
|
||||
.msi_setup = pci_fire_msi_setup,
|
||||
.msi_teardown = pci_fire_msi_teardown,
|
||||
.msiq_alloc = pci_fire_msiq_alloc,
|
||||
.msiq_free = pci_fire_msiq_free,
|
||||
.msiq_build_irq = pci_fire_msiq_build_irq,
|
||||
};
|
||||
|
||||
static void pci_fire_msi_init(struct pci_pbm_info *pbm)
|
||||
{
|
||||
sparc64_pbm_msi_init(pbm, &pci_fire_msiq_ops);
|
||||
}
|
||||
#else /* CONFIG_PCI_MSI */
|
||||
static void pci_fire_msi_init(struct pci_pbm_info *pbm)
|
||||
{
|
||||
}
|
||||
#endif /* !(CONFIG_PCI_MSI) */
|
||||
|
||||
/* Based at pbm->controller_regs */
|
||||
#define FIRE_PARITY_CONTROL 0x470010UL
|
||||
#define FIRE_PARITY_ENAB 0x8000000000000000UL
|
||||
@@ -176,6 +439,7 @@ static int pci_fire_pbm_init(struct pci_controller_info *p,
|
||||
{
|
||||
const struct linux_prom64_registers *regs;
|
||||
struct pci_pbm_info *pbm;
|
||||
int err;
|
||||
|
||||
if ((portid & 1) == 0)
|
||||
pbm = &p->pbm_A;
|
||||
@@ -208,7 +472,13 @@ static int pci_fire_pbm_init(struct pci_controller_info *p,
|
||||
|
||||
pci_fire_hw_init(pbm);
|
||||
|
||||
return pci_fire_pbm_iommu_init(pbm);
|
||||
err = pci_fire_pbm_iommu_init(pbm);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pci_fire_msi_init(pbm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int portid_compare(u32 x, u32 y)
|
||||
@@ -249,13 +519,6 @@ void fire_pci_init(struct device_node *dp, const char *model_name)
|
||||
|
||||
p->pbm_B.iommu = iommu;
|
||||
|
||||
/* XXX MSI support XXX */
|
||||
|
||||
/* Like PSYCHO and SCHIZO we have a 2GB aligned area
|
||||
* for memory space.
|
||||
*/
|
||||
pci_memspace_mask = 0x7fffffffUL;
|
||||
|
||||
if (pci_fire_pbm_init(p, dp, portid))
|
||||
goto fatal_memory_error;
|
||||
|
||||
|
||||
@@ -29,6 +29,33 @@
|
||||
#define PCI_STC_FLUSHFLAG_SET(STC) \
|
||||
(*((STC)->strbuf_flushflag) != 0UL)
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
struct pci_pbm_info;
|
||||
struct sparc64_msiq_ops {
|
||||
int (*get_head)(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long *head);
|
||||
int (*dequeue_msi)(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long *head, unsigned long *msi);
|
||||
int (*set_head)(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long head);
|
||||
int (*msi_setup)(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long msi, int is_msi64);
|
||||
int (*msi_teardown)(struct pci_pbm_info *pbm, unsigned long msi);
|
||||
int (*msiq_alloc)(struct pci_pbm_info *pbm);
|
||||
void (*msiq_free)(struct pci_pbm_info *pbm);
|
||||
int (*msiq_build_irq)(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long devino);
|
||||
};
|
||||
|
||||
extern void sparc64_pbm_msi_init(struct pci_pbm_info *pbm,
|
||||
const struct sparc64_msiq_ops *ops);
|
||||
|
||||
struct sparc64_msiq_cookie {
|
||||
struct pci_pbm_info *pbm;
|
||||
unsigned long msiqid;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct pci_controller_info;
|
||||
|
||||
struct pci_pbm_info {
|
||||
@@ -90,6 +117,8 @@ struct pci_pbm_info {
|
||||
u32 msiq_ent_count;
|
||||
u32 msiq_first;
|
||||
u32 msiq_first_devino;
|
||||
u32 msiq_rotor;
|
||||
struct sparc64_msiq_cookie *msiq_irq_cookies;
|
||||
u32 msi_num;
|
||||
u32 msi_first;
|
||||
u32 msi_data_mask;
|
||||
@@ -100,9 +129,11 @@ struct pci_pbm_info {
|
||||
u32 msi64_len;
|
||||
void *msi_queues;
|
||||
unsigned long *msi_bitmap;
|
||||
unsigned int *msi_irq_table;
|
||||
int (*setup_msi_irq)(unsigned int *virt_irq_p, struct pci_dev *pdev,
|
||||
struct msi_desc *entry);
|
||||
void (*teardown_msi_irq)(unsigned int virt_irq, struct pci_dev *pdev);
|
||||
const struct sparc64_msiq_ops *msi_ops;
|
||||
#endif /* !(CONFIG_PCI_MSI) */
|
||||
|
||||
/* This PBM's streaming buffer. */
|
||||
@@ -126,7 +157,6 @@ struct pci_controller_info {
|
||||
};
|
||||
|
||||
extern struct pci_pbm_info *pci_pbm_root;
|
||||
extern unsigned long pci_memspace_mask;
|
||||
|
||||
extern int pci_num_pbms;
|
||||
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
/* pci_msi.c: Sparc64 MSI support common layer.
|
||||
*
|
||||
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include "pci_impl.h"
|
||||
|
||||
static irqreturn_t sparc64_msiq_interrupt(int irq, void *cookie)
|
||||
{
|
||||
struct sparc64_msiq_cookie *msiq_cookie = cookie;
|
||||
struct pci_pbm_info *pbm = msiq_cookie->pbm;
|
||||
unsigned long msiqid = msiq_cookie->msiqid;
|
||||
const struct sparc64_msiq_ops *ops;
|
||||
unsigned long orig_head, head;
|
||||
int err;
|
||||
|
||||
ops = pbm->msi_ops;
|
||||
|
||||
err = ops->get_head(pbm, msiqid, &head);
|
||||
if (unlikely(err < 0))
|
||||
goto err_get_head;
|
||||
|
||||
orig_head = head;
|
||||
for (;;) {
|
||||
unsigned long msi;
|
||||
|
||||
err = ops->dequeue_msi(pbm, msiqid, &head, &msi);
|
||||
if (likely(err > 0))
|
||||
__do_IRQ(pbm->msi_irq_table[msi - pbm->msi_first]);
|
||||
|
||||
if (unlikely(err < 0))
|
||||
goto err_dequeue;
|
||||
|
||||
if (err == 0)
|
||||
break;
|
||||
}
|
||||
if (likely(head != orig_head)) {
|
||||
err = ops->set_head(pbm, msiqid, head);
|
||||
if (unlikely(err < 0))
|
||||
goto err_set_head;
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
|
||||
err_get_head:
|
||||
printk(KERN_EMERG "MSI: Get head on msiqid[%lu] gives error %d\n",
|
||||
msiqid, err);
|
||||
goto err_out;
|
||||
|
||||
err_dequeue:
|
||||
printk(KERN_EMERG "MSI: Dequeue head[%lu] from msiqid[%lu] "
|
||||
"gives error %d\n",
|
||||
head, msiqid, err);
|
||||
goto err_out;
|
||||
|
||||
err_set_head:
|
||||
printk(KERN_EMERG "MSI: Set head[%lu] on msiqid[%lu] "
|
||||
"gives error %d\n",
|
||||
head, msiqid, err);
|
||||
goto err_out;
|
||||
|
||||
err_out:
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static u32 pick_msiq(struct pci_pbm_info *pbm)
|
||||
{
|
||||
static DEFINE_SPINLOCK(rotor_lock);
|
||||
unsigned long flags;
|
||||
u32 ret, rotor;
|
||||
|
||||
spin_lock_irqsave(&rotor_lock, flags);
|
||||
|
||||
rotor = pbm->msiq_rotor;
|
||||
ret = pbm->msiq_first + rotor;
|
||||
|
||||
if (++rotor >= pbm->msiq_num)
|
||||
rotor = 0;
|
||||
pbm->msiq_rotor = rotor;
|
||||
|
||||
spin_unlock_irqrestore(&rotor_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int alloc_msi(struct pci_pbm_info *pbm)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pbm->msi_num; i++) {
|
||||
if (!test_and_set_bit(i, pbm->msi_bitmap))
|
||||
return i + pbm->msi_first;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void free_msi(struct pci_pbm_info *pbm, int msi_num)
|
||||
{
|
||||
msi_num -= pbm->msi_first;
|
||||
clear_bit(msi_num, pbm->msi_bitmap);
|
||||
}
|
||||
|
||||
static struct irq_chip msi_irq = {
|
||||
.typename = "PCI-MSI",
|
||||
.mask = mask_msi_irq,
|
||||
.unmask = unmask_msi_irq,
|
||||
.enable = unmask_msi_irq,
|
||||
.disable = mask_msi_irq,
|
||||
/* XXX affinity XXX */
|
||||
};
|
||||
|
||||
int sparc64_setup_msi_irq(unsigned int *virt_irq_p,
|
||||
struct pci_dev *pdev,
|
||||
struct msi_desc *entry)
|
||||
{
|
||||
struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
|
||||
const struct sparc64_msiq_ops *ops = pbm->msi_ops;
|
||||
struct msi_msg msg;
|
||||
int msi, err;
|
||||
u32 msiqid;
|
||||
|
||||
*virt_irq_p = virt_irq_alloc(0, 0);
|
||||
err = -ENOMEM;
|
||||
if (!*virt_irq_p)
|
||||
goto out_err;
|
||||
|
||||
set_irq_chip(*virt_irq_p, &msi_irq);
|
||||
|
||||
err = alloc_msi(pbm);
|
||||
if (unlikely(err < 0))
|
||||
goto out_virt_irq_free;
|
||||
|
||||
msi = err;
|
||||
|
||||
msiqid = pick_msiq(pbm);
|
||||
|
||||
err = ops->msi_setup(pbm, msiqid, msi,
|
||||
(entry->msi_attrib.is_64 ? 1 : 0));
|
||||
if (err)
|
||||
goto out_msi_free;
|
||||
|
||||
pbm->msi_irq_table[msi - pbm->msi_first] = *virt_irq_p;
|
||||
|
||||
if (entry->msi_attrib.is_64) {
|
||||
msg.address_hi = pbm->msi64_start >> 32;
|
||||
msg.address_lo = pbm->msi64_start & 0xffffffff;
|
||||
} else {
|
||||
msg.address_hi = 0;
|
||||
msg.address_lo = pbm->msi32_start;
|
||||
}
|
||||
msg.data = msi;
|
||||
|
||||
set_irq_msi(*virt_irq_p, entry);
|
||||
write_msi_msg(*virt_irq_p, &msg);
|
||||
|
||||
return 0;
|
||||
|
||||
out_msi_free:
|
||||
free_msi(pbm, msi);
|
||||
|
||||
out_virt_irq_free:
|
||||
set_irq_chip(*virt_irq_p, NULL);
|
||||
virt_irq_free(*virt_irq_p);
|
||||
*virt_irq_p = 0;
|
||||
|
||||
out_err:
|
||||
return err;
|
||||
}
|
||||
|
||||
void sparc64_teardown_msi_irq(unsigned int virt_irq,
|
||||
struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
|
||||
const struct sparc64_msiq_ops *ops = pbm->msi_ops;
|
||||
unsigned int msi_num;
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < pbm->msi_num; i++) {
|
||||
if (pbm->msi_irq_table[i] == virt_irq)
|
||||
break;
|
||||
}
|
||||
if (i >= pbm->msi_num) {
|
||||
printk(KERN_ERR "%s: teardown: No MSI for irq %u\n",
|
||||
pbm->name, virt_irq);
|
||||
return;
|
||||
}
|
||||
|
||||
msi_num = pbm->msi_first + i;
|
||||
pbm->msi_irq_table[i] = ~0U;
|
||||
|
||||
err = ops->msi_teardown(pbm, msi_num);
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: teardown: ops->teardown() on MSI %u, "
|
||||
"irq %u, gives error %d\n",
|
||||
pbm->name, msi_num, virt_irq, err);
|
||||
return;
|
||||
}
|
||||
|
||||
free_msi(pbm, msi_num);
|
||||
|
||||
set_irq_chip(virt_irq, NULL);
|
||||
virt_irq_free(virt_irq);
|
||||
}
|
||||
|
||||
static int msi_bitmap_alloc(struct pci_pbm_info *pbm)
|
||||
{
|
||||
unsigned long size, bits_per_ulong;
|
||||
|
||||
bits_per_ulong = sizeof(unsigned long) * 8;
|
||||
size = (pbm->msi_num + (bits_per_ulong - 1)) & ~(bits_per_ulong - 1);
|
||||
size /= 8;
|
||||
BUG_ON(size % sizeof(unsigned long));
|
||||
|
||||
pbm->msi_bitmap = kzalloc(size, GFP_KERNEL);
|
||||
if (!pbm->msi_bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msi_bitmap_free(struct pci_pbm_info *pbm)
|
||||
{
|
||||
kfree(pbm->msi_bitmap);
|
||||
pbm->msi_bitmap = NULL;
|
||||
}
|
||||
|
||||
static int msi_table_alloc(struct pci_pbm_info *pbm)
|
||||
{
|
||||
int size, i;
|
||||
|
||||
size = pbm->msiq_num * sizeof(struct sparc64_msiq_cookie);
|
||||
pbm->msiq_irq_cookies = kzalloc(size, GFP_KERNEL);
|
||||
if (!pbm->msiq_irq_cookies)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < pbm->msiq_num; i++) {
|
||||
struct sparc64_msiq_cookie *p;
|
||||
|
||||
p = &pbm->msiq_irq_cookies[i];
|
||||
p->pbm = pbm;
|
||||
p->msiqid = pbm->msiq_first + i;
|
||||
}
|
||||
|
||||
size = pbm->msi_num * sizeof(unsigned int);
|
||||
pbm->msi_irq_table = kzalloc(size, GFP_KERNEL);
|
||||
if (!pbm->msi_irq_table) {
|
||||
kfree(pbm->msiq_irq_cookies);
|
||||
pbm->msiq_irq_cookies = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msi_table_free(struct pci_pbm_info *pbm)
|
||||
{
|
||||
kfree(pbm->msiq_irq_cookies);
|
||||
pbm->msiq_irq_cookies = NULL;
|
||||
|
||||
kfree(pbm->msi_irq_table);
|
||||
pbm->msi_irq_table = NULL;
|
||||
}
|
||||
|
||||
static int bringup_one_msi_queue(struct pci_pbm_info *pbm,
|
||||
const struct sparc64_msiq_ops *ops,
|
||||
unsigned long msiqid,
|
||||
unsigned long devino)
|
||||
{
|
||||
int irq = ops->msiq_build_irq(pbm, msiqid, devino);
|
||||
int err;
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = request_irq(irq, sparc64_msiq_interrupt, 0,
|
||||
"MSIQ",
|
||||
&pbm->msiq_irq_cookies[msiqid - pbm->msiq_first]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sparc64_bringup_msi_queues(struct pci_pbm_info *pbm,
|
||||
const struct sparc64_msiq_ops *ops)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pbm->msiq_num; i++) {
|
||||
unsigned long msiqid = i + pbm->msiq_first;
|
||||
unsigned long devino = i + pbm->msiq_first_devino;
|
||||
int err;
|
||||
|
||||
err = bringup_one_msi_queue(pbm, ops, msiqid, devino);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sparc64_pbm_msi_init(struct pci_pbm_info *pbm,
|
||||
const struct sparc64_msiq_ops *ops)
|
||||
{
|
||||
const u32 *val;
|
||||
int len;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "#msi-eqs", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
pbm->msiq_num = *val;
|
||||
if (pbm->msiq_num) {
|
||||
const struct msiq_prop {
|
||||
u32 first_msiq;
|
||||
u32 num_msiq;
|
||||
u32 first_devino;
|
||||
} *mqp;
|
||||
const struct msi_range_prop {
|
||||
u32 first_msi;
|
||||
u32 num_msi;
|
||||
} *mrng;
|
||||
const struct addr_range_prop {
|
||||
u32 msi32_high;
|
||||
u32 msi32_low;
|
||||
u32 msi32_len;
|
||||
u32 msi64_high;
|
||||
u32 msi64_low;
|
||||
u32 msi64_len;
|
||||
} *arng;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "msi-eq-size", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
|
||||
pbm->msiq_ent_count = *val;
|
||||
|
||||
mqp = of_get_property(pbm->prom_node,
|
||||
"msi-eq-to-devino", &len);
|
||||
if (!mqp)
|
||||
mqp = of_get_property(pbm->prom_node,
|
||||
"msi-eq-devino", &len);
|
||||
if (!mqp || len != sizeof(struct msiq_prop))
|
||||
goto no_msi;
|
||||
|
||||
pbm->msiq_first = mqp->first_msiq;
|
||||
pbm->msiq_first_devino = mqp->first_devino;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "#msi", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
pbm->msi_num = *val;
|
||||
|
||||
mrng = of_get_property(pbm->prom_node, "msi-ranges", &len);
|
||||
if (!mrng || len != sizeof(struct msi_range_prop))
|
||||
goto no_msi;
|
||||
pbm->msi_first = mrng->first_msi;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "msi-data-mask", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
pbm->msi_data_mask = *val;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "msix-data-width", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
pbm->msix_data_width = *val;
|
||||
|
||||
arng = of_get_property(pbm->prom_node, "msi-address-ranges",
|
||||
&len);
|
||||
if (!arng || len != sizeof(struct addr_range_prop))
|
||||
goto no_msi;
|
||||
pbm->msi32_start = ((u64)arng->msi32_high << 32) |
|
||||
(u64) arng->msi32_low;
|
||||
pbm->msi64_start = ((u64)arng->msi64_high << 32) |
|
||||
(u64) arng->msi64_low;
|
||||
pbm->msi32_len = arng->msi32_len;
|
||||
pbm->msi64_len = arng->msi64_len;
|
||||
|
||||
if (msi_bitmap_alloc(pbm))
|
||||
goto no_msi;
|
||||
|
||||
if (msi_table_alloc(pbm)) {
|
||||
msi_bitmap_free(pbm);
|
||||
goto no_msi;
|
||||
}
|
||||
|
||||
if (ops->msiq_alloc(pbm)) {
|
||||
msi_table_free(pbm);
|
||||
msi_bitmap_free(pbm);
|
||||
goto no_msi;
|
||||
}
|
||||
|
||||
if (sparc64_bringup_msi_queues(pbm, ops)) {
|
||||
ops->msiq_free(pbm);
|
||||
msi_table_free(pbm);
|
||||
msi_bitmap_free(pbm);
|
||||
goto no_msi;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: MSI Queue first[%u] num[%u] count[%u] "
|
||||
"devino[0x%x]\n",
|
||||
pbm->name,
|
||||
pbm->msiq_first, pbm->msiq_num,
|
||||
pbm->msiq_ent_count,
|
||||
pbm->msiq_first_devino);
|
||||
printk(KERN_INFO "%s: MSI first[%u] num[%u] mask[0x%x] "
|
||||
"width[%u]\n",
|
||||
pbm->name,
|
||||
pbm->msi_first, pbm->msi_num, pbm->msi_data_mask,
|
||||
pbm->msix_data_width);
|
||||
printk(KERN_INFO "%s: MSI addr32[0x%lx:0x%x] "
|
||||
"addr64[0x%lx:0x%x]\n",
|
||||
pbm->name,
|
||||
pbm->msi32_start, pbm->msi32_len,
|
||||
pbm->msi64_start, pbm->msi64_len);
|
||||
printk(KERN_INFO "%s: MSI queues at RA [%016lx]\n",
|
||||
pbm->name,
|
||||
__pa(pbm->msi_queues));
|
||||
|
||||
pbm->msi_ops = ops;
|
||||
pbm->setup_msi_irq = sparc64_setup_msi_irq;
|
||||
pbm->teardown_msi_irq = sparc64_teardown_msi_irq;
|
||||
}
|
||||
return;
|
||||
|
||||
no_msi:
|
||||
pbm->msiq_num = 0;
|
||||
printk(KERN_INFO "%s: No MSI support.\n", pbm->name);
|
||||
}
|
||||
@@ -1058,12 +1058,6 @@ void psycho_init(struct device_node *dp, char *model_name)
|
||||
p->pbm_A.config_space = p->pbm_B.config_space =
|
||||
(pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE);
|
||||
|
||||
/*
|
||||
* Psycho's PCI MEM space is mapped to a 2GB aligned area, so
|
||||
* we need to adjust our MEM space mask.
|
||||
*/
|
||||
pci_memspace_mask = 0x7fffffffUL;
|
||||
|
||||
psycho_controller_hwinit(&p->pbm_A);
|
||||
|
||||
if (psycho_iommu_init(&p->pbm_A))
|
||||
|
||||
@@ -1464,9 +1464,6 @@ static void __schizo_init(struct device_node *dp, char *model_name, int chip_typ
|
||||
|
||||
p->pbm_B.iommu = iommu;
|
||||
|
||||
/* Like PSYCHO we have a 2GB aligned area for memory space. */
|
||||
pci_memspace_mask = 0x7fffffffUL;
|
||||
|
||||
if (schizo_pbm_init(p, dp, portid, chip_type))
|
||||
goto fatal_memory_error;
|
||||
|
||||
|
||||
+120
-307
@@ -748,111 +748,102 @@ struct pci_sun4v_msiq_entry {
|
||||
u64 reserved2;
|
||||
};
|
||||
|
||||
/* For now this just runs as a pre-handler for the real interrupt handler.
|
||||
* So we just walk through the queue and ACK all the entries, update the
|
||||
* head pointer, and return.
|
||||
*
|
||||
* In the longer term it would be nice to do something more integrated
|
||||
* wherein we can pass in some of this MSI info to the drivers. This
|
||||
* would be most useful for PCIe fabric error messages, although we could
|
||||
* invoke those directly from the loop here in order to pass the info around.
|
||||
*/
|
||||
static void pci_sun4v_msi_prehandler(unsigned int ino, void *data1, void *data2)
|
||||
static int pci_sun4v_get_head(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long *head)
|
||||
{
|
||||
struct pci_pbm_info *pbm = data1;
|
||||
struct pci_sun4v_msiq_entry *base, *ep;
|
||||
unsigned long msiqid, orig_head, head, type, err;
|
||||
unsigned long err, limit;
|
||||
|
||||
msiqid = (unsigned long) data2;
|
||||
|
||||
head = 0xdeadbeef;
|
||||
err = pci_sun4v_msiq_gethead(pbm->devhandle, msiqid, &head);
|
||||
err = pci_sun4v_msiq_gethead(pbm->devhandle, msiqid, head);
|
||||
if (unlikely(err))
|
||||
goto hv_error_get;
|
||||
return -ENXIO;
|
||||
|
||||
if (unlikely(head >= (pbm->msiq_ent_count * sizeof(struct pci_sun4v_msiq_entry))))
|
||||
goto bad_offset;
|
||||
|
||||
head /= sizeof(struct pci_sun4v_msiq_entry);
|
||||
orig_head = head;
|
||||
base = (pbm->msi_queues + ((msiqid - pbm->msiq_first) *
|
||||
(pbm->msiq_ent_count *
|
||||
sizeof(struct pci_sun4v_msiq_entry))));
|
||||
ep = &base[head];
|
||||
while ((ep->version_type & MSIQ_TYPE_MASK) != 0) {
|
||||
type = (ep->version_type & MSIQ_TYPE_MASK) >> MSIQ_TYPE_SHIFT;
|
||||
if (unlikely(type != MSIQ_TYPE_MSI32 &&
|
||||
type != MSIQ_TYPE_MSI64))
|
||||
goto bad_type;
|
||||
|
||||
pci_sun4v_msi_setstate(pbm->devhandle,
|
||||
ep->msi_data /* msi_num */,
|
||||
HV_MSISTATE_IDLE);
|
||||
|
||||
/* Clear the entry. */
|
||||
ep->version_type &= ~MSIQ_TYPE_MASK;
|
||||
|
||||
/* Go to next entry in ring. */
|
||||
head++;
|
||||
if (head >= pbm->msiq_ent_count)
|
||||
head = 0;
|
||||
ep = &base[head];
|
||||
}
|
||||
|
||||
if (likely(head != orig_head)) {
|
||||
/* ACK entries by updating head pointer. */
|
||||
head *= sizeof(struct pci_sun4v_msiq_entry);
|
||||
err = pci_sun4v_msiq_sethead(pbm->devhandle, msiqid, head);
|
||||
if (unlikely(err))
|
||||
goto hv_error_set;
|
||||
}
|
||||
return;
|
||||
|
||||
hv_error_set:
|
||||
printk(KERN_EMERG "MSI: Hypervisor set head gives error %lu\n", err);
|
||||
goto hv_error_cont;
|
||||
|
||||
hv_error_get:
|
||||
printk(KERN_EMERG "MSI: Hypervisor get head gives error %lu\n", err);
|
||||
|
||||
hv_error_cont:
|
||||
printk(KERN_EMERG "MSI: devhandle[%x] msiqid[%lx] head[%lu]\n",
|
||||
pbm->devhandle, msiqid, head);
|
||||
return;
|
||||
|
||||
bad_offset:
|
||||
printk(KERN_EMERG "MSI: Hypervisor gives bad offset %lx max(%lx)\n",
|
||||
head, pbm->msiq_ent_count * sizeof(struct pci_sun4v_msiq_entry));
|
||||
return;
|
||||
|
||||
bad_type:
|
||||
printk(KERN_EMERG "MSI: Entry has bad type %lx\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
static int msi_bitmap_alloc(struct pci_pbm_info *pbm)
|
||||
{
|
||||
unsigned long size, bits_per_ulong;
|
||||
|
||||
bits_per_ulong = sizeof(unsigned long) * 8;
|
||||
size = (pbm->msi_num + (bits_per_ulong - 1)) & ~(bits_per_ulong - 1);
|
||||
size /= 8;
|
||||
BUG_ON(size % sizeof(unsigned long));
|
||||
|
||||
pbm->msi_bitmap = kzalloc(size, GFP_KERNEL);
|
||||
if (!pbm->msi_bitmap)
|
||||
return -ENOMEM;
|
||||
limit = pbm->msiq_ent_count * sizeof(struct pci_sun4v_msiq_entry);
|
||||
if (unlikely(*head >= limit))
|
||||
return -EFBIG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msi_bitmap_free(struct pci_pbm_info *pbm)
|
||||
static int pci_sun4v_dequeue_msi(struct pci_pbm_info *pbm,
|
||||
unsigned long msiqid, unsigned long *head,
|
||||
unsigned long *msi)
|
||||
{
|
||||
kfree(pbm->msi_bitmap);
|
||||
pbm->msi_bitmap = NULL;
|
||||
struct pci_sun4v_msiq_entry *ep;
|
||||
unsigned long err, type;
|
||||
|
||||
/* Note: void pointer arithmetic, 'head' is a byte offset */
|
||||
ep = (pbm->msi_queues + ((msiqid - pbm->msiq_first) *
|
||||
(pbm->msiq_ent_count *
|
||||
sizeof(struct pci_sun4v_msiq_entry))) +
|
||||
*head);
|
||||
|
||||
if ((ep->version_type & MSIQ_TYPE_MASK) == 0)
|
||||
return 0;
|
||||
|
||||
type = (ep->version_type & MSIQ_TYPE_MASK) >> MSIQ_TYPE_SHIFT;
|
||||
if (unlikely(type != MSIQ_TYPE_MSI32 &&
|
||||
type != MSIQ_TYPE_MSI64))
|
||||
return -EINVAL;
|
||||
|
||||
*msi = ep->msi_data;
|
||||
|
||||
err = pci_sun4v_msi_setstate(pbm->devhandle,
|
||||
ep->msi_data /* msi_num */,
|
||||
HV_MSISTATE_IDLE);
|
||||
if (unlikely(err))
|
||||
return -ENXIO;
|
||||
|
||||
/* Clear the entry. */
|
||||
ep->version_type &= ~MSIQ_TYPE_MASK;
|
||||
|
||||
(*head) += sizeof(struct pci_sun4v_msiq_entry);
|
||||
if (*head >=
|
||||
(pbm->msiq_ent_count * sizeof(struct pci_sun4v_msiq_entry)))
|
||||
*head = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int msi_queue_alloc(struct pci_pbm_info *pbm)
|
||||
static int pci_sun4v_set_head(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long head)
|
||||
{
|
||||
unsigned long err;
|
||||
|
||||
err = pci_sun4v_msiq_sethead(pbm->devhandle, msiqid, head);
|
||||
if (unlikely(err))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_sun4v_msi_setup(struct pci_pbm_info *pbm, unsigned long msiqid,
|
||||
unsigned long msi, int is_msi64)
|
||||
{
|
||||
if (pci_sun4v_msi_setmsiq(pbm->devhandle, msi, msiqid,
|
||||
(is_msi64 ?
|
||||
HV_MSITYPE_MSI64 : HV_MSITYPE_MSI32)))
|
||||
return -ENXIO;
|
||||
if (pci_sun4v_msi_setstate(pbm->devhandle, msi, HV_MSISTATE_IDLE))
|
||||
return -ENXIO;
|
||||
if (pci_sun4v_msi_setvalid(pbm->devhandle, msi, HV_MSIVALID_VALID))
|
||||
return -ENXIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_sun4v_msi_teardown(struct pci_pbm_info *pbm, unsigned long msi)
|
||||
{
|
||||
unsigned long err, msiqid;
|
||||
|
||||
err = pci_sun4v_msi_getmsiq(pbm->devhandle, msi, &msiqid);
|
||||
if (err)
|
||||
return -ENXIO;
|
||||
|
||||
pci_sun4v_msi_setvalid(pbm->devhandle, msi, HV_MSIVALID_INVALID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_sun4v_msiq_alloc(struct pci_pbm_info *pbm)
|
||||
{
|
||||
unsigned long q_size, alloc_size, pages, order;
|
||||
int i;
|
||||
@@ -906,232 +897,59 @@ h_error:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int alloc_msi(struct pci_pbm_info *pbm)
|
||||
static void pci_sun4v_msiq_free(struct pci_pbm_info *pbm)
|
||||
{
|
||||
unsigned long q_size, alloc_size, pages, order;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pbm->msi_num; i++) {
|
||||
if (!test_and_set_bit(i, pbm->msi_bitmap))
|
||||
return i + pbm->msi_first;
|
||||
for (i = 0; i < pbm->msiq_num; i++) {
|
||||
unsigned long msiqid = pbm->msiq_first + i;
|
||||
|
||||
(void) pci_sun4v_msiq_conf(pbm->devhandle, msiqid, 0UL, 0);
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
q_size = pbm->msiq_ent_count * sizeof(struct pci_sun4v_msiq_entry);
|
||||
alloc_size = (pbm->msiq_num * q_size);
|
||||
order = get_order(alloc_size);
|
||||
|
||||
pages = (unsigned long) pbm->msi_queues;
|
||||
|
||||
free_pages(pages, order);
|
||||
|
||||
pbm->msi_queues = NULL;
|
||||
}
|
||||
|
||||
static void free_msi(struct pci_pbm_info *pbm, int msi_num)
|
||||
static int pci_sun4v_msiq_build_irq(struct pci_pbm_info *pbm,
|
||||
unsigned long msiqid,
|
||||
unsigned long devino)
|
||||
{
|
||||
msi_num -= pbm->msi_first;
|
||||
clear_bit(msi_num, pbm->msi_bitmap);
|
||||
}
|
||||
unsigned int virt_irq = sun4v_build_irq(pbm->devhandle, devino);
|
||||
|
||||
static int pci_sun4v_setup_msi_irq(unsigned int *virt_irq_p,
|
||||
struct pci_dev *pdev,
|
||||
struct msi_desc *entry)
|
||||
{
|
||||
struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
|
||||
unsigned long devino, msiqid;
|
||||
struct msi_msg msg;
|
||||
int msi_num, err;
|
||||
if (!virt_irq)
|
||||
return -ENOMEM;
|
||||
|
||||
*virt_irq_p = 0;
|
||||
|
||||
msi_num = alloc_msi(pbm);
|
||||
if (msi_num < 0)
|
||||
return msi_num;
|
||||
|
||||
err = sun4v_build_msi(pbm->devhandle, virt_irq_p,
|
||||
pbm->msiq_first_devino,
|
||||
(pbm->msiq_first_devino +
|
||||
pbm->msiq_num));
|
||||
if (err < 0)
|
||||
goto out_err;
|
||||
devino = err;
|
||||
|
||||
msiqid = ((devino - pbm->msiq_first_devino) +
|
||||
pbm->msiq_first);
|
||||
|
||||
err = -EINVAL;
|
||||
if (pci_sun4v_msiq_setstate(pbm->devhandle, msiqid, HV_MSIQSTATE_IDLE))
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
return -EINVAL;
|
||||
if (pci_sun4v_msiq_setvalid(pbm->devhandle, msiqid, HV_MSIQ_VALID))
|
||||
goto out_err;
|
||||
|
||||
if (pci_sun4v_msi_setmsiq(pbm->devhandle,
|
||||
msi_num, msiqid,
|
||||
(entry->msi_attrib.is_64 ?
|
||||
HV_MSITYPE_MSI64 : HV_MSITYPE_MSI32)))
|
||||
goto out_err;
|
||||
|
||||
if (pci_sun4v_msi_setstate(pbm->devhandle, msi_num, HV_MSISTATE_IDLE))
|
||||
goto out_err;
|
||||
|
||||
if (pci_sun4v_msi_setvalid(pbm->devhandle, msi_num, HV_MSIVALID_VALID))
|
||||
goto out_err;
|
||||
|
||||
sparc64_set_msi(*virt_irq_p, msi_num);
|
||||
|
||||
if (entry->msi_attrib.is_64) {
|
||||
msg.address_hi = pbm->msi64_start >> 32;
|
||||
msg.address_lo = pbm->msi64_start & 0xffffffff;
|
||||
} else {
|
||||
msg.address_hi = 0;
|
||||
msg.address_lo = pbm->msi32_start;
|
||||
}
|
||||
msg.data = msi_num;
|
||||
|
||||
set_irq_msi(*virt_irq_p, entry);
|
||||
write_msi_msg(*virt_irq_p, &msg);
|
||||
|
||||
irq_install_pre_handler(*virt_irq_p,
|
||||
pci_sun4v_msi_prehandler,
|
||||
pbm, (void *) msiqid);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
free_msi(pbm, msi_num);
|
||||
return err;
|
||||
return -EINVAL;
|
||||
|
||||
return virt_irq;
|
||||
}
|
||||
|
||||
static void pci_sun4v_teardown_msi_irq(unsigned int virt_irq,
|
||||
struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
|
||||
unsigned long msiqid, err;
|
||||
unsigned int msi_num;
|
||||
|
||||
msi_num = sparc64_get_msi(virt_irq);
|
||||
err = pci_sun4v_msi_getmsiq(pbm->devhandle, msi_num, &msiqid);
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: getmsiq gives error %lu\n",
|
||||
pbm->name, err);
|
||||
return;
|
||||
}
|
||||
|
||||
pci_sun4v_msi_setvalid(pbm->devhandle, msi_num, HV_MSIVALID_INVALID);
|
||||
pci_sun4v_msiq_setvalid(pbm->devhandle, msiqid, HV_MSIQ_INVALID);
|
||||
|
||||
free_msi(pbm, msi_num);
|
||||
|
||||
/* The sun4v_destroy_msi() will liberate the devino and thus the MSIQ
|
||||
* allocation.
|
||||
*/
|
||||
sun4v_destroy_msi(virt_irq);
|
||||
}
|
||||
static const struct sparc64_msiq_ops pci_sun4v_msiq_ops = {
|
||||
.get_head = pci_sun4v_get_head,
|
||||
.dequeue_msi = pci_sun4v_dequeue_msi,
|
||||
.set_head = pci_sun4v_set_head,
|
||||
.msi_setup = pci_sun4v_msi_setup,
|
||||
.msi_teardown = pci_sun4v_msi_teardown,
|
||||
.msiq_alloc = pci_sun4v_msiq_alloc,
|
||||
.msiq_free = pci_sun4v_msiq_free,
|
||||
.msiq_build_irq = pci_sun4v_msiq_build_irq,
|
||||
};
|
||||
|
||||
static void pci_sun4v_msi_init(struct pci_pbm_info *pbm)
|
||||
{
|
||||
const u32 *val;
|
||||
int len;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "#msi-eqs", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
pbm->msiq_num = *val;
|
||||
if (pbm->msiq_num) {
|
||||
const struct msiq_prop {
|
||||
u32 first_msiq;
|
||||
u32 num_msiq;
|
||||
u32 first_devino;
|
||||
} *mqp;
|
||||
const struct msi_range_prop {
|
||||
u32 first_msi;
|
||||
u32 num_msi;
|
||||
} *mrng;
|
||||
const struct addr_range_prop {
|
||||
u32 msi32_high;
|
||||
u32 msi32_low;
|
||||
u32 msi32_len;
|
||||
u32 msi64_high;
|
||||
u32 msi64_low;
|
||||
u32 msi64_len;
|
||||
} *arng;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "msi-eq-size", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
|
||||
pbm->msiq_ent_count = *val;
|
||||
|
||||
mqp = of_get_property(pbm->prom_node,
|
||||
"msi-eq-to-devino", &len);
|
||||
if (!mqp || len != sizeof(struct msiq_prop))
|
||||
goto no_msi;
|
||||
|
||||
pbm->msiq_first = mqp->first_msiq;
|
||||
pbm->msiq_first_devino = mqp->first_devino;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "#msi", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
pbm->msi_num = *val;
|
||||
|
||||
mrng = of_get_property(pbm->prom_node, "msi-ranges", &len);
|
||||
if (!mrng || len != sizeof(struct msi_range_prop))
|
||||
goto no_msi;
|
||||
pbm->msi_first = mrng->first_msi;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "msi-data-mask", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
pbm->msi_data_mask = *val;
|
||||
|
||||
val = of_get_property(pbm->prom_node, "msix-data-width", &len);
|
||||
if (!val || len != 4)
|
||||
goto no_msi;
|
||||
pbm->msix_data_width = *val;
|
||||
|
||||
arng = of_get_property(pbm->prom_node, "msi-address-ranges",
|
||||
&len);
|
||||
if (!arng || len != sizeof(struct addr_range_prop))
|
||||
goto no_msi;
|
||||
pbm->msi32_start = ((u64)arng->msi32_high << 32) |
|
||||
(u64) arng->msi32_low;
|
||||
pbm->msi64_start = ((u64)arng->msi64_high << 32) |
|
||||
(u64) arng->msi64_low;
|
||||
pbm->msi32_len = arng->msi32_len;
|
||||
pbm->msi64_len = arng->msi64_len;
|
||||
|
||||
if (msi_bitmap_alloc(pbm))
|
||||
goto no_msi;
|
||||
|
||||
if (msi_queue_alloc(pbm)) {
|
||||
msi_bitmap_free(pbm);
|
||||
goto no_msi;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: MSI Queue first[%u] num[%u] count[%u] "
|
||||
"devino[0x%x]\n",
|
||||
pbm->name,
|
||||
pbm->msiq_first, pbm->msiq_num,
|
||||
pbm->msiq_ent_count,
|
||||
pbm->msiq_first_devino);
|
||||
printk(KERN_INFO "%s: MSI first[%u] num[%u] mask[0x%x] "
|
||||
"width[%u]\n",
|
||||
pbm->name,
|
||||
pbm->msi_first, pbm->msi_num, pbm->msi_data_mask,
|
||||
pbm->msix_data_width);
|
||||
printk(KERN_INFO "%s: MSI addr32[0x%lx:0x%x] "
|
||||
"addr64[0x%lx:0x%x]\n",
|
||||
pbm->name,
|
||||
pbm->msi32_start, pbm->msi32_len,
|
||||
pbm->msi64_start, pbm->msi64_len);
|
||||
printk(KERN_INFO "%s: MSI queues at RA [%p]\n",
|
||||
pbm->name,
|
||||
pbm->msi_queues);
|
||||
}
|
||||
pbm->setup_msi_irq = pci_sun4v_setup_msi_irq;
|
||||
pbm->teardown_msi_irq = pci_sun4v_teardown_msi_irq;
|
||||
|
||||
return;
|
||||
|
||||
no_msi:
|
||||
pbm->msiq_num = 0;
|
||||
printk(KERN_INFO "%s: No MSI support.\n", pbm->name);
|
||||
sparc64_pbm_msi_init(pbm, &pci_sun4v_msiq_ops);
|
||||
}
|
||||
#else /* CONFIG_PCI_MSI */
|
||||
static void pci_sun4v_msi_init(struct pci_pbm_info *pbm)
|
||||
@@ -1237,11 +1055,6 @@ void __init sun4v_pci_init(struct device_node *dp, char *model_name)
|
||||
|
||||
p->pbm_B.iommu = iommu;
|
||||
|
||||
/* Like PSYCHO and SCHIZO we have a 2GB aligned area
|
||||
* for memory space.
|
||||
*/
|
||||
pci_memspace_mask = 0x7fffffffUL;
|
||||
|
||||
pci_sun4v_pbm_init(p, dp, devhandle);
|
||||
return;
|
||||
|
||||
|
||||
@@ -105,9 +105,11 @@ static struct of_device_id power_match[] = {
|
||||
};
|
||||
|
||||
static struct of_platform_driver power_driver = {
|
||||
.name = "power",
|
||||
.match_table = power_match,
|
||||
.probe = power_probe,
|
||||
.driver = {
|
||||
.name = "power",
|
||||
},
|
||||
};
|
||||
|
||||
void __init power_init(void)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user