#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/bug.h>
#include <linux/container_of.h>
#include <linux/dev_printk.h>
#include <linux/dpll.h>
#include <linux/err.h>
#include <linux/kthread.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sprintf.h>
#include "core.h"
#include "dpll.h"
#include "prop.h"
#include "regs.h"
#define ZL3073X_DPLL_REF_NONE ZL3073X_NUM_REFS
#define ZL3073X_DPLL_REF_IS_VALID(_ref) ((_ref) != ZL3073X_DPLL_REF_NONE)
struct zl3073x_dpll_pin {
struct list_head list;
struct zl3073x_dpll *dpll;
struct dpll_pin *dpll_pin;
char label[8];
enum dpll_pin_direction dir;
u8 id;
u8 prio;
bool selectable;
bool esync_control;
enum dpll_pin_state pin_state;
s64 phase_offset;
s64 freq_offset;
};
static const struct dpll_pin_frequency esync_freq_ranges[] = {
DPLL_PIN_FREQUENCY_RANGE(0, 1),
};
static bool
zl3073x_dpll_is_input_pin(struct zl3073x_dpll_pin *pin)
{
return pin->dir == DPLL_PIN_DIRECTION_INPUT;
}
static bool
zl3073x_dpll_is_p_pin(struct zl3073x_dpll_pin *pin)
{
return zl3073x_is_p_pin(pin->id);
}
static int
zl3073x_dpll_pin_direction_get(const struct dpll_pin *dpll_pin, void *pin_priv,
const struct dpll_device *dpll, void *dpll_priv,
enum dpll_pin_direction *direction,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll_pin *pin = pin_priv;
*direction = pin->dir;
return 0;
}
static int
zl3073x_dpll_input_ref_frequency_get(struct zl3073x_dpll *zldpll, u8 ref_id,
u32 *frequency)
{
struct zl3073x_dev *zldev = zldpll->dev;
u16 base, mult, num, denom;
int rc;
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
ZL_REG_REF_MB_MASK, BIT(ref_id));
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &base);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &mult);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &num);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &denom);
if (rc)
return rc;
if (!denom) {
dev_err(zldev->dev,
"Zero divisor for ref %u frequency got from device\n",
ref_id);
return -EINVAL;
}
*frequency = mul_u64_u32_div(base * mult, num, denom);
return rc;
}
static int
zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
struct dpll_pin_esync *esync,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
u8 ref, ref_sync_ctrl, sync_mode;
u32 esync_div, ref_freq;
int rc;
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_dpll_input_ref_frequency_get(zldpll, pin->id, &ref_freq);
if (rc)
return rc;
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
ZL_REG_REF_MB_MASK, BIT(ref));
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl);
if (rc)
return rc;
rc = zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &esync_div);
if (rc)
return rc;
sync_mode = FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref_sync_ctrl);
switch (sync_mode) {
case ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75:
esync->freq = (esync_div == ZL_REF_ESYNC_DIV_1HZ) ? 1 : 0;
esync->pulse = 25;
break;
default:
esync->freq = 0;
esync->pulse = 0;
break;
}
if (pin->esync_control && ref_freq > 1) {
esync->range = esync_freq_ranges;
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
} else {
esync->range = NULL;
esync->range_num = 0;
}
return rc;
}
static int
zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv, u64 freq,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
u8 ref, ref_sync_ctrl, sync_mode;
int rc;
guard(mutex)(&zldev->multiop_lock);
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
ZL_REG_REF_MB_MASK, BIT(ref));
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl);
if (rc)
return rc;
if (!freq)
sync_mode = ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF;
else
sync_mode = ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75;
ref_sync_ctrl &= ~ZL_REF_SYNC_CTRL_MODE;
ref_sync_ctrl |= FIELD_PREP(ZL_REF_SYNC_CTRL_MODE, sync_mode);
rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL, ref_sync_ctrl);
if (rc)
return rc;
if (freq) {
rc = zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV,
ZL_REF_ESYNC_DIV_1HZ);
if (rc)
return rc;
}
return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
ZL_REG_REF_MB_MASK, BIT(ref));
}
static int
zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
const struct dpll_device *dpll, void *dpll_priv,
s64 *ffo, struct netlink_ext_ack *extack)
{
struct zl3073x_dpll_pin *pin = pin_priv;
*ffo = pin->freq_offset;
return 0;
}
static int
zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv, u64 *frequency,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dpll_pin *pin = pin_priv;
u32 ref_freq;
u8 ref;
int rc;
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_dpll_input_ref_frequency_get(zldpll, ref, &ref_freq);
if (!rc)
*frequency = ref_freq;
return rc;
}
static int
zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv, u64 frequency,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
u16 base, mult;
u8 ref;
int rc;
rc = zl3073x_ref_freq_factorize(frequency, &base, &mult);
if (rc)
return rc;
guard(mutex)(&zldev->multiop_lock);
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
ZL_REG_REF_MB_MASK, BIT(ref));
rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE, base);
if (rc)
return rc;
rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT, mult);
if (rc)
return rc;
rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M, 1);
if (rc)
return rc;
rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N, 1);
if (rc)
return rc;
return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
ZL_REG_REF_MB_MASK, BIT(ref));
}
static int
zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
{
struct zl3073x_dev *zldev = zldpll->dev;
u8 state, value;
int rc;
switch (zldpll->refsel_mode) {
case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
rc = zl3073x_read_u8(zldev,
ZL_REG_DPLL_REFSEL_STATUS(zldpll->id),
&value);
if (rc)
return rc;
state = FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, value);
if (state == ZL_DPLL_REFSEL_STATUS_STATE_LOCK)
*ref = FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, value);
else
*ref = ZL3073X_DPLL_REF_NONE;
break;
case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
*ref = zldpll->forced_ref;
break;
default:
*ref = ZL3073X_DPLL_REF_NONE;
break;
}
return 0;
}
static int
zl3073x_dpll_selected_ref_set(struct zl3073x_dpll *zldpll, u8 ref)
{
struct zl3073x_dev *zldev = zldpll->dev;
u8 mode, mode_refsel;
int rc;
mode = zldpll->refsel_mode;
switch (mode) {
case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
if (ref == ZL3073X_DPLL_REF_NONE) {
switch (zldpll->lock_status) {
case DPLL_LOCK_STATUS_LOCKED_HO_ACQ:
case DPLL_LOCK_STATUS_HOLDOVER:
mode = ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER;
break;
default:
mode = ZL_DPLL_MODE_REFSEL_MODE_FREERUN;
break;
}
ref = zldpll->forced_ref;
} else if (ref == zldpll->forced_ref) {
return 0;
}
break;
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
if (ref == ZL3073X_DPLL_REF_NONE)
return 0;
mode = ZL_DPLL_MODE_REFSEL_MODE_REFLOCK;
break;
default:
return -EOPNOTSUPP;
}
mode_refsel = FIELD_PREP(ZL_DPLL_MODE_REFSEL_MODE, mode) |
FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id),
mode_refsel);
if (rc)
return rc;
zldpll->refsel_mode = mode;
zldpll->forced_ref = ref;
return rc;
}
static int
zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
{
struct zl3073x_dev *zldev = zldpll->dev;
int rc;
rc = zl3073x_dpll_selected_ref_get(zldpll, ref);
if (rc)
return rc;
if (ZL3073X_DPLL_REF_IS_VALID(*ref)) {
u8 ref_status;
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(*ref),
&ref_status);
if (rc)
return rc;
if (ref_status != ZL_REF_MON_STATUS_OK)
*ref = ZL3073X_DPLL_REF_NONE;
}
return 0;
}
static int
zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv, s64 *phase_offset,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
u8 conn_ref, ref, ref_status;
s64 ref_phase;
int rc;
rc = zl3073x_dpll_connected_ref_get(zldpll, &conn_ref);
if (rc)
return rc;
ref = zl3073x_input_pin_ref_get(pin->id);
if (!zldpll->phase_monitor && ref != conn_ref) {
*phase_offset = 0;
return 0;
}
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &ref_status);
if (rc)
return rc;
if (ref_status != ZL_REF_MON_STATUS_OK) {
*phase_offset = 0;
return 0;
}
ref_phase = pin->phase_offset;
if (ZL3073X_DPLL_REF_IS_VALID(conn_ref) && conn_ref != ref) {
u32 conn_freq, ref_freq;
rc = zl3073x_dpll_input_ref_frequency_get(zldpll, conn_ref,
&conn_freq);
if (rc)
return rc;
rc = zl3073x_dpll_input_ref_frequency_get(zldpll, ref,
&ref_freq);
if (rc)
return rc;
if (conn_freq > ref_freq) {
s64 conn_period, div_factor;
conn_period = div_s64(PSEC_PER_SEC, conn_freq);
div_factor = div64_s64(ref_phase, conn_period);
ref_phase -= conn_period * div_factor;
}
}
*phase_offset = ref_phase * DPLL_PHASE_OFFSET_DIVIDER;
return rc;
}
static int
zl3073x_dpll_input_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
s32 *phase_adjust,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
s64 phase_comp;
u8 ref;
int rc;
guard(mutex)(&zldev->multiop_lock);
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
ZL_REG_REF_MB_MASK, BIT(ref));
if (rc)
return rc;
rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP, &phase_comp);
if (rc)
return rc;
phase_comp = sign_extend64(phase_comp, 47);
*phase_adjust = (s32)-phase_comp;
return rc;
}
static int
zl3073x_dpll_input_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
s32 phase_adjust,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
s64 phase_comp;
u8 ref;
int rc;
phase_comp = -phase_adjust;
guard(mutex)(&zldev->multiop_lock);
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
ZL_REG_REF_MB_MASK, BIT(ref));
if (rc)
return rc;
rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP, phase_comp);
if (rc)
return rc;
return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
ZL_REG_REF_MB_MASK, BIT(ref));
}
static int
zl3073x_dpll_ref_prio_get(struct zl3073x_dpll_pin *pin, u8 *prio)
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_dev *zldev = zldpll->dev;
u8 ref, ref_prio;
int rc;
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
ZL_REG_DPLL_MB_MASK, BIT(zldpll->id));
if (rc)
return rc;
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2),
&ref_prio);
if (rc)
return rc;
if (zl3073x_dpll_is_p_pin(pin))
*prio = FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, ref_prio);
else
*prio = FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, ref_prio);
return rc;
}
static int
zl3073x_dpll_ref_prio_set(struct zl3073x_dpll_pin *pin, u8 prio)
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_dev *zldev = zldpll->dev;
u8 ref, ref_prio;
int rc;
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
ZL_REG_DPLL_MB_MASK, BIT(zldpll->id));
if (rc)
return rc;
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), &ref_prio);
if (rc)
return rc;
if (zl3073x_dpll_is_p_pin(pin)) {
ref_prio &= ~ZL_DPLL_REF_PRIO_REF_P;
ref_prio |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_P, prio);
} else {
ref_prio &= ~ZL_DPLL_REF_PRIO_REF_N;
ref_prio |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_N, prio);
}
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), ref_prio);
if (rc)
return rc;
return zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR,
ZL_REG_DPLL_MB_MASK, BIT(zldpll->id));
}
static int
zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
enum dpll_pin_state *state)
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_dev *zldev = zldpll->dev;
u8 ref, ref_conn, status;
int rc;
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_dpll_connected_ref_get(zldpll, &ref_conn);
if (rc)
return rc;
if (ref == ref_conn) {
*state = DPLL_PIN_STATE_CONNECTED;
return 0;
}
if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
pin->selectable) {
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref),
&status);
if (rc)
return rc;
if (status == ZL_REF_MON_STATUS_OK) {
*state = DPLL_PIN_STATE_SELECTABLE;
return 0;
}
}
*state = DPLL_PIN_STATE_DISCONNECTED;
return 0;
}
static int
zl3073x_dpll_input_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
enum dpll_pin_state *state,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll_pin *pin = pin_priv;
return zl3073x_dpll_ref_state_get(pin, state);
}
static int
zl3073x_dpll_input_pin_state_on_dpll_set(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
enum dpll_pin_state state,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dpll_pin *pin = pin_priv;
u8 new_ref;
int rc;
switch (zldpll->refsel_mode) {
case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
if (state == DPLL_PIN_STATE_CONNECTED) {
new_ref = zl3073x_input_pin_ref_get(pin->id);
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
new_ref = ZL3073X_DPLL_REF_NONE;
} else {
NL_SET_ERR_MSG_MOD(extack,
"Invalid pin state for manual mode");
return -EINVAL;
}
rc = zl3073x_dpll_selected_ref_set(zldpll, new_ref);
break;
case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
if (state == DPLL_PIN_STATE_SELECTABLE) {
if (pin->selectable)
return 0;
rc = zl3073x_dpll_ref_prio_set(pin, pin->prio);
if (rc)
return rc;
pin->selectable = true;
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
if (!pin->selectable)
return 0;
rc = zl3073x_dpll_ref_prio_set(pin,
ZL_DPLL_REF_PRIO_NONE);
if (rc)
return rc;
pin->selectable = false;
} else {
NL_SET_ERR_MSG(extack,
"Invalid pin state for automatic mode");
return -EINVAL;
}
break;
default:
NL_SET_ERR_MSG(extack,
"Pin state cannot be changed in current mode");
rc = -EOPNOTSUPP;
break;
}
return rc;
}
static int
zl3073x_dpll_input_pin_prio_get(const struct dpll_pin *dpll_pin, void *pin_priv,
const struct dpll_device *dpll, void *dpll_priv,
u32 *prio, struct netlink_ext_ack *extack)
{
struct zl3073x_dpll_pin *pin = pin_priv;
*prio = pin->prio;
return 0;
}
static int
zl3073x_dpll_input_pin_prio_set(const struct dpll_pin *dpll_pin, void *pin_priv,
const struct dpll_device *dpll, void *dpll_priv,
u32 prio, struct netlink_ext_ack *extack)
{
struct zl3073x_dpll_pin *pin = pin_priv;
int rc;
if (prio > ZL_DPLL_REF_PRIO_MAX)
return -EINVAL;
if (pin->selectable) {
rc = zl3073x_dpll_ref_prio_set(pin, prio);
if (rc)
return rc;
}
pin->prio = prio;
return 0;
}
static int
zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
struct dpll_pin_esync *esync,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
struct device *dev = zldev->dev;
u32 esync_period, esync_width;
u8 clock_type, synth;
u8 out, output_mode;
u32 output_div;
u32 synth_freq;
int rc;
out = zl3073x_output_pin_out_get(pin->id);
switch (zl3073x_out_signal_format_get(zldev, out)) {
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
return -EOPNOTSUPP;
default:
break;
}
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
if (rc)
return rc;
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
if (rc)
return rc;
if (!output_div) {
dev_err(dev, "Zero divisor for OUTPUT%u got from device\n",
out);
return -EINVAL;
}
synth = zl3073x_out_synth_get(zldev, out);
synth_freq = zl3073x_synth_freq_get(zldev, synth);
clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, output_mode);
if (clock_type != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
esync->freq = 0;
esync->pulse = 0;
goto finish;
}
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &esync_period);
if (rc)
return rc;
if (!esync_period) {
dev_err(dev, "Zero esync divisor for OUTPUT%u got from device\n",
out);
return -EINVAL;
}
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, &esync_width);
if (rc)
return rc;
esync->freq = synth_freq / output_div / esync_period;
esync->pulse = (50 * esync_width) / output_div;
finish:
if (pin->esync_control && (synth_freq / output_div) > 1) {
esync->range = esync_freq_ranges;
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
} else {
esync->range = NULL;
esync->range_num = 0;
}
return 0;
}
static int
zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv, u64 freq,
struct netlink_ext_ack *extack)
{
u32 esync_period, esync_width, output_div;
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
u8 clock_type, out, output_mode, synth;
u32 synth_freq;
int rc;
out = zl3073x_output_pin_out_get(pin->id);
switch (zl3073x_out_signal_format_get(zldev, out)) {
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
return -EOPNOTSUPP;
default:
break;
}
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
if (rc)
return rc;
if (freq)
clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC;
else
clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL;
output_mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE;
output_mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type);
rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_MODE, output_mode);
if (rc)
return rc;
if (!freq)
goto write_mailbox;
synth = zl3073x_out_synth_get(zldev, out);
synth_freq = zl3073x_synth_freq_get(zldev, synth);
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
if (rc)
return rc;
if (!output_div) {
dev_err(zldev->dev,
"Zero divisor for OUTPUT%u got from device\n", out);
return -EINVAL;
}
esync_period = synth_freq / (u32)freq / output_div;
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, esync_period);
if (rc)
return rc;
esync_width = output_div / 2;
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, esync_width);
if (rc)
return rc;
write_mailbox:
return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
}
static int
zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv, u64 *frequency,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
struct device *dev = zldev->dev;
u8 out, signal_format, synth;
u32 output_div, synth_freq;
int rc;
out = zl3073x_output_pin_out_get(pin->id);
synth = zl3073x_out_synth_get(zldev, out);
synth_freq = zl3073x_synth_freq_get(zldev, synth);
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
if (rc)
return rc;
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
if (rc)
return rc;
if (!output_div) {
dev_err(dev, "Zero divisor for output %u got from device\n",
out);
return -EINVAL;
}
signal_format = zl3073x_out_signal_format_get(zldev, out);
switch (signal_format) {
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
if (zl3073x_dpll_is_p_pin(pin)) {
*frequency = synth_freq / output_div;
} else {
u32 ndiv;
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
&ndiv);
if (rc)
return rc;
if (!ndiv) {
dev_err(dev,
"Zero N-pin divisor for output %u got from device\n",
out);
return -EINVAL;
}
*frequency = synth_freq / output_div / ndiv;
}
break;
default:
*frequency = synth_freq / output_div;
break;
}
return rc;
}
static int
zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv, u64 frequency,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
struct device *dev = zldev->dev;
u32 output_n_freq, output_p_freq;
u8 out, signal_format, synth;
u32 cur_div, new_div, ndiv;
u32 synth_freq;
int rc;
out = zl3073x_output_pin_out_get(pin->id);
synth = zl3073x_out_synth_get(zldev, out);
synth_freq = zl3073x_synth_freq_get(zldev, synth);
new_div = synth_freq / (u32)frequency;
signal_format = zl3073x_out_signal_format_get(zldev, out);
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
if (rc)
return rc;
if (signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV &&
signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV) {
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div);
if (rc)
return rc;
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div);
if (rc)
return rc;
return zl3073x_mb_op(zldev,
ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
}
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &cur_div);
if (rc)
return rc;
if (!cur_div) {
dev_err(dev, "Zero divisor for output %u got from device\n",
out);
return -EINVAL;
}
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &ndiv);
if (rc)
return rc;
if (!ndiv) {
dev_err(dev,
"Zero N-pin divisor for output %u got from device\n",
out);
return -EINVAL;
}
output_p_freq = synth_freq / cur_div;
output_n_freq = output_p_freq / ndiv;
if (zl3073x_dpll_is_p_pin(pin)) {
if (frequency <= output_n_freq)
return -EINVAL;
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div);
if (rc)
return rc;
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div);
if (rc)
return rc;
ndiv = (u32)frequency / output_n_freq;
} else {
if (output_p_freq <= frequency)
return -EINVAL;
ndiv = output_p_freq / (u32)frequency;
}
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, ndiv);
if (rc)
return rc;
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, ndiv);
if (rc)
return rc;
return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
}
static int
zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
s32 *phase_adjust,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
u32 synth_freq;
s32 phase_comp;
u8 out, synth;
int rc;
out = zl3073x_output_pin_out_get(pin->id);
synth = zl3073x_out_synth_get(zldev, out);
synth_freq = zl3073x_synth_freq_get(zldev, synth);
if (!synth_freq) {
dev_err(zldev->dev, "Got zero synth frequency for output %u\n",
out);
return -EINVAL;
}
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
if (rc)
return rc;
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, &phase_comp);
if (rc)
return rc;
phase_comp *= (int)div_u64(PSEC_PER_SEC, 2 * synth_freq);
*phase_adjust = -phase_comp;
return rc;
}
static int
zl3073x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
s32 phase_adjust,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
int half_synth_cycle;
u32 synth_freq;
u8 out, synth;
int rc;
out = zl3073x_output_pin_out_get(pin->id);
synth = zl3073x_out_synth_get(zldev, out);
synth_freq = zl3073x_synth_freq_get(zldev, synth);
half_synth_cycle = (int)div_u64(PSEC_PER_SEC, 2 * synth_freq);
if ((phase_adjust % half_synth_cycle) != 0) {
NL_SET_ERR_MSG_FMT(extack,
"Phase adjustment value has to be multiple of %d",
half_synth_cycle);
return -EINVAL;
}
phase_adjust /= half_synth_cycle;
phase_adjust = -phase_adjust;
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
if (rc)
return rc;
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, phase_adjust);
if (rc)
return rc;
return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
ZL_REG_OUTPUT_MB_MASK, BIT(out));
}
static int
zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv,
enum dpll_pin_state *state,
struct netlink_ext_ack *extack)
{
*state = DPLL_PIN_STATE_CONNECTED;
return 0;
}
static int
zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
enum dpll_lock_status *status,
enum dpll_lock_status_error *status_error,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
u8 mon_status, state;
int rc;
switch (zldpll->refsel_mode) {
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
case ZL_DPLL_MODE_REFSEL_MODE_NCO:
*status = DPLL_LOCK_STATUS_UNLOCKED;
return 0;
default:
break;
}
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(zldpll->id),
&mon_status);
if (rc)
return rc;
state = FIELD_GET(ZL_DPLL_MON_STATUS_STATE, mon_status);
switch (state) {
case ZL_DPLL_MON_STATUS_STATE_LOCK:
if (FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, mon_status))
*status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ;
else
*status = DPLL_LOCK_STATUS_LOCKED;
break;
case ZL_DPLL_MON_STATUS_STATE_HOLDOVER:
case ZL_DPLL_MON_STATUS_STATE_ACQUIRING:
*status = DPLL_LOCK_STATUS_HOLDOVER;
break;
default:
dev_warn(zldev->dev, "Unknown DPLL monitor status: 0x%02x\n",
mon_status);
*status = DPLL_LOCK_STATUS_UNLOCKED;
break;
}
return 0;
}
static int
zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
enum dpll_mode *mode, struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
switch (zldpll->refsel_mode) {
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
case ZL_DPLL_MODE_REFSEL_MODE_NCO:
case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
*mode = DPLL_MODE_MANUAL;
break;
case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
*mode = DPLL_MODE_AUTOMATIC;
break;
default:
return -EINVAL;
}
return 0;
}
static int
zl3073x_dpll_phase_offset_monitor_get(const struct dpll_device *dpll,
void *dpll_priv,
enum dpll_feature_state *state,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
if (zldpll->phase_monitor)
*state = DPLL_FEATURE_STATE_ENABLE;
else
*state = DPLL_FEATURE_STATE_DISABLE;
return 0;
}
static int
zl3073x_dpll_phase_offset_monitor_set(const struct dpll_device *dpll,
void *dpll_priv,
enum dpll_feature_state state,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
zldpll->phase_monitor = (state == DPLL_FEATURE_STATE_ENABLE);
return 0;
}
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
.direction_get = zl3073x_dpll_pin_direction_get,
.esync_get = zl3073x_dpll_input_pin_esync_get,
.esync_set = zl3073x_dpll_input_pin_esync_set,
.ffo_get = zl3073x_dpll_input_pin_ffo_get,
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
.phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get,
.phase_adjust_get = zl3073x_dpll_input_pin_phase_adjust_get,
.phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set,
.prio_get = zl3073x_dpll_input_pin_prio_get,
.prio_set = zl3073x_dpll_input_pin_prio_set,
.state_on_dpll_get = zl3073x_dpll_input_pin_state_on_dpll_get,
.state_on_dpll_set = zl3073x_dpll_input_pin_state_on_dpll_set,
};
static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = {
.direction_get = zl3073x_dpll_pin_direction_get,
.esync_get = zl3073x_dpll_output_pin_esync_get,
.esync_set = zl3073x_dpll_output_pin_esync_set,
.frequency_get = zl3073x_dpll_output_pin_frequency_get,
.frequency_set = zl3073x_dpll_output_pin_frequency_set,
.phase_adjust_get = zl3073x_dpll_output_pin_phase_adjust_get,
.phase_adjust_set = zl3073x_dpll_output_pin_phase_adjust_set,
.state_on_dpll_get = zl3073x_dpll_output_pin_state_on_dpll_get,
};
static const struct dpll_device_ops zl3073x_dpll_device_ops = {
.lock_status_get = zl3073x_dpll_lock_status_get,
.mode_get = zl3073x_dpll_mode_get,
.phase_offset_monitor_get = zl3073x_dpll_phase_offset_monitor_get,
.phase_offset_monitor_set = zl3073x_dpll_phase_offset_monitor_set,
};
static struct zl3073x_dpll_pin *
zl3073x_dpll_pin_alloc(struct zl3073x_dpll *zldpll, enum dpll_pin_direction dir,
u8 id)
{
struct zl3073x_dpll_pin *pin;
pin = kzalloc(sizeof(*pin), GFP_KERNEL);
if (!pin)
return ERR_PTR(-ENOMEM);
pin->dpll = zldpll;
pin->dir = dir;
pin->id = id;
return pin;
}
static void
zl3073x_dpll_pin_free(struct zl3073x_dpll_pin *pin)
{
WARN(pin->dpll_pin, "DPLL pin is still registered\n");
kfree(pin);
}
static int
zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_pin_props *props;
const struct dpll_pin_ops *ops;
int rc;
props = zl3073x_pin_props_get(zldpll->dev, pin->dir, pin->id);
if (IS_ERR(props))
return PTR_ERR(props);
strscpy(pin->label, props->package_label);
pin->esync_control = props->esync_control;
if (zl3073x_dpll_is_input_pin(pin)) {
rc = zl3073x_dpll_ref_prio_get(pin, &pin->prio);
if (rc)
goto err_prio_get;
if (pin->prio == ZL_DPLL_REF_PRIO_NONE) {
pin->prio = ZL_DPLL_REF_PRIO_MAX;
pin->selectable = false;
} else {
pin->selectable = true;
}
}
pin->dpll_pin = dpll_pin_get(zldpll->dev->clock_id, index, THIS_MODULE,
&props->dpll_props);
if (IS_ERR(pin->dpll_pin)) {
rc = PTR_ERR(pin->dpll_pin);
goto err_pin_get;
}
if (zl3073x_dpll_is_input_pin(pin))
ops = &zl3073x_dpll_input_pin_ops;
else
ops = &zl3073x_dpll_output_pin_ops;
rc = dpll_pin_register(zldpll->dpll_dev, pin->dpll_pin, ops, pin);
if (rc)
goto err_register;
zl3073x_pin_props_put(props);
return 0;
err_register:
dpll_pin_put(pin->dpll_pin);
err_prio_get:
pin->dpll_pin = NULL;
err_pin_get:
zl3073x_pin_props_put(props);
return rc;
}
static void
zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *pin)
{
struct zl3073x_dpll *zldpll = pin->dpll;
const struct dpll_pin_ops *ops;
WARN(!pin->dpll_pin, "DPLL pin is not registered\n");
if (zl3073x_dpll_is_input_pin(pin))
ops = &zl3073x_dpll_input_pin_ops;
else
ops = &zl3073x_dpll_output_pin_ops;
dpll_pin_unregister(zldpll->dpll_dev, pin->dpll_pin, ops, pin);
dpll_pin_put(pin->dpll_pin);
pin->dpll_pin = NULL;
}
static void
zl3073x_dpll_pins_unregister(struct zl3073x_dpll *zldpll)
{
struct zl3073x_dpll_pin *pin, *next;
list_for_each_entry_safe(pin, next, &zldpll->pins, list) {
zl3073x_dpll_pin_unregister(pin);
list_del(&pin->list);
zl3073x_dpll_pin_free(pin);
}
}
static bool
zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll,
enum dpll_pin_direction dir, u8 index)
{
struct zl3073x_dev *zldev = zldpll->dev;
bool is_diff, is_enabled;
const char *name;
if (dir == DPLL_PIN_DIRECTION_INPUT) {
u8 ref = zl3073x_input_pin_ref_get(index);
name = "REF";
if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_NCO)
return false;
is_diff = zl3073x_ref_is_diff(zldev, ref);
is_enabled = zl3073x_ref_is_enabled(zldev, ref);
} else {
u8 out = zl3073x_output_pin_out_get(index);
name = "OUT";
if (zl3073x_out_dpll_get(zldev, out) != zldpll->id) {
dev_dbg(zldev->dev,
"%s%u is driven by different DPLL\n", name,
out);
return false;
}
is_diff = zl3073x_out_is_diff(zldev, out);
is_enabled = zl3073x_out_is_enabled(zldev, out);
}
if (is_diff && zl3073x_is_n_pin(index)) {
dev_dbg(zldev->dev, "%s%u is differential, skipping N-pin\n",
name, index / 2);
return false;
}
if (!is_enabled) {
dev_dbg(zldev->dev, "%s%u%c is disabled\n", name, index / 2,
zl3073x_is_p_pin(index) ? 'P' : 'N');
return false;
}
return true;
}
static int
zl3073x_dpll_pins_register(struct zl3073x_dpll *zldpll)
{
struct zl3073x_dpll_pin *pin;
enum dpll_pin_direction dir;
u8 id, index;
int rc;
for (index = 0; index < ZL3073X_NUM_PINS; index++) {
if (index < ZL3073X_NUM_INPUT_PINS) {
id = index;
dir = DPLL_PIN_DIRECTION_INPUT;
} else {
id = index - ZL3073X_NUM_INPUT_PINS;
dir = DPLL_PIN_DIRECTION_OUTPUT;
}
if (!zl3073x_dpll_pin_is_registrable(zldpll, dir, id))
continue;
pin = zl3073x_dpll_pin_alloc(zldpll, dir, id);
if (IS_ERR(pin)) {
rc = PTR_ERR(pin);
goto error;
}
rc = zl3073x_dpll_pin_register(pin, index);
if (rc)
goto error;
list_add(&pin->list, &zldpll->pins);
}
return 0;
error:
zl3073x_dpll_pins_unregister(zldpll);
return rc;
}
static int
zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
{
struct zl3073x_dev *zldev = zldpll->dev;
u8 dpll_mode_refsel;
int rc;
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id),
&dpll_mode_refsel);
if (rc)
return rc;
zldpll->refsel_mode = FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE,
dpll_mode_refsel);
zldpll->forced_ref = FIELD_GET(ZL_DPLL_MODE_REFSEL_REF,
dpll_mode_refsel);
zldpll->dpll_dev = dpll_device_get(zldev->clock_id, zldpll->id,
THIS_MODULE);
if (IS_ERR(zldpll->dpll_dev)) {
rc = PTR_ERR(zldpll->dpll_dev);
zldpll->dpll_dev = NULL;
return rc;
}
rc = dpll_device_register(zldpll->dpll_dev,
zl3073x_prop_dpll_type_get(zldev, zldpll->id),
&zl3073x_dpll_device_ops, zldpll);
if (rc) {
dpll_device_put(zldpll->dpll_dev);
zldpll->dpll_dev = NULL;
}
return rc;
}
static void
zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
{
WARN(!zldpll->dpll_dev, "DPLL device is not registered\n");
dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops,
zldpll);
dpll_device_put(zldpll->dpll_dev);
zldpll->dpll_dev = NULL;
}
static bool
zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin)
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_dev *zldev = zldpll->dev;
unsigned int reg;
s64 phase_offset;
u8 ref;
int rc;
ref = zl3073x_input_pin_ref_get(pin->id);
if (pin->pin_state == DPLL_PIN_STATE_CONNECTED) {
reg = ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id);
} else if (zldpll->phase_monitor) {
u8 status;
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref),
&status);
if (rc) {
dev_err(zldev->dev,
"Failed to read %s refmon status: %pe\n",
pin->label, ERR_PTR(rc));
return false;
}
if (status != ZL_REF_MON_STATUS_OK)
return false;
reg = ZL_REG_REF_PHASE(ref);
} else {
return false;
}
rc = zl3073x_read_u48(zldev, reg, &phase_offset);
if (rc) {
dev_err(zldev->dev, "Failed to read ref phase offset: %pe\n",
ERR_PTR(rc));
return false;
}
phase_offset = div_s64(sign_extend64(phase_offset, 47), 100);
if (phase_offset != pin->phase_offset) {
dev_dbg(zldev->dev, "%s phase offset changed: %lld -> %lld\n",
pin->label, pin->phase_offset, phase_offset);
pin->phase_offset = phase_offset;
return true;
}
return false;
}
static bool
zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_dev *zldev = zldpll->dev;
u8 ref, status;
s64 ffo;
int rc;
ref = zl3073x_input_pin_ref_get(pin->id);
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &status);
if (rc) {
dev_err(zldev->dev, "Failed to read %s refmon status: %pe\n",
pin->label, ERR_PTR(rc));
return false;
}
if (status != ZL_REF_MON_STATUS_OK)
return false;
ffo = zl3073x_ref_ffo_get(zldev, ref);
if (pin->freq_offset != ffo) {
dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n",
pin->label, pin->freq_offset, ffo);
pin->freq_offset = ffo;
return true;
}
return false;
}
void
zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
{
struct zl3073x_dev *zldev = zldpll->dev;
enum dpll_lock_status lock_status;
struct device *dev = zldev->dev;
struct zl3073x_dpll_pin *pin;
int rc;
zldpll->check_count++;
rc = zl3073x_dpll_lock_status_get(zldpll->dpll_dev, zldpll,
&lock_status, NULL, NULL);
if (rc) {
dev_err(dev, "Failed to get DPLL%u lock status: %pe\n",
zldpll->id, ERR_PTR(rc));
return;
}
if (zldpll->lock_status != lock_status) {
zldpll->lock_status = lock_status;
dpll_device_change_ntf(zldpll->dpll_dev);
}
if (zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK)
return;
if (zldpll->phase_monitor) {
rc = zl3073x_ref_phase_offsets_update(zldev, zldpll->id);
if (rc) {
dev_err(zldev->dev,
"Failed to update phase offsets: %pe\n",
ERR_PTR(rc));
return;
}
}
list_for_each_entry(pin, &zldpll->pins, list) {
enum dpll_pin_state state;
bool pin_changed = false;
if (!zl3073x_dpll_is_input_pin(pin))
continue;
rc = zl3073x_dpll_ref_state_get(pin, &state);
if (rc) {
dev_err(dev,
"Failed to get %s on DPLL%u state: %pe\n",
pin->label, zldpll->id, ERR_PTR(rc));
return;
}
if (state != pin->pin_state) {
dev_dbg(dev, "%s state changed: %u->%u\n", pin->label,
pin->pin_state, state);
pin->pin_state = state;
pin_changed = true;
}
if (zldpll->check_count % 2 == 0) {
if (zl3073x_dpll_pin_phase_offset_check(pin))
pin_changed = true;
if (zl3073x_dpll_pin_ffo_check(pin))
pin_changed = true;
}
if (pin_changed)
dpll_pin_change_ntf(pin->dpll_pin);
}
}
int
zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dev *zldev)
{
int rc;
rc = zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_MASK, 0x1f);
if (rc)
return rc;
rc = zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_INTVL, 0x01);
if (rc)
return rc;
rc = zl3073x_write_u16(zldev, ZL_REG_SYNTH_PHASE_SHIFT_DATA, 0xffff);
if (rc)
return rc;
rc = zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_CTRL, 0x01);
if (rc)
return rc;
return rc;
}
struct zl3073x_dpll *
zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch)
{
struct zl3073x_dpll *zldpll;
zldpll = kzalloc(sizeof(*zldpll), GFP_KERNEL);
if (!zldpll)
return ERR_PTR(-ENOMEM);
zldpll->dev = zldev;
zldpll->id = ch;
INIT_LIST_HEAD(&zldpll->pins);
return zldpll;
}
void
zl3073x_dpll_free(struct zl3073x_dpll *zldpll)
{
WARN(zldpll->dpll_dev, "DPLL device is still registered\n");
kfree(zldpll);
}
int
zl3073x_dpll_register(struct zl3073x_dpll *zldpll)
{
int rc;
rc = zl3073x_dpll_device_register(zldpll);
if (rc)
return rc;
rc = zl3073x_dpll_pins_register(zldpll);
if (rc) {
zl3073x_dpll_device_unregister(zldpll);
return rc;
}
return 0;
}
void
zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll)
{
zl3073x_dpll_pins_unregister(zldpll);
zl3073x_dpll_device_unregister(zldpll);
}