nt9856x/BSP/linux-kernel/drivers/ptp/ptp_nvt.c
2023-03-28 15:07:53 +08:00

411 lines
10 KiB
C

/**
NVT ptp function
NVT ptp driver
@file ptp_nvt.c
@ingroup
@note
Copyright Novatek Microelectronics Corp. 2021. All rights reserved.
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/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/types.h>
#include <linux/clocksource.h>
#include <linux/time.h>
#include <linux/timecounter.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#define PTP_MAX_VALUE 0xFFFFFFFF
#define PTP_SHIFT 32ULL
#define PTP_MAX_HZ 3000000
#define PTP_MAX_DIV_BIT 255
#define PTP_TIMER_NUM (18)
#define PTP_TIMER_DIV_SRC 1
#if (PTP_TIMER_DIV_SRC == 0)
#define PTP_TIMER_DIV_DEF 0xFFFFFF00
#else
#define PTP_TIMER_DIV_DEF 0xFFFF00FF
#endif
#define PTP_TIMER_DIV_BASE 0x30
#define PTP_TIMER_DIV_VAL (1 << ( 16 + PTP_TIMER_DIV_SRC) )
#define PTP_TIMER_IRQ_ENABLE 0x00
#define PTP_TIMER_IRQ_STS 0x10
#define PTP_TIMER_IRQ_BASE 0x20
#define PTP_TIMER_IRQ_VAL (1 << PTP_TIMER_NUM)
#define PTP_TIMER_CTL_BASE (0x10 * PTP_TIMER_NUM + 0x100)
#define PTP_TIMER_TVAL_BASE (PTP_TIMER_CTL_BASE + 0x04)
#define PTP_TIMER_VALUE_BASE (PTP_TIMER_CTL_BASE + 0x08)
#define PTP_TIMER_CNT_BASE (PTP_TIMER_CTL_BASE + 0x0C)
#define PTP_TIMER_ENABLE 1
#define PTP_TIMER_DISABLE 0
#define PTP_TIMER_FREQ_SHIFT (PTP_TIMER_DIV_SRC * 8)
#define PTP_TIMER_UP (0x3 + 0x4 * PTP_TIMER_DIV_SRC)
/* ptp dte priv structure */
struct ptp_dte {
void __iomem *regs;
struct ptp_clock *ptp_clk;
struct ptp_clock_info caps;
struct device *dev;
spinlock_t lock;
struct cyclecounter cc;
struct timecounter tc;
struct clk *clk;
u32 freq;
};
struct ptp_dte *ptp_dte;
static int ptp_cyclecounter_init(struct ptp_dte *ptp);
static int ptp_dte_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on);
#define MAX_TIMER_NUM 20
#ifdef CONFIG_NOVATEK_TIMER
extern void __iomem *nvttmr_base;
extern spinlock_t nvttmr_lock;
extern u32 nvttmr_in_use;
#else
void __iomem *nvttmr_base;
spinlock_t nvttmr_lock;
u32 nvttmr_in_use;
#endif
static s64 dte_read_nco(void __iomem *regs)
{
u32 sum1;
s64 ns;
unsigned long flags;
spin_lock_irqsave(&nvttmr_lock, flags);
sum1 = readl(regs + PTP_TIMER_VALUE_BASE);
ns = (s64)sum1;
spin_unlock_irqrestore(&nvttmr_lock, flags);
return ns;
}
static int ptp_dte_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
{
unsigned long flags;
struct ptp_dte *ptp_dte = container_of(ptp, struct ptp_dte, caps);
dev_err(ptp_dte->dev, "%s not support\n", __func__);
return -EINVAL;
if (abs(ppb) > ptp_dte->caps.max_adj || ppb < 0) {
dev_err(ptp_dte->dev, "ppb adj is wrong value\n");
return -EINVAL;
}
ptp_dte_enable(&ptp_dte->caps ,0,PTP_TIMER_DISABLE);
spin_lock_irqsave(&ptp_dte->lock, flags);
ptp_dte->freq = ppb;
spin_unlock_irqrestore(&ptp_dte->lock, flags);
ptp_dte_enable(&ptp_dte->caps ,0,PTP_TIMER_ENABLE);
ptp_cyclecounter_init(ptp_dte);
return 0;
}
static int ptp_dte_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
unsigned long flags;
struct ptp_dte *ptp_dte = container_of(ptp, struct ptp_dte, caps);
spin_lock_irqsave(&ptp_dte->lock, flags);
timecounter_adjtime(&ptp_dte->tc, delta);
spin_unlock_irqrestore(&ptp_dte->lock, flags);
return 0;
}
static int ptp_dte_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
unsigned long flags;
struct ptp_dte *ptp_dte = container_of(ptp, struct ptp_dte, caps);
spin_lock_irqsave(&ptp_dte->lock, flags);
*ts = ns_to_timespec64(timecounter_read(&ptp_dte->tc));
spin_unlock_irqrestore(&ptp_dte->lock, flags);
return 0;
}
static int ptp_dte_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
unsigned long flags;
struct ptp_dte *ptp_dte = container_of(ptp, struct ptp_dte, caps);
s64 ns, new_ns;
spin_lock_irqsave(&ptp_dte->lock, flags);
new_ns = timespec64_to_ns(ts);
ns = timecounter_read(&ptp_dte->tc);
new_ns -= ns;
timecounter_adjtime(&ptp_dte->tc, new_ns);
spin_unlock_irqrestore(&ptp_dte->lock, flags);
return 0;
}
static int ptp_dte_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
unsigned long flags;
struct ptp_dte *ptp_dte = container_of(ptp, struct ptp_dte, caps);
u32 val;
u8 freq = (PTP_MAX_HZ / ptp_dte->freq) - 1 ;
if ((PTP_MAX_HZ / ptp_dte->freq) > PTP_MAX_DIV_BIT) {
dev_err(ptp_dte->dev, "%s: freq too slow should > %d\n", __func__, PTP_MAX_HZ / PTP_MAX_DIV_BIT);
return -EINVAL;
}
spin_lock_irqsave(&nvttmr_lock, flags);
if (on == PTP_TIMER_ENABLE) {
val = readl(ptp_dte->regs + PTP_TIMER_IRQ_ENABLE);
val |= PTP_MAX_VALUE;
writel( val, (ptp_dte->regs + PTP_TIMER_IRQ_ENABLE));
val = readl(ptp_dte->regs + PTP_TIMER_IRQ_BASE);
val &= ~(PTP_TIMER_IRQ_VAL);
writel( val, (ptp_dte->regs + PTP_TIMER_IRQ_BASE));
val = readl(ptp_dte->regs + PTP_TIMER_DIV_BASE);
val &= PTP_TIMER_DIV_DEF;
val |= (freq << PTP_TIMER_FREQ_SHIFT) + PTP_TIMER_DIV_VAL;
writel( val, (ptp_dte->regs + PTP_TIMER_DIV_BASE));
val = readl(ptp_dte->regs + PTP_TIMER_TVAL_BASE);
val |= PTP_MAX_VALUE;
writel( val, (ptp_dte->regs + PTP_TIMER_TVAL_BASE));
val = readl(ptp_dte->regs + PTP_TIMER_CNT_BASE);
val &= ~(PTP_MAX_VALUE);
writel( val, (ptp_dte->regs + PTP_TIMER_CNT_BASE));
val = readl(ptp_dte->regs + PTP_TIMER_CTL_BASE);
val |= PTP_TIMER_UP;
writel( val, (ptp_dte->regs + PTP_TIMER_CTL_BASE));
} else if (on == PTP_TIMER_DISABLE) {
val = readl(ptp_dte->regs + PTP_TIMER_CTL_BASE);
val &= ~(PTP_TIMER_UP);
writel( val, (ptp_dte->regs + PTP_TIMER_CTL_BASE));
} else {
dev_err(ptp_dte->dev, "%s: on/off should be set 1 or 0\n", __func__);
}
spin_unlock_irqrestore(&nvttmr_lock, flags);
return 0;
}
static const struct ptp_clock_info ptp_dte_caps = {
.owner = THIS_MODULE,
.name = "DTE PTP timer",
.max_adj = 50000000,
.n_ext_ts = 0,
.n_pins = 0,
.pps = 0,
.adjfreq = ptp_dte_adjfreq,
.adjtime = ptp_dte_adjtime,
.gettime64 = ptp_dte_gettime,
.settime64 = ptp_dte_settime,
.enable = ptp_dte_enable,
};
static u64 ptp_cyclecounter_read(const struct cyclecounter *cc)
{
return (u64)(dte_read_nco(ptp_dte->regs));
}
static int ptp_cyclecounter_init(struct ptp_dte *ptp)
{
u64 start_count;
u32 mult, shift;
unsigned long flags;
spin_lock_irqsave(&ptp_dte->lock, flags);
start_count = (u64)ktime_to_ns(ktime_get_real());
clocks_calc_mult_shift(&mult,&shift,ptp->freq,NSEC_PER_SEC,60);
ptp->cc.read = ptp_cyclecounter_read;
ptp->cc.mask = CLOCKSOURCE_MASK(32);
ptp->cc.mult = mult;
ptp->cc.shift = shift;
timecounter_init(&ptp->tc,
&ptp->cc, start_count);
spin_unlock_irqrestore(&ptp_dte->lock, flags);
return 0;
}
/*static irqreturn_t nvt_interrupt(int irq, void *dev_id)
{
struct clock_event_device *clkevt = (struct clock_event_device *)dev_id;
u32 val;
unsigned long flags;
int ret = IRQ_HANDLED;
spin_lock_irqsave(&nvttmr_lock, flags);
val = readl(ptp_dte->regs + PTP_TIMER_IRQ_STS);
if (val & (1 << PTP_TIMER_NUM)) {
val = (1 << PTP_TIMER_NUM);
writel( val, (ptp_dte->regs + PTP_TIMER_IRQ_STS));
}
else
ret = IRQ_NONE;
spin_unlock_irqrestore(&nvttmr_lock, flags);
return ret;
}*/
static int ptp_dte_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct device_node *node = pdev->dev.of_node;
u32 *pfreq;
int ret = 0;
if (MAX_TIMER_NUM < PTP_TIMER_NUM) {
dev_err(dev, "Timer num not support\n");
return -EINVAL;
}
if ((nvttmr_in_use & (1 << PTP_TIMER_NUM))) {
dev_err(dev, "Timer num %d already in use\n",PTP_TIMER_NUM);
return -EINVAL;
} else {
nvttmr_in_use |= (1 << PTP_TIMER_NUM);
}
ptp_dte = devm_kzalloc(dev, sizeof(struct ptp_dte), GFP_KERNEL);
if (!ptp_dte)
goto kzalloc_err;
#ifdef CONFIG_NOVATEK_TIMER
ptp_dte->regs = nvttmr_base;
#else
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ptp_dte->regs = devm_ioremap_resource(dev, res);
#endif
if (IS_ERR(ptp_dte->regs)) {
dev_err(dev, "%s: io remap failed\n", __func__);
goto remap_err;
}
#ifndef CONFIG_NOVATEK_TIMER
spin_lock_init(&nvttmr_lock);
#endif
spin_lock_init(&ptp_dte->lock);
ptp_dte->clk = clk_get(dev, "f0040000.timer");
if (IS_ERR(ptp_dte->clk)) {
dev_err(dev, "%s:%d clk_get fail\n", __func__, __LINE__);
goto clk_err;
}
if (clk_prepare_enable(ptp_dte->clk) != 0) {
dev_err(dev, "%s:%d enable clk fail\n", __func__, __LINE__);
goto clk_err;
}
ptp_dte->freq = (u32)(clk_get_rate(ptp_dte->clk));
pfreq = (u32 *)of_get_property(node, "clock-frequency", NULL);
if (pfreq != NULL) {
ptp_dte->freq = __be32_to_cpu(*pfreq);
}
ptp_dte_enable(&ptp_dte->caps ,0,1);
ptp_cyclecounter_init(ptp_dte);
ptp_dte->dev = dev;
ptp_dte->caps = ptp_dte_caps;
/* int irq_num = platform_get_irq(pdev, 0);
if (request_irq(irq_num, nvt_interrupt, IRQF_SHARED, "TIMER_TEST", ptp_dte->dev))
{
printk("%s:%d request_irq fail %d\n", __func__, __LINE__, irq_num);
return -1;
}*/
return 0;
clk_err:
#ifndef CONFIG_NOVATEK_TIMER
iounmap(ptp_dte->regs);
#endif
remap_err:
kfree(ptp_dte);
kzalloc_err:
nvttmr_in_use &= ~(1 << PTP_TIMER_NUM);
return ret;
}
static int ptp_dte_remove(struct platform_device *pdev)
{
nvttmr_in_use &= ~(1 << PTP_TIMER_NUM);
if (ptp_dte->regs)
ptp_dte_enable(&ptp_dte->caps , 0, PTP_TIMER_DISABLE);
if (ptp_dte->clk) {
clk_disable(ptp_dte->clk);
}
//free_irq(irq_num, ptp_dte->dev);
#ifndef CONFIG_NOVATEK_TIMER
if (ptp_dte->regs) {
iounmap(ptp_dte->regs);
}
#endif
if (ptp_dte) {
kfree(ptp_dte);
}
return 0;
}
static const struct of_device_id ptp_dte_of_match[] = {
{ .compatible = "nvt,ptp-dte", },
{},
};
MODULE_DEVICE_TABLE(of, ptp_dte_of_match);
static struct platform_driver ptp_dte_driver = {
.driver = {
.name = "ptp-dte",
.pm = NULL,
.of_match_table = ptp_dte_of_match,
},
.probe = ptp_dte_probe,
.remove = ptp_dte_remove,
};
module_platform_driver(ptp_dte_driver);
MODULE_AUTHOR("Novatek");
MODULE_VERSION("1.0.0");
MODULE_DESCRIPTION("Novatek PTP Clock driver");
MODULE_LICENSE("GPL v2");