ckfwq/linux-3.0.4/arch/mips/loongson/gsc3280/timer.c

390 lines
10 KiB
C

/*
*
* Copyright (c) 2012 Loongson Co., Ltd.
*
* GSC3280 - Timer Driver <ansonn.wang@gmail.com>
*
*/
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/time.h>
#include <asm/clock.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <gsc3280/gsc3280_int.h>
#include <gsc3280/gsc3280_regs.h>
#include <gsc3280/sysctl.h>
#include <gsc3280/timer.h>
//#define CONFIG_GSC3280_TIMER_DEBUG
#ifdef CONFIG_GSC3280_TIMER_DEBUG
#define DBG(msg...) do { \
printk(KERN_INFO msg); \
} while (0)
#else
#define DBG(msg...) do { } while(0)
#endif
#define TIMER_LC 0x00
#define TIMER_CV 0x04
#define TIMER_CR 0x08
#define TIMER_EOI 0x0C
#define TIMER_IS 0x10
typedef union {
/** raw register data */
u32 w32;
/** register bits */
struct {
//0:disable, 1:enable
unsigned enable:1;
//0:one, 1:loop
unsigned mode:1;
//0:irq enable, 1:irq mask
unsigned irq_mask:1;
unsigned reserved3_31:29;
} b;
} timer_tcr_u;
enum {
TIMER0,
TIMER1,
TIMER2,
TIMER3,
GSC3280_H_TIMER_NR
};
struct gsc3280_timer {
bool in_use;
void __iomem *base;
unsigned long rate;
struct gsc3280_hard_timer *h_timer;
char *name;
};
static struct gsc3280_timer gsc3280_timer_priv[GSC3280_H_TIMER_NR] = {
{false, (__iomem void *)GSC3280_TIMER_BASEADDR + 0x00, 0, NULL, "Timer0"},
{false, (__iomem void *)GSC3280_TIMER_BASEADDR + 0x14, 0, NULL, "Timer1"},
{false, (__iomem void *)GSC3280_TIMER_BASEADDR + 0x28, 0, NULL, "Timer2"},
{false, (__iomem void *)GSC3280_TIMER_BASEADDR + 0x3C, 0, NULL, "Timer3"}
};
static irqreturn_t gsc3280_timer_interrupt(int irq, void *_ptr)
{
irqreturn_t ret = IRQ_NONE;
struct gsc3280_timer *timer = (struct gsc3280_timer *)_ptr;
if (__raw_readl(timer->base + TIMER_IS) & 0x01) {
//is real interrupt
timer->h_timer->function(timer->h_timer->data);
ret = IRQ_HANDLED;
}
//clr interrupt bit
__raw_readl(timer->base + TIMER_EOI);
return ret;
}
static void start_hard_timer(struct gsc3280_timer *timer)
{
unsigned long tlc = 0;
timer_tcr_u tcr = {.w32 = 0};
if (timer->h_timer->value_type == 0)
tlc = (timer->rate * timer->h_timer->expires) /1000;
else
tlc = timer->rate / timer->h_timer->bps - 1;
tcr.w32 = __raw_readl(timer->base + TIMER_CR);
if (timer->h_timer->type == LOOP)
tcr.b.mode = 1;
else
tcr.b.mode = 0;
__raw_writel(tcr.w32, timer->base + TIMER_CR);
__raw_writel(tlc, timer->base + TIMER_LC);
//Enable Timer Interupt
tcr.w32 = __raw_readl(timer->base + TIMER_CR);
tcr.b.irq_mask = 0; //not mask timer irq
tcr.b.enable = 1; //enable timer
__raw_writel(tcr.w32, timer->base + TIMER_CR);
__raw_readl(timer->base + TIMER_EOI);
timer->in_use = true;
}
static void stop_hard_timer(struct gsc3280_timer *timer)
{
timer_tcr_u tcr = {.w32 = 0};
tcr.w32 = __raw_readl(timer->base + TIMER_CR);
tcr.b.irq_mask = 1; //mask timer irq
tcr.b.enable = 0; //disable timer
__raw_writel(tcr.w32, timer->base + TIMER_CR);
timer->in_use = false;
}
int gsc3280_timer_start(struct gsc3280_hard_timer *h_timer)
{
int i =0;
if ((h_timer->data == 0) || (h_timer->function == NULL)) {
printk(KERN_ERR "GSC3280 Request Timer, But The Param Wrong!");
return -EINVAL;
}
for (i = 0; i < GSC3280_H_TIMER_NR; i++) {
if (gsc3280_timer_priv[i].h_timer == h_timer) {
if (!gsc3280_timer_priv[i].in_use)
start_hard_timer(&gsc3280_timer_priv[i]);
return 0;
}
}
DBG("gsc3280_timer_start error!!!!\n");
return -EBUSY;
}
EXPORT_SYMBOL(gsc3280_timer_start);
void gsc3280_timer_stop(struct gsc3280_hard_timer *h_timer)
{
int i= 0;
for (i = 0; i < GSC3280_H_TIMER_NR; i++) {
if (gsc3280_timer_priv[i].h_timer == h_timer) {
if (gsc3280_timer_priv[i].in_use) {
stop_hard_timer(&gsc3280_timer_priv[i]);
break;
}
}
}
}
EXPORT_SYMBOL(gsc3280_timer_stop);
int gsc3280_mod_timer(struct gsc3280_hard_timer *h_timer)
{
int i = 0;
for (i = 0; i < GSC3280_H_TIMER_NR; i++) {
if (gsc3280_timer_priv[i].h_timer == h_timer) {
if (gsc3280_timer_priv[i].in_use) {
stop_hard_timer(&gsc3280_timer_priv[i]);
start_hard_timer(&gsc3280_timer_priv[i]);
}
}
}
return 0;
}
EXPORT_SYMBOL(gsc3280_mod_timer);
int gsc3280_request_hard_timer(struct gsc3280_hard_timer *h_timer)
{
int i = 0, ret = 0;
for (i = 0; i < GSC3280_H_TIMER_NR; i++) {
if (gsc3280_timer_priv[i].h_timer == NULL) {
gsc3280_timer_priv[i].h_timer = h_timer;
ret = request_irq(EXT_GSC3280_TIMER_IRQ, &gsc3280_timer_interrupt,
IRQF_SHARED, gsc3280_timer_priv[i].name,
&gsc3280_timer_priv[i]);
if (ret) {
printk(KERN_ERR "Start hard Timer request irq failed!\n");
return ret;
}
DBG("GSC3280 Request Timer Success.");
return 0;
}
}
DBG("GSC3280 Request Timer error!");
return -EBUSY;
}
EXPORT_SYMBOL(gsc3280_request_hard_timer);
void gsc3280_free_hard_timer(struct gsc3280_hard_timer *h_timer)
{
int i = 0;
for (i = 0; i < GSC3280_H_TIMER_NR; i++) {
if (gsc3280_timer_priv[i].h_timer == h_timer) {
if (gsc3280_timer_priv[i].in_use)
gsc3280_timer_stop(h_timer);
free_irq(EXT_GSC3280_TIMER_IRQ, &gsc3280_timer_priv[i]);
gsc3280_timer_priv[i].h_timer = NULL;
}
}
}
EXPORT_SYMBOL(gsc3280_free_hard_timer);
static int __init gsc3280_timer_init(void)
{
int i = 0;
struct clk *clk = NULL;
timer_tcr_u tcr = {.w32 = 0};
DBG("############");
DBG("gsc3280 timer init start\n");
//enable timer modules
sysctl_mod_enable(SYSCTL_MOD_TIMER0);
sysctl_mod_enable(SYSCTL_MOD_TIMER1);
sysctl_mod_enable(SYSCTL_MOD_TIMER2);
sysctl_mod_enable(SYSCTL_MOD_TIMER3);
//get 4 timer rate, use this cal LC
clk = clk_get(NULL, "timer0");
gsc3280_timer_priv[TIMER0].rate = clk_get_rate(clk);
clk = clk_get(NULL, "timer1");
gsc3280_timer_priv[TIMER1].rate = clk_get_rate(clk);
clk = clk_get(NULL, "timer2");
gsc3280_timer_priv[TIMER2].rate = clk_get_rate(clk);
clk = clk_get(NULL, "timer3");
gsc3280_timer_priv[TIMER3].rate = clk_get_rate(clk);
for (i = 0; i < GSC3280_H_TIMER_NR; i++) {
if (gsc3280_timer_priv[i].rate == 0) {
DBG("GSC3280 TIMER%d Get Rate Failed!", i);
return -EAGAIN;
}
DBG("timer[%d] = %ld\n", i, gsc3280_timer_priv[i].rate);
tcr.w32 = __raw_readl(gsc3280_timer_priv[i].base + TIMER_CR);
tcr.b.enable = 0; //disable timer
tcr.b.irq_mask = 1; //mask timer irq
__raw_writel(tcr.w32, gsc3280_timer_priv[i].base + TIMER_CR);
}
printk(KERN_INFO "GSC3280 TIMER0~3 Enable Success!");
DBG("############");
return 0;
}
core_initcall(gsc3280_timer_init);
#ifdef CONFIG_DEBUG_FS
struct gsc3280_timer_debug {
int timer_idx;
unsigned int int_nr;
struct timeval start;
struct timeval end;
struct gsc3280_hard_timer h_timer;
};
unsigned int timer_lc[GSC3280_H_TIMER_NR] = { 1000, 1000, 1000, 1000 };
static struct gsc3280_timer_debug timer_debug[GSC3280_H_TIMER_NR];
static void timer_handle(unsigned long arg)
{
struct timeval cur_time;
struct gsc3280_timer_debug *ptr = (struct gsc3280_timer_debug *)arg;
ptr->int_nr++;
do_gettimeofday(&cur_time);
if ((cur_time.tv_sec - ptr->end.tv_sec) >= 1) {
printk(KERN_ERR "Timer%d:start[%ld.%ld] --- current[%ld.%ld] interrupt_nr[%u]",
ptr->timer_idx,
ptr->start.tv_sec, ptr->start.tv_usec,
cur_time.tv_sec, cur_time.tv_usec,
ptr->int_nr);
do_gettimeofday(&ptr->end);
}
return;
}
static int gsc3280_timer_debug_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static int gsc3280_timer_debug_write(struct file *file, const char __user *buf,
size_t len, loff_t *p_pos)
{
int timer_idx = TIMER0;
char buffer[64] = { 0 };
if (len > 32) {
printk(KERN_ERR "input len in not correct! only can start stop");
return -1;
}
if (!strcmp("timer0", (char *)file->private_data))
timer_idx = TIMER0;
else if (!strcmp("timer1", (char *)file->private_data))
timer_idx = TIMER1;
else if (!strcmp("timer2", (char *)file->private_data))
timer_idx = TIMER2;
else if (!strcmp("timer3", (char *)file->private_data))
timer_idx = TIMER3;
else
return -1;
copy_from_user(buffer, buf, len);
if (!strncmp(buffer, "start", 5)) {
printk(KERN_ERR "\nstart timer%d", timer_idx);
timer_debug[timer_idx].timer_idx = timer_idx;
timer_debug[timer_idx].int_nr = 0;
do_gettimeofday(&timer_debug[timer_idx].start);
do_gettimeofday(&timer_debug[timer_idx].end);
timer_debug[timer_idx].h_timer.type = LOOP;
timer_debug[timer_idx].h_timer.expires = timer_lc[timer_idx];
timer_debug[timer_idx].h_timer.data = (unsigned long)&timer_debug[timer_idx];
timer_debug[timer_idx].h_timer.function = timer_handle;
gsc3280_free_hard_timer(&timer_debug[timer_idx].h_timer);
gsc3280_request_hard_timer(&timer_debug[timer_idx].h_timer);
gsc3280_timer_start(&timer_debug[timer_idx].h_timer);
}
else if (!strncmp(buffer, "stop", 4)) {
printk(KERN_ERR "\nstop timer%d", timer_idx);
gsc3280_free_hard_timer(&timer_debug[timer_idx].h_timer);
}
else {
printk(KERN_ERR "can only write start&stop cmd=%s", buffer);
return -1;
}
return len;
}
static int gsc3280_timer_debug_read(struct file *file, char __user *buf,
size_t len, loff_t *p_pos)
{
printk(KERN_ERR "debug file read %s", (char *)file->private_data);
return 0;
}
static const struct file_operations gsc3280_timer_debug_opt = {
.open = gsc3280_timer_debug_open,
.write = gsc3280_timer_debug_write,
.read = gsc3280_timer_debug_read,
};
/*---------------------------------------------------------------------------*/
static int __init gsc3280_timer_debug_init(void)
{
struct dentry *timer_debug = debugfs_create_dir("timer", NULL);
(void)debugfs_create_file("timer0", 0644, timer_debug, "timer0",
&gsc3280_timer_debug_opt);
(void)debugfs_create_file("timer1", 0644, timer_debug, "timer1",
&gsc3280_timer_debug_opt);
(void)debugfs_create_file("timer2", 0644, timer_debug, "timer2",
&gsc3280_timer_debug_opt);
(void)debugfs_create_file("timer3", 0644, timer_debug, "timer3",
&gsc3280_timer_debug_opt);
(void)debugfs_create_u32("timer0_lc", 0644, timer_debug, &timer_lc[0]);
(void)debugfs_create_u32("timer1_lc", 0644, timer_debug, &timer_lc[1]);
(void)debugfs_create_u32("timer2_lc", 0644, timer_debug, &timer_lc[2]);
(void)debugfs_create_u32("timer3_lc", 0644, timer_debug, &timer_lc[3]);
return 0;
}
subsys_initcall(gsc3280_timer_debug_init);
#endif