Commit 867a89e0 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc

* 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc:
  [RAPIDIO] Change RapidIO doorbell source and target ID field to 16-bit
  [RAPIDIO] Add RapidIO connection info print out and re-training for broken connections
  [RAPIDIO] Add serial RapidIO controller support, which includes MPC8548, MPC8641
  [RAPIDIO] Add RapidIO node probing into MPC86xx_HPCN board id table
  [RAPIDIO] Add RapidIO node into MPC8641HPCN dts file
  [RAPIDIO] Auto-probe the RapidIO system size
  [RAPIDIO] Add OF-tree support to RapidIO controller driver
  [RAPIDIO] Add RapidIO multi mport support
  [RAPIDIO] Move include/asm-ppc/rio.h to asm-powerpc
  [RAPIDIO] Add RapidIO option to kernel configuration
  [RAPIDIO] Change RIO function mpc85xx_ to fsl_
  [POWERPC] Provide walk_memory_resource() for powerpc
  [POWERPC] Update lmb data structures for hotplug memory add/remove
  [POWERPC] Hotplug memory remove notifications for powerpc
  [POWERPC] windfarm: Add PowerMac 12,1 support
  [POWERPC] Fix building of pmac32 when CONFIG_NVRAM=m
  [POWERPC] Add IRQSTACKS support on ppc32
  [POWERPC] Use __always_inline for xchg* and cmpxchg*
  [POWERPC] Add fast little-endian switch system call
