summaryrefslogtreecommitdiffhomepage
path: root/drivers/dpll/zl3073x/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dpll/zl3073x/core.c')
-rw-r--r--drivers/dpll/zl3073x/core.c243
1 files changed, 139 insertions, 104 deletions
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
index 37f3c33570ee..5f1e70f3e40a 100644
--- a/drivers/dpll/zl3073x/core.c
+++ b/drivers/dpll/zl3073x/core.c
@@ -20,79 +20,30 @@
#include "dpll.h"
#include "regs.h"
-/* Chip IDs for zl30731 */
-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");
-
-/* Chip IDs for zl30732 */
-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");
-
-/* Chip IDs for zl30733 */
-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");
+#define ZL_CHIP_INFO(_id, _nchannels, _flags) \
+ { .id = (_id), .num_channels = (_nchannels), .flags = (_flags) }
-/* Chip IDs for zl30734 */
-static const u16 zl30734_ids[] = {
- 0x0E96,
- 0x1E96,
- 0x2E96,
+static const struct zl3073x_chip_info zl3073x_chip_ids[] = {
+ ZL_CHIP_INFO(0x0E30, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
+ ZL_CHIP_INFO(0x0E93, 1, ZL3073X_FLAG_REF_PHASE_COMP_32),
+ ZL_CHIP_INFO(0x0E94, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
+ ZL_CHIP_INFO(0x0E95, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),
+ ZL_CHIP_INFO(0x0E96, 4, ZL3073X_FLAG_REF_PHASE_COMP_32),
+ ZL_CHIP_INFO(0x0E97, 5, ZL3073X_FLAG_REF_PHASE_COMP_32),
+ ZL_CHIP_INFO(0x1E93, 1, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x1E94, 2, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x1E95, 3, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x1E96, 4, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x1E97, 5, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x1F60, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
+ ZL_CHIP_INFO(0x2E93, 1, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x2E94, 2, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x2E95, 3, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x2E96, 4, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x2E97, 5, ZL3073X_FLAG_DIE_TEMP),
+ ZL_CHIP_INFO(0x3FC4, 2, ZL3073X_FLAG_DIE_TEMP),
};
-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");
-
-/* Chip IDs for zl30735 */
-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 256
@@ -588,17 +539,26 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
}
}
+ for (i = 0; i < zldev->info->num_channels; i++) {
+ rc = zl3073x_chan_state_fetch(zldev, i);
+ if (rc) {
+ dev_err(zldev->dev,
+ "Failed to fetch channel state: %pe\n",
+ ERR_PTR(rc));
+ return rc;
+ }
+ }
+
return rc;
}
static void
-zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
+zl3073x_dev_ref_states_update(struct zl3073x_dev *zldev)
{
int i, rc;
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(i),
- &zldev->ref[i].mon_status);
+ rc = zl3073x_ref_state_update(zldev, i);
if (rc)
dev_warn(zldev->dev,
"Failed to get REF%u status: %pe\n", i,
@@ -606,6 +566,20 @@ zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
}
}
+static void
+zl3073x_dev_chan_states_update(struct zl3073x_dev *zldev)
+{
+ int i, rc;
+
+ for (i = 0; i < zldev->info->num_channels; i++) {
+ rc = zl3073x_chan_state_update(zldev, i);
+ if (rc)
+ dev_warn(zldev->dev,
+ "Failed to get DPLL%u state: %pe\n", i,
+ ERR_PTR(rc));
+ }
+}
+
/**
* zl3073x_ref_phase_offsets_update - update reference phase offsets
* @zldev: pointer to zl3073x_dev structure
@@ -658,22 +632,21 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
}
/**
- * zl3073x_ref_ffo_update - update reference fractional frequency offsets
+ * zl3073x_ref_freq_meas_latch - latch reference frequency measurements
* @zldev: pointer to zl3073x_dev structure
+ * @type: measurement type (ZL_REF_FREQ_MEAS_CTRL_*)
*
- * The function asks device to update fractional frequency offsets latch
- * registers the latest measured values, reads and stores them into
+ * The function waits for the previous measurement to finish, selects all
+ * references and requests a new measurement of the given type.
*
* Return: 0 on success, <0 on error
*/
static int
-zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
+zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, u8 type)
{
- int i, rc;
+ int rc;
- /* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero
- * to ensure that the measured data are coherent.
- */
+ /* Wait for previous measurement to finish */
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL);
if (rc)
@@ -689,15 +662,64 @@ zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
if (rc)
return rc;
- /* Request frequency offset measurement */
- rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
- ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
+ /* Request measurement */
+ rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, type);
if (rc)
return rc;
/* Wait for finish */
- rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
- ZL_REF_FREQ_MEAS_CTRL);
+ return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
+ ZL_REF_FREQ_MEAS_CTRL);
+}
+
+/**
+ * zl3073x_ref_freq_meas_update - update measured input reference frequencies
+ * @zldev: pointer to zl3073x_dev structure
+ *
+ * The function asks device to latch measured input reference frequencies
+ * and stores the results in the ref state.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int
+zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev)
+{
+ int i, rc;
+
+ rc = zl3073x_ref_freq_meas_latch(zldev, ZL_REF_FREQ_MEAS_CTRL_REF_FREQ);
+ if (rc)
+ return rc;
+
+ /* Read measured frequencies in Hz (unsigned 32-bit, LSB = 1 Hz) */
+ for (i = 0; i < ZL3073X_NUM_REFS; i++) {
+ u32 value;
+
+ rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
+ if (rc)
+ return rc;
+
+ zldev->ref[i].meas_freq = value;
+ }
+
+ return 0;
+}
+
+/**
+ * zl3073x_ref_ffo_update - update reference fractional frequency offsets
+ * @zldev: pointer to zl3073x_dev structure
+ *
+ * The function asks device to latch the latest measured fractional
+ * frequency offset values, reads and stores them into the ref state.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int
+zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
+{
+ int i, rc;
+
+ rc = zl3073x_ref_freq_meas_latch(zldev,
+ ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
if (rc)
return rc;
@@ -728,8 +750,11 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
struct zl3073x_dpll *zldpll;
int rc;
- /* Update input references status */
- zl3073x_dev_ref_status_update(zldev);
+ /* Update input references' states */
+ zl3073x_dev_ref_states_update(zldev);
+
+ /* Update DPLL channels' states */
+ zl3073x_dev_chan_states_update(zldev);
/* Update DPLL-to-connected-ref phase offsets registers */
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
@@ -737,6 +762,20 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
ERR_PTR(rc));
+ /* Update measured input reference frequencies if any DPLL has
+ * frequency monitoring enabled.
+ */
+ list_for_each_entry(zldpll, &zldev->dplls, list) {
+ if (zldpll->freq_monitor) {
+ rc = zl3073x_ref_freq_meas_update(zldev);
+ if (rc)
+ dev_warn(zldev->dev,
+ "Failed to update measured frequencies: %pe\n",
+ ERR_PTR(rc));
+ break;
+ }
+ }
+
/* Update references' fractional frequency offsets */
rc = zl3073x_ref_ffo_update(zldev);
if (rc)
@@ -766,8 +805,7 @@ int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor)
value = (factor + 1) & 0x0f;
/* Update phase measurement control register */
- dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
- dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, value);
+ FIELD_MODIFY(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, &dpll_meas_ctrl, value);
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl);
if (rc)
return rc;
@@ -942,7 +980,7 @@ static void zl3073x_dev_dpll_fini(void *ptr)
}
static int
-zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
+zl3073x_devm_dpll_init(struct zl3073x_dev *zldev)
{
struct kthread_worker *kworker;
struct zl3073x_dpll *zldpll;
@@ -952,7 +990,7 @@ zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
INIT_LIST_HEAD(&zldev->dplls);
/* Allocate all DPLLs */
- for (i = 0; i < num_dplls; i++) {
+ for (i = 0; i < zldev->info->num_channels; i++) {
zldpll = zl3073x_dpll_alloc(zldev, i);
if (IS_ERR(zldpll)) {
dev_err_probe(zldev->dev, PTR_ERR(zldpll),
@@ -992,14 +1030,12 @@ error:
/**
* zl3073x_dev_probe - initialize zl3073x device
* @zldev: pointer to zl3073x device
- * @chip_info: chip info based on compatible
*
* Common initialization of zl3073x device structure.
*
* Returns: 0 on success, <0 on error
*/
-int zl3073x_dev_probe(struct zl3073x_dev *zldev,
- const struct zl3073x_chip_info *chip_info)
+int zl3073x_dev_probe(struct zl3073x_dev *zldev)
{
u16 id, revision, fw_ver;
unsigned int i;
@@ -1011,18 +1047,17 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
if (rc)
return rc;
- /* Check it matches */
- for (i = 0; i < chip_info->num_ids; i++) {
- if (id == chip_info->ids[i])
+ /* Detect chip variant */
+ for (i = 0; i < ARRAY_SIZE(zl3073x_chip_ids); i++) {
+ if (zl3073x_chip_ids[i].id == id)
break;
}
- if (i == chip_info->num_ids) {
+ if (i == ARRAY_SIZE(zl3073x_chip_ids))
return dev_err_probe(zldev->dev, -ENODEV,
- "Unknown or non-match chip ID: 0x%0x\n",
- id);
- }
- zldev->chip_id = id;
+ "Unknown chip ID: 0x%04x\n", id);
+
+ zldev->info = &zl3073x_chip_ids[i];
/* Read revision, firmware version and custom config version */
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
@@ -1061,7 +1096,7 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
"Failed to initialize mutex\n");
/* Register DPLL channels */
- rc = zl3073x_devm_dpll_init(zldev, chip_info->num_channels);
+ rc = zl3073x_devm_dpll_init(zldev);
if (rc)
return rc;