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 int rc; 22 23 rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index), 24 &chan->mon_status); 25 if (rc) 26 return rc; 27 28 return zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index), 29 &chan->refsel_status); 30 } 31 32 /** 33 * zl3073x_chan_state_fetch - fetch DPLL channel state from hardware 34 * @zldev: pointer to zl3073x_dev structure 35 * @index: DPLL channel index to fetch state for 36 * 37 * Reads the mode_refsel register and reference priority registers for 38 * the given DPLL channel and stores the raw values for later use. 39 * 40 * Return: 0 on success, <0 on error 41 */ 42 int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index) 43 { 44 struct zl3073x_chan *chan = &zldev->chan[index]; 45 int rc, i; 46 47 rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index), 48 &chan->mode_refsel); 49 if (rc) 50 return rc; 51 52 dev_dbg(zldev->dev, "DPLL%u mode: %u, ref: %u\n", index, 53 zl3073x_chan_mode_get(chan), zl3073x_chan_ref_get(chan)); 54 55 rc = zl3073x_chan_state_update(zldev, index); 56 if (rc) 57 return rc; 58 59 dev_dbg(zldev->dev, 60 "DPLL%u lock_state: %u, ho: %u, sel_state: %u, sel_ref: %u\n", 61 index, zl3073x_chan_lock_state_get(chan), 62 zl3073x_chan_is_ho_ready(chan) ? 1 : 0, 63 zl3073x_chan_refsel_state_get(chan), 64 zl3073x_chan_refsel_ref_get(chan)); 65 66 guard(mutex)(&zldev->multiop_lock); 67 68 /* Read DPLL configuration from mailbox */ 69 rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD, 70 ZL_REG_DPLL_MB_MASK, BIT(index)); 71 if (rc) 72 return rc; 73 74 /* Read reference priority registers */ 75 for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) { 76 rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(i), 77 &chan->ref_prio[i]); 78 if (rc) 79 return rc; 80 } 81 82 return 0; 83 } 84 85 /** 86 * zl3073x_chan_state_get - get current DPLL channel state 87 * @zldev: pointer to zl3073x_dev structure 88 * @index: DPLL channel index to get state for 89 * 90 * Return: pointer to given DPLL channel state 91 */ 92 const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev, 93 u8 index) 94 { 95 return &zldev->chan[index]; 96 } 97 98 /** 99 * zl3073x_chan_state_set - commit DPLL channel state changes to hardware 100 * @zldev: pointer to zl3073x_dev structure 101 * @index: DPLL channel index to set state for 102 * @chan: desired channel state 103 * 104 * Skips the HW write if the configuration is unchanged, and otherwise 105 * writes only the changed registers to hardware. The mode_refsel register 106 * is written directly, while the reference priority registers are written 107 * via the DPLL mailbox interface. 108 * 109 * Return: 0 on success, <0 on HW error 110 */ 111 int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index, 112 const struct zl3073x_chan *chan) 113 { 114 struct zl3073x_chan *dchan = &zldev->chan[index]; 115 int rc, i; 116 117 /* Skip HW write if configuration hasn't changed */ 118 if (!memcmp(&dchan->cfg, &chan->cfg, sizeof(chan->cfg))) 119 return 0; 120 121 /* Direct register write for mode_refsel */ 122 if (dchan->mode_refsel != chan->mode_refsel) { 123 rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index), 124 chan->mode_refsel); 125 if (rc) 126 return rc; 127 dchan->mode_refsel = chan->mode_refsel; 128 } 129 130 /* Mailbox write for ref_prio if changed */ 131 if (!memcmp(dchan->ref_prio, chan->ref_prio, sizeof(chan->ref_prio))) { 132 dchan->cfg = chan->cfg; 133 return 0; 134 } 135 136 guard(mutex)(&zldev->multiop_lock); 137 138 /* Read DPLL configuration into mailbox */ 139 rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD, 140 ZL_REG_DPLL_MB_MASK, BIT(index)); 141 if (rc) 142 return rc; 143 144 /* Update changed ref_prio registers */ 145 for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) { 146 if (dchan->ref_prio[i] != chan->ref_prio[i]) { 147 rc = zl3073x_write_u8(zldev, 148 ZL_REG_DPLL_REF_PRIO(i), 149 chan->ref_prio[i]); 150 if (rc) 151 return rc; 152 } 153 } 154 155 /* Commit DPLL configuration */ 156 rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR, 157 ZL_REG_DPLL_MB_MASK, BIT(index)); 158 if (rc) 159 return rc; 160 161 /* After successful write store new state */ 162 dchan->cfg = chan->cfg; 163 164 return 0; 165 } 166