blob: 197a2c49ea1a28bc01f3023bd314d9f6f2b73dc2 [file] [log] [blame]
/*
* Copyright (C) 2009 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/init.h>
#include <linux/clocksource.h>
#include <linux/printk.h>
#include <linux/spinlock.h>
#include <asm/time.h>
#include <linux/brcmstb/brcmstb.h>
/* MIPS clock measured at boot time. Value is not changed by PM. */
unsigned long brcm_cpu_khz;
/* Sampling period for MIPS calibration. 50 = 1/50 of a second. */
#define SAMPLE_PERIOD 50
/***********************************************************************
* UPG clocksource
***********************************************************************/
static cycle_t upg_cs_read(struct clocksource *cs)
{
return BDEV_RD_F(TIMER_TIMER3_STAT, COUNTER_VAL);
}
static struct clocksource clocksource_upg = {
.name = "upg",
.read = upg_cs_read,
.mask = CLOCKSOURCE_MASK(30),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static inline void __init init_upg_clocksource(void)
{
BDEV_WR_RB(BCHP_TIMER_TIMER3_CTRL, 0);
BDEV_WR_F_RB(TIMER_TIMER_IS, TMR3TO, 1);
BDEV_WR_RB(BCHP_TIMER_TIMER3_CTRL, 0xbfffffff);
clocksource_upg.rating = 250;
clocksource_register_hz(&clocksource_upg, UPGTMR_FREQ);
}
#ifdef CONFIG_BRCM_HAS_WKTMR
/***********************************************************************
* WKTMR clocksource
***********************************************************************/
static DEFINE_SPINLOCK(wktmr_lock);
static cycle_t wktmr_cs_read(struct clocksource *cs)
{
struct wktmr_time t;
unsigned long flags;
spin_lock_irqsave(&wktmr_lock, flags);
wktmr_read(&t);
spin_unlock_irqrestore(&wktmr_lock, flags);
return (t.sec * (cycle_t)WKTMR_FREQ) + t.pre;
}
static struct clocksource clocksource_wktmr = {
.name = "wktmr",
.read = wktmr_cs_read,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static inline void __init init_wktmr_clocksource(void)
{
clocksource_wktmr.rating = 250;
clocksource_register_hz(&clocksource_wktmr, WKTMR_FREQ);
}
/*
* MIPS frequency calibration (WKTMR)
*/
static __init unsigned long brcm_mips_freq(void)
{
struct wktmr_time start;
wktmr_read(&start);
write_c0_count(0);
while (wktmr_elapsed(&start) < (WKTMR_FREQ / SAMPLE_PERIOD))
;
return read_c0_count() * SAMPLE_PERIOD;
}
void read_persistent_clock(struct timespec *ts)
{
struct wktmr_time now;
wktmr_read(&now);
ts->tv_sec = now.sec;
ts->tv_nsec = now.pre * (1000000000/WKTMR_FREQ);
}
#else /* CONFIG_BRCM_HAS_WKTMR */
/*
* MIPS frequency calibration (UPG TIMER3)
*/
static __init unsigned long brcm_mips_freq(void)
{
unsigned long ret;
/* reset countdown timer */
BDEV_WR_RB(BCHP_TIMER_TIMER3_CTRL, 0);
BDEV_WR_F_RB(TIMER_TIMER_IS, TMR3TO, 1);
/* set up for countdown */
BDEV_WR(BCHP_TIMER_TIMER0_CTRL, 0xc0000000 |
(UPGTMR_FREQ / SAMPLE_PERIOD));
write_c0_count(0);
while ((BDEV_RD(BCHP_TIMER_TIMER_IS) & 1) == 0)
;
ret = read_c0_count();
BDEV_WR(BCHP_TIMER_TIMER0_CTRL, 0);
return ret * SAMPLE_PERIOD;
}
#endif /* CONFIG_BRCM_HAS_WKTMR */
/***********************************************************************
* Timer setup
***********************************************************************/
void __init plat_time_init(void)
{
unsigned int khz;
pr_info("Measuring MIPS counter frequency...\n");
mips_hpt_frequency = brcm_mips_freq();
khz = mips_hpt_frequency / 1000;
#ifdef CONFIG_CPU_BMIPS5000
brcm_cpu_khz = mips_hpt_frequency * 8 / 1000;
#else
brcm_cpu_khz = mips_hpt_frequency * 2 / 1000;
#endif
pr_info("Detected MIPS clock frequency: %lu MHz (%u.%03u MHz counter)\n",
brcm_cpu_khz / 1000, khz / 1000, khz % 1000);
#ifdef CONFIG_CSRC_WKTMR
init_wktmr_clocksource();
#endif
#ifdef CONFIG_CSRC_UPG
init_upg_clocksource();
#endif
}