parents 44473d99 6c39103c
......@@ -608,6 +608,19 @@ source "drivers/pcmcia/Kconfig"
source "drivers/pci/hotplug/Kconfig"
config HAS_RAPIDIO
bool
default n
config RAPIDIO
bool "RapidIO support"
depends on HAS_RAPIDIO
help
If you say Y here, the kernel will include drivers and
infrastructure code to support RapidIO interconnect devices.
source "drivers/rapidio/Kconfig"
endmenu
menu "Advanced setup"
......
......@@ -118,7 +118,6 @@ config XMON_DISASSEMBLY
config IRQSTACKS
bool "Use separate kernel stacks when processing interrupts"
depends on PPC64
help
If you say Y here the kernel will use separate kernel stacks
for handling hard and soft interrupts. This can help avoid
......
......@@ -26,6 +26,7 @@ aliases {
serial1 = &serial1;
pci0 = &pci0;
pci1 = &pci1;
rapidio0 = &rapidio0;
};
cpus {
......@@ -500,4 +501,15 @@ pcie@0 {
0x0 0x00100000>;
};
};
rapidio0: rapidio@f80c0000 {
#address-cells = <2>;
#size-cells = <2>;
compatible = "fsl,rapidio-delta";
reg = <0xf80c0000 0x20000>;
ranges = <0 0 0xc0000000 0 0x20000000>;
interrupt-parent = <&mpic>;
/* err_irq bell_outb_irq bell_inb_irq
msg1_tx_irq msg1_rx_irq msg2_tx_irq msg2_rx_irq */
interrupts = <48 2 49 2 50 2 53 2 54 2 55 2 56 2>;
};
};
......@@ -696,6 +696,7 @@ CONFIG_WINDFARM=y
CONFIG_WINDFARM_PM81=y
CONFIG_WINDFARM_PM91=y
CONFIG_WINDFARM_PM112=y
CONFIG_WINDFARM_PM121=y
# CONFIG_PMAC_RACKMETER is not set
CONFIG_NETDEVICES=y
# CONFIG_NETDEVICES_MULTIQUEUE is not set
......
......@@ -73,7 +73,6 @@ pci64-$(CONFIG_PPC64) += pci_dn.o isa-bridge.o
obj-$(CONFIG_PCI) += pci_$(CONFIG_WORD_SIZE).o $(pci64-y) \
pci-common.o
obj-$(CONFIG_PCI_MSI) += msi.o
obj-$(CONFIG_RAPIDIO) += rio.o
obj-$(CONFIG_KEXEC) += machine_kexec.o crash.o \
machine_kexec_$(CONFIG_WORD_SIZE).o
obj-$(CONFIG_AUDIT) += audit.o
......
......@@ -63,6 +63,7 @@ int main(void)
#endif /* CONFIG_PPC64 */
DEFINE(KSP, offsetof(struct thread_struct, ksp));
DEFINE(KSP_LIMIT, offsetof(struct thread_struct, ksp_limit));
DEFINE(PT_REGS, offsetof(struct thread_struct, regs));
DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode));
DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0]));
......
......@@ -137,11 +137,12 @@ transfer_to_handler:
2: /* if from kernel, check interrupted DOZE/NAP mode and
* check for stack overflow
*/
lwz r9,THREAD_INFO-THREAD(r12)
cmplw r1,r9 /* if r1 <= current->thread_info */
lwz r9,KSP_LIMIT(r12)
cmplw r1,r9 /* if r1 <= ksp_limit */
ble- stack_ovf /* then the kernel stack overflowed */
5:
#ifdef CONFIG_6xx
rlwinm r9,r1,0,0,31-THREAD_SHIFT
tophys(r9,r9) /* check local flags */
lwz r12,TI_LOCAL_FLAGS(r9)
mtcrf 0x01,r12
......
......@@ -239,6 +239,10 @@ instruction_access_slb_pSeries:
.globl system_call_pSeries
system_call_pSeries:
HMT_MEDIUM
BEGIN_FTR_SECTION
cmpdi r0,0x1ebe
beq- 1f
END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE)
mr r9,r13
mfmsr r10
mfspr r13,SPRN_SPRG3
......@@ -253,6 +257,13 @@ system_call_pSeries:
rfid
b . /* prevent speculative execution */
/* Fast LE/BE switch system call */
1: mfspr r12,SPRN_SRR1
xori r12,r12,MSR_LE
mtspr SPRN_SRR1,r12
rfid /* return to userspace */
b .
STD_EXCEPTION_PSERIES(0xd00, single_step)
STD_EXCEPTION_PSERIES(0xe00, trap_0e)
......
......@@ -307,6 +307,7 @@ void do_IRQ(struct pt_regs *regs)
if (curtp != irqtp) {
struct irq_desc *desc = irq_desc + irq;
void *handler = desc->handle_irq;
unsigned long saved_sp_limit = current->thread.ksp_limit;
if (handler == NULL)
handler = &__do_IRQ;
irqtp->task = curtp->task;
......@@ -319,7 +320,10 @@ void do_IRQ(struct pt_regs *regs)
(irqtp->preempt_count & ~SOFTIRQ_MASK) |
(curtp->preempt_count & SOFTIRQ_MASK);
current->thread.ksp_limit = (unsigned long)irqtp +
_ALIGN_UP(sizeof(struct thread_info), 16);
call_handle_irq(irq, desc, irqtp, handler);
current->thread.ksp_limit = saved_sp_limit;
irqtp->task = NULL;
......@@ -352,9 +356,7 @@ void __init init_IRQ(void)
{
if (ppc_md.init_IRQ)
ppc_md.init_IRQ();
#ifdef CONFIG_PPC64
irq_ctx_init();
#endif
}
......@@ -383,11 +385,15 @@ void irq_ctx_init(void)
static inline void do_softirq_onstack(void)
{
struct thread_info *curtp, *irqtp;
unsigned long saved_sp_limit = current->thread.ksp_limit;
curtp = current_thread_info();
irqtp = softirq_ctx[smp_processor_id()];
irqtp->task = curtp->task;
current->thread.ksp_limit = (unsigned long)irqtp +
_ALIGN_UP(sizeof(struct thread_info), 16);
call_do_softirq(irqtp);
current->thread.ksp_limit = saved_sp_limit;
irqtp->task = NULL;
}
......
......@@ -32,6 +32,31 @@
.text
#ifdef CONFIG_IRQSTACKS
_GLOBAL(call_do_softirq)
mflr r0
stw r0,4(r1)
stwu r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r3)
mr r1,r3
bl __do_softirq
lwz r1,0(r1)
lwz r0,4(r1)
mtlr r0
blr
_GLOBAL(call_handle_irq)
mflr r0
stw r0,4(r1)
mtctr r6
stwu r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r5)
mr r1,r5
bctrl
lwz r1,0(r1)
lwz r0,4(r1)
mtlr r0
blr
#endif /* CONFIG_IRQSTACKS */
/*
* This returns the high 64 bits of the product of two 64-bit numbers.
*/
......
......@@ -589,6 +589,8 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
kregs = (struct pt_regs *) sp;
sp -= STACK_FRAME_OVERHEAD;
p->thread.ksp = sp;
p->thread.ksp_limit = (unsigned long)task_stack_page(p) +
_ALIGN_UP(sizeof(struct thread_info), 16);
#ifdef CONFIG_PPC64
if (cpu_has_feature(CPU_FTR_SLB)) {
......
/*
* RapidIO PPC32 support
*
* 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/init.h>
#include <linux/kernel.h>
#include <linux/rio.h>
#include <asm/rio.h>
/**
* platform_rio_init - Do platform specific RIO init
*
* Any platform specific initialization of RapdIO
* hardware is done here as well as registration
* of any active master ports in the system.
*/
void __attribute__ ((weak))
platform_rio_init(void)
{
printk(KERN_WARNING "RIO: No platform_rio_init() present\n");
}
/**
* ppc_rio_init - Do PPC32 RIO init
*
* Calls platform-specific RIO init code and then calls
* rio_init_mports() to initialize any master ports that
* have been registered with the RIO subsystem.
*/
static int __init ppc_rio_init(void)
{
printk(KERN_INFO "RIO: RapidIO init\n");
/* Platform specific initialization */
platform_rio_init();
/* Enumerate all registered ports */
rio_init_mports();
return 0;
}
subsys_initcall(ppc_rio_init);
......@@ -16,6 +16,7 @@
#include <linux/root_dev.h>
#include <linux/cpu.h>
#include <linux/console.h>
#include <linux/lmb.h>
#include <asm/io.h>
#include <asm/prom.h>
......@@ -229,6 +230,24 @@ int __init ppc_init(void)
arch_initcall(ppc_init);
#ifdef CONFIG_IRQSTACKS
static void __init irqstack_early_init(void)
{
unsigned int i;
/* interrupt stacks must be in lowmem, we get that for free on ppc32
* as the lmb is limited to lowmem by LMB_REAL_LIMIT */
for_each_possible_cpu(i) {
softirq_ctx[i] = (struct thread_info *)
__va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
hardirq_ctx[i] = (struct thread_info *)
__va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
}
}
#else
#define irqstack_early_init()
#endif
/* Warning, IO base is not yet inited */
void __init setup_arch(char **cmdline_p)
{
......@@ -286,6 +305,8 @@ void __init setup_arch(char **cmdline_p)
init_mm.end_data = (unsigned long) _edata;
init_mm.brk = klimit;
irqstack_early_init();
/* set up the bootmem stuff with available memory */
do_init_bootmem();
if ( ppc_md.progress ) ppc_md.progress("setup_arch: bootmem", 0x3eab);
......
......@@ -154,19 +154,35 @@ int remove_memory(u64 start, u64 size)
/*
* walk_memory_resource() needs to make sure there is no holes in a given
* memory range. On PPC64, since this range comes from /sysfs, the range
* is guaranteed to be valid, non-overlapping and can not contain any
* holes. By the time we get here (memory add or remove), /proc/device-tree
* is updated and correct. Only reason we need to check against device-tree
* would be if we allow user-land to specify a memory range through a
* system call/ioctl etc. instead of doing offline/online through /sysfs.
* memory range. PPC64 does not maintain the memory layout in /proc/iomem.
* Instead it maintains it in lmb.memory structures. Walk through the
* memory regions, find holes and callback for contiguous regions.
*/
int
walk_memory_resource(unsigned long start_pfn, unsigned long nr_pages, void *arg,
int (*func)(unsigned long, unsigned long, void *))
{
return (*func)(start_pfn, nr_pages, arg);
struct lmb_property res;
unsigned long pfn, len;
u64 end;
int ret = -1;
res.base = (u64) start_pfn << PAGE_SHIFT;
res.size = (u64) nr_pages << PAGE_SHIFT;
end = res.base + res.size - 1;
while ((res.base < end) && (lmb_find(&res) >= 0)) {
pfn = (unsigned long)(res.base >> PAGE_SHIFT);
len = (unsigned long)(res.size >> PAGE_SHIFT);
ret = (*func)(pfn, len, arg);
if (ret)
break;
res.base += (res.size + 1);
res.size = (end - res.base + 1);
}
return ret;
}
EXPORT_SYMBOL_GPL(walk_memory_resource);
#endif /* CONFIG_MEMORY_HOTPLUG */
......
......@@ -8,6 +8,7 @@ config MPC8641_HPCN
select PPC_I8259
select DEFAULT_UIMAGE
select FSL_ULI1575
select HAS_RAPIDIO
help
This option enables support for the MPC8641 HPCN board.
......
......@@ -221,6 +221,7 @@ mpc86xx_time_init(void)
static __initdata struct of_device_id of_bus_ids[] = {
{ .compatible = "simple-bus", },
{ .compatible = "fsl,rapidio-delta", },
{},
};
......
......@@ -6,7 +6,10 @@ obj-y += pic.o setup.o time.o feature.o pci.o \
obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o
obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o
obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o
obj-$(CONFIG_NVRAM) += nvram.o
# CONFIG_NVRAM is an arch. independant tristate symbol, for pmac32 we really
# need this to be a bool. Cheat here and pretend CONFIG_NVRAM=m is really
# CONFIG_NVRAM=y
obj-$(CONFIG_NVRAM:m=y) += nvram.o
# ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff
obj-$(CONFIG_PPC64) += nvram.o
obj-$(CONFIG_PPC32) += bootx_init.o
......
......@@ -337,7 +337,8 @@ static void __init pmac_setup_arch(void)
find_via_pmu();
smu_init();
#if defined(CONFIG_NVRAM) || defined(CONFIG_PPC64)
#if defined(CONFIG_NVRAM) || defined(CONFIG_NVRAM_MODULE) || \
defined(CONFIG_PPC64)
pmac_nvram_init();
#endif
......
......@@ -18,6 +18,7 @@ obj-$(CONFIG_PCI) += pci.o pci_dlpar.o
obj-$(CONFIG_PCI_MSI) += msi.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o
obj-$(CONFIG_MEMORY_HOTPLUG) += hotplug-memory.o
obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o
obj-$(CONFIG_HVCS) += hvcserver.o
......
/*
* pseries Memory Hotplug infrastructure.
*
* Copyright (C) 2008 Badari Pulavarty, 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/of.h>
#include <linux/lmb.h>
#include <asm/firmware.h>
#include <asm/machdep.h>
#include <asm/pSeries_reconfig.h>
static int pseries_remove_memory(struct device_node *np)
{
const char *type;
const unsigned int *my_index;
const unsigned int *regs;
u64 start_pfn, start;
struct zone *zone;
int ret = -EINVAL;
/*
* Check to see if we are actually removing memory
*/
type = of_get_property(np, "device_type", NULL);
if (type == NULL || strcmp(type, "memory") != 0)
return 0;
/*
* Find the memory index and size of the removing section
*/
my_index = of_get_property(np, "ibm,my-drc-index", NULL);
if (!my_index)
return ret;
regs = of_get_property(np, "reg", NULL);
if (!regs)
return ret;
start_pfn = section_nr_to_pfn(*my_index & 0xffff);
zone = page_zone(pfn_to_page(start_pfn));
/*
* Remove section mappings and sysfs entries for the
* section of the memory we are removing.
*
* NOTE: Ideally, this should be done in generic code like
* remove_memory(). But remove_memory() gets called by writing
* to sysfs "state" file and we can't remove sysfs entries
* while writing to it. So we have to defer it to here.
*/
ret = __remove_pages(zone, start_pfn, regs[3] >> PAGE_SHIFT);
if (ret)
return ret;
/*
* Update memory regions for memory remove
*/
lmb_remove(start_pfn << PAGE_SHIFT, regs[3]);
/*
* Remove htab bolted mappings for this section of memory
*/
start = (unsigned long)__va(start_pfn << PAGE_SHIFT);
ret = remove_section_mapping(start, start + regs[3]);
return ret;
}
static int pseries_add_memory(struct device_node *np)
{
const char *type;
const unsigned int *my_index;
const unsigned int *regs;
u64 start_pfn;
int ret = -EINVAL;
/*
* Check to see if we are actually adding memory
*/
type = of_get_property(np, "device_type", NULL);
if (type == NULL || strcmp(type, "memory") != 0)
return 0;
/*
* Find the memory index and size of the added section
*/
my_index = of_get_property(np, "ibm,my-drc-index", NULL);
if (!my_index)
return ret;
regs = of_get_property(np, "reg", NULL);
if (!regs)
return ret;
start_pfn = section_nr_to_pfn(*my_index & 0xffff);
/*
* Update memory region to represent the memory add
*/
lmb_add(start_pfn << PAGE_SHIFT, regs[3]);
return 0;
}
static int pseries_memory_notifier(struct notifier_block *nb,
unsigned long action, void *node)
{
int err = NOTIFY_OK;
switch (action) {
case PSERIES_RECONFIG_ADD:
if (pseries_add_memory(node))
err = NOTIFY_BAD;
break;
case PSERIES_RECONFIG_REMOVE:
if (pseries_remove_memory(node))
err = NOTIFY_BAD;
break;
default:
err = NOTIFY_DONE;
break;
}
return err;
}
static struct notifier_block pseries_mem_nb = {
.notifier_call = pseries_memory_notifier,
};
static int __init pseries_memory_hotplug_init(void)
{
if (firmware_has_feature(FW_FEATURE_LPAR))
pSeries_reconfig_notifier_register(&pseries_mem_nb);
return 0;
}
machine_device_initcall(pseries, pseries_memory_hotplug_init);
/*
* MPC85xx RapidIO support
* Freescale MPC85xx/MPC86xx RapidIO support
*
* Copyright (C) 2007, 2008 Freescale Semiconductor, Inc.
* Zhang Wei <wei.zhang@freescale.com>
*
* Copyright 2005 MontaVista Software, Inc.
* Matt Porter <mporter@kernel.crashing.org>
......@@ -17,12 +20,23 @@
#include <linux/interrupt.h>
#include <linux/rio.h>
#include <linux/rio_drv.h>
#include <linux/of_platform.h>
#include <linux/delay.h>
#include <asm/io.h>
#define RIO_REGS_BASE (CCSRBAR + 0xc0000)
/* RapidIO definition irq, which read from OF-tree */
#define IRQ_RIO_BELL(m) (((struct rio_priv *)(m->priv))->bellirq)
#define IRQ_RIO_TX(m) (((struct rio_priv *)(m->priv))->txirq)
#define IRQ_RIO_RX(m) (((struct rio_priv *)(m->priv))->rxirq)
#define RIO_ATMU_REGS_OFFSET 0x10c00
#define RIO_MSG_REGS_OFFSET 0x11000
#define RIO_P_MSG_REGS_OFFSET 0x11000
#define RIO_S_MSG_REGS_OFFSET 0x13000
#define RIO_ESCSR 0x158
#define RIO_CCSR 0x15c
#define RIO_ISR_AACR 0x10120
#define RIO_ISR_AACR_AA 0x1 /* Accept All ID */
#define RIO_MAINT_WIN_SIZE 0x400000
#define RIO_DBELL_WIN_SIZE 0x1000
......@@ -50,18 +64,18 @@
#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_TID_OFFSET 0x02
#define DOORBELL_SID_OFFSET 0x04
#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_SID(x) (*(u16 *)(x + DOORBELL_SID_OFFSET))
#define DBELL_TID(x) (*(u16 *)(x + DOORBELL_TID_OFFSET))
#define DBELL_INF(x) (*(u16 *)(x + DOORBELL_INFO_OFFSET))
struct rio_atmu_regs {
u32 rowtar;
u32 pad1;
u32 rowtear;
u32 rowbar;
u32 pad2;
u32 rowar;
......@@ -87,7 +101,15 @@ struct rio_msg_regs {
u32 ifqdpar;
u32 pad6;
u32 ifqepar;
u32 pad7[250];
u32 pad7[226];
u32 odmr;
u32 odsr;
u32 res0[4];
u32 oddpr;
u32 oddatr;
u32 res1[3];
u32 odretcr;
u32 res2[12];
u32 dmr;
u32 dsr;
u32 pad8;
......@@ -112,20 +134,12 @@ struct rio_tx_desc {
u32 res4;
};
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 {
struct rio_dbell_ring {
void *virt;
dma_addr_t phys;
} dbell_ring;
};
static struct rio_msg_tx_ring {
struct rio_msg_tx_ring {
void *virt;
dma_addr_t phys;
void *virt_buffer[RIO_MAX_TX_RING_SIZE];
......@@ -133,19 +147,35 @@ static struct rio_msg_tx_ring {
int tx_slot;
int size;
void *dev_id;
} msg_tx_ring;
};
static struct rio_msg_rx_ring {
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;
};
struct rio_priv {
void __iomem *regs_win;
struct rio_atmu_regs __iomem *atmu_regs;
struct rio_atmu_regs __iomem *maint_atmu_regs;
struct rio_atmu_regs __iomem *dbell_atmu_regs;
void __iomem *dbell_win;
void __iomem *maint_win;
struct rio_msg_regs __iomem *msg_regs;
struct rio_dbell_ring dbell_ring;
struct rio_msg_tx_ring msg_tx_ring;
struct rio_msg_rx_ring msg_rx_ring;
int bellirq;
int txirq;
int rxirq;
};
/**
* mpc85xx_rio_doorbell_send - Send a MPC85xx doorbell message
* fsl_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
......@@ -153,18 +183,34 @@ static struct rio_msg_rx_ring {
* 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)
static int fsl_rio_doorbell_send(struct rio_mport *mport,
int index, u16 destid, u16 data)
{
pr_debug("mpc85xx_doorbell_send: index %d destid %4.4x data %4.4x\n",
struct rio_priv *priv = mport->priv;
pr_debug("fsl_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);
switch (mport->phy_type) {
case RIO_PHY_PARALLEL:
out_be32(&priv->dbell_atmu_regs->rowtar, destid << 22);
out_be16(priv->dbell_win, data);
break;
case RIO_PHY_SERIAL:
/* In the serial version silicons, such as MPC8548, MPC8641,
* below operations is must be.
*/
out_be32(&priv->msg_regs->odmr, 0x00000000);
out_be32(&priv->msg_regs->odretcr, 0x00000004);
out_be32(&priv->msg_regs->oddpr, destid << 16);
out_be32(&priv->msg_regs->oddatr, data);
out_be32(&priv->msg_regs->odmr, 0x00000001);
break;
}
return 0;
}
/**
* mpc85xx_local_config_read - Generate a MPC85xx local config space read
* fsl_local_config_read - Generate a MPC85xx local config space read
* @index: ID of RapdiIO interface
* @offset: Offset into configuration space
* @len: Length (in bytes) of the maintenance transaction
......@@ -173,17 +219,19 @@ 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
* @index: ID of RapdiIO interface
* @offset: Offset into configuration space
* @len: Length (in bytes) of the maintenance transaction
......@@ -192,18 +240,20 @@ 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",
("fsl_local_config_write: index %d offset %8.8x data %8.8x\n",
index, offset, data);
out_be32((void *)(regs_win + 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
* @index: ID of RapdiIO interface
* @destid: Destination ID of transaction
* @hopcount: Number of hops to target device
......@@ -215,18 +265,19 @@ 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;
pr_debug
("mpc85xx_rio_config_read: index %d destid %d hopcount %d offset %8.8x len %d\n",
("fsl_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,
out_be32(&priv->maint_atmu_regs->rowtar,
(destid << 22) | (hopcount << 12) | ((offset & ~0x3) >> 9));
data = (u8 *) maint_win + offset;
data = (u8 *) priv->maint_win + offset;
switch (len) {
case 1:
*val = in_8((u8 *) data);
......@@ -243,7 +294,7 @@ mpc85xx_rio_config_read(int index, u16 destid, u8 hopcount, u32 offset, int len,
}
/**
* mpc85xx_rio_config_write - Generate a MPC85xx write maintenance transaction
* fsl_rio_config_write - Generate a MPC85xx write maintenance transaction
* @index: ID of RapdiIO interface
* @destid: Destination ID of transaction
* @hopcount: Number of hops to target device
......@@ -255,17 +306,18 @@ 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",
("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);
out_be32((void *)&maint_atmu_regs->rowtar,
out_be32(&priv->maint_atmu_regs->rowtar,
(destid << 22) | (hopcount << 12) | ((offset & ~0x3) >> 9));
data = (u8 *) maint_win + offset;
data = (u8 *) priv->maint_win + offset;
switch (len) {
case 1:
out_8((u8 *) data, val);
......@@ -296,9 +348,10 @@ int
rio_hw_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox,
void *buffer, size_t len)
{
struct rio_priv *priv = mport->priv;
u32 omr;
struct rio_tx_desc *desc =
(struct rio_tx_desc *)msg_tx_ring.virt + msg_tx_ring.tx_slot;
struct rio_tx_desc *desc = (struct rio_tx_desc *)priv->msg_tx_ring.virt
+ priv->msg_tx_ring.tx_slot;
int ret = 0;
pr_debug
......@@ -311,31 +364,43 @@ rio_hw_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox,
}
/* Copy and clear rest of buffer */
memcpy(msg_tx_ring.virt_buffer[msg_tx_ring.tx_slot], buffer, len);
memcpy(priv->msg_tx_ring.virt_buffer[priv->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);
memset(priv->msg_tx_ring.virt_buffer[priv->msg_tx_ring.tx_slot]
+ len, 0, RIO_MAX_MSG_SIZE - len);
/* Set mbox field for message */
desc->dport = mbox & 0x3;
switch (mport->phy_type) {
case RIO_PHY_PARALLEL:
/* Set mbox field for message */
desc->dport = mbox & 0x3;
/* Enable EOMI interrupt, set priority, and set destid */
desc->dattr = 0x28000000 | (rdev->destid << 2);
/* Enable EOMI interrupt, set priority, and set destid */
desc->dattr = 0x28000000 | (rdev->destid << 2);
break;
case RIO_PHY_SERIAL:
/* Set mbox field for message, and set destid */
desc->dport = (rdev->destid << 16) | (mbox & 0x3);
/* Enable EOMI interrupt and priority */
desc->dattr = 0x28000000;
break;
}
/* 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];
desc->saddr = 0x00000004
| priv->msg_tx_ring.phys_buffer[priv->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);
omr = in_be32(&priv->msg_regs->omr);
out_be32(&priv->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;
if (++priv->msg_tx_ring.tx_slot == priv->msg_tx_ring.size)
priv->msg_tx_ring.tx_slot = 0;
out:
return ret;
......@@ -344,7 +409,7 @@ rio_hw_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox,
EXPORT_SYMBOL_GPL(rio_hw_add_outb_message);
/**
* mpc85xx_rio_tx_handler - MPC85xx outbound message interrupt handler
* fsl_rio_tx_handler - MPC85xx outbound message interrupt handler
* @irq: Linux interrupt number
* @dev_instance: Pointer to interrupt-specific data
*
......@@ -352,32 +417,34 @@ EXPORT_SYMBOL_GPL(rio_hw_add_outb_message);
* mailbox event handler and acks the interrupt occurrence.
*/
static irqreturn_t
mpc85xx_rio_tx_handler(int irq, void *dev_instance)
fsl_rio_tx_handler(int irq, void *dev_instance)
{
int osr;
struct rio_mport *port = (struct rio_mport *)dev_instance;
struct rio_priv *priv = port->priv;
osr = in_be32((void *)&msg_regs->osr);
osr = in_be32(&priv->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);
out_be32(&priv->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);
out_be32(&priv->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);
u32 dqp = in_be32(&priv->msg_regs->odqdpar);
int slot = (dqp - priv->msg_tx_ring.phys) >> 5;
port->outb_msg[0].mcback(port, priv->msg_tx_ring.dev_id, -1,
slot);
/* Ack the end-of-message interrupt */
out_be32((void *)&msg_regs->osr, RIO_MSG_OSR_EOMI);
out_be32(&priv->msg_regs->osr, RIO_MSG_OSR_EOMI);
}
out:
......@@ -398,6 +465,7 @@ mpc85xx_rio_tx_handler(int irq, void *dev_instance)
int rio_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;
if ((entries < RIO_MIN_TX_RING_SIZE) ||
(entries > RIO_MAX_TX_RING_SIZE) || (!is_power_of_2(entries))) {
......@@ -406,54 +474,53 @@ int rio_open_outb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entr
}
/* 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))) {
priv->msg_tx_ring.dev_id = dev_id;
priv->msg_tx_ring.size = entries;
for (i = 0; i < priv->msg_tx_ring.size; i++) {
priv->msg_tx_ring.virt_buffer[i] =
dma_alloc_coherent(NULL, RIO_MSG_BUFFER_SIZE,
&priv->msg_tx_ring.phys_buffer[i], GFP_KERNEL);
if (!priv->msg_tx_ring.virt_buffer[i]) {
rc = -ENOMEM;
for (j = 0; j < msg_tx_ring.size; j++)
if (msg_tx_ring.virt_buffer[j])
for (j = 0; j < priv->msg_tx_ring.size; j++)
if (priv->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]);
RIO_MSG_BUFFER_SIZE,
priv->msg_tx_ring.
virt_buffer[j],
priv->msg_tx_ring.
phys_buffer[j]);
goto out;
}
}
/* 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))) {
priv->msg_tx_ring.virt = dma_alloc_coherent(NULL,
priv->msg_tx_ring.size * RIO_MSG_DESC_SIZE,
&priv->msg_tx_ring.phys, GFP_KERNEL);
if (!priv->msg_tx_ring.virt) {
rc = -ENOMEM;
goto out_dma;
}
memset(msg_tx_ring.virt, 0, msg_tx_ring.size * RIO_MSG_DESC_SIZE);
msg_tx_ring.tx_slot = 0;
memset(priv->msg_tx_ring.virt, 0,
priv->msg_tx_ring.size * RIO_MSG_DESC_SIZE);
priv->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);
out_be32(&priv->msg_regs->odqdpar, priv->msg_tx_ring.phys);
out_be32(&priv->msg_regs->odqepar, priv->msg_tx_ring.phys);
/* Configure for snooping */
out_be32((void *)&msg_regs->osar, 0x00000004);
out_be32(&priv->msg_regs->osar, 0x00000004);
/* Clear interrupt status */
out_be32((void *)&msg_regs->osr, 0x000000b3);
out_be32(&priv->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)
rc = request_irq(IRQ_RIO_TX(mport), fsl_rio_tx_handler, 0,
"msg_tx", (void *)mport);
if (rc < 0)
goto out_irq;
/*
......@@ -463,28 +530,28 @@ int rio_open_outb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entr
* Chaining mode
* Disable
*/
out_be32((void *)&msg_regs->omr, 0x00100220);
out_be32(&priv->msg_regs->omr, 0x00100220);
/* Set number of entries */
out_be32((void *)&msg_regs->omr,
in_be32((void *)&msg_regs->omr) |
out_be32(&priv->msg_regs->omr,
in_be32(&priv->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_be32(&priv->msg_regs->omr, in_be32(&priv->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);
dma_free_coherent(NULL, priv->msg_tx_ring.size * RIO_MSG_DESC_SIZE,
priv->msg_tx_ring.virt, priv->msg_tx_ring.phys);
out_dma:
for (i = 0; i < msg_tx_ring.size; i++)
for (i = 0; i < priv->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]);
priv->msg_tx_ring.virt_buffer[i],
priv->msg_tx_ring.phys_buffer[i]);
return rc;
}
......@@ -499,19 +566,20 @@ int rio_open_outb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entr
*/
void rio_close_outb_mbox(struct rio_mport *mport, int mbox)
{
struct rio_priv *priv = mport->priv;
/* Disable inbound message unit */
out_be32((void *)&msg_regs->omr, 0);
out_be32(&priv->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);
dma_free_coherent(NULL, priv->msg_tx_ring.size * RIO_MSG_DESC_SIZE,
priv->msg_tx_ring.virt, priv->msg_tx_ring.phys);
/* Free interrupt */
free_irq(MPC85xx_IRQ_RIO_TX, (void *)mport);
free_irq(IRQ_RIO_TX(mport), (void *)mport);
}
/**
* mpc85xx_rio_rx_handler - MPC85xx inbound message interrupt handler
* fsl_rio_rx_handler - MPC85xx inbound message interrupt handler
* @irq: Linux interrupt number
* @dev_instance: Pointer to interrupt-specific data
*
......@@ -519,16 +587,17 @@ void rio_close_outb_mbox(struct rio_mport *mport, int mbox)
* mailbox event handler and acks the interrupt occurrence.
*/
static irqreturn_t
mpc85xx_rio_rx_handler(int irq, void *dev_instance)
fsl_rio_rx_handler(int irq, void *dev_instance)
{
int isr;
struct rio_mport *port = (struct rio_mport *)dev_instance;
struct rio_priv *priv = port->priv;
isr = in_be32((void *)&msg_regs->isr);
isr = in_be32(&priv->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);
out_be32((void *)&priv->msg_regs->isr, RIO_MSG_ISR_TE);
goto out;
}
......@@ -540,10 +609,10 @@ mpc85xx_rio_rx_handler(int irq, void *dev_instance)
* make the callback with an unknown/invalid mailbox number
* argument.
*/
port->inb_msg[0].mcback(port, msg_rx_ring.dev_id, -1, -1);
port->inb_msg[0].mcback(port, priv->msg_rx_ring.dev_id, -1, -1);
/* Ack the queueing interrupt */
out_be32((void *)&msg_regs->isr, RIO_MSG_ISR_DIQI);
out_be32(&priv->msg_regs->isr, RIO_MSG_ISR_DIQI);
}
out:
......@@ -564,6 +633,7 @@ mpc85xx_rio_rx_handler(int irq, void *dev_instance)
int rio_open_inb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries)
{
int i, rc = 0;
struct rio_priv *priv = mport->priv;
if ((entries < RIO_MIN_RX_RING_SIZE) ||
(entries > RIO_MAX_RX_RING_SIZE) || (!is_power_of_2(entries))) {
......@@ -572,36 +642,35 @@ int rio_open_inb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entri
}
/* 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;
priv->msg_rx_ring.dev_id = dev_id;
priv->msg_rx_ring.size = entries;
priv->msg_rx_ring.rx_slot = 0;
for (i = 0; i < priv->msg_rx_ring.size; i++)
priv->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))) {
priv->msg_rx_ring.virt = dma_alloc_coherent(NULL,
priv->msg_rx_ring.size * RIO_MAX_MSG_SIZE,
&priv->msg_rx_ring.phys, GFP_KERNEL);
if (!priv->msg_rx_ring.virt) {
rc = -ENOMEM;
goto out;
}
/* 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);
out_be32(&priv->msg_regs->ifqdpar, (u32) priv->msg_rx_ring.phys);
out_be32(&priv->msg_regs->ifqepar, (u32) priv->msg_rx_ring.phys);
/* Clear interrupt status */
out_be32((void *)&msg_regs->isr, 0x00000091);
out_be32(&priv->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) {
rc = request_irq(IRQ_RIO_RX(mport), fsl_rio_rx_handler, 0,
"msg_rx", (void *)mport);
if (rc < 0) {
dma_free_coherent(NULL, RIO_MSG_BUFFER_SIZE,
msg_tx_ring.virt_buffer[i],
msg_tx_ring.phys_buffer[i]);
priv->msg_tx_ring.virt_buffer[i],
priv->msg_tx_ring.phys_buffer[i]);
goto out;
}
......@@ -612,15 +681,13 @@ int rio_open_inb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entri
* Unmask all interrupt sources
* Disable
*/
out_be32((void *)&msg_regs->imr, 0x001b0060);
out_be32(&priv->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));
setbits32(&priv->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);
setbits32(&priv->msg_regs->imr, 0x1);
out:
return rc;
......@@ -636,15 +703,16 @@ int rio_open_inb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entri
*/
void rio_close_inb_mbox(struct rio_mport *mport, int mbox)
{
struct rio_priv *priv = mport->priv;
/* Disable inbound message unit */
out_be32((void *)&msg_regs->imr, 0);
out_be32(&priv->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);
dma_free_coherent(NULL, priv->msg_rx_ring.size * RIO_MAX_MSG_SIZE,
priv->msg_rx_ring.virt, priv->msg_rx_ring.phys);
/* Free interrupt */
free_irq(MPC85xx_IRQ_RIO_RX, (void *)mport);
free_irq(IRQ_RIO_RX(mport), (void *)mport);
}
/**
......@@ -659,21 +727,22 @@ void rio_close_inb_mbox(struct rio_mport *mport, int mbox)
int rio_hw_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf)
{
int rc = 0;
struct rio_priv *priv = mport->priv;
pr_debug("RIO: rio_hw_add_inb_buffer(), msg_rx_ring.rx_slot %d\n",
msg_rx_ring.rx_slot);
priv->msg_rx_ring.rx_slot);
if (msg_rx_ring.virt_buffer[msg_rx_ring.rx_slot]) {
if (priv->msg_rx_ring.virt_buffer[priv->msg_rx_ring.rx_slot]) {
printk(KERN_ERR
"RIO: error adding inbound buffer %d, buffer exists\n",
msg_rx_ring.rx_slot);
priv->msg_rx_ring.rx_slot);
rc = -EINVAL;
goto out;
}
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;
priv->msg_rx_ring.virt_buffer[priv->msg_rx_ring.rx_slot] = buf;
if (++priv->msg_rx_ring.rx_slot == priv->msg_rx_ring.size)
priv->msg_rx_ring.rx_slot = 0;
out:
return rc;
......@@ -691,20 +760,21 @@ EXPORT_SYMBOL_GPL(rio_hw_add_inb_buffer);
*/
void *rio_hw_get_inb_message(struct rio_mport *mport, int mbox)
{
u32 imr;
struct rio_priv *priv = mport->priv;
u32 phys_buf, virt_buf;
void *buf = NULL;
int buf_idx;
phys_buf = in_be32((void *)&msg_regs->ifqdpar);
phys_buf = in_be32(&priv->msg_regs->ifqdpar);
/* If no more messages, then bail out */
if (phys_buf == in_be32((void *)&msg_regs->ifqepar))
if (phys_buf == in_be32(&priv->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];
virt_buf = (u32) priv->msg_rx_ring.virt + (phys_buf
- priv->msg_rx_ring.phys);
buf_idx = (phys_buf - priv->msg_rx_ring.phys) / RIO_MAX_MSG_SIZE;
buf = priv->msg_rx_ring.virt_buffer[buf_idx];
if (!buf) {
printk(KERN_ERR
......@@ -716,11 +786,10 @@ void *rio_hw_get_inb_message(struct rio_mport *mport, int mbox)
memcpy(buf, (void *)virt_buf, RIO_MAX_MSG_SIZE);
/* Clear the available buffer */
msg_rx_ring.virt_buffer[buf_idx] = NULL;
priv->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);
setbits32(&priv->msg_regs->imr, RIO_MSG_IMR_MI);
out2:
return buf;
......@@ -729,7 +798,7 @@ void *rio_hw_get_inb_message(struct rio_mport *mport, int mbox)
EXPORT_SYMBOL_GPL(rio_hw_get_inb_message);
/**
* mpc85xx_rio_dbell_handler - MPC85xx doorbell interrupt handler
* fsl_rio_dbell_handler - MPC85xx doorbell interrupt handler
* @irq: Linux interrupt number
* @dev_instance: Pointer to interrupt-specific data
*
......@@ -737,31 +806,31 @@ EXPORT_SYMBOL_GPL(rio_hw_get_inb_message);
* doorbell event handlers and executes a matching event handler.
*/
static irqreturn_t
mpc85xx_rio_dbell_handler(int irq, void *dev_instance)
fsl_rio_dbell_handler(int irq, void *dev_instance)
{
int dsr;
struct rio_mport *port = (struct rio_mport *)dev_instance;
struct rio_priv *priv = port->priv;
dsr = in_be32((void *)&msg_regs->dsr);
dsr = in_be32(&priv->msg_regs->dsr);
if (dsr & DOORBELL_DSR_TE) {
pr_info("RIO: doorbell reception error\n");
out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_TE);
out_be32(&priv->msg_regs->dsr, DOORBELL_DSR_TE);
goto out;
}
if (dsr & DOORBELL_DSR_QFI) {
pr_info("RIO: doorbell queue full\n");
out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_QFI);
out_be32(&priv->msg_regs->dsr, DOORBELL_DSR_QFI);
goto out;
}
/* 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;
(u32) priv->dbell_ring.virt +
(in_be32(&priv->msg_regs->dqdpar) & 0xfff);
struct rio_dbell *dbell;
int found = 0;
......@@ -784,9 +853,8 @@ mpc85xx_rio_dbell_handler(int irq, void *dev_instance)
("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);
setbits32(&priv->msg_regs->dmr, DOORBELL_DMR_DI);
out_be32(&priv->msg_regs->dsr, DOORBELL_DSR_DIQI);
}
out:
......@@ -794,21 +862,22 @@ mpc85xx_rio_dbell_handler(int irq, void *dev_instance)
}
/**
* mpc85xx_rio_doorbell_init - MPC85xx doorbell interface init
* 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 mpc85xx_rio_setup(). Returns %0 on success
* ring. Called from fsl_rio_setup(). Returns %0 on success
* or %-ENOMEM on failure.
*/
static int mpc85xx_rio_doorbell_init(struct rio_mport *mport)
static int fsl_rio_doorbell_init(struct rio_mport *mport)
{
struct rio_priv *priv = mport->priv;
int rc = 0;
/* Map outbound doorbell window immediately after maintenance window */
if (!(dbell_win =
(u32) ioremap(mport->iores.start + RIO_MAINT_WIN_SIZE,
RIO_DBELL_WIN_SIZE))) {
priv->dbell_win = ioremap(mport->iores.start + RIO_MAINT_WIN_SIZE,
RIO_DBELL_WIN_SIZE);
if (!priv->dbell_win) {
printk(KERN_ERR
"RIO: unable to map outbound doorbell window\n");
rc = -ENOMEM;
......@@ -816,37 +885,36 @@ static int mpc85xx_rio_doorbell_init(struct rio_mport *mport)
}
/* Initialize inbound doorbells */
if (!(dbell_ring.virt = dma_alloc_coherent(NULL,
512 * DOORBELL_MESSAGE_SIZE,
&dbell_ring.phys,
GFP_KERNEL))) {
priv->dbell_ring.virt = dma_alloc_coherent(NULL, 512 *
DOORBELL_MESSAGE_SIZE, &priv->dbell_ring.phys, GFP_KERNEL);
if (!priv->dbell_ring.virt) {
printk(KERN_ERR "RIO: unable allocate inbound doorbell ring\n");
rc = -ENOMEM;
iounmap((void *)dbell_win);
iounmap(priv->dbell_win);
goto out;
}
/* 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);
out_be32(&priv->msg_regs->dqdpar, (u32) priv->dbell_ring.phys);
out_be32(&priv->msg_regs->dqepar, (u32) priv->dbell_ring.phys);
/* Clear interrupt status */
out_be32((void *)&msg_regs->dsr, 0x00000091);
out_be32(&priv->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);
rc = request_irq(IRQ_RIO_BELL(mport), fsl_rio_dbell_handler, 0,
"dbell_rx", (void *)mport);
if (rc < 0) {
iounmap(priv->dbell_win);
dma_free_coherent(NULL, 512 * DOORBELL_MESSAGE_SIZE,
dbell_ring.virt, dbell_ring.phys);
priv->dbell_ring.virt, priv->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((void *)&msg_regs->dmr, 0x00108161);
out_be32(&priv->msg_regs->dmr, 0x00108161);
out:
return rc;
......@@ -854,7 +922,7 @@ static int mpc85xx_rio_doorbell_init(struct rio_mport *mport)
static char *cmdline = NULL;
static int mpc85xx_rio_get_hdid(int index)
static int fsl_rio_get_hdid(int index)
{
/* XXX Need to parse multiple entries in some format */
if (!cmdline)
......@@ -863,7 +931,7 @@ static int mpc85xx_rio_get_hdid(int index)
return simple_strtol(cmdline, NULL, 0);
}
static int mpc85xx_rio_get_cmdline(char *s)
static int fsl_rio_get_cmdline(char *s)
{
if (!s)
return 0;
......@@ -872,61 +940,266 @@ static int mpc85xx_rio_get_cmdline(char *s)
return 1;
}
__setup("riohdid=", mpc85xx_rio_get_cmdline);
__setup("riohdid=", fsl_rio_get_cmdline);
static inline void fsl_rio_info(struct device *dev, u32 ccsr)
{
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");
}
}
/**
* mpc85xx_rio_setup - Setup MPC85xx RapidIO interface
* @law_start: Starting physical address of RapidIO LAW
* @law_size: Size of RapidIO LAW
* fsl_rio_setup - Setup MPC85xx RapidIO interface
* @fsl_rio_setup - Setup Freescale PowerPC RapidIO interface
*
* 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)
int fsl_rio_setup(struct of_device *dev)
{
struct rio_ops *ops;
struct rio_mport *port;
struct rio_priv *priv;
int rc = 0;
const u32 *dt_range, *cell;
struct resource regs;
int rlen;
u32 ccsr;
u64 law_start, law_size;
int paw, aw, sw;
if (!dev->node) {
dev_err(&dev->dev, "Device OF-Node is NULL");
return -EFAULT;
}
rc = of_address_to_resource(dev->node, 0, &regs);
if (rc) {
dev_err(&dev->dev, "Can't get %s property 'reg'\n",
dev->node->full_name);
return -EFAULT;
}
dev_info(&dev->dev, "Of-device full name %s\n", dev->node->full_name);
dev_info(&dev->dev, "Regs start 0x%08x size 0x%08x\n", regs.start,
regs.end - regs.start + 1);
dt_range = of_get_property(dev->node, "ranges", &rlen);
if (!dt_range) {
dev_err(&dev->dev, "Can't get %s property 'ranges'\n",
dev->node->full_name);
return -EFAULT;
}
/* Get node address wide */
cell = of_get_property(dev->node, "#address-cells", NULL);
if (cell)
aw = *cell;
else
aw = of_n_addr_cells(dev->node);
/* Get node size wide */
cell = of_get_property(dev->node, "#size-cells", NULL);
if (cell)
sw = *cell;
else
sw = of_n_size_cells(dev->node);
/* Get parent address wide wide */
paw = of_n_addr_cells(dev->node);
law_start = of_read_number(dt_range + aw, paw);
law_size = of_read_number(dt_range + aw + paw, sw);
dev_info(&dev->dev, "LAW start 0x%016llx, size 0x%016llx.\n",
law_start, law_size);
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;
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;
port = kmalloc(sizeof(struct rio_mport), GFP_KERNEL);
port = kzalloc(sizeof(struct rio_mport), GFP_KERNEL);
port->id = 0;
port->index = 0;
priv = kzalloc(sizeof(struct rio_priv), GFP_KERNEL);
if (!priv) {
printk(KERN_ERR "Can't alloc memory for 'priv'\n");
rc = -ENOMEM;
goto err;
}
INIT_LIST_HEAD(&port->dbells);
port->iores.start = law_start;
port->iores.end = law_start + law_size;
port->iores.flags = IORESOURCE_MEM;
priv->bellirq = irq_of_parse_and_map(dev->node, 2);
priv->txirq = irq_of_parse_and_map(dev->node, 3);
priv->rxirq = irq_of_parse_and_map(dev->node, 4);
dev_info(&dev->dev, "bellirq: %d, txirq: %d, rxirq %d\n", priv->bellirq,
priv->txirq, priv->rxirq);
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);
port->host_deviceid = fsl_rio_get_hdid(port->id);
port->priv = priv;
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);
priv->regs_win = ioremap(regs.start, regs.end - regs.start + 1);
/* Probe the master port phy type */
ccsr = in_be32(priv->regs_win + RIO_CCSR);
port->phy_type = (ccsr & 1) ? RIO_PHY_SERIAL : RIO_PHY_PARALLEL;
dev_info(&dev->dev, "RapidIO PHY type: %s\n",
(port->phy_type == RIO_PHY_PARALLEL) ? "parallel" :
((port->phy_type == RIO_PHY_SERIAL) ? "serial" :
"unknown"));
/* Checking the port training status */
if (in_be32((priv->regs_win + RIO_ESCSR)) & 1) {
dev_err(&dev->dev, "Port is not ready. "
"Try to restart connection...\n");
switch (port->phy_type) {
case RIO_PHY_SERIAL:
/* Disable ports */
out_be32(priv->regs_win + RIO_CCSR, 0);
/* Set 1x lane */
setbits32(priv->regs_win + RIO_CCSR, 0x02000000);
/* Enable ports */
setbits32(priv->regs_win + RIO_CCSR, 0x00600000);
break;
case RIO_PHY_PARALLEL:
/* Disable ports */
out_be32(priv->regs_win + RIO_CCSR, 0x22000000);
/* Enable ports */
out_be32(priv->regs_win + RIO_CCSR, 0x44000000);
break;
}
msleep(100);
if (in_be32((priv->regs_win + RIO_ESCSR)) & 1) {
dev_err(&dev->dev, "Port restart failed.\n");
rc = -ENOLINK;
goto err;
}
dev_info(&dev->dev, "Port restart success!\n");
}
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);
priv->atmu_regs = (struct rio_atmu_regs *)(priv->regs_win
+ RIO_ATMU_REGS_OFFSET);
priv->maint_atmu_regs = priv->atmu_regs + 1;
priv->dbell_atmu_regs = priv->atmu_regs + 2;
priv->msg_regs = (struct rio_msg_regs *)(priv->regs_win +
((port->phy_type == RIO_PHY_SERIAL) ?
RIO_S_MSG_REGS_OFFSET : RIO_P_MSG_REGS_OFFSET));
/* 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), RIO_ISR_AACR_AA);
/* Configure maintenance transaction window */
out_be32((void *)&maint_atmu_regs->rowbar, 0x000c0000);
out_be32((void *)&maint_atmu_regs->rowar, 0x80077015);
out_be32(&priv->maint_atmu_regs->rowbar, 0x000c0000);
out_be32(&priv->maint_atmu_regs->rowar, 0x80077015);
maint_win = (u32) ioremap(law_start, RIO_MAINT_WIN_SIZE);
priv->maint_win = 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);
out_be32(&priv->dbell_atmu_regs->rowbar, 0x000c0400);
out_be32(&priv->dbell_atmu_regs->rowar, 0x8004200b);
fsl_rio_doorbell_init(port);
return 0;
err:
if (priv)
iounmap(priv->regs_win);
kfree(ops);
kfree(priv);
kfree(port);
return rc;
}
/* The probe function for RapidIO peer-to-peer network.
*/
static int __devinit fsl_of_rio_rpn_probe(struct of_device *dev,
const struct of_device_id *match)
{
int rc;
printk(KERN_INFO "Setting up RapidIO peer-to-peer network %s\n",
dev->node->full_name);
rc = fsl_rio_setup(dev);
if (rc)
goto out;
/* Enumerate all registered ports */
rc = rio_init_mports();
out:
return rc;
};
static const struct of_device_id fsl_of_rio_rpn_ids[] = {
{
.compatible = "fsl,rapidio-delta",
},
{},
};
static struct of_platform_driver fsl_of_rio_rpn_driver = {
.name = "fsl-of-rio",
.match_table = fsl_of_rio_rpn_ids,
.probe = fsl_of_rio_rpn_probe,
};
static __init int fsl_of_rio_rpn_init(void)
{
return of_register_platform_driver(&fsl_of_rio_rpn_driver);
}
subsys_initcall(fsl_of_rio_rpn_init);
/*
* MPC85xx RapidIO definitions
*
* 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.
*/
#ifndef __PPC_SYSLIB_PPC85XX_RIO_H
#define __PPC_SYSLIB_PPC85XX_RIO_H
#include <linux/init.h>
extern void mpc85xx_rio_setup(int law_start, int law_size);
#endif /* __PPC_SYSLIB_PPC85XX_RIO_H */
......@@ -234,6 +234,14 @@ config WINDFARM_PM112
which are the recent dual and quad G5 machines using the
970MP dual-core processor.
config WINDFARM_PM121
tristate "Support for thermal management on PowerMac12,1"
depends on WINDFARM && I2C && PMAC_SMU
select I2C_POWERMAC
help
This driver provides thermal control for the PowerMac12,1
which is the iMac G5 (iSight).
config ANSLCD
tristate "Support for ANS LCD display"
depends on ADB_CUDA && PPC_PMAC
......
......@@ -42,4 +42,9 @@ obj-$(CONFIG_WINDFARM_PM112) += windfarm_pm112.o windfarm_smu_sat.o \
windfarm_smu_sensors.o \
windfarm_max6690_sensor.o \
windfarm_lm75_sensor.o windfarm_pid.o
obj-$(CONFIG_WINDFARM_PM121) += windfarm_pm121.o windfarm_smu_sat.o \
windfarm_smu_controls.o \
windfarm_smu_sensors.o \
windfarm_max6690_sensor.o \
windfarm_lm75_sensor.o windfarm_pid.o
obj-$(CONFIG_PMAC_RACKMETER) += rack-meter.o
......@@ -127,6 +127,12 @@ static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
*/
if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY"))
lm->sens.name = "hd-temp";
else if (!strcmp(loc, "Incoming Air Temp"))
lm->sens.name = "incoming-air-temp";
else if (!strcmp(loc, "ODD Temp"))
lm->sens.name = "optical-drive-temp";
else if (!strcmp(loc, "HD Temp"))
lm->sens.name = "hard-drive-temp";
else
goto fail;
......
......@@ -77,18 +77,28 @@ static struct wf_sensor_ops wf_max6690_ops = {
.owner = THIS_MODULE,
};
static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr)
static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr,
const char *loc)
{
struct wf_6690_sensor *max;
char *name = "backside-temp";
char *name;
max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
if (max == NULL) {
printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: "
"no memory\n", name);
"no memory\n", loc);
return;
}
if (!strcmp(loc, "BACKSIDE"))
name = "backside-temp";
else if (!strcmp(loc, "NB Ambient"))
name = "north-bridge-temp";
else if (!strcmp(loc, "GPU Ambient"))
name = "gpu-temp";
else
goto fail;
max->sens.ops = &wf_max6690_ops;
max->sens.name = name;
max->i2c.addr = addr >> 1;
......@@ -138,9 +148,7 @@ static int wf_max6690_attach(struct i2c_adapter *adapter)
if (loc == NULL || addr == 0)
continue;
printk("found max6690, loc=%s addr=0x%02x\n", loc, addr);
if (strcmp(loc, "BACKSIDE"))
continue;
wf_max6690_create(adapter, addr);
wf_max6690_create(adapter, addr, loc);
}
return 0;
......
/*
* Windfarm PowerMac thermal control. iMac G5 iSight
*
* (c) Copyright 2007 Étienne Bersac <bersace@gmail.com>
*
* Bits & pieces from windfarm_pm81.c by (c) Copyright 2005 Benjamin
* Herrenschmidt, IBM Corp. <benh@kernel.crashing.org>
*
* Released under the term of the GNU GPL v2.
*
*
*
* PowerMac12,1
* ============
*
*
* The algorithm used is the PID control algorithm, used the same way
* the published Darwin code does, using the same values that are
* present in the Darwin 8.10 snapshot property lists (note however
* that none of the code has been re-used, it's a complete
* re-implementation
*
* There is two models using PowerMac12,1. Model 2 is iMac G5 iSight
* 17" while Model 3 is iMac G5 20". They do have both the same
* controls with a tiny difference. The control-ids of hard-drive-fan
* and cpu-fan is swapped.
*
*
* Target Correction :
*
* controls have a target correction calculated as :
*
* new_min = ((((average_power * slope) >> 16) + offset) >> 16) + min_value
* new_value = max(new_value, max(new_min, 0))
*
* OD Fan control correction.
*
* # model_id: 2
* offset : -19563152
* slope : 1956315
*
* # model_id: 3
* offset : -15650652
* slope : 1565065
*
* HD Fan control correction.
*
* # model_id: 2
* offset : -15650652
* slope : 1565065
*
* # model_id: 3
* offset : -19563152
* slope : 1956315
*
* CPU Fan control correction.
*
* # model_id: 2
* offset : -25431900
* slope : 2543190
*
* # model_id: 3
* offset : -15650652
* slope : 1565065
*
*
* Target rubber-banding :
*
* Some controls have a target correction which depends on another
* control value. The correction is computed in the following way :
*
* new_min = ref_value * slope + offset
*
* ref_value is the value of the reference control. If new_min is
* greater than 0, then we correct the target value using :
*
* new_target = max (new_target, new_min >> 16)
*
*
* # model_id : 2
* control : cpu-fan
* ref : optical-drive-fan
* offset : -15650652
* slope : 1565065
*
* # model_id : 3
* control : optical-drive-fan
* ref : hard-drive-fan
* offset : -32768000
* slope : 65536
*
*
* In order to have the moste efficient correction with those
* dependencies, we must trigger HD loop before OD loop before CPU
* loop.
*
*
* The various control loops found in Darwin config file are:
*
* HD Fan control loop.
*
* # model_id: 2
* control : hard-drive-fan
* sensor : hard-drive-temp
* PID params : G_d = 0x00000000
* G_p = 0x002D70A3
* G_r = 0x00019999
* History = 2 entries
* Input target = 0x370000
* Interval = 5s
*
* # model_id: 3
* control : hard-drive-fan
* sensor : hard-drive-temp
* PID params : G_d = 0x00000000
* G_p = 0x002170A3
* G_r = 0x00019999
* History = 2 entries
* Input target = 0x370000
* Interval = 5s
*
* OD Fan control loop.
*
* # model_id: 2
* control : optical-drive-fan
* sensor : optical-drive-temp
* PID params : G_d = 0x00000000
* G_p = 0x001FAE14
* G_r = 0x00019999
* History = 2 entries
* Input target = 0x320000
* Interval = 5s
*
* # model_id: 3
* control : optical-drive-fan
* sensor : optical-drive-temp
* PID params : G_d = 0x00000000
* G_p = 0x001FAE14
* G_r = 0x00019999
* History = 2 entries
* Input target = 0x320000
* Interval = 5s
*
* GPU Fan control loop.
*
* # model_id: 2
* control : hard-drive-fan
* sensor : gpu-temp
* PID params : G_d = 0x00000000
* G_p = 0x002A6666
* G_r = 0x00019999
* History = 2 entries
* Input target = 0x5A0000
* Interval = 5s
*
* # model_id: 3
* control : cpu-fan
* sensor : gpu-temp
* PID params : G_d = 0x00000000
* G_p = 0x0010CCCC
* G_r = 0x00019999
* History = 2 entries
* Input target = 0x500000
* Interval = 5s
*
* KODIAK (aka northbridge) Fan control loop.
*
* # model_id: 2
* control : optical-drive-fan
* sensor : north-bridge-temp
* PID params : G_d = 0x00000000
* G_p = 0x003BD70A
* G_r = 0x00019999
* History = 2 entries
* Input target = 0x550000
* Interval = 5s
*
* # model_id: 3
* control : hard-drive-fan
* sensor : north-bridge-temp
* PID params : G_d = 0x00000000
* G_p = 0x0030F5C2
* G_r = 0x00019999
* History = 2 entries
* Input target = 0x550000
* Interval = 5s
*
* CPU Fan control loop.
*
* control : cpu-fan
* sensors : cpu-temp, cpu-power
* PID params : from SDB partition
*
*
* CPU Slew control loop.
*
* control : cpufreq-clamp
* sensor : cpu-temp
*
*/
#undef DEBUG
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/kmod.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
#include "windfarm.h"
#include "windfarm_pid.h"
#define VERSION "0.3"
static int pm121_mach_model; /* machine model id */
/* Controls & sensors */
static struct wf_sensor *sensor_cpu_power;
static struct wf_sensor *sensor_cpu_temp;
static struct wf_sensor *sensor_cpu_voltage;
static struct wf_sensor *sensor_cpu_current;
static struct wf_sensor *sensor_gpu_temp;
static struct wf_sensor *sensor_north_bridge_temp;
static struct wf_sensor *sensor_hard_drive_temp;
static struct wf_sensor *sensor_optical_drive_temp;
static struct wf_sensor *sensor_incoming_air_temp; /* unused ! */
enum {
FAN_CPU,
FAN_HD,
FAN_OD,
CPUFREQ,
N_CONTROLS
};
static struct wf_control *controls[N_CONTROLS] = {};
/* Set to kick the control loop into life */
static int pm121_all_controls_ok, pm121_all_sensors_ok, pm121_started;
enum {
FAILURE_FAN = 1 << 0,
FAILURE_SENSOR = 1 << 1,
FAILURE_OVERTEMP = 1 << 2
};
/* All sys loops. Note the HD before the OD loop in order to have it
run before. */
enum {
LOOP_GPU, /* control = hd or cpu, but luckily,
it doesn't matter */
LOOP_HD, /* control = hd */
LOOP_KODIAK, /* control = hd or od */
LOOP_OD, /* control = od */
N_LOOPS
};
static const char *loop_names[N_LOOPS] = {
"GPU",
"HD",
"KODIAK",
"OD",
};
#define PM121_NUM_CONFIGS 2
static unsigned int pm121_failure_state;
static int pm121_readjust, pm121_skipping;
static s32 average_power;
struct pm121_correction {
int offset;
int slope;
};
static struct pm121_correction corrections[N_CONTROLS][PM121_NUM_CONFIGS] = {
/* FAN_OD */
{
/* MODEL 2 */
{ .offset = -19563152,
.slope = 1956315
},
/* MODEL 3 */
{ .offset = -15650652,
.slope = 1565065
},
},
/* FAN_HD */
{
/* MODEL 2 */
{ .offset = -15650652,
.slope = 1565065
},
/* MODEL 3 */
{ .offset = -19563152,
.slope = 1956315
},
},
/* FAN_CPU */
{
/* MODEL 2 */
{ .offset = -25431900,
.slope = 2543190
},
/* MODEL 3 */
{ .offset = -15650652,
.slope = 1565065
},
},
/* CPUFREQ has no correction (and is not implemented at all) */
};
struct pm121_connection {
unsigned int control_id;
unsigned int ref_id;
struct pm121_correction correction;
};
static struct pm121_connection pm121_connections[] = {
/* MODEL 2 */
{ .control_id = FAN_CPU,
.ref_id = FAN_OD,
{ .offset = -32768000,
.slope = 65536
}
},
/* MODEL 3 */
{ .control_id = FAN_OD,
.ref_id = FAN_HD,
{ .offset = -32768000,
.slope = 65536
}
},
};
/* pointer to the current model connection */
static struct pm121_connection *pm121_connection;
/*
* ****** System Fans Control Loop ******
*
*/
/* Since each loop handles only one control and we want to avoid
* writing virtual control, we store the control correction with the
* loop params. Some data are not set, there are common to all loop
* and thus, hardcoded.
*/
struct pm121_sys_param {
/* purely informative since we use mach_model-2 as index */
int model_id;
struct wf_sensor **sensor; /* use sensor_id instead ? */
s32 gp, itarget;
unsigned int control_id;
};
static struct pm121_sys_param
pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] = {
/* GPU Fan control loop */
{
{ .model_id = 2,
.sensor = &sensor_gpu_temp,
.gp = 0x002A6666,
.itarget = 0x5A0000,
.control_id = FAN_HD,
},
{ .model_id = 3,
.sensor = &sensor_gpu_temp,
.gp = 0x0010CCCC,
.itarget = 0x500000,
.control_id = FAN_CPU,
},
},
/* HD Fan control loop */
{
{ .model_id = 2,
.sensor = &sensor_hard_drive_temp,
.gp = 0x002D70A3,
.itarget = 0x370000,
.control_id = FAN_HD,
},
{ .model_id = 3,
.sensor = &sensor_hard_drive_temp,
.gp = 0x002170A3,
.itarget = 0x370000,
.control_id = FAN_HD,
},
},
/* KODIAK Fan control loop */
{
{ .model_id = 2,
.sensor = &sensor_north_bridge_temp,
.gp = 0x003BD70A,
.itarget = 0x550000,
.control_id = FAN_OD,
},
{ .model_id = 3,
.sensor = &sensor_north_bridge_temp,
.gp = 0x0030F5C2,
.itarget = 0x550000,
.control_id = FAN_HD,
},
},
/* OD Fan control loop */
{
{ .model_id = 2,
.sensor = &sensor_optical_drive_temp,
.gp = 0x001FAE14,
.itarget = 0x320000,
.control_id = FAN_OD,
},
{ .model_id = 3,
.sensor = &sensor_optical_drive_temp,
.gp = 0x001FAE14,
.itarget = 0x320000,
.control_id = FAN_OD,
},
},
};
/* the hardcoded values */
#define PM121_SYS_GD 0x00000000
#define PM121_SYS_GR 0x00019999
#define PM121_SYS_HISTORY_SIZE 2
#define PM121_SYS_INTERVAL 5
/* State data used by the system fans control loop
*/
struct pm121_sys_state {
int ticks;
s32 setpoint;
struct wf_pid_state pid;
};
struct pm121_sys_state *pm121_sys_state[N_LOOPS] = {};
/*
* ****** CPU Fans Control Loop ******
*
*/
#define PM121_CPU_INTERVAL 1
/* State data used by the cpu fans control loop
*/
struct pm121_cpu_state {
int ticks;
s32 setpoint;
struct wf_cpu_pid_state pid;
};
static struct pm121_cpu_state *pm121_cpu_state;
/*
* ***** Implementation *****
*
*/
/* correction the value using the output-low-bound correction algo */
static s32 pm121_correct(s32 new_setpoint,
unsigned int control_id,
s32 min)
{
s32 new_min;
struct pm121_correction *correction;
correction = &corrections[control_id][pm121_mach_model - 2];
new_min = (average_power * correction->slope) >> 16;
new_min += correction->offset;
new_min = (new_min >> 16) + min;
return max(new_setpoint, max(new_min, 0));
}
static s32 pm121_connect(unsigned int control_id, s32 setpoint)
{
s32 new_min, value, new_setpoint;
if (pm121_connection->control_id == control_id) {
controls[control_id]->ops->get_value(controls[control_id],
&value);
new_min = value * pm121_connection->correction.slope;
new_min += pm121_connection->correction.offset;
if (new_min > 0) {
new_setpoint = max(setpoint, (new_min >> 16));
if (new_setpoint != setpoint) {
pr_debug("pm121: %s depending on %s, "
"corrected from %d to %d RPM\n",
controls[control_id]->name,
controls[pm121_connection->ref_id]->name,
(int) setpoint, (int) new_setpoint);
}
} else
new_setpoint = setpoint;
}
/* no connection */
else
new_setpoint = setpoint;
return new_setpoint;
}
/* FAN LOOPS */
static void pm121_create_sys_fans(int loop_id)
{
struct pm121_sys_param *param = NULL;
struct wf_pid_param pid_param;
struct wf_control *control = NULL;
int i;
/* First, locate the params for this model */
for (i = 0; i < PM121_NUM_CONFIGS; i++) {
if (pm121_sys_all_params[loop_id][i].model_id == pm121_mach_model) {
param = &(pm121_sys_all_params[loop_id][i]);
break;
}
}
/* No params found, put fans to max */
if (param == NULL) {
printk(KERN_WARNING "pm121: %s fan config not found "
" for this machine model\n",
loop_names[loop_id]);
goto fail;
}
control = controls[param->control_id];
/* Alloc & initialize state */
pm121_sys_state[loop_id] = kmalloc(sizeof(struct pm121_sys_state),
GFP_KERNEL);
if (pm121_sys_state[loop_id] == NULL) {
printk(KERN_WARNING "pm121: Memory allocation error\n");
goto fail;
}
pm121_sys_state[loop_id]->ticks = 1;
/* Fill PID params */
pid_param.gd = PM121_SYS_GD;
pid_param.gp = param->gp;
pid_param.gr = PM121_SYS_GR;
pid_param.interval = PM121_SYS_INTERVAL;
pid_param.history_len = PM121_SYS_HISTORY_SIZE;
pid_param.itarget = param->itarget;
pid_param.min = control->ops->get_min(control);
pid_param.max = control->ops->get_max(control);
wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param);
pr_debug("pm121: %s Fan control loop initialized.\n"
" itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
loop_names[loop_id], FIX32TOPRINT(pid_param.itarget),
pid_param.min, pid_param.max);
return;
fail:
/* note that this is not optimal since another loop may still
control the same control */
printk(KERN_WARNING "pm121: failed to set up %s loop "
"setting \"%s\" to max speed.\n",
loop_names[loop_id], control->name);
if (control)
wf_control_set_max(control);
}
static void pm121_sys_fans_tick(int loop_id)
{
struct pm121_sys_param *param;
struct pm121_sys_state *st;
struct wf_sensor *sensor;
struct wf_control *control;
s32 temp, new_setpoint;
int rc;
param = &(pm121_sys_all_params[loop_id][pm121_mach_model-2]);
st = pm121_sys_state[loop_id];
sensor = *(param->sensor);
control = controls[param->control_id];
if (--st->ticks != 0) {
if (pm121_readjust)
goto readjust;
return;
}
st->ticks = PM121_SYS_INTERVAL;
rc = sensor->ops->get_value(sensor, &temp);
if (rc) {
printk(KERN_WARNING "windfarm: %s sensor error %d\n",
sensor->name, rc);
pm121_failure_state |= FAILURE_SENSOR;
return;
}
pr_debug("pm121: %s Fan tick ! %s: %d.%03d\n",
loop_names[loop_id], sensor->name,
FIX32TOPRINT(temp));
new_setpoint = wf_pid_run(&st->pid, temp);
/* correction */
new_setpoint = pm121_correct(new_setpoint,
param->control_id,
st->pid.param.min);
/* linked corretion */
new_setpoint = pm121_connect(param->control_id, new_setpoint);
if (new_setpoint == st->setpoint)
return;
st->setpoint = new_setpoint;
pr_debug("pm121: %s corrected setpoint: %d RPM\n",
control->name, (int)new_setpoint);
readjust:
if (control && pm121_failure_state == 0) {
rc = control->ops->set_value(control, st->setpoint);
if (rc) {
printk(KERN_WARNING "windfarm: %s fan error %d\n",
control->name, rc);
pm121_failure_state |= FAILURE_FAN;
}
}
}
/* CPU LOOP */
static void pm121_create_cpu_fans(void)
{
struct wf_cpu_pid_param pid_param;
const struct smu_sdbp_header *hdr;
struct smu_sdbp_cpupiddata *piddata;
struct smu_sdbp_fvt *fvt;
struct wf_control *fan_cpu;
s32 tmax, tdelta, maxpow, powadj;
fan_cpu = controls[FAN_CPU];
/* First, locate the PID params in SMU SBD */
hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
if (hdr == 0) {
printk(KERN_WARNING "pm121: CPU PID fan config not found.\n");
goto fail;
}
piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
/* Get the FVT params for operating point 0 (the only supported one
* for now) in order to get tmax
*/
hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
if (hdr) {
fvt = (struct smu_sdbp_fvt *)&hdr[1];
tmax = ((s32)fvt->maxtemp) << 16;
} else
tmax = 0x5e0000; /* 94 degree default */
/* Alloc & initialize state */
pm121_cpu_state = kmalloc(sizeof(struct pm121_cpu_state),
GFP_KERNEL);
if (pm121_cpu_state == NULL)
goto fail;
pm121_cpu_state->ticks = 1;
/* Fill PID params */
pid_param.interval = PM121_CPU_INTERVAL;
pid_param.history_len = piddata->history_len;
if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
printk(KERN_WARNING "pm121: History size overflow on "
"CPU control loop (%d)\n", piddata->history_len);
pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
}
pid_param.gd = piddata->gd;
pid_param.gp = piddata->gp;
pid_param.gr = piddata->gr / pid_param.history_len;
tdelta = ((s32)piddata->target_temp_delta) << 16;
maxpow = ((s32)piddata->max_power) << 16;
powadj = ((s32)piddata->power_adj) << 16;
pid_param.tmax = tmax;
pid_param.ttarget = tmax - tdelta;
pid_param.pmaxadj = maxpow - powadj;
pid_param.min = fan_cpu->ops->get_min(fan_cpu);
pid_param.max = fan_cpu->ops->get_max(fan_cpu);
wf_cpu_pid_init(&pm121_cpu_state->pid, &pid_param);
pr_debug("pm121: CPU Fan control initialized.\n");
pr_debug(" ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM,\n",
FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
pid_param.min, pid_param.max);
return;
fail:
printk(KERN_WARNING "pm121: CPU fan config not found, max fan speed\n");
if (controls[CPUFREQ])
wf_control_set_max(controls[CPUFREQ]);
if (fan_cpu)
wf_control_set_max(fan_cpu);
}
static void pm121_cpu_fans_tick(struct pm121_cpu_state *st)
{
s32 new_setpoint, temp, power;
struct wf_control *fan_cpu = NULL;
int rc;
if (--st->ticks != 0) {
if (pm121_readjust)
goto readjust;
return;
}
st->ticks = PM121_CPU_INTERVAL;
fan_cpu = controls[FAN_CPU];
rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
if (rc) {
printk(KERN_WARNING "pm121: CPU temp sensor error %d\n",
rc);
pm121_failure_state |= FAILURE_SENSOR;
return;
}
rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
if (rc) {
printk(KERN_WARNING "pm121: CPU power sensor error %d\n",
rc);
pm121_failure_state |= FAILURE_SENSOR;
return;
}
pr_debug("pm121: CPU Fans tick ! CPU temp: %d.%03d°C, power: %d.%03d\n",
FIX32TOPRINT(temp), FIX32TOPRINT(power));
if (temp > st->pid.param.tmax)
pm121_failure_state |= FAILURE_OVERTEMP;
new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
/* correction */
new_setpoint = pm121_correct(new_setpoint,
FAN_CPU,
st->pid.param.min);
/* connected correction */
new_setpoint = pm121_connect(FAN_CPU, new_setpoint);
if (st->setpoint == new_setpoint)
return;
st->setpoint = new_setpoint;
pr_debug("pm121: CPU corrected setpoint: %d RPM\n", (int)new_setpoint);
readjust:
if (fan_cpu && pm121_failure_state == 0) {
rc = fan_cpu->ops->set_value(fan_cpu, st->setpoint);
if (rc) {
printk(KERN_WARNING "pm121: %s fan error %d\n",
fan_cpu->name, rc);
pm121_failure_state |= FAILURE_FAN;
}
}
}
/*
* ****** Common ******
*
*/
static void pm121_tick(void)
{
unsigned int last_failure = pm121_failure_state;
unsigned int new_failure;
s32 total_power;
int i;
if (!pm121_started) {
pr_debug("pm121: creating control loops !\n");
for (i = 0; i < N_LOOPS; i++)
pm121_create_sys_fans(i);
pm121_create_cpu_fans();
pm121_started = 1;
}
/* skipping ticks */
if (pm121_skipping && --pm121_skipping)
return;
/* compute average power */
total_power = 0;
for (i = 0; i < pm121_cpu_state->pid.param.history_len; i++)
total_power += pm121_cpu_state->pid.powers[i];
average_power = total_power / pm121_cpu_state->pid.param.history_len;
pm121_failure_state = 0;
for (i = 0 ; i < N_LOOPS; i++) {
if (pm121_sys_state[i])
pm121_sys_fans_tick(i);
}
if (pm121_cpu_state)
pm121_cpu_fans_tick(pm121_cpu_state);
pm121_readjust = 0;
new_failure = pm121_failure_state & ~last_failure;
/* If entering failure mode, clamp cpufreq and ramp all
* fans to full speed.
*/
if (pm121_failure_state && !last_failure) {
for (i = 0; i < N_CONTROLS; i++) {
if (controls[i])
wf_control_set_max(controls[i]);
}
}
/* If leaving failure mode, unclamp cpufreq and readjust
* all fans on next iteration
*/
if (!pm121_failure_state && last_failure) {
if (controls[CPUFREQ])
wf_control_set_min(controls[CPUFREQ]);
pm121_readjust = 1;
}
/* Overtemp condition detected, notify and start skipping a couple
* ticks to let the temperature go down
*/
if (new_failure & FAILURE_OVERTEMP) {
wf_set_overtemp();
pm121_skipping = 2;
}
/* We only clear the overtemp condition if overtemp is cleared
* _and_ no other failure is present. Since a sensor error will
* clear the overtemp condition (can't measure temperature) at
* the control loop levels, but we don't want to keep it clear
* here in this case
*/
if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
wf_clear_overtemp();
}
static struct wf_control* pm121_register_control(struct wf_control *ct,
const char *match,
unsigned int id)
{
if (controls[id] == NULL && !strcmp(ct->name, match)) {
if (wf_get_control(ct) == 0)
controls[id] = ct;
}
return controls[id];
}
static void pm121_new_control(struct wf_control *ct)
{
int all = 1;
if (pm121_all_controls_ok)
return;
all = pm121_register_control(ct, "optical-drive-fan", FAN_OD) && all;
all = pm121_register_control(ct, "hard-drive-fan", FAN_HD) && all;
all = pm121_register_control(ct, "cpu-fan", FAN_CPU) && all;
all = pm121_register_control(ct, "cpufreq-clamp", CPUFREQ) && all;
if (all)
pm121_all_controls_ok = 1;
}
static struct wf_sensor* pm121_register_sensor(struct wf_sensor *sensor,
const char *match,
struct wf_sensor **var)
{
if (*var == NULL && !strcmp(sensor->name, match)) {
if (wf_get_sensor(sensor) == 0)
*var = sensor;
}
return *var;
}
static void pm121_new_sensor(struct wf_sensor *sr)
{
int all = 1;
if (pm121_all_sensors_ok)
return;
all = pm121_register_sensor(sr, "cpu-temp",
&sensor_cpu_temp) && all;
all = pm121_register_sensor(sr, "cpu-current",
&sensor_cpu_current) && all;
all = pm121_register_sensor(sr, "cpu-voltage",
&sensor_cpu_voltage) && all;
all = pm121_register_sensor(sr, "cpu-power",
&sensor_cpu_power) && all;
all = pm121_register_sensor(sr, "hard-drive-temp",
&sensor_hard_drive_temp) && all;
all = pm121_register_sensor(sr, "optical-drive-temp",
&sensor_optical_drive_temp) && all;
all = pm121_register_sensor(sr, "incoming-air-temp",
&sensor_incoming_air_temp) && all;
all = pm121_register_sensor(sr, "north-bridge-temp",
&sensor_north_bridge_temp) && all;
all = pm121_register_sensor(sr, "gpu-temp",
&sensor_gpu_temp) && all;
if (all)
pm121_all_sensors_ok = 1;
}
static int pm121_notify(struct notifier_block *self,
unsigned long event, void *data)
{
switch (event) {
case WF_EVENT_NEW_CONTROL:
pr_debug("pm121: new control %s detected\n",
((struct wf_control *)data)->name);
pm121_new_control(data);
break;
case WF_EVENT_NEW_SENSOR:
pr_debug("pm121: new sensor %s detected\n",
((struct wf_sensor *)data)->name);
pm121_new_sensor(data);
break;
case WF_EVENT_TICK:
if (pm121_all_controls_ok && pm121_all_sensors_ok)
pm121_tick();
break;
}
return 0;
}
static struct notifier_block pm121_events = {
.notifier_call = pm121_notify,
};
static int pm121_init_pm(void)
{
const struct smu_sdbp_header *hdr;
hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
if (hdr != 0) {
struct smu_sdbp_sensortree *st =
(struct smu_sdbp_sensortree *)&hdr[1];
pm121_mach_model = st->model_id;
}
pm121_connection = &pm121_connections[pm121_mach_model - 2];
printk(KERN_INFO "pm121: Initializing for iMac G5 iSight model ID %d\n",
pm121_mach_model);
return 0;
}
static int pm121_probe(struct platform_device *ddev)
{
wf_register_client(&pm121_events);
return 0;
}
static int __devexit pm121_remove(struct platform_device *ddev)
{
wf_unregister_client(&pm121_events);
return 0;
}
static struct platform_driver pm121_driver = {
.probe = pm121_probe,
.remove = __devexit_p(pm121_remove),
.driver = {
.name = "windfarm",
.bus = &platform_bus_type,
},
};
static int __init pm121_init(void)
{
int rc = -ENODEV;
if (machine_is_compatible("PowerMac12,1"))
rc = pm121_init_pm();
if (rc == 0) {
request_module("windfarm_smu_controls");
request_module("windfarm_smu_sensors");
request_module("windfarm_smu_sat");
request_module("windfarm_lm75_sensor");
request_module("windfarm_max6690_sensor");
request_module("windfarm_cpufreq_clamp");
platform_driver_register(&pm121_driver);
}
return rc;
}
static void __exit pm121_exit(void)
{
platform_driver_unregister(&pm121_driver);
}
module_init(pm121_init);
module_exit(pm121_exit);
MODULE_AUTHOR("Étienne Bersac <bersace@gmail.com>");
MODULE_DESCRIPTION("Thermal control logic for iMac G5 (iSight)");
MODULE_LICENSE("GPL");
......@@ -218,6 +218,10 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
fct->ctrl.name = "cpu-fan";
else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
fct->ctrl.name = "drive-bay-fan";
else if (!strcmp(l, "HDD Fan")) /* seen on iMac G5 iSight */
fct->ctrl.name = "hard-drive-fan";
else if (!strcmp(l, "ODD Fan")) /* same */
fct->ctrl.name = "optical-drive-fan";
/* Unrecognized fan, bail out */
if (fct->ctrl.name == NULL)
......
......@@ -77,7 +77,7 @@ static int rionet_capable = 1;
* could be made into a hash table to save memory depending
* on system trade-offs.
*/
static struct rio_dev *rionet_active[RIO_MAX_ROUTE_ENTRIES];
static struct rio_dev **rionet_active;
#define is_rionet_capable(pef, src_ops, dst_ops) \
((pef & RIO_PEF_INB_MBOX) && \
......@@ -195,7 +195,8 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
}
if (eth->h_dest[0] & 0x01) {
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES; i++)
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size);
i++)
if (rionet_active[i])
rionet_queue_tx_msg(skb, ndev,
rionet_active[i]);
......@@ -385,6 +386,8 @@ static void rionet_remove(struct rio_dev *rdev)
struct net_device *ndev = NULL;
struct rionet_peer *peer, *tmp;
free_pages((unsigned long)rionet_active, rdev->net->hport->sys_size ?
__ilog2(sizeof(void *)) + 4 : 0);
unregister_netdev(ndev);
kfree(ndev);
......@@ -443,6 +446,15 @@ static int rionet_setup_netdev(struct rio_mport *mport)
goto out;
}
rionet_active = (struct rio_dev **)__get_free_pages(GFP_KERNEL,
mport->sys_size ? __ilog2(sizeof(void *)) + 4 : 0);
if (!rionet_active) {
rc = -ENOMEM;
goto out;
}
memset((void *)rionet_active, 0, sizeof(void *) *
RIO_MAX_ROUTE_ENTRIES(mport->sys_size));
/* Set up private area */
rnet = (struct rionet_private *)ndev->priv;
rnet->mport = mport;
......
#
# RapidIO configuration
#
config RAPIDIO_8_BIT_TRANSPORT
bool "8-bit transport addressing"
depends on RAPIDIO
---help---
By default, the kernel assumes a 16-bit addressed RapidIO
network. By selecting this option, the kernel will support
an 8-bit addressed network.
config RAPIDIO_DISC_TIMEOUT
int "Discovery timeout duration (seconds)"
depends on RAPIDIO
......
......@@ -48,7 +48,7 @@ int __rio_local_read_config_##size \
u32 data = 0; \
if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
spin_lock_irqsave(&rio_config_lock, flags); \
res = mport->ops->lcread(mport->id, offset, len, &data); \
res = mport->ops->lcread(mport, mport->id, offset, len, &data); \
*value = (type)data; \
spin_unlock_irqrestore(&rio_config_lock, flags); \
return res; \
......@@ -71,7 +71,7 @@ int __rio_local_write_config_##size \
unsigned long flags; \
if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
spin_lock_irqsave(&rio_config_lock, flags); \
res = mport->ops->lcwrite(mport->id, offset, len, value); \
res = mport->ops->lcwrite(mport, mport->id, offset, len, value);\
spin_unlock_irqrestore(&rio_config_lock, flags); \
return res; \
}
......@@ -108,7 +108,7 @@ int rio_mport_read_config_##size \
u32 data = 0; \
if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
spin_lock_irqsave(&rio_config_lock, flags); \
res = mport->ops->cread(mport->id, destid, hopcount, offset, len, &data); \
res = mport->ops->cread(mport, mport->id, destid, hopcount, offset, len, &data); \
*value = (type)data; \
spin_unlock_irqrestore(&rio_config_lock, flags); \
return res; \
......@@ -131,7 +131,7 @@ int rio_mport_write_config_##size \
unsigned long flags; \
if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
spin_lock_irqsave(&rio_config_lock, flags); \
res = mport->ops->cwrite(mport->id, destid, hopcount, offset, len, value); \
res = mport->ops->cwrite(mport, mport->id, destid, hopcount, offset, len, value); \
spin_unlock_irqrestore(&rio_config_lock, flags); \
return res; \
}
......@@ -166,7 +166,7 @@ int rio_mport_send_doorbell(struct rio_mport *mport, u16 destid, u16 data)
unsigned long flags;
spin_lock_irqsave(&rio_doorbell_lock, flags);
res = mport->ops->dsend(mport->id, destid, data);
res = mport->ops->dsend(mport, mport->id, destid, data);
spin_unlock_irqrestore(&rio_doorbell_lock, flags);
return res;
......
......@@ -73,7 +73,7 @@ static u16 rio_get_device_id(struct rio_mport *port, u16 destid, u8 hopcount)
rio_mport_read_config_32(port, destid, hopcount, RIO_DID_CSR, &result);
return RIO_GET_DID(result);
return RIO_GET_DID(port->sys_size, result);
}
/**
......@@ -88,7 +88,7 @@ static u16 rio_get_device_id(struct rio_mport *port, u16 destid, u8 hopcount)
static void rio_set_device_id(struct rio_mport *port, u16 destid, u8 hopcount, u16 did)
{
rio_mport_write_config_32(port, destid, hopcount, RIO_DID_CSR,
RIO_SET_DID(did));
RIO_SET_DID(port->sys_size, did));
}
/**
......@@ -100,7 +100,8 @@ static void rio_set_device_id(struct rio_mport *port, u16 destid, u8 hopcount, u
*/
static void rio_local_set_device_id(struct rio_mport *port, u16 did)
{
rio_local_write_config_32(port, RIO_DID_CSR, RIO_SET_DID(did));
rio_local_write_config_32(port, RIO_DID_CSR, RIO_SET_DID(port->sys_size,
did));
}
/**
......@@ -350,8 +351,18 @@ static struct rio_dev *rio_setup_device(struct rio_net *net,
rswitch->switchid = next_switchid;
rswitch->hopcount = hopcount;
rswitch->destid = destid;
rswitch->route_table = kzalloc(sizeof(u8)*
RIO_MAX_ROUTE_ENTRIES(port->sys_size),
GFP_KERNEL);
if (!rswitch->route_table) {
kfree(rdev);
rdev = NULL;
kfree(rswitch);
goto out;
}
/* Initialize switch route table */
for (rdid = 0; rdid < RIO_MAX_ROUTE_ENTRIES; rdid++)
for (rdid = 0; rdid < RIO_MAX_ROUTE_ENTRIES(port->sys_size);
rdid++)
rswitch->route_table[rdid] = RIO_INVALID_ROUTE;
rdev->rswitch = rswitch;
sprintf(rio_name(rdev), "%02x:s:%04x", rdev->net->id,
......@@ -480,7 +491,7 @@ static u16 rio_get_host_deviceid_lock(struct rio_mport *port, u8 hopcount)
{
u32 result;
rio_mport_read_config_32(port, RIO_ANY_DESTID, hopcount,
rio_mport_read_config_32(port, RIO_ANY_DESTID(port->sys_size), hopcount,
RIO_HOST_DID_LOCK_CSR, &result);
return (u16) (result & 0xffff);
......@@ -571,14 +582,16 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
}
/* Attempt to acquire device lock */
rio_mport_write_config_32(port, RIO_ANY_DESTID, hopcount,
rio_mport_write_config_32(port, RIO_ANY_DESTID(port->sys_size),
hopcount,
RIO_HOST_DID_LOCK_CSR, port->host_deviceid);
while ((tmp = rio_get_host_deviceid_lock(port, hopcount))
< port->host_deviceid) {
/* Delay a bit */
mdelay(1);
/* Attempt to acquire device lock again */
rio_mport_write_config_32(port, RIO_ANY_DESTID, hopcount,
rio_mport_write_config_32(port, RIO_ANY_DESTID(port->sys_size),
hopcount,
RIO_HOST_DID_LOCK_CSR,
port->host_deviceid);
}
......@@ -590,7 +603,9 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
}
/* Setup new RIO device */
if ((rdev = rio_setup_device(net, port, RIO_ANY_DESTID, hopcount, 1))) {
rdev = rio_setup_device(net, port, RIO_ANY_DESTID(port->sys_size),
hopcount, 1);
if (rdev) {
/* Add device to the global and bus/net specific list. */
list_add_tail(&rdev->net_list, &net->devices);
} else
......@@ -598,7 +613,8 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
if (rio_is_switch(rdev)) {
next_switchid++;
sw_inport = rio_get_swpinfo_inport(port, RIO_ANY_DESTID, hopcount);
sw_inport = rio_get_swpinfo_inport(port,
RIO_ANY_DESTID(port->sys_size), hopcount);
rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE,
port->host_deviceid, sw_inport);
rdev->rswitch->route_table[port->host_deviceid] = sw_inport;
......@@ -612,7 +628,8 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
}
num_ports =
rio_get_swpinfo_tports(port, RIO_ANY_DESTID, hopcount);
rio_get_swpinfo_tports(port, RIO_ANY_DESTID(port->sys_size),
hopcount);
pr_debug(
"RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
rio_name(rdev), rdev->vid, rdev->did, num_ports);
......@@ -624,13 +641,15 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
cur_destid = next_destid;
if (rio_sport_is_active
(port, RIO_ANY_DESTID, hopcount, port_num)) {
(port, RIO_ANY_DESTID(port->sys_size), hopcount,
port_num)) {
pr_debug(
"RIO: scanning device on port %d\n",
port_num);
rio_route_add_entry(port, rdev->rswitch,
RIO_GLOBAL_TABLE,
RIO_ANY_DESTID, port_num);
RIO_GLOBAL_TABLE,
RIO_ANY_DESTID(port->sys_size),
port_num);
if (rio_enum_peer(net, port, hopcount + 1) < 0)
return -1;
......@@ -735,7 +754,8 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
pr_debug(
"RIO: scanning device on port %d\n",
port_num);
for (ndestid = 0; ndestid < RIO_ANY_DESTID;
for (ndestid = 0;
ndestid < RIO_ANY_DESTID(port->sys_size);
ndestid++) {
rio_route_get_entry(port, rdev->rswitch,
RIO_GLOBAL_TABLE,
......@@ -917,7 +937,9 @@ static void rio_build_route_tables(void)
list_for_each_entry(rdev, &rio_devices, global_list)
if (rio_is_switch(rdev))
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES; i++) {
for (i = 0;
i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size);
i++) {
if (rio_route_get_entry
(rdev->net->hport, rdev->rswitch, RIO_GLOBAL_TABLE,
i, &sport) < 0)
......@@ -981,7 +1003,8 @@ int rio_disc_mport(struct rio_mport *mport)
del_timer_sync(&rio_enum_timer);
pr_debug("done\n");
if (rio_disc_peer(net, mport, RIO_ANY_DESTID, 0) < 0) {
if (rio_disc_peer(net, mport, RIO_ANY_DESTID(mport->sys_size),
0) < 0) {
printk(KERN_INFO
"RIO: master port %d device has failed discovery\n",
mport->id);
......
......@@ -43,7 +43,8 @@ static ssize_t routes_show(struct device *dev, struct device_attribute *attr, ch
if (!rdev->rswitch)
goto out;
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES; i++) {
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size);
i++) {
if (rdev->rswitch->route_table[i] == RIO_INVALID_ROUTE)
continue;
str +=
......
......@@ -43,7 +43,7 @@ u16 rio_local_get_device_id(struct rio_mport *port)
rio_local_read_config_32(port, RIO_DID_CSR, &result);
return (RIO_GET_DID(result));
return (RIO_GET_DID(port->sys_size, result));
}
/**
......
......@@ -51,10 +51,5 @@ extern struct rio_route_ops __end_rio_route_ops[];
DECLARE_RIO_ROUTE_SECTION(.rio_route_ops, \
vid, did, add_hook, get_hook)
#ifdef CONFIG_RAPIDIO_8_BIT_TRANSPORT
#define RIO_GET_DID(x) ((x & 0x00ff0000) >> 16)
#define RIO_SET_DID(x) ((x & 0x000000ff) << 16)
#else
#define RIO_GET_DID(x) (x & 0xffff)
#define RIO_SET_DID(x) (x & 0xffff)
#endif
#define RIO_GET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x00ff0000) >> 16))
#define RIO_SET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x000000ff) << 16))
......@@ -138,6 +138,8 @@ typedef struct {
struct thread_struct {
unsigned long ksp; /* Kernel stack pointer */
unsigned long ksp_limit; /* if ksp <= ksp_limit stack overflow */
#ifdef CONFIG_PPC64
unsigned long ksp_vsid;
#endif
......@@ -182,11 +184,14 @@ struct thread_struct {
#define ARCH_MIN_TASKALIGN 16
#define INIT_SP (sizeof(init_stack) + (unsigned long) &init_stack)
#define INIT_SP_LIMIT \
(_ALIGN_UP(sizeof(init_thread_info), 16) + (unsigned long) &init_stack)
#ifdef CONFIG_PPC32
#define INIT_THREAD { \
.ksp = INIT_SP, \
.ksp_limit = INIT_SP_LIMIT, \
.fs = KERNEL_DS, \
.pgdir = swapper_pg_dir, \
.fpexc_mode = MSR_FE0 | MSR_FE1, \
......@@ -194,6 +199,7 @@ struct thread_struct {
#else
#define INIT_THREAD { \
.ksp = INIT_SP, \
.ksp_limit = INIT_SP_LIMIT, \
.regs = (struct pt_regs *)INIT_SP - 1, /* XXX bogus, I think */ \
.fs = KERNEL_DS, \
.fpr = {0}, \
......
......@@ -204,7 +204,7 @@ extern int powersave_nap; /* set if nap mode can be used in idle loop */
* Changes the memory location '*ptr' to be val and returns
* the previous value stored there.
*/
static __inline__ unsigned long
static __always_inline unsigned long
__xchg_u32(volatile void *p, unsigned long val)
{
unsigned long prev;
......@@ -229,7 +229,7 @@ __xchg_u32(volatile void *p, unsigned long val)
* Changes the memory location '*ptr' to be val and returns
* the previous value stored there.
*/
static __inline__ unsigned long
static __always_inline unsigned long
__xchg_u32_local(volatile void *p, unsigned long val)
{
unsigned long prev;
......@@ -247,7 +247,7 @@ __xchg_u32_local(volatile void *p, unsigned long val)
}
#ifdef CONFIG_PPC64
static __inline__ unsigned long
static __always_inline unsigned long
__xchg_u64(volatile void *p, unsigned long val)
{
unsigned long prev;
......@@ -266,7 +266,7 @@ __xchg_u64(volatile void *p, unsigned long val)
return prev;
}
static __inline__ unsigned long
static __always_inline unsigned long
__xchg_u64_local(volatile void *p, unsigned long val)
{
unsigned long prev;
......@@ -290,7 +290,7 @@ __xchg_u64_local(volatile void *p, unsigned long val)
*/
extern void __xchg_called_with_bad_pointer(void);
static __inline__ unsigned long
static __always_inline unsigned long
__xchg(volatile void *ptr, unsigned long x, unsigned int size)
{
switch (size) {
......@@ -305,7 +305,7 @@ __xchg(volatile void *ptr, unsigned long x, unsigned int size)
return x;
}
static __inline__ unsigned long
static __always_inline unsigned long
__xchg_local(volatile void *ptr, unsigned long x, unsigned int size)
{
switch (size) {
......@@ -338,7 +338,7 @@ __xchg_local(volatile void *ptr, unsigned long x, unsigned int size)
*/
#define __HAVE_ARCH_CMPXCHG 1
static __inline__ unsigned long
static __always_inline unsigned long
__cmpxchg_u32(volatile unsigned int *p, unsigned long old, unsigned long new)
{
unsigned int prev;
......@@ -361,7 +361,7 @@ __cmpxchg_u32(volatile unsigned int *p, unsigned long old, unsigned long new)
return prev;
}
static __inline__ unsigned long
static __always_inline unsigned long
__cmpxchg_u32_local(volatile unsigned int *p, unsigned long old,
unsigned long new)
{
......@@ -384,7 +384,7 @@ __cmpxchg_u32_local(volatile unsigned int *p, unsigned long old,
}
#ifdef CONFIG_PPC64
static __inline__ unsigned long
static __always_inline unsigned long
__cmpxchg_u64(volatile unsigned long *p, unsigned long old, unsigned long new)
{
unsigned long prev;
......@@ -406,7 +406,7 @@ __cmpxchg_u64(volatile unsigned long *p, unsigned long old, unsigned long new)
return prev;
}
static __inline__ unsigned long
static __always_inline unsigned long
__cmpxchg_u64_local(volatile unsigned long *p, unsigned long old,
unsigned long new)
{
......@@ -432,7 +432,7 @@ __cmpxchg_u64_local(volatile unsigned long *p, unsigned long old,
if something tries to do an invalid cmpxchg(). */
extern void __cmpxchg_called_with_bad_pointer(void);
static __inline__ unsigned long
static __always_inline unsigned long
__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new,
unsigned int size)
{
......@@ -448,7 +448,7 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new,
return old;
}
static __inline__ unsigned long
static __always_inline unsigned long
__cmpxchg_local(volatile void *ptr, unsigned long old, unsigned long new,
unsigned int size)
{
......
......@@ -40,7 +40,8 @@ extern struct lmb lmb;
extern void __init lmb_init(void);
extern void __init lmb_analyze(void);
extern long __init lmb_add(u64 base, u64 size);
extern long lmb_add(u64 base, u64 size);
extern long lmb_remove(u64 base, u64 size);
extern long __init lmb_reserve(u64 base, u64 size);
extern u64 __init lmb_alloc_nid(u64 size, u64 align, int nid,
u64 (*nid_range)(u64, u64, int *));
......@@ -53,6 +54,7 @@ extern u64 __init lmb_phys_mem_size(void);
extern u64 __init lmb_end_of_DRAM(void);
extern void __init lmb_enforce_memory_limit(u64 memory_limit);
extern int __init lmb_is_reserved(u64 addr);
extern int lmb_find(struct lmb_property *res);
extern void lmb_dump_all(void);
......
......@@ -23,7 +23,6 @@
#include <linux/device.h>
#include <linux/rio_regs.h>
#define RIO_ANY_DESTID 0xff
#define RIO_NO_HOPCOUNT -1
#define RIO_INVALID_DESTID 0xffff
......@@ -39,11 +38,8 @@
entry is invalid (no route
exists for the device ID) */
#ifdef CONFIG_RAPIDIO_8_BIT_TRANSPORT
#define RIO_MAX_ROUTE_ENTRIES (1 << 8)
#else
#define RIO_MAX_ROUTE_ENTRIES (1 << 16)
#endif
#define RIO_MAX_ROUTE_ENTRIES(size) (size ? (1 << 16) : (1 << 8))
#define RIO_ANY_DESTID(size) (size ? 0xffff : 0xff)
#define RIO_MAX_MBOX 4
#define RIO_MAX_MSG_SIZE 0x1000
......@@ -149,6 +145,11 @@ struct rio_dbell {
void *dev_id;
};
enum rio_phy_type {
RIO_PHY_PARALLEL,
RIO_PHY_SERIAL,
};
/**
* struct rio_mport - RIO master port info
* @dbells: List of doorbell events
......@@ -163,6 +164,7 @@ struct rio_dbell {
* @id: Port ID, unique among all ports
* @index: Port index, unique among all port interfaces of the same type
* @name: Port name string
* @priv: Master port private data
*/
struct rio_mport {
struct list_head dbells; /* list of doorbell events */
......@@ -177,7 +179,13 @@ struct rio_mport {
unsigned char id; /* port ID, unique among all ports */
unsigned char index; /* port index, unique among all port
interfaces of the same type */
unsigned int sys_size; /* RapidIO common transport system size.
* 0 - Small size. 256 devices.
* 1 - Large size, 65536 devices.
*/
enum rio_phy_type phy_type; /* RapidIO phy type */
unsigned char name[40];
void *priv; /* Master port private data */
};
/**
......@@ -211,7 +219,7 @@ struct rio_switch {
u16 switchid;
u16 hopcount;
u16 destid;
u8 route_table[RIO_MAX_ROUTE_ENTRIES];
u8 *route_table;
int (*add_entry) (struct rio_mport * mport, u16 destid, u8 hopcount,
u16 table, u16 route_destid, u8 route_port);
int (*get_entry) (struct rio_mport * mport, u16 destid, u8 hopcount,
......@@ -229,13 +237,15 @@ struct rio_switch {
* @dsend: Callback to send a doorbell message.
*/
struct rio_ops {
int (*lcread) (int index, u32 offset, int len, u32 * data);
int (*lcwrite) (int index, u32 offset, int len, u32 data);
int (*cread) (int index, u16 destid, u8 hopcount, u32 offset, int len,
u32 * data);
int (*cwrite) (int index, u16 destid, u8 hopcount, u32 offset, int len,
u32 data);
int (*dsend) (int index, u16 destid, u16 data);
int (*lcread) (struct rio_mport *mport, int index, u32 offset, int len,
u32 *data);
int (*lcwrite) (struct rio_mport *mport, int index, u32 offset, int len,
u32 data);
int (*cread) (struct rio_mport *mport, int index, u16 destid,
u8 hopcount, u32 offset, int len, u32 *data);
int (*cwrite) (struct rio_mport *mport, int index, u16 destid,
u8 hopcount, u32 offset, int len, u32 data);
int (*dsend) (struct rio_mport *mport, int index, u16 destid, u16 data);
};
#define RIO_RESOURCE_MEM 0x00000100
......
......@@ -46,14 +46,13 @@ void lmb_dump_all(void)
#endif /* DEBUG */
}
static unsigned long __init lmb_addrs_overlap(u64 base1, u64 size1,
u64 base2, u64 size2)
static unsigned long lmb_addrs_overlap(u64 base1, u64 size1, u64 base2,
u64 size2)
{
return ((base1 < (base2 + size2)) && (base2 < (base1 + size1)));
}
static long __init lmb_addrs_adjacent(u64 base1, u64 size1,
u64 base2, u64 size2)
static long lmb_addrs_adjacent(u64 base1, u64 size1, u64 base2, u64 size2)
{
if (base2 == base1 + size1)
return 1;
......@@ -63,7 +62,7 @@ static long __init lmb_addrs_adjacent(u64 base1, u64 size1,
return 0;
}
static long __init lmb_regions_adjacent(struct lmb_region *rgn,
static long lmb_regions_adjacent(struct lmb_region *rgn,
unsigned long r1, unsigned long r2)
{
u64 base1 = rgn->region[r1].base;
......@@ -74,7 +73,7 @@ static long __init lmb_regions_adjacent(struct lmb_region *rgn,
return lmb_addrs_adjacent(base1, size1, base2, size2);
}
static void __init lmb_remove_region(struct lmb_region *rgn, unsigned long r)
static void lmb_remove_region(struct lmb_region *rgn, unsigned long r)
{
unsigned long i;
......@@ -86,7 +85,7 @@ static void __init lmb_remove_region(struct lmb_region *rgn, unsigned long r)
}
/* Assumption: base addr of region 1 < base addr of region 2 */
static void __init lmb_coalesce_regions(struct lmb_region *rgn,
static void lmb_coalesce_regions(struct lmb_region *rgn,
unsigned long r1, unsigned long r2)
{
rgn->region[r1].size += rgn->region[r2].size;
......@@ -118,7 +117,7 @@ void __init lmb_analyze(void)
lmb.memory.size += lmb.memory.region[i].size;
}
static long __init lmb_add_region(struct lmb_region *rgn, u64 base, u64 size)
static long lmb_add_region(struct lmb_region *rgn, u64 base, u64 size)
{
unsigned long coalesced = 0;
long adjacent, i;
......@@ -182,7 +181,7 @@ static long __init lmb_add_region(struct lmb_region *rgn, u64 base, u64 size)
return 0;
}
long __init lmb_add(u64 base, u64 size)
long lmb_add(u64 base, u64 size)
{
struct lmb_region *_rgn = &lmb.memory;
......@@ -194,6 +193,55 @@ long __init lmb_add(u64 base, u64 size)
}
long lmb_remove(u64 base, u64 size)
{
struct lmb_region *rgn = &(lmb.memory);
u64 rgnbegin, rgnend;
u64 end = base + size;
int i;
rgnbegin = rgnend = 0; /* supress gcc warnings */
/* Find the region where (base, size) belongs to */
for (i=0; i < rgn->cnt; i++) {
rgnbegin = rgn->region[i].base;
rgnend = rgnbegin + rgn->region[i].size;
if ((rgnbegin <= base) && (end <= rgnend))
break;
}
/* Didn't find the region */
if (i == rgn->cnt)
return -1;
/* Check to see if we are removing entire region */
if ((rgnbegin == base) && (rgnend == end)) {
lmb_remove_region(rgn, i);
return 0;
}
/* Check to see if region is matching at the front */
if (rgnbegin == base) {
rgn->region[i].base = end;
rgn->region[i].size -= size;
return 0;
}
/* Check to see if the region is matching at the end */
if (rgnend == end) {
rgn->region[i].size -= size;
return 0;
}
/*
* We need to split the entry - adjust the current one to the
* beginging of the hole and add the region after hole.
*/
rgn->region[i].size = base - rgn->region[i].base;
return lmb_add_region(rgn, end, rgnend - end);
}
long __init lmb_reserve(u64 base, u64 size)
{
struct lmb_region *_rgn = &lmb.reserved;
......@@ -426,3 +474,36 @@ int __init lmb_is_reserved(u64 addr)
}
return 0;
}
/*
* Given a <base, len>, find which memory regions belong to this range.
* Adjust the request and return a contiguous chunk.
*/
int lmb_find(struct lmb_property *res)
{
int i;
u64 rstart, rend;
rstart = res->base;
rend = rstart + res->size - 1;
for (i = 0; i < lmb.memory.cnt; i++) {
u64 start = lmb.memory.region[i].base;
u64 end = start + lmb.memory.region[i].size - 1;
if (start > rend)
return -1;
if ((end >= rstart) && (start < rend)) {
/* adjust the request */
if (rstart < start)
rstart = start;
if (rend > end)
rend = end;
res->base = rstart;
res->size = rend - rstart + 1;
return 0;
}
}
return -1;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment