mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 04:04:43 +01:00
dpll: zl3073x: fix REF_PHASE_OFFSET_COMP register width for some chip IDs
The REF_PHASE_OFFSET_COMP register is 48-bit wide on most zl3073x chip
variants, but only 32-bit wide on chip IDs 0x0E30, 0x0E93..0x0E97 and
0x1F60. The driver unconditionally uses 48-bit read/write operations,
which on 32-bit variants causes reading 2 bytes past the register
boundary (corrupting the value) and writing 2 bytes into the adjacent
register.
Fix this by storing the chip ID in the device structure during probe
and adding a helper to detect the affected variants. Use the correct
register width for read/write operations and the matching sign extension
bit (31 vs 47) when interpreting the phase compensation value.
Fixes: 6287262f76 ("dpll: zl3073x: Add support to adjust phase")
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260220155755.448185-1-ivecera@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
ca220141fa
commit
4cfe066a82
5 changed files with 55 additions and 7 deletions
|
|
@ -1026,6 +1026,7 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
|||
"Unknown or non-match chip ID: 0x%0x\n",
|
||||
id);
|
||||
}
|
||||
zldev->chip_id = id;
|
||||
|
||||
/* Read revision, firmware version and custom config version */
|
||||
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ struct zl3073x_dpll;
|
|||
* @dev: pointer to device
|
||||
* @regmap: regmap to access device registers
|
||||
* @multiop_lock: to serialize multiple register operations
|
||||
* @chip_id: chip ID read from hardware
|
||||
* @ref: array of input references' invariants
|
||||
* @out: array of outs' invariants
|
||||
* @synth: array of synths' invariants
|
||||
|
|
@ -48,6 +49,7 @@ struct zl3073x_dev {
|
|||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct mutex multiop_lock;
|
||||
u16 chip_id;
|
||||
|
||||
/* Invariants */
|
||||
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
|
||||
|
|
@ -144,6 +146,32 @@ int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
|
|||
|
||||
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);
|
||||
|
||||
/**
|
||||
* zl3073x_dev_is_ref_phase_comp_32bit - check ref phase comp register size
|
||||
* @zldev: pointer to zl3073x device
|
||||
*
|
||||
* Some chip IDs have a 32-bit wide ref_phase_offset_comp register instead
|
||||
* of the default 48-bit.
|
||||
*
|
||||
* Return: true if the register is 32-bit, false if 48-bit
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_dev_is_ref_phase_comp_32bit(struct zl3073x_dev *zldev)
|
||||
{
|
||||
switch (zldev->chip_id) {
|
||||
case 0x0E30:
|
||||
case 0x0E93:
|
||||
case 0x0E94:
|
||||
case 0x0E95:
|
||||
case 0x0E96:
|
||||
case 0x0E97:
|
||||
case 0x1F60:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
zl3073x_is_n_pin(u8 id)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -475,8 +475,11 @@ zl3073x_dpll_input_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,
|
|||
ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
ref = zl3073x_ref_state_get(zldev, ref_id);
|
||||
|
||||
/* Perform sign extension for 48bit signed value */
|
||||
phase_comp = sign_extend64(ref->phase_comp, 47);
|
||||
/* Perform sign extension based on register width */
|
||||
if (zl3073x_dev_is_ref_phase_comp_32bit(zldev))
|
||||
phase_comp = sign_extend64(ref->phase_comp, 31);
|
||||
else
|
||||
phase_comp = sign_extend64(ref->phase_comp, 47);
|
||||
|
||||
/* Reverse two's complement negation applied during set and convert
|
||||
* to 32bit signed int
|
||||
|
|
|
|||
|
|
@ -121,8 +121,16 @@ int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
|||
return rc;
|
||||
|
||||
/* Read phase compensation register */
|
||||
rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
|
||||
&ref->phase_comp);
|
||||
if (zl3073x_dev_is_ref_phase_comp_32bit(zldev)) {
|
||||
u32 val;
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_REF_PHASE_OFFSET_COMP_32,
|
||||
&val);
|
||||
ref->phase_comp = val;
|
||||
} else {
|
||||
rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
|
||||
&ref->phase_comp);
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
|
@ -179,9 +187,16 @@ int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
|||
if (!rc && dref->sync_ctrl != ref->sync_ctrl)
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL,
|
||||
ref->sync_ctrl);
|
||||
if (!rc && dref->phase_comp != ref->phase_comp)
|
||||
rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
|
||||
ref->phase_comp);
|
||||
if (!rc && dref->phase_comp != ref->phase_comp) {
|
||||
if (zl3073x_dev_is_ref_phase_comp_32bit(zldev))
|
||||
rc = zl3073x_write_u32(zldev,
|
||||
ZL_REG_REF_PHASE_OFFSET_COMP_32,
|
||||
ref->phase_comp);
|
||||
else
|
||||
rc = zl3073x_write_u48(zldev,
|
||||
ZL_REG_REF_PHASE_OFFSET_COMP,
|
||||
ref->phase_comp);
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@
|
|||
#define ZL_REF_CONFIG_DIFF_EN BIT(2)
|
||||
|
||||
#define ZL_REG_REF_PHASE_OFFSET_COMP ZL_REG(10, 0x28, 6)
|
||||
#define ZL_REG_REF_PHASE_OFFSET_COMP_32 ZL_REG(10, 0x28, 4)
|
||||
|
||||
#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1)
|
||||
#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue