spbx/roms/srcs/images/ethernet/gianfar_eth/mpic_timer.c

469 lines
10 KiB
C
Raw Permalink Normal View History

2019-03-11 00:13:23 +00:00
/*
* Copyright (c) 2008-2010 Freescale Semiconductor, Inc. All rights reserved.
* Dave Liu <daveliu@freescale.com>
* copy from the 83xx GTM driver and modify for MPIC global timer,
* implement the global timer 0 function.
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <sysdev/fsl_soc.h>
#include "log.h"
#include "mpic_timer.h"
#define MPIC_TIMER_TCR_OFFSET 0x200
#define MPIC_TIMER_TCR_CLKDIV_64 0x00000300
#define MPIC_TIMER_STOP 0x80000000
struct mpic_tm_regs
{
u32 gtccr;
u32 res0[3];
u32 gtbcr;
u32 res1[3];
u32 gtvpr;
u32 res2[3];
u32 gtdr;
u32 res3[3];
};
struct mpic_tm_priv
{
struct mpic_tm_regs __iomem* regs;
int irq;
int ticks_per_sec;
int ticks_per_msec;
spinlock_t lock;
};
struct mpic_type
{
int has_tcr;
};
struct irq_hook
{
PMIPC_TIMER_RUNTIME hook;
void* data;
};
static unsigned int g_CntPerMs = 0;
static unsigned int g_CurrentVal = 0;
static struct irq_hook irq_hooks[32];
static volatile uint32_t poll_hook_bitmap = 0xffffffff;
int register_mpic_timer_irq(PMIPC_TIMER_RUNTIME phooks, void* data)
{
int ret = -ENOSPC;
int idx = 0;
if((idx = fls(poll_hook_bitmap) - 1) >= 0)
{
poll_hook_bitmap &= ~(1 << idx);
irq_hooks[idx].hook = phooks;
irq_hooks[idx].data = data;
ret = idx;
printk("irq_poll_register %x\n", ret);
}
return ret;
}
EXPORT_SYMBOL(register_mpic_timer_irq);
int unregister_mpic_timer_irq(int idx)
{
int ret = -EINVAL;
printk("irq_poll_unregister %x\n", idx);
if(!(poll_hook_bitmap & (1 << idx)))
{
poll_hook_bitmap |= (1 << idx);
}
return ret;
}
EXPORT_SYMBOL(unregister_mpic_timer_irq);
static unsigned int g_TimerCnt = 0;
static struct timeval g_Clock;
static irqreturn_t mpic_tm_isr(int irq, void* dev_id)
{
struct mpic_tm_priv* priv = dev_id;
uint32_t bits;
int i;
unsigned int newTimerCnt;
struct timeval ts;
unsigned int rlt, need;
/*
unsigned long flags;
unsigned long temp;
spin_lock_irqsave(&priv->lock, flags);
temp = in_be32(&priv->regs->gtbcr);
temp |= MPIC_TIMER_STOP; // counting inhibited
out_be32(&priv->regs->gtbcr, temp);
out_be32(&priv->regs->gtbcr, priv->ticks_per_msec);
spin_unlock_irqrestore(&priv->lock, flags);
//spin_lock_irqsave(&priv->lock, flags);
//out_be32(&priv->regs->gtbcr, newTimerCnt | MPIC_TIMER_STOP);
//spin_unlock_irqrestore(&priv->lock, flags);
*/
bits = ~poll_hook_bitmap;
while((i = fls(bits) - 1) >= 0)
{
bits &= ~(1 << i);
irq_hooks[i].hook(irq_hooks[i].data);
}
do_gettimeofday(&ts);
// <20><><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EEA3AC>λΪ US
if(ts.tv_usec < g_Clock.tv_usec)
{
rlt = (ts.tv_sec - g_Clock.tv_sec - 1) * 1000000 +
((1000000 + ts.tv_usec) - g_Clock.tv_usec);
}
else
{
rlt = (ts.tv_sec - g_Clock.tv_sec) * 1000000 +
(ts.tv_usec - g_Clock.tv_usec);
}
#if 1
// <20><>ǰ<EFBFBD><C7B0>׼ʱ<D7BC><CAB1><EFBFBD><EFBFBD>
need = g_TimerCnt * 1000;
// У<><D0A3>ʱ<EFBFBD><CAB1>
if(need > rlt)
{
newTimerCnt = g_CntPerMs + (need - rlt) * (g_CntPerMs / 1000);
}
else
{
if((rlt - need) > 1000)
{
newTimerCnt = g_CntPerMs / 2;
}
else
{
newTimerCnt = g_CntPerMs - (rlt - need) * (g_CntPerMs / 1000);
}
}
g_CurrentVal = newTimerCnt;
#else
// <20><>ǰ<EFBFBD><C7B0>׼ʱ<D7BC><CAB1><EFBFBD><EFBFBD>
need = g_TimerCnt * 100;
// У<><D0A3>ʱ<EFBFBD><CAB1>
if(need > rlt)
{
newTimerCnt = g_CntPerMs + (need - rlt) * (g_CntPerMs / 100);
}
else
{
if((rlt - need) > 100)
{
newTimerCnt = g_CntPerMs / 2;
}
else
{
newTimerCnt = g_CntPerMs - (rlt - need) * (g_CntPerMs / 100);
}
}
g_CurrentVal = newTimerCnt;
#endif
// <20><><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1>
out_be32(&priv->regs->gtbcr, newTimerCnt | MPIC_TIMER_STOP);
out_be32(&priv->regs->gtbcr, newTimerCnt & 0x7FFFFFFF);
g_TimerCnt++;
/*
if(g_TimerCnt % 1000 == 0)
{
//printk("[T]%d.%d - %d.%d\n", ts.tv_sec, ts.tv_usec, g_Clock.tv_sec, g_Clock.tv_usec);
//printk("[T]need = %d, rlt = %d, newTimerCnt = %d\n", need, rlt, newTimerCnt);
printk("[%d]Time:%d.%d\n", g_TimerCnt, ts.tv_sec, ts.tv_usec / 1000);
}
*/
return IRQ_HANDLED;
}
static ssize_t mpic_tm_timeout_store(struct device* dev,
struct device_attribute* attr,
const char* buf, size_t count)
{
struct mpic_tm_priv* priv = dev_get_drvdata(dev);
unsigned long interval = simple_strtoul(buf, NULL, 0);
unsigned long temp;
if(interval > 0x7fffffff)
{
dev_dbg(dev, "mpic_tm: interval %lu (in s) too long\n", interval);
return -EINVAL;
}
temp = interval;
interval *= priv->ticks_per_sec;
if(interval > 0x7fffffff || (interval / priv->ticks_per_sec) != temp)
{
dev_dbg(dev, "mpic_tm: interval %lu (in ticks) too long\n",
interval);
return -EINVAL;
}
spin_lock_irq(&priv->lock);
/* stop timer 0 */
temp = in_be32(&priv->regs->gtbcr);
temp |= MPIC_TIMER_STOP; /* counting inhibited */
out_be32(&priv->regs->gtbcr, temp);
if(interval != 0)
{
/* start timer */
out_be32(&priv->regs->gtbcr, interval | MPIC_TIMER_STOP);
out_be32(&priv->regs->gtbcr, interval);
}
spin_unlock_irq(&priv->lock);
return count;
}
struct mpic_tm_priv* g_tm_priv = NULL;
int mpic_tm_get_current(void)
{
return in_be32(&g_tm_priv->regs->gtccr);
}
EXPORT_SYMBOL(mpic_tm_get_current);
static ssize_t mpic_tm_timeout_show(struct device* dev,
struct device_attribute* attr,
char* buf)
{
struct mpic_tm_priv* priv = dev_get_drvdata(dev);
int timeout = 0;
spin_lock_irq(&priv->lock);
if(!(in_be32(&priv->regs->gtbcr) & MPIC_TIMER_STOP))
{
timeout = in_be32(&priv->regs->gtccr);
timeout += priv->ticks_per_sec - 1;
timeout /= priv->ticks_per_sec;
}
spin_unlock_irq(&priv->lock);
return sprintf(buf, "TimeOut:0x%08X - TimerCnt:0x%08X\n", timeout,
g_CurrentVal);
}
static DEVICE_ATTR(timeout, 0660, mpic_tm_timeout_show, mpic_tm_timeout_store);
static int mpic_tm_probe(struct of_device* dev,
const struct of_device_id* match)
{
struct device_node* np = dev->dev.of_node;
struct resource res;
struct mpic_tm_priv* priv;
struct mpic_type* type = match->data;
int has_tcr = type->has_tcr;
u32 busfreq = fsl_get_sys_freq();
int ret = 0;
if(busfreq == 0)
{
dev_err(&dev->dev, "mpic_tm: No bus frequency in device tree.\n");
return -ENODEV;
}
priv = kmalloc(sizeof(struct mpic_tm_priv), GFP_KERNEL);
if(!priv)
{
return -ENOMEM;
}
spin_lock_init(&priv->lock);
dev_set_drvdata(&dev->dev, priv);
ret = of_address_to_resource(np, 0, &res);
if(ret)
{
goto out;
}
priv->irq = irq_of_parse_and_map(np, 0);
if(priv->irq == NO_IRQ)
{
dev_err(&dev->dev, "MPIC global timer0 exists in device tree "
"without an IRQ.\n");
ret = -ENODEV;
goto out;
}
ret = request_irq(priv->irq, mpic_tm_isr, 0, "mpic timer 0", priv);
if(ret)
{
goto out;
}
priv->regs = ioremap(res.start, res.end - res.start + 1);
if(!priv->regs)
{
ret = -ENOMEM;
goto out;
}
/*
* MPIC implementation from Freescale has the TCR register,
* the MPIC_TIMER_TCR_OFFSET is 0x200 from global timer base
* the default clock source to the MPIC timer 0 is CCB freq / 8.
* to extend the timer period, we divide the timer clock source
* as CCB freq / 64, so the max timer period is 336 seconds
* when the CCB frequence is 400MHz.
*/
if(!has_tcr)
{
priv->ticks_per_sec = busfreq / 8;
}
else
{
u32 __iomem* tcr;
tcr = (u32 __iomem*)((u32)priv->regs + MPIC_TIMER_TCR_OFFSET);
out_be32(tcr, in_be32(tcr) | MPIC_TIMER_TCR_CLKDIV_64);
priv->ticks_per_sec = busfreq / 64;
}
g_CntPerMs = priv->ticks_per_sec / 1000;
priv->ticks_per_msec = g_CntPerMs;
LOG_EX(LOG_Info, "freq = 0x%08X, msec = 0x%08X\n",
busfreq, priv->ticks_per_msec);
ret = device_create_file(&dev->dev, &dev_attr_timeout);
if(ret)
{
goto out;
}
/* start timer now */
out_be32(&priv->regs->gtbcr, priv->ticks_per_msec | MPIC_TIMER_STOP);
out_be32(&priv->regs->gtbcr, priv->ticks_per_msec);
do_gettimeofday(&g_Clock);
g_tm_priv = priv;
return 0;
out:
kfree(priv);
return ret;
}
static int mpic_tm_remove(struct of_device* dev)
{
struct mpic_tm_priv* priv = dev_get_drvdata(&dev->dev);
device_remove_file(&dev->dev, &dev_attr_timeout);
free_irq(priv->irq, priv);
iounmap(priv->regs);
dev_set_drvdata(&dev->dev, NULL);
kfree(priv);
return 0;
}
static struct mpic_type mpic_types[] =
{
{
.has_tcr = 0,
},
{
.has_tcr = 1,
}
};
static struct of_device_id mpic_tm_match[] =
{
{
.compatible = "fsl,mpic-global-timer",
.data = &mpic_types[1],
},
{},
};
static struct of_platform_driver mpic_tm_driver =
{
.driver = {
.name = "mpic-global-timer",
.owner = THIS_MODULE,
.of_match_table = mpic_tm_match,
},
.probe = mpic_tm_probe,
.remove = mpic_tm_remove,
};
static int __init mpic_tm_init(void)
{
printk("Gianfar Build:%s(%s)\n", __DATE__, __TIME__);
return of_register_platform_driver(&mpic_tm_driver);
}
static void __exit mpic_tm_exit(void)
{
of_unregister_platform_driver(&mpic_tm_driver);
}
module_init(mpic_tm_init);
module_exit(mpic_tm_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc");
MODULE_DESCRIPTION("MPIC Timer Driver");
MODULE_LICENSE("GPL");