// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2018 Integrated Device Technology, Inc
//
#define pr_fmt(fmt) "IDT_82p33xxx: " fmt
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/timekeeping.h>
#include <linux/bitops.h>
#include <linux/of.h>
#include <linux/mfd/rsmu.h>
#include <linux/mfd/idt82p33_reg.h>
#include "ptp_private.h"
#include "ptp_idt82p33.h"
MODULE_DESCRIPTION("Driver for IDT 82p33xxx clock devices");
MODULE_AUTHOR("IDT support-1588 <IDT-support-1588@lm.renesas.com>");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(FW_FILENAME);
/* Module Parameters */
static u32 phase_snap_threshold = SNAP_THRESHOLD_NS;
module_param(phase_snap_threshold, uint, 0);
MODULE_PARM_DESC(phase_snap_threshold,
"threshold (10000ns by default) below which adjtime would use double dco");
static char *firmware;
module_param(firmware, charp, 0);
static inline int idt82p33_read(struct idt82p33 *idt82p33, u16 regaddr,
u8 *buf, u16 count)
{
return regmap_bulk_read(idt82p33->regmap, regaddr, buf, count);
}
static inline int idt82p33_write(struct idt82p33 *idt82p33, u16 regaddr,
u8 *buf, u16 count)
{
return regmap_bulk_write(idt82p33->regmap, regaddr, buf, count);
}
static void idt82p33_byte_array_to_timespec(struct timespec64 *ts,
u8 buf[TOD_BYTE_COUNT])
{
time64_t sec;
s32 nsec;
u8 i;
nsec = buf[3];
for (i = 0; i < 3; i++) {
nsec <<= 8;
nsec |= buf[2 - i];
}
sec = buf[9];
for (i = 0; i < 5; i++) {
sec <<= 8;
sec |= buf[8 - i];
}
ts->tv_sec = sec;
ts->tv_nsec = nsec;
}
static void idt82p33_timespec_to_byte_array(struct timespec64 const *ts,
u8 buf[TOD_BYTE_COUNT])
{
time64_t sec;
s32 nsec;
u8 i;
nsec = ts->tv_nsec;
sec = ts->tv_sec;
for (i = 0; i < 4; i++) {
buf[i] = nsec & 0xff;
nsec >>= 8;
}
for (i = 4; i < TOD_BYTE_COUNT; i++) {
buf[i] = sec & 0xff;
sec >>= 8;
}
}
static int idt82p33_dpll_set_mode(struct idt82p33_channel *channel,
enum pll_mode mode)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
u8 dpll_mode;
int err;
if (channel->pll_mode == mode)
return 0;
err = idt82p33_read(idt82p33, channel->dpll_mode_cnfg,
&dpll_mode, sizeof(dpll_mode));
if (err)
return err;
dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
dpll_mode |= (mode << PLL_MODE_SHIFT);
err = idt82p33_write(idt82p33, channel->dpll_mode_cnfg,
&dpll_mode, sizeof(dpll_mode));
if (err)
return err;
channel->pll_mode = mode;
return 0;
}
static int _idt82p33_gettime(struct idt82p33_channel *channel,
struct timespec64 *ts)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
u8 buf[TOD_BYTE_COUNT];
u8 trigger;
int err;
trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG,
HW_TOD_RD_TRIG_SEL_LSB_TOD_STS);
err = idt82p33_write(idt82p33, channel->dpll_tod_trigger,
&trigger, sizeof(trigger));
if (err)
return err;
if (idt82p33->calculate_overhead_flag)
idt82p33->start_time = ktime_get_raw();
err = idt82p33_read(idt82p33, channel->dpll_tod_sts, buf, sizeof(buf));
if (err)
return err;
idt82p33_byte_array_to_timespec(ts, buf);
return 0;
}
/*
* TOD Trigger:
* Bits[7:4] Write 0x9, MSB write
* Bits[3:0] Read 0x9, LSB read
*/
static int _idt82p33_settime(struct idt82p33_channel *channel