1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/cleanup.h> 4 #include <linux/dev_printk.h> 5 #include <linux/string.h> 6 #include <linux/types.h> 7 8 #include "chan.h" 9 #include "core.h" 10 11 /** 12 * zl3073x_chan_state_update - update DPLL channel status from HW 13 * @zldev: pointer to zl3073x_dev structure 14 * @index: DPLL channel index 15 * 16 * Return: 0 on success, <0 on error 17 */ 18 int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index) 19 { 20 struct zl3073x_chan *chan = &zldev->chan[index]; 21 u64 val; 22 int rc; 23 24 rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index), 25 &chan->mon_status); 26 if (rc) 27 return rc; 28 29 rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index), 30 &chan->refsel_status); 31 if (rc) 32 return rc; 33 34 /* Read df_offset vs tracked reference */ 35 rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index), 36 ZL_DPLL_DF_READ_SEM); 37 if (rc) 38 return rc; 39 40 rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_DF_READ(index), 41 ZL_DPLL_DF_READ_SEM | ZL_DPLL_DF_READ_REF_OFST); 42 if (rc) 43 return rc; 44 45 rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index), 46 ZL_DPLL_DF_READ_SEM); 47 if (rc) 48 return rc; 49 50 rc = zl3073x_read_u48(zldev, ZL_REG_DPLL_DF_OFFSET(index), &val); 51 if (rc) 52 return rc; 53 54 chan->df_offset = sign_extend64(val, 47); 55 56 return 0; 57 } 58 59 /** 60 * zl3073x_chan_state_fetch - fetch DPLL channel state from hardware 61 * @zldev: pointer to zl3073x_dev structure 62 * @index: DPLL channel index to fetch state for 63 * 64 * Reads the mode_refsel register and reference priority registers for 65 * the given DPLL channel and stores the raw values for later use. 66 * 67 * Return: 0 on success, <0 on error 68 */ 69 int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index) 70 { 71 struct zl3073x_chan *chan = &zldev->chan[index]; 72 int rc, i; 73 74 rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index), 75 &chan->mode_refsel); 76 if (rc) 77 return rc; 78 79 dev_dbg(zldev->dev, "DPLL%u mode: %u, ref: %u\n", index, 80 zl3073x_chan_mode_get(chan), zl3073x_chan_ref_get(chan)); 81 82 rc = zl3073x_chan_state_update(zldev, index); 83 if (rc) 84 return rc; 85 86 dev_dbg(zldev->dev, 87 "DPLL%u lock_state: %u, ho: %u, sel_state: %u, sel_ref: %u\n", 88 index, zl3073x_chan_lock_state_get(chan), 89 zl3073x_chan_is_ho_ready(chan) ? 1 : 0, 90 zl3073x_chan_refsel_state_get(chan), 91 zl3073x_chan_refsel_ref_get(chan)); 92 93 guard(mutex)(&zldev->multiop_lock); 94 95 /* Read DPLL configuration from mailbox */ 96 rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD, 97 ZL_REG_DPLL_MB_MASK, BIT(index)); 98 if (rc) 99 return rc; 100 101 /* Read reference priority registers */ 102 for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) { 103 rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(i), 104 &chan->ref_prio[i]); 105 if (rc) 106 return rc; 107 } 108 109 return 0; 110 } 111 112 /** 113 * zl3073x_chan_state_get - get current DPLL channel state 114 * @zldev: pointer to zl3073x_dev structure 115 * @index: DPLL channel index to get state for 116 * 117 * Return: pointer to given DPLL channel state 118 */ 119 const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev, 120 u8 index) 121 { 122 return &zldev->chan[index]; 123 } 124 125 /** 126 * zl3073x_chan_state_set - commit DPLL channel state changes to hardware 127 * @zldev: pointer to zl3073x_dev structure 128 * @index: DPLL channel index to set state for 129 * @chan: desired channel state 130 * 131 * Skips the HW write if the configuration is unchanged, and otherwise 132 * writes only the changed registers to hardware. The mode_refsel register 133 * is written directly, while the reference priority registers are written 134 * via the DPLL mailbox interface. 135 * 136 * Return: 0 on success, <0 on HW error 137 */ 138 int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index, 139 const struct zl3073x_chan *chan) 140 { 141 struct zl3073x_chan *dchan = &zldev->chan[index]; 142 int rc, i; 143 144 /* Skip HW write if configuration hasn't changed */ 145 if (!memcmp(&dchan->cfg, &chan->cfg, sizeof(chan->cfg))) 146 return 0; 147 148 /* Direct register write for mode_refsel */ 149 if (dchan->mode_refsel != chan->mode_refsel) { 150 rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index), 151 chan->mode_refsel); 152 if (rc) 153 return rc; 154 dchan->mode_refsel = chan->mode_refsel; 155 } 156 157 /* Mailbox write for ref_prio if changed */ 158 if (!memcmp(dchan->ref_prio, chan->ref_prio, sizeof(chan->ref_prio))) { 159 dchan->cfg = chan->cfg; 160 return 0; 161 } 162 163 guard(mutex)(&zldev->multiop_lock); 164 165 /* Read DPLL configuration into mailbox */ 166 rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD, 167 ZL_REG_DPLL_MB_MASK, BIT(index)); 168 if (rc) 169 return rc; 170 171 /* Update changed ref_prio registers */ 172 for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) { 173 if (dchan->ref_prio[i] != chan->ref_prio[i]) { 174 rc = zl3073x_write_u8(zldev, 175 ZL_REG_DPLL_REF_PRIO(i), 176 chan->ref_prio[i]); 177 if (rc) 178 return rc; 179 } 180 } 181 182 /* Commit DPLL configuration */ 183 rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR, 184 ZL_REG_DPLL_MB_MASK, BIT(index)); 185 if (rc) 186 return rc; 187 188 /* After successful write store new state */ 189 dchan->cfg = chan->cfg; 190 191 return 0; 192 } 193