// 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/i2c.h>
#include <linux/module.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/timekeeping.h>
#include <linux/bitops.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 Parameters */
static u32 sync_tod_timeout = SYNC_TOD_TIMEOUT_SEC;
module_param(sync_tod_timeout, uint, 0);
MODULE_PARM_DESC(sync_tod_timeout,
"duration in second to keep SYNC_TOD on (set to 0 to keep it always on)");
static u32 phase_snap_threshold = SNAP_THRESHOLD_NS;
module_param(phase_snap_threshold, uint, 0);
MODULE_PARM_DESC(phase_snap_threshold,
"threshold (150000ns by default) below which adjtime would ignore");
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_xfer(struct idt82p33 *idt82p33,
unsigned char regaddr,
unsigned char *buf,
unsigned int count,
int write)
{
struct i2c_client *client = idt82p33->client;
struct i2c_msg msg[2];
int cnt;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = ®addr;
msg[1].addr = client->addr;
msg[1].flags = write ? 0 : I2C_M_RD;
msg[1].len = count;
msg[1].buf = buf;
cnt = i2c_transfer(client->adapter, msg, 2);
if (cnt < 0) {
dev_err(&client->dev, "i2c_transfer returned %d\n", cnt);
return cnt;
} else if (cnt != 2) {
dev_err(&client->dev,
"i2c_transfer sent only %d of %d messages\n", cnt, 2);
return -EIO;
}
return 0;
}
static int idt82p33_page_offset(struct idt82p33 *idt82p33, unsigned char val)
{
int err;
if (idt82p33->page_offset == val)
return 0;
err = idt82p33_xfer(idt82p33, PAGE_ADDR, &val, sizeof(val), 1);
if (err)
dev_err(&idt82p33->client->dev,
"failed to set page offset %d\n", val);
else
idt82p33->page_offset = val;
return err;
}
static int idt82p33_rdwr(struct idt82p33 *idt82p33, unsigned int regaddr,
unsigned char *buf, unsigned int count, bool write