Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 6287262f761e ("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>

authored by

Ivan Vecera and committed by
Jakub Kicinski
4cfe066a ca220141

+55 -7
+1
drivers/dpll/zl3073x/core.c
··· 1026 1026 "Unknown or non-match chip ID: 0x%0x\n", 1027 1027 id); 1028 1028 } 1029 + zldev->chip_id = id; 1029 1030 1030 1031 /* Read revision, firmware version and custom config version */ 1031 1032 rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
+28
drivers/dpll/zl3073x/core.h
··· 35 35 * @dev: pointer to device 36 36 * @regmap: regmap to access device registers 37 37 * @multiop_lock: to serialize multiple register operations 38 + * @chip_id: chip ID read from hardware 38 39 * @ref: array of input references' invariants 39 40 * @out: array of outs' invariants 40 41 * @synth: array of synths' invariants ··· 49 48 struct device *dev; 50 49 struct regmap *regmap; 51 50 struct mutex multiop_lock; 51 + u16 chip_id; 52 52 53 53 /* Invariants */ 54 54 struct zl3073x_ref ref[ZL3073X_NUM_REFS]; ··· 145 143 *****************/ 146 144 147 145 int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel); 146 + 147 + /** 148 + * zl3073x_dev_is_ref_phase_comp_32bit - check ref phase comp register size 149 + * @zldev: pointer to zl3073x device 150 + * 151 + * Some chip IDs have a 32-bit wide ref_phase_offset_comp register instead 152 + * of the default 48-bit. 153 + * 154 + * Return: true if the register is 32-bit, false if 48-bit 155 + */ 156 + static inline bool 157 + zl3073x_dev_is_ref_phase_comp_32bit(struct zl3073x_dev *zldev) 158 + { 159 + switch (zldev->chip_id) { 160 + case 0x0E30: 161 + case 0x0E93: 162 + case 0x0E94: 163 + case 0x0E95: 164 + case 0x0E96: 165 + case 0x0E97: 166 + case 0x1F60: 167 + return true; 168 + default: 169 + return false; 170 + } 171 + } 148 172 149 173 static inline bool 150 174 zl3073x_is_n_pin(u8 id)
+5 -2
drivers/dpll/zl3073x/dpll.c
··· 475 475 ref_id = zl3073x_input_pin_ref_get(pin->id); 476 476 ref = zl3073x_ref_state_get(zldev, ref_id); 477 477 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); 480 483 481 484 /* Reverse two's complement negation applied during set and convert 482 485 * to 32bit signed int
+20 -5
drivers/dpll/zl3073x/ref.c
··· 121 121 return rc; 122 122 123 123 /* 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 + } 126 134 if (rc) 127 135 return rc; 128 136 ··· 187 179 if (!rc && dref->sync_ctrl != ref->sync_ctrl) 188 180 rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL, 189 181 ref->sync_ctrl); 190 - if (!rc && dref->phase_comp != ref->phase_comp) 191 - rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP, 192 - ref->phase_comp); 182 + if (!rc && dref->phase_comp != ref->phase_comp) { 183 + if (zl3073x_dev_is_ref_phase_comp_32bit(zldev)) 184 + rc = zl3073x_write_u32(zldev, 185 + ZL_REG_REF_PHASE_OFFSET_COMP_32, 186 + ref->phase_comp); 187 + else 188 + rc = zl3073x_write_u48(zldev, 189 + ZL_REG_REF_PHASE_OFFSET_COMP, 190 + ref->phase_comp); 191 + } 193 192 if (rc) 194 193 return rc; 195 194
+1
drivers/dpll/zl3073x/regs.h
··· 194 194 #define ZL_REF_CONFIG_DIFF_EN BIT(2) 195 195 196 196 #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) 197 198 198 199 #define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1) 199 200 #define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0)