#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/dev_printk.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/regmap.h>
#include <linux/sprintf.h>
#include <linux/string_choices.h>
#include <linux/unaligned.h>
#include <net/devlink.h>
#include "core.h"
#include "devlink.h"
#include "dpll.h"
#include "regs.h"
static const u16 zl30731_ids[] = {
0x0E93,
0x1E93,
0x2E93,
};
const struct zl3073x_chip_info zl30731_chip_info = {
.ids = zl30731_ids,
.num_ids = ARRAY_SIZE(zl30731_ids),
.num_channels = 1,
};
EXPORT_SYMBOL_NS_GPL(zl30731_chip_info, "ZL3073X");
static const u16 zl30732_ids[] = {
0x0E30,
0x0E94,
0x1E94,
0x1F60,
0x2E94,
0x3FC4,
};
const struct zl3073x_chip_info zl30732_chip_info = {
.ids = zl30732_ids,
.num_ids = ARRAY_SIZE(zl30732_ids),
.num_channels = 2,
};
EXPORT_SYMBOL_NS_GPL(zl30732_chip_info, "ZL3073X");
static const u16 zl30733_ids[] = {
0x0E95,
0x1E95,
0x2E95,
};
const struct zl3073x_chip_info zl30733_chip_info = {
.ids = zl30733_ids,
.num_ids = ARRAY_SIZE(zl30733_ids),
.num_channels = 3,
};
EXPORT_SYMBOL_NS_GPL(zl30733_chip_info, "ZL3073X");
static const u16 zl30734_ids[] = {
0x0E96,
0x1E96,
0x2E96,
};
const struct zl3073x_chip_info zl30734_chip_info = {
.ids = zl30734_ids,
.num_ids = ARRAY_SIZE(zl30734_ids),
.num_channels = 4,
};
EXPORT_SYMBOL_NS_GPL(zl30734_chip_info, "ZL3073X");
static const u16 zl30735_ids[] = {
0x0E97,
0x1E97,
0x2E97,
};
const struct zl3073x_chip_info zl30735_chip_info = {
.ids = zl30735_ids,
.num_ids = ARRAY_SIZE(zl30735_ids),
.num_channels = 5,
};
EXPORT_SYMBOL_NS_GPL(zl30735_chip_info, "ZL3073X");
#define ZL_RANGE_OFFSET 0x80
#define ZL_PAGE_SIZE 0x80
#define ZL_NUM_PAGES 15
#define ZL_PAGE_SEL 0x7F
#define ZL_PAGE_SEL_MASK GENMASK(3, 0)
#define ZL_NUM_REGS (ZL_NUM_PAGES * ZL_PAGE_SIZE)
static const struct regmap_range_cfg zl3073x_regmap_range = {
.range_min = ZL_RANGE_OFFSET,
.range_max = ZL_RANGE_OFFSET + ZL_NUM_REGS - 1,
.selector_reg = ZL_PAGE_SEL,
.selector_mask = ZL_PAGE_SEL_MASK,
.selector_shift = 0,
.window_start = 0,
.window_len = ZL_PAGE_SIZE,
};
static bool
zl3073x_is_volatile_reg(struct device *dev __maybe_unused, unsigned int reg)
{
return reg != ZL_PAGE_SEL;
}
const struct regmap_config zl3073x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ZL_RANGE_OFFSET + ZL_NUM_REGS - 1,
.ranges = &zl3073x_regmap_range,
.num_ranges = 1,
.cache_type = REGCACHE_MAPLE,
.volatile_reg = zl3073x_is_volatile_reg,
};
EXPORT_SYMBOL_NS_GPL(zl3073x_regmap_config, "ZL3073X");
int
zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
{
static const u16 base_freqs[] = {
1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50, 64, 80, 100, 125,
128, 160, 200, 250, 256, 320, 400, 500, 625, 640, 800, 1000,
1250, 1280, 1600, 2000, 2500, 3125, 3200, 4000, 5000, 6250,
6400, 8000, 10000, 12500, 15625, 16000, 20000, 25000, 31250,
32000, 40000, 50000, 62500,
};
u32 div;
int i;
for (i = 0; i < ARRAY_SIZE(base_freqs); i++) {
div = freq / base_freqs[i];
if (div <= U16_MAX && (freq % base_freqs[i]) == 0) {
if (base)
*base = base_freqs[i];
if (mult)
*mult = div;
return 0;
}
}
return -EINVAL;
}
static bool
zl3073x_check_reg(struct zl3073x_dev *zldev, unsigned int reg, size_t size)
{
if (ZL_REG_PAGE(reg) >= 10)
lockdep_assert_held(&zldev->multiop_lock);
if (ZL_REG_OFFSET(reg) > ZL_REG_MAX_OFFSET(reg)) {
dev_err(zldev->dev, "Index out of range for reg 0x%04lx\n",
ZL_REG_ADDR(reg));
return false;
}
if (ZL_REG_SIZE(reg) != size) {
dev_err(zldev->dev, "Invalid size %zu for reg 0x%04lx\n",
size, ZL_REG_ADDR(reg));
return false;
}
return true;
}
static int
zl3073x_read_reg(struct zl3073x_dev *zldev, unsigned int reg, void *val,
size_t size)
{
int rc;
if (!zl3073x_check_reg(zldev, reg, size))
return -EINVAL;
reg = ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET;
rc = regmap_bulk_read(zldev->regmap, reg, val, size);
if (rc) {
dev_err(zldev->dev, "Failed to read reg 0x%04x: %pe\n", reg,
ERR_PTR(rc));
return rc;
}
return 0;
}
static int
zl3073x_write_reg(struct zl3073x_dev *zldev, unsigned int reg, const void *val,
size_t size)
{
int rc;
if (!zl3073x_check_reg(zldev, reg, size))
return -EINVAL;
reg = ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET;
rc = regmap_bulk_write(zldev->regmap, reg, val, size);
if (rc) {
dev_err(zldev->dev, "Failed to write reg 0x%04x: %pe\n", reg,
ERR_PTR(rc));
return rc;
}
return 0;
}
int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val)
{
return zl3073x_read_reg(zldev, reg, val, sizeof(*val));
}
int zl3073x_write_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 val)
{
return zl3073x_write_reg(zldev, reg, &val, sizeof(val));
}
int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val)
{
int rc;
rc = zl3073x_read_reg(zldev, reg, val, sizeof(*val));
if (!rc)
be16_to_cpus(val);
return rc;
}
int zl3073x_write_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 val)
{
cpu_to_be16s(&val);
return zl3073x_write_reg(zldev, reg, &val, sizeof(val));
}
int zl3073x_read_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 *val)
{
int rc;
rc = zl3073x_read_reg(zldev, reg, val, sizeof(*val));
if (!rc)
be32_to_cpus(val);
return rc;
}
int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val)
{
cpu_to_be32s(&val);
return zl3073x_write_reg(zldev, reg, &val, sizeof(val));
}
int zl3073x_read_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 *val)
{
u8 buf[6];
int rc;
rc = zl3073x_read_reg(zldev, reg, buf, sizeof(buf));
if (!rc)
*val = get_unaligned_be48(buf);
return rc;
}
int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val)
{
u8 buf[6];
if (val > GENMASK_ULL(47, 0) && val < GENMASK_ULL(63, 47)) {
dev_err(zldev->dev, "Value 0x%0llx out of range\n", val);
return -EINVAL;
}
put_unaligned_be48(val, buf);
return zl3073x_write_reg(zldev, reg, buf, sizeof(buf));
}
int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask)
{
#define ZL_POLL_SLEEP_US 10
#define ZL_POLL_TIMEOUT_US 2000000
unsigned int val;
if (ZL_REG_SIZE(reg) != 1) {
dev_err(zldev->dev, "Invalid reg 0x%04lx size for polling\n",
ZL_REG_ADDR(reg));
return -EINVAL;
}
reg = ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET;
return regmap_read_poll_timeout(zldev->regmap, reg, val, !(val & mask),
ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US);
}
int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
unsigned int mask_reg, u16 mask_val)
{
int rc;
rc = zl3073x_write_u16(zldev, mask_reg, mask_val);
if (rc)
return rc;
rc = zl3073x_write_u8(zldev, op_reg, op_val);
if (rc)
return rc;
return zl3073x_poll_zero_u8(zldev, op_reg, op_val);
}
static int
zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
{
struct zl3073x_ref *input = &zldev->ref[index];
u8 ref_config;
int rc;
if (zl3073x_is_n_pin(index) &&
zl3073x_ref_is_diff(zldev, index - 1)) {
input->enabled = zl3073x_ref_is_enabled(zldev, index - 1);
input->diff = true;
return 0;
}
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(index));
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref_config);
if (rc)
return rc;
input->enabled = FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config);
input->diff = FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config);
dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
str_enabled_disabled(input->enabled),
input->diff ? "differential" : "single-ended");
return rc;
}
static int
zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
{
struct zl3073x_out *out = &zldev->out[index];
u8 output_ctrl, output_mode;
int rc;
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl);
if (rc)
return rc;
out->enabled = FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl);
out->synth = FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl);
dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
str_enabled_disabled(out->enabled), out->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(index));
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
if (rc)
return rc;
out->signal_format = FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT,
output_mode);
dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
out->signal_format);
return rc;
}
static int
zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
{
struct zl3073x_synth *synth = &zldev->synth[index];
u16 base, m, n;
u8 synth_ctrl;
u32 mult;
int rc;
rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl);
if (rc)
return rc;
synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl);
synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl);
dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index,
str_enabled_disabled(synth->enabled), synth->dpll);
guard(mutex)(&zldev->multiop_lock);
rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
ZL_REG_SYNTH_MB_MASK, BIT(index));
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base);
if (rc)
return rc;
rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n);
if (rc)
return rc;
if (!n) {
dev_err(zldev->dev,
"Zero divisor for SYNTH%u retrieved from device\n",
index);
return -EINVAL;
}
zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n);
dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
zldev->synth[index].freq);
return rc;
}
static int
zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
{
int rc;
u8 i;
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
rc = zl3073x_ref_state_fetch(zldev, i);
if (rc) {
dev_err(zldev->dev,
"Failed to fetch input state: %pe\n",
ERR_PTR(rc));
return rc;
}
}
for (i = 0; i < ZL3073X_NUM_SYNTHS; i++) {
rc = zl3073x_synth_state_fetch(zldev, i);
if (rc) {
dev_err(zldev->dev,
"Failed to fetch synth state: %pe\n",
ERR_PTR(rc));
return rc;
}
}
for (i = 0; i < ZL3073X_NUM_OUTS; i++) {
rc = zl3073x_out_state_fetch(zldev, i);
if (rc) {
dev_err(zldev->dev,
"Failed to fetch output state: %pe\n",
ERR_PTR(rc));
return rc;
}
}
return rc;
}
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
{
int rc;
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST,
ZL_REF_PHASE_ERR_READ_RQST_RD);
if (rc)
return rc;
if (channel != -1) {
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_IDX, channel);
if (rc)
return rc;
}
rc = zl3073x_write_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST,
ZL_REF_PHASE_ERR_READ_RQST_RD);
if (rc)
return rc;
return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST,
ZL_REF_PHASE_ERR_READ_RQST_RD);
}
static int
zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
{
int i, rc;
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL);
if (rc)
return rc;
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_MASK_3_0,
GENMASK(7, 0));
if (rc)
return rc;
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_MASK_4,
GENMASK(1, 0));
if (rc)
return rc;
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
if (rc)
return rc;
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL);
if (rc)
return rc;
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
s32 value;
rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
if (rc)
return rc;
zldev->ref[i].ffo = mul_s64_u64_shr(value, 1000000, 32);
}
return 0;
}
static void
zl3073x_dev_periodic_work(struct kthread_work *work)
{
struct zl3073x_dev *zldev = container_of(work, struct zl3073x_dev,
work.work);
struct zl3073x_dpll *zldpll;
int rc;
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
if (rc)
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
ERR_PTR(rc));
rc = zl3073x_ref_ffo_update(zldev);
if (rc)
dev_warn(zldev->dev,
"Failed to update fractional frequency offsets: %pe\n",
ERR_PTR(rc));
list_for_each_entry(zldpll, &zldev->dplls, list)
zl3073x_dpll_changes_check(zldpll);
kthread_queue_delayed_work(zldev->kworker, &zldev->work,
msecs_to_jiffies(500));
}
static void zl3073x_dev_dpll_fini(void *ptr)
{
struct zl3073x_dpll *zldpll, *next;
struct zl3073x_dev *zldev = ptr;
if (zldev->kworker) {
kthread_cancel_delayed_work_sync(&zldev->work);
kthread_destroy_worker(zldev->kworker);
zldev->kworker = NULL;
}
list_for_each_entry_safe(zldpll, next, &zldev->dplls, list) {
zl3073x_dpll_unregister(zldpll);
list_del(&zldpll->list);
zl3073x_dpll_free(zldpll);
}
}
static int
zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
{
struct kthread_worker *kworker;
struct zl3073x_dpll *zldpll;
unsigned int i;
int rc;
INIT_LIST_HEAD(&zldev->dplls);
for (i = 0; i < num_dplls; i++) {
zldpll = zl3073x_dpll_alloc(zldev, i);
if (IS_ERR(zldpll)) {
dev_err_probe(zldev->dev, PTR_ERR(zldpll),
"Failed to alloc DPLL%u\n", i);
rc = PTR_ERR(zldpll);
goto error;
}
rc = zl3073x_dpll_register(zldpll);
if (rc) {
dev_err_probe(zldev->dev, rc,
"Failed to register DPLL%u\n", i);
zl3073x_dpll_free(zldpll);
goto error;
}
list_add_tail(&zldpll->list, &zldev->dplls);
}
rc = zl3073x_dpll_init_fine_phase_adjust(zldev);
if (rc) {
dev_err_probe(zldev->dev, rc,
"Failed to init fine phase correction\n");
goto error;
}
kthread_init_delayed_work(&zldev->work, zl3073x_dev_periodic_work);
kworker = kthread_run_worker(0, "zl3073x-%s", dev_name(zldev->dev));
if (IS_ERR(kworker)) {
rc = PTR_ERR(kworker);
goto error;
}
zldev->kworker = kworker;
kthread_queue_delayed_work(zldev->kworker, &zldev->work, 0);
rc = devm_add_action_or_reset(zldev->dev, zl3073x_dev_dpll_fini, zldev);
if (rc)
goto error;
return 0;
error:
zl3073x_dev_dpll_fini(zldev);
return rc;
}
static int
zl3073x_dev_phase_meas_setup(struct zl3073x_dev *zldev, int num_channels)
{
u8 dpll_meas_ctrl, mask;
int i, rc;
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl);
if (rc)
return rc;
dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, 3);
dpll_meas_ctrl |= ZL_DPLL_MEAS_CTRL_EN;
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl);
if (rc)
return rc;
for (i = 0, mask = 0; i < num_channels; i++)
mask |= BIT(i);
return zl3073x_write_u8(zldev, ZL_REG_DPLL_PHASE_ERR_READ_MASK, mask);
}
int zl3073x_dev_probe(struct zl3073x_dev *zldev,
const struct zl3073x_chip_info *chip_info)
{
u16 id, revision, fw_ver;
unsigned int i;
u32 cfg_ver;
int rc;
rc = zl3073x_read_u16(zldev, ZL_REG_ID, &id);
if (rc)
return rc;
for (i = 0; i < chip_info->num_ids; i++) {
if (id == chip_info->ids[i])
break;
}
if (i == chip_info->num_ids) {
return dev_err_probe(zldev->dev, -ENODEV,
"Unknown or non-match chip ID: 0x%0x\n",
id);
}
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, &fw_ver);
if (rc)
return rc;
rc = zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, &cfg_ver);
if (rc)
return rc;
dev_dbg(zldev->dev, "ChipID(%X), ChipRev(%X), FwVer(%u)\n", id,
revision, fw_ver);
dev_dbg(zldev->dev, "Custom config version: %lu.%lu.%lu.%lu\n",
FIELD_GET(GENMASK(31, 24), cfg_ver),
FIELD_GET(GENMASK(23, 16), cfg_ver),
FIELD_GET(GENMASK(15, 8), cfg_ver),
FIELD_GET(GENMASK(7, 0), cfg_ver));
zldev->clock_id = get_random_u64();
rc = devm_mutex_init(zldev->dev, &zldev->multiop_lock);
if (rc)
return dev_err_probe(zldev->dev, rc,
"Failed to initialize mutex\n");
rc = zl3073x_dev_state_fetch(zldev);
if (rc)
return rc;
rc = zl3073x_dev_phase_meas_setup(zldev, chip_info->num_channels);
if (rc)
return dev_err_probe(zldev->dev, rc,
"Failed to setup phase measurement\n");
rc = zl3073x_devm_dpll_init(zldev, chip_info->num_channels);
if (rc)
return rc;
rc = zl3073x_devlink_register(zldev);
if (rc)
return dev_err_probe(zldev->dev, rc,
"Failed to register devlink instance\n");
return 0;
}
EXPORT_SYMBOL_NS_GPL(zl3073x_dev_probe, "ZL3073X");
MODULE_AUTHOR("Ivan Vecera <[email protected]>");
MODULE_DESCRIPTION("Microchip ZL3073x core driver");
MODULE_LICENSE("GPL");