// SPDX-License-Identifier: GPL-2.0
/* Microchip KSZ PTP Implementation
*
* Copyright (C) 2020 ARRI Lighting
* Copyright (C) 2022 Microchip Technology Inc.
*/
#include <linux/dsa/ksz_common.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
#include "ksz_common.h"
#include "ksz_ptp.h"
#include "ksz_ptp_reg.h"
#define ptp_caps_to_data(d) container_of((d), struct ksz_ptp_data, caps)
#define ptp_data_to_ksz_dev(d) container_of((d), struct ksz_device, ptp_data)
#define work_to_xmit_work(w) \
container_of((w), struct ksz_deferred_xmit_work, work)
/* Sub-nanoseconds-adj,max * sub-nanoseconds / 40ns * 1ns
* = (2^30-1) * (2 ^ 32) / 40 ns * 1 ns = 6249999
*/
#define KSZ_MAX_DRIFT_CORR 6249999
#define KSZ_MAX_PULSE_WIDTH 125000000LL
#define KSZ_PTP_INC_NS 40ULL /* HW clock is incremented every 40 ns (by 40) */
#define KSZ_PTP_SUBNS_BITS 32
#define KSZ_PTP_INT_START 13
static int ksz_ptp_tou_gpio(struct ksz_device *dev)
{
int ret;
if (!is_lan937x(dev))
return 0;
ret = ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, GPIO_OUT,
GPIO_OUT);
if (ret)
return ret;
ret = ksz_rmw32(dev, REG_SW_GLOBAL_LED_OVR__4, LED_OVR_1 | LED_OVR_2,
LED_OVR_1 | LED_OVR_2);
if (ret)
return ret;
return ksz_rmw32(dev, REG_SW_GLOBAL_LED_SRC__4,
LED_SRC_PTP_GPIO_1 | LED_SRC_PTP_GPIO_2,
LED_SRC_PTP_GPIO_1 | LED_SRC_PTP_GPIO_2);
}
static int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit)
{
u32 data;
int ret;
/* Reset trigger unit (clears TRIGGER_EN, but not GPIOSTATx) */
ret = ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, TRIG_RESET, TRIG_RESET);
data = FIELD_PREP(TRIG_DONE_M, BIT(unit));
ret = ksz_write32(dev, REG_PTP_TRIG_STATUS__4, data);
if (ret)
return ret;
data = FIELD_PREP(TRIG_INT_M, BIT(unit));
ret = ksz_write32(dev, REG_PTP_INT_STATUS__4, data);
if (ret)
return ret;
/* Clear reset and set GPIO direction */
return ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, (TRIG_RESET | TRIG_ENABLE),
0);
}
static int ksz_ptp_tou_pulse_verify(u64 pulse_ns)
{
u32 data;
if (pulse_ns & 0x3)
return -EINVAL;
data = (pulse_ns / 8);
if (!FIELD_FIT(TRIG_PULSE_WIDTH_M, data))
return -ERANGE;
return 0;
}
static int ksz_ptp_tou_target_time_set(struct ksz_device *dev,
struct timespec64 const *ts)
{
int ret;
/* Hardware has only 32 bit */
if ((ts->tv_sec & 0xffffffff) != ts->tv_sec)
return -EINVAL;
ret = ksz_write32(dev, REG_TRIG_TARGET_NANOSEC, ts->tv_nsec);
if (ret)
return ret;
ret = ksz_write32(dev, REG_TRIG_TARGET_SEC, ts->tv_sec);
if (ret)
return ret;
return 0;
}
static int ksz_ptp_tou_start(struct ksz_device *dev, u8 unit)
{
u32 data;
int ret;
ret = ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, TRIG_ENABLE, TRIG_ENABLE);
if (ret)
return ret;
/* Check error flag:
* - the ACTIVE flag is NOT cleared an error!
*/
ret = ksz_read32(dev, REG_PTP_TRIG_STATUS__4, &data);
if (ret)
return ret;
if (FIELD_GET(TRIG_ERROR_M, data) & (1 << unit)) {
dev_err(dev->dev, "%s: Trigger unit%d error!\n", __func__,
unit);
ret = -EIO;
/* Unit will be reset on next access */
return ret;
}
return 0;
}
static int ksz_ptp_configure_perout(struct ksz_device *dev,
u32 cycle_width_ns, u32 pulse_width_ns,
struct timespec64 const *target_time,
u8 index)
{
u32 data;
int ret;
data = FIELD_PREP(TRIG_NOTIFY, 1) |
FIELD_PREP(TRIG_GPO_M, index