Commit 8c3076d4 authored by Anton Blanchard's avatar Anton Blanchard

ppc64: iSeries updates from iSeries team

ppc64: time updates from Mike Corrigan
ppc64: add flush_hash_range for pSeries LPAR
ppc64: align syscall tables to 8 bytes
parent 0eef6190
......@@ -53,6 +53,8 @@ struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue )
struct HvLpEvent * nextLpEvent =
(struct HvLpEvent *)lpQueue->xSlicCurEventPtr;
if ( nextLpEvent->xFlags.xValid ) {
/* rmb() needed only for weakly consistent machines (regatta) */
rmb();
/* Set pointer to next potential event */
lpQueue->xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 +
LpEventAlign ) /
......
......@@ -144,7 +144,9 @@ struct ItLpNaca itLpNaca = {
struct ItIplParmsReal xItIplParmsReal = {};
struct IoHriProcessorVpd xIoHriProcessorVpd[maxProcessors] = {
#define maxPhysicalProcessors 32
struct IoHriProcessorVpd xIoHriProcessorVpd[maxPhysicalProcessors] = {
{
xInstCacheOperandSize: 32,
xDataCacheOperandSize: 32,
......@@ -172,7 +174,7 @@ struct ItVpdAreas itVpdAreas = {
0, 0,
26, /* # VPD array entries */
10, /* # DMA array entries */
maxProcessors*2, maxProcessors, /* Max logical, physical procs */
maxProcessors*2, maxPhysicalProcessors, /* Max logical, physical procs */
offsetof(struct ItVpdAreas,xPlicDmaToks),/* offset to DMA toks */
offsetof(struct ItVpdAreas,xSlicVpdAdrs),/* offset to VPD addrs */
offsetof(struct ItVpdAreas,xPlicDmaLens),/* offset to DMA lens */
......
......@@ -342,12 +342,15 @@ chrp_progress(char *s, unsigned short hex)
extern void setup_default_decr(void);
extern unsigned long ppc_proc_freq;
extern unsigned long ppc_tb_freq;
void __init pSeries_calibrate_decr(void)
{
struct device_node *cpu;
struct div_result divres;
int *fp;
unsigned long freq;
unsigned long freq, processor_freq;
/*
* The cpu node should have a timebase-frequency property
......@@ -360,8 +363,19 @@ void __init pSeries_calibrate_decr(void)
if (fp != 0)
freq = *fp;
}
ppc_tb_freq = freq;
processor_freq = freq;
if (cpu != 0) {
fp = (int *) get_property(cpu, "clock-frequency", NULL);
if (fp != 0)
processor_freq = *fp;
}
ppc_proc_freq = processor_freq;
printk("time_init: decrementer frequency = %lu.%.6lu MHz\n",
freq/1000000, freq%1000000 );
printk("time_init: processor frequency = %lu.%.6lu MHz\n",
processor_freq/1000000, processor_freq%1000000 );
tb_ticks_per_jiffy = freq / HZ;
tb_ticks_per_sec = tb_ticks_per_jiffy * HZ;
......
......@@ -106,7 +106,7 @@ int __init iSeries_allocate_IRQ(HvBusNumber busNumber, HvSubBusNumber subBusNumb
{
u8 idsel = (deviceId >> 4);
u8 function = deviceId & 0x0F;
int irq = ((((busNumber-1)*16 + (idsel-1)*8 + function)*9/8) % 254) + 1;
int irq = ((((busNumber-1)*16 + (idsel-1)*8 + function)*9/8) % 253) + 2;
return irq;
}
......
This diff is collapsed.
......@@ -26,6 +26,7 @@
#include <linux/string.h>
#include <linux/bootmem.h>
#include <linux/blk.h>
#include <linux/seq_file.h>
#include <asm/processor.h>
#include <asm/machdep.h>
......@@ -587,6 +588,7 @@ static void __init build_iSeries_Memory_Map(void)
lmb_init();
lmb_add( 0, naca->physicalMemorySize );
lmb_analyze(); /* ?? */
lmb_reserve( 0, __pa(klimit));
/*
......@@ -603,8 +605,10 @@ static void __init build_iSeries_Memory_Map(void)
static void __init setup_iSeries_cache_sizes(void)
{
unsigned i,n;
naca->iCacheL1LineSize = xIoHriProcessorVpd[0].xInstCacheOperandSize;
naca->dCacheL1LineSize = xIoHriProcessorVpd[0].xDataCacheOperandSize;
unsigned procIx = get_paca()->xLpPaca.xDynHvPhysicalProcIndex;
naca->iCacheL1LineSize = xIoHriProcessorVpd[procIx].xInstCacheOperandSize;
naca->dCacheL1LineSize = xIoHriProcessorVpd[procIx].xDataCacheOperandSize;
naca->iCacheL1LinesPerPage = PAGE_SIZE / naca->iCacheL1LineSize;
naca->dCacheL1LinesPerPage = PAGE_SIZE / naca->dCacheL1LineSize;
i = naca->iCacheL1LineSize;
......@@ -652,6 +656,9 @@ static void __init iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr)
}
#endif /* CONFIG_PPC_ISERIES */
extern unsigned long ppc_proc_freq;
extern unsigned long ppc_tb_freq;
/*
* Document me.
*/
......@@ -659,6 +666,7 @@ void __init
iSeries_setup_arch(void)
{
void * eventStack;
unsigned procIx = get_paca()->xLpPaca.xDynHvPhysicalProcIndex;
/* Setup the Lp Event Queue */
......@@ -685,15 +693,19 @@ iSeries_setup_arch(void)
xItLpQueue.xIndex = 0;
/* Compute processor frequency */
procFreqHz = (((1UL<<34) * 1000000) / xIoHriProcessorVpd[0].xProcFreq );
procFreqHz = (((1UL<<34) * 1000000) / xIoHriProcessorVpd[procIx].xProcFreq );
procFreqMhz = procFreqHz / 1000000;
procFreqMhzHundreths = (procFreqHz/10000) - (procFreqMhz*100);
ppc_proc_freq = procFreqHz;
/* Compute time base frequency */
tbFreqHz = (((1UL<<32) * 1000000) / xIoHriProcessorVpd[0].xTimeBaseFreq );
tbFreqHz = (((1UL<<32) * 1000000) / xIoHriProcessorVpd[procIx].xTimeBaseFreq );
tbFreqMhz = tbFreqHz / 1000000;
tbFreqMhzHundreths = (tbFreqHz/10000) - (tbFreqMhz*100);
ppc_tb_freq = tbFreqHz;
printk("Max logical processors = %d\n",
itVpdAreas.xSlicMaxLogicalProcs );
printk("Max physical processors = %d\n",
......@@ -705,12 +717,12 @@ iSeries_setup_arch(void)
tbFreqMhz,
tbFreqMhzHundreths );
printk("Processor version = %x\n",
xIoHriProcessorVpd[0].xPVR );
xIoHriProcessorVpd[procIx].xPVR );
}
/*
* int iSeries_setup_residual()
* int as400_setup_residual()
*
* Description:
* This routine pretty-prints CPU information gathered from the VPD
......@@ -726,20 +738,25 @@ iSeries_setup_arch(void)
* The number of bytes copied into 'buffer' if OK, otherwise zero or less
* on error.
*/
void
iSeries_setup_residual(struct seq_file *m, unsigned long cpu_id)
void iSeries_setup_residual(struct seq_file *m)
{
seq_printf(m, "clock\t\t: %lu.%02luMhz\n", procFreqMhz,
procFreqMhzHundreths);
seq_printf(m, "time base\t: %lu.%02luMHz\n", tbFreqMhz,
tbFreqMhzHundreths);
seq_printf(m, "i-cache\t\t: %d\n", naca->iCacheL1LineSize);
seq_printf(m, "d-cache\t\t: %d\n", naca->dCacheL1LineSize);
seq_printf(m,"clock\t\t: %lu.%02luMhz\n",
procFreqMhz, procFreqMhzHundreths );
seq_printf(m,"time base\t: %lu.%02luMHz\n",
tbFreqMhz, tbFreqMhzHundreths );
seq_printf(m,"i-cache\t\t: %d\n",
naca->iCacheL1LineSize);
seq_printf(m,"d-cache\t\t: %d\n",
naca->dCacheL1LineSize);
}
void iSeries_get_cpuinfo(struct seq_file *m)
{
seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n");
seq_printf(m,"machine\t\t: 64-bit iSeries Logical Partition\n");
}
/*
......@@ -805,9 +822,7 @@ extern void setup_default_decr(void);
void __init
iSeries_calibrate_decr(void)
{
unsigned long freq;
unsigned long cyclesPerUsec;
unsigned long tbf;
struct div_result divres;
......@@ -815,19 +830,21 @@ iSeries_calibrate_decr(void)
* in cycles/sec
*/
tbf = xIoHriProcessorVpd[0].xTimeBaseFreq;
freq = 0x0100000000;
freq *= 1000000; /* 2^32 * 10^6 */
freq = freq / tbf; /* cycles / sec */
cyclesPerUsec = freq / 1000000; /* cycles / usec */
cyclesPerUsec = ppc_tb_freq / 1000000; /* cycles / usec */
/* Set the amount to refresh the decrementer by. This
* is the number of decrementer ticks it takes for
* 1/HZ seconds.
*/
tb_ticks_per_jiffy = freq / HZ;
tb_ticks_per_jiffy = ppc_tb_freq / HZ;
#if 0
/* TEST CODE FOR ADJTIME */
tb_ticks_per_jiffy += tb_ticks_per_jiffy / 5000;
/* END OF TEST CODE */
#endif
/*
* tb_ticks_per_sec = freq; would give better accuracy
* but tb_ticks_per_sec = tb_ticks_per_jiffy*HZ; assures
......@@ -836,7 +853,7 @@ iSeries_calibrate_decr(void)
*/
tb_ticks_per_sec = tb_ticks_per_jiffy * HZ;
tb_ticks_per_usec = cyclesPerUsec;
tb_to_us = mulhwu_scale_factor(freq, 1000000);
tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000);
div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres );
tb_to_xs = divres.result_low;
setup_default_decr();
......
......@@ -19,8 +19,6 @@
#ifndef __ISERIES_SETUP_H__
#define __ISERIES_SETUP_H__
#include <linux/seq_file.h>
extern void iSeries_init_early(void);
extern void iSeries_init(unsigned long r3,
unsigned long ird_start,
......@@ -28,8 +26,7 @@ extern void iSeries_init(unsigned long r3,
unsigned long cline_start,
unsigned long cline_end);
extern void iSeries_setup_arch(void);
extern void iSeries_setup_residual(struct seq_file *m,
unsigned long cpu_id);
extern void iSeries_setup_residual(struct seq_file *m);
extern void iSeries_get_cpuinfo(struct seq_file *m);
extern void iSeries_init_IRQ(void);
extern int iSeries_get_irq(struct pt_regs *regs);
......
......@@ -516,8 +516,7 @@ _GLOBAL(kernel_thread)
#ifdef CONFIG_BINFMT_ELF32
/* Why isn't this a) automatic, b) written in 'C'? */
.data
.align 8
.balign 8
_GLOBAL(sys_call_table32)
.llong .sys_ni_syscall /* 0 - old "setup()" system call */
.llong .sys32_exit
......@@ -544,7 +543,7 @@ _GLOBAL(sys_call_table32)
.llong .sys_oldumount
.llong .sys_setuid
.llong .sys_getuid
.llong .ppc64_sys_stime /* 25 */
.llong .ppc64_sys32_stime /* 25 */
.llong .sys32_ptrace
.llong .sys_alarm
.llong .sys32_fstat
......@@ -745,8 +744,8 @@ _GLOBAL(sys_call_table32)
.llong .sys_ni_syscall
.endr
#endif
.data
.align 8
.balign 8
_GLOBAL(sys_call_table)
.llong .sys_ni_syscall /* 0 - old "setup()" system call */
.llong .sys_exit
......
......@@ -782,10 +782,6 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp,
}
}
/*
* Take a spinlock around flushes to avoid bouncing the hypervisor tlbie
* lock.
*/
static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
int large, int local)
{
......@@ -801,10 +797,8 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
avpn = vpn >> 11;
spin_lock_irqsave(&pSeries_lpar_tlbie_lock, flags);
lpar_rc = plpar_pte_remove(H_AVPN, slot, (vpn >> 4) & ~0x7fUL, &dummy1,
&dummy2);
spin_unlock_irqrestore(&pSeries_lpar_tlbie_lock, flags);
if (lpar_rc == H_Not_Found) {
udbg_printf("invalidate missed\n");
......@@ -846,4 +840,5 @@ void pSeries_lpar_mm_init(void)
ppc_md.insert_hpte = pSeries_lpar_insert_hpte;
ppc_md.remove_hpte = pSeries_lpar_remove_hpte;
ppc_md.make_pte = pSeries_lpar_make_pte;
ppc_md.flush_hash_range = pSeries_lpar_flush_hash_range;
}
......@@ -277,6 +277,9 @@ void machine_halt(void)
ppc_md.halt();
}
unsigned long ppc_proc_freq;
unsigned long ppc_tb_freq;
static int show_cpuinfo(struct seq_file *m, void *v)
{
unsigned long cpu_id = (unsigned long)v - 1;
......@@ -286,10 +289,6 @@ static int show_cpuinfo(struct seq_file *m, void *v)
#ifdef CONFIG_SMP
if (cpu_id == NR_CPUS) {
unsigned long bogosum = smp_num_cpus * loops_per_jiffy;
seq_printf(m, "total bogomips\t: %lu.%02lu\n",
bogosum/(500000/HZ),
bogosum/(5000/HZ) % 100);
if (ppc_md.get_cpuinfo != NULL)
ppc_md.get_cpuinfo(m);
......@@ -357,10 +356,6 @@ static int show_cpuinfo(struct seq_file *m, void *v)
seq_printf(m, "revision\t: %hd.%hd\n", maj, min);
seq_printf(m, "bogomips\t: %lu.%02lu\n\n",
loops_per_jiffy/(500000/HZ),
loops_per_jiffy/(5000/HZ) % 100);
return 0;
}
......
......@@ -53,6 +53,7 @@
#include <asm/nvram.h>
#include <asm/cache.h>
#include <asm/machdep.h>
#include <asm/init.h>
#ifdef CONFIG_PPC_ISERIES
#include <asm/iSeries/HvCallXm.h>
#endif
......@@ -83,6 +84,7 @@ unsigned long next_xtime_sync_tb;
unsigned long xtime_sync_interval;
unsigned long tb_to_xs;
unsigned tb_to_us;
unsigned long processor_freq;
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
struct gettimeofday_struct do_gtod;
......@@ -97,6 +99,10 @@ extern unsigned long prof_len;
extern unsigned long prof_shift;
extern char _stext;
void ppc_adjtimex(void);
static unsigned adjusting_time = 0;
static inline void ppc_do_profile (unsigned long nip)
{
if (!prof_buffer)
......@@ -269,6 +275,8 @@ int timer_interrupt(struct pt_regs * regs)
timer_sync_xtime( cur_tb );
timer_check_rtc();
write_unlock(&xtime_lock);
if ( adjusting_time && (time_adjust == 0) )
ppc_adjtimex();
}
paca->next_jiffy_update_tb += tb_ticks_per_jiffy;
}
......@@ -386,13 +394,35 @@ void do_settimeofday(struct timeval *tv)
* fields itself. This way, the fields which are used for
* do_settimeofday get updated too.
*/
long ppc64_sys_stime(int * tptr)
long ppc64_sys32_stime(int* tptr)
{
int value;
struct timeval myTimeval;
PPCDBG(PPCDBG_SYS32, "ppc64_sys_stime - entered - tptr=%p, *tptr=0x%x \n", tptr, *tptr);
if (!capable(CAP_SYS_TIME))
return -EPERM;
if (get_user(value, tptr))
return -EFAULT;
myTimeval.tv_sec = value;
myTimeval.tv_usec = 0;
do_settimeofday(&myTimeval);
return 0;
}
/*
* This function is a copy of the architecture independent function
* but which calls do_settimeofday rather than setting the xtime
* fields itself. This way, the fields which are used for
* do_settimeofday get updated too.
*/
long ppc64_sys_stime(long* tptr)
{
long value;
struct timeval myTimeval;
if (!capable(CAP_SYS_TIME))
return -EPERM;
......@@ -405,7 +435,6 @@ long ppc64_sys_stime(int * tptr)
do_settimeofday(&myTimeval);
PPCDBG(PPCDBG_SYS32, "ppc64_sys_stime - exiting w/ 0 \n");
return 0;
}
......@@ -426,6 +455,7 @@ void __init time_init(void)
tb_last_stamp = get_tb();
do_gtod.tb_orig_stamp = tb_last_stamp;
do_gtod.varp = &do_gtod.vars[0];
do_gtod.var_idx = 0;
do_gtod.varp->stamp_xsec = xtime.tv_sec * XSEC_PER_SEC;
do_gtod.tb_ticks_per_sec = tb_ticks_per_sec;
do_gtod.varp->tb_to_xs = tb_to_xs;
......@@ -434,6 +464,8 @@ void __init time_init(void)
xtime_sync_interval = tb_ticks_per_sec - (tb_ticks_per_sec/8);
next_xtime_sync_tb = tb_last_stamp + xtime_sync_interval;
time_freq = 0;
xtime.tv_usec = 0;
last_rtc_update = xtime.tv_sec;
write_unlock_irqrestore(&xtime_lock, flags);
......@@ -452,10 +484,12 @@ void __init time_init(void)
* to microseconds to keep do_gettimeofday synchronized
* with ntpd.
* Use the time_freq and time_offset computed by adjtimex to
* Use the time_adjust, time_freq and time_offset computed by adjtimex to
* adjust the frequency.
*/
/* #define DEBUG_PPC_ADJTIMEX 1 */
void ppc_adjtimex(void)
{
unsigned long den, new_tb_ticks_per_sec, tb_ticks, old_xsec, new_tb_to_xs, new_xsec, new_stamp_xsec;
......@@ -464,7 +498,12 @@ void ppc_adjtimex(void)
struct div_result divres;
unsigned long flags;
struct gettimeofday_vars * temp_varp;
unsigned temp_idx;
long singleshot_ppm = 0;
/* Compute parts per million frequency adjustment to accomplish the time adjustment
implied by time_offset to be applied over the elapsed time indicated by time_constant.
Use SHIFT_USEC to get it into the same units as time_freq. */
if ( time_offset < 0 ) {
ltemp = -time_offset;
ltemp <<= SHIFT_USEC - SHIFT_UPDATE;
......@@ -476,8 +515,40 @@ void ppc_adjtimex(void)
ltemp <<= SHIFT_USEC - SHIFT_UPDATE;
ltemp >>= SHIFT_KG + time_constant;
}
delta_freq = time_freq + ltemp;
/* If there is a single shot time adjustment in progress */
if ( time_adjust ) {
#ifdef DEBUG_PPC_ADJTIMEX
printk("ppc_adjtimex: ");
if ( adjusting_time == 0 )
printk("starting ");
printk("single shot time_adjust = %ld\n", time_adjust);
#endif
adjusting_time = 1;
/* Compute parts per million frequency adjustment to match time_adjust */
singleshot_ppm = tickadj * HZ;
/* The adjustment should be tickadj*HZ to match the code in linux/kernel/timer.c, but
experiments show that this is too large. 3/4 of tickadj*HZ seems about right */
singleshot_ppm -= singleshot_ppm / 4;
/* Use SHIFT_USEC to get it into the same units as time_freq */
singleshot_ppm <<= SHIFT_USEC;
if ( time_adjust < 0 )
singleshot_ppm = -singleshot_ppm;
}
else {
#ifdef DEBUG_PPC_ADJTIMEX
if ( adjusting_time )
printk("ppc_adjtimex: ending single shot time_adjust\n");
#endif
adjusting_time = 0;
}
/* Add up all of the frequency adjustments */
delta_freq = time_freq + ltemp + singleshot_ppm;
/* Compute a new value for tb_ticks_per_sec based on the frequency adjustment */
den = 1000000 * (1 << (SHIFT_USEC - 8));
if ( delta_freq < 0 ) {
tb_ticks_per_sec_delta = ( tb_ticks_per_sec * ( (-delta_freq) >> (SHIFT_USEC - 8))) / den;
......@@ -487,6 +558,16 @@ void ppc_adjtimex(void)
tb_ticks_per_sec_delta = ( tb_ticks_per_sec * ( delta_freq >> (SHIFT_USEC - 8))) / den;
new_tb_ticks_per_sec = tb_ticks_per_sec - tb_ticks_per_sec_delta;
}
#ifdef DEBUG_PPC_ADJTIMEX
printk("ppc_adjtimex: ltemp = %ld, time_freq = %ld, singleshot_ppm = %ld\n", ltemp, time_freq, singleshot_ppm);
printk("ppc_adjtimex: tb_ticks_per_sec - base = %ld new = %ld\n", tb_ticks_per_sec, new_tb_ticks_per_sec);
#endif
/* Compute a new value of tb_to_xs (used to convert tb to microseconds and a new value of
stamp_xsec which is the time (in 1/2^20 second units) corresponding to tb_orig_stamp. This
new value of stamp_xsec compensates for the change in frequency (implied by the new tb_to_xs)
which guarantees that the current time remains the same */
tb_ticks = get_tb() - do_gtod.tb_orig_stamp;
div128_by_32( 1024*1024, 0, new_tb_ticks_per_sec, &divres );
new_tb_to_xs = divres.result_low;
......@@ -496,14 +577,23 @@ void ppc_adjtimex(void)
old_xsec = mulhdu( tb_ticks, do_gtod.varp->tb_to_xs );
new_stamp_xsec = do_gtod.varp->stamp_xsec + old_xsec - new_xsec;
if (do_gtod.varp == &do_gtod.vars[0])
/* There are two copies of tb_to_xs and stamp_xsec so that no lock is needed to access and use these
values in do_gettimeofday. We alternate the copies and as long as a reasonable time elapses between
changes, there will never be inconsistent values. ntpd has a minimum of one minute between updates */
if (do_gtod.var_idx == 0) {
temp_varp = &do_gtod.vars[1];
else
temp_idx = 1;
}
else {
temp_varp = &do_gtod.vars[0];
temp_idx = 0;
}
temp_varp->tb_to_xs = new_tb_to_xs;
temp_varp->stamp_xsec = new_stamp_xsec;
mb();
do_gtod.varp = temp_varp;
do_gtod.var_idx = temp_idx;
write_unlock_irqrestore( &xtime_lock, flags );
......
......@@ -262,8 +262,10 @@ static int find_tb_table(unsigned long codeaddr, struct tbtable *tab);
static inline void disable_surveillance(void)
{
#ifndef CONFIG_PPC_ISERIES
rtas_call(rtas_token("set-indicator"), 3, 1, NULL, SURVEILLANCE_TOKEN,
0, 0);
#endif
}
void
......
......@@ -97,6 +97,8 @@ struct HvCallPci_BridgeInfo {
struct HvCallPci_BusUnitInfo busUnitInfo; // Generic bus unit info
u8 subBusNumber; // Bus number of secondary bus
u8 maxAgents; // Max idsels on secondary bus
u8 maxSubBusNumber; // Max Sub Bus
u8 logicalSlotNumber; // Logical Slot Number for IOA
};
......
......@@ -17,7 +17,7 @@
extern unsigned long reloc_offset(void);
#define MAX_LMB_REGIONS 32
#define MAX_LMB_REGIONS 64
union lmb_reg_property {
struct reg_property32 addr32[MAX_LMB_REGIONS];
......
......@@ -50,6 +50,7 @@ struct gettimeofday_struct {
unsigned long tb_ticks_per_sec;
struct gettimeofday_vars vars[2];
struct gettimeofday_vars * volatile varp;
unsigned var_idx;
unsigned tb_to_us;
};
......
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