Skip to content

Commit 4cfe066

Browse files
ivecerakuba-moo
authored andcommitted
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: 6287262 ("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>
1 parent ca22014 commit 4cfe066

5 files changed

Lines changed: 55 additions & 7 deletions

File tree

drivers/dpll/zl3073x/core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,7 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
10261026
"Unknown or non-match chip ID: 0x%0x\n",
10271027
id);
10281028
}
1029+
zldev->chip_id = id;
10291030

10301031
/* Read revision, firmware version and custom config version */
10311032
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);

drivers/dpll/zl3073x/core.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct zl3073x_dpll;
3535
* @dev: pointer to device
3636
* @regmap: regmap to access device registers
3737
* @multiop_lock: to serialize multiple register operations
38+
* @chip_id: chip ID read from hardware
3839
* @ref: array of input references' invariants
3940
* @out: array of outs' invariants
4041
* @synth: array of synths' invariants
@@ -48,6 +49,7 @@ struct zl3073x_dev {
4849
struct device *dev;
4950
struct regmap *regmap;
5051
struct mutex multiop_lock;
52+
u16 chip_id;
5153

5254
/* Invariants */
5355
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
@@ -144,6 +146,32 @@ int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
144146

145147
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);
146148

149+
/**
150+
* zl3073x_dev_is_ref_phase_comp_32bit - check ref phase comp register size
151+
* @zldev: pointer to zl3073x device
152+
*
153+
* Some chip IDs have a 32-bit wide ref_phase_offset_comp register instead
154+
* of the default 48-bit.
155+
*
156+
* Return: true if the register is 32-bit, false if 48-bit
157+
*/
158+
static inline bool
159+
zl3073x_dev_is_ref_phase_comp_32bit(struct zl3073x_dev *zldev)
160+
{
161+
switch (zldev->chip_id) {
162+
case 0x0E30:
163+
case 0x0E93:
164+
case 0x0E94:
165+
case 0x0E95:
166+
case 0x0E96:
167+
case 0x0E97:
168+
case 0x1F60:
169+
return true;
170+
default:
171+
return false;
172+
}
173+
}
174+
147175
static inline bool
148176
zl3073x_is_n_pin(u8 id)
149177
{

drivers/dpll/zl3073x/dpll.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,8 +475,11 @@ zl3073x_dpll_input_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,
475475
ref_id = zl3073x_input_pin_ref_get(pin->id);
476476
ref = zl3073x_ref_state_get(zldev, ref_id);
477477

478-
/* Perform sign extension for 48bit signed value */
479-
phase_comp = sign_extend64(ref->phase_comp, 47);
478+
/* Perform sign extension based on register width */
479+
if (zl3073x_dev_is_ref_phase_comp_32bit(zldev))
480+
phase_comp = sign_extend64(ref->phase_comp, 31);
481+
else
482+
phase_comp = sign_extend64(ref->phase_comp, 47);
480483

481484
/* Reverse two's complement negation applied during set and convert
482485
* to 32bit signed int

drivers/dpll/zl3073x/ref.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,16 @@ int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
121121
return rc;
122122

123123
/* Read phase compensation register */
124-
rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
125-
&ref->phase_comp);
124+
if (zl3073x_dev_is_ref_phase_comp_32bit(zldev)) {
125+
u32 val;
126+
127+
rc = zl3073x_read_u32(zldev, ZL_REG_REF_PHASE_OFFSET_COMP_32,
128+
&val);
129+
ref->phase_comp = val;
130+
} else {
131+
rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
132+
&ref->phase_comp);
133+
}
126134
if (rc)
127135
return rc;
128136

@@ -179,9 +187,16 @@ int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
179187
if (!rc && dref->sync_ctrl != ref->sync_ctrl)
180188
rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL,
181189
ref->sync_ctrl);
182-
if (!rc && dref->phase_comp != ref->phase_comp)
183-
rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
184-
ref->phase_comp);
190+
if (!rc && dref->phase_comp != ref->phase_comp) {
191+
if (zl3073x_dev_is_ref_phase_comp_32bit(zldev))
192+
rc = zl3073x_write_u32(zldev,
193+
ZL_REG_REF_PHASE_OFFSET_COMP_32,
194+
ref->phase_comp);
195+
else
196+
rc = zl3073x_write_u48(zldev,
197+
ZL_REG_REF_PHASE_OFFSET_COMP,
198+
ref->phase_comp);
199+
}
185200
if (rc)
186201
return rc;
187202

drivers/dpll/zl3073x/regs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@
194194
#define ZL_REF_CONFIG_DIFF_EN BIT(2)
195195

196196
#define ZL_REG_REF_PHASE_OFFSET_COMP ZL_REG(10, 0x28, 6)
197+
#define ZL_REG_REF_PHASE_OFFSET_COMP_32 ZL_REG(10, 0x28, 4)
197198

198199
#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1)
199200
#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0)

0 commit comments

Comments
 (0)