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

reset: allow using reset_control_reset with shared reset

Some SoCs (for example Amlogic GXBB) implement a reset controller which
only supports a reset pulse (triggered via reset_control_reset). At the
same time multiple devices (in case of the Amlogic GXBB SoC both USB
PHYs) are sharing the same reset line.

This patch allows using reset_control_reset also for shared resets.
There are limitations though:
reset_control_reset can only be used if reset_control_assert was not
used yet.
reset_control_assert can only be used if reset_control_reset was not
used yet.
For shared resets the reset is only triggered once for the lifetime of
the reset_control instance (the reset can be triggered again if all
consumers of that specific reset_control are gone, as the reset
framework will free the reset_control instance in that case).

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>

authored by

Martin Blumenstingl and committed by
Philipp Zabel
7da33a37 cdd24f76

+37 -6
+37 -6
drivers/reset/core.c
··· 32 32 * @refcnt: Number of gets of this reset_control 33 33 * @shared: Is this a shared (1), or an exclusive (0) reset_control? 34 34 * @deassert_cnt: Number of times this reset line has been deasserted 35 + * @triggered_count: Number of times this reset line has been reset. Currently 36 + * only used for shared resets, which means that the value 37 + * will be either 0 or 1. 35 38 */ 36 39 struct reset_control { 37 40 struct reset_controller_dev *rcdev; ··· 43 40 unsigned int refcnt; 44 41 int shared; 45 42 atomic_t deassert_count; 43 + atomic_t triggered_count; 46 44 }; 47 45 48 46 /** ··· 138 134 * reset_control_reset - reset the controlled device 139 135 * @rstc: reset controller 140 136 * 141 - * Calling this on a shared reset controller is an error. 137 + * On a shared reset line the actual reset pulse is only triggered once for the 138 + * lifetime of the reset_control instance: for all but the first caller this is 139 + * a no-op. 140 + * Consumers must not use reset_control_(de)assert on shared reset lines when 141 + * reset_control_reset has been used. 142 142 */ 143 143 int reset_control_reset(struct reset_control *rstc) 144 144 { 145 - if (WARN_ON(IS_ERR_OR_NULL(rstc)) || 146 - WARN_ON(rstc->shared)) 145 + int ret; 146 + 147 + if (WARN_ON(IS_ERR_OR_NULL(rstc))) 147 148 return -EINVAL; 148 149 149 - if (rstc->rcdev->ops->reset) 150 - return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); 150 + if (!rstc->rcdev->ops->reset) 151 + return -ENOTSUPP; 151 152 152 - return -ENOTSUPP; 153 + if (rstc->shared) { 154 + if (WARN_ON(atomic_read(&rstc->deassert_count) != 0)) 155 + return -EINVAL; 156 + 157 + if (atomic_inc_return(&rstc->triggered_count) != 1) 158 + return 0; 159 + } 160 + 161 + ret = rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); 162 + if (rstc->shared && !ret) 163 + atomic_dec(&rstc->triggered_count); 164 + 165 + return ret; 153 166 } 154 167 EXPORT_SYMBOL_GPL(reset_control_reset); 155 168 ··· 180 159 * 181 160 * For shared reset controls a driver cannot expect the hw's registers and 182 161 * internal state to be reset, but must be prepared for this to happen. 162 + * Consumers must not use reset_control_reset on shared reset lines when 163 + * reset_control_(de)assert has been used. 183 164 */ 184 165 int reset_control_assert(struct reset_control *rstc) 185 166 { ··· 192 169 return -ENOTSUPP; 193 170 194 171 if (rstc->shared) { 172 + if (WARN_ON(atomic_read(&rstc->triggered_count) != 0)) 173 + return -EINVAL; 174 + 195 175 if (WARN_ON(atomic_read(&rstc->deassert_count) == 0)) 196 176 return -EINVAL; 197 177 ··· 211 185 * @rstc: reset controller 212 186 * 213 187 * After calling this function, the reset is guaranteed to be deasserted. 188 + * Consumers must not use reset_control_reset on shared reset lines when 189 + * reset_control_(de)assert has been used. 214 190 */ 215 191 int reset_control_deassert(struct reset_control *rstc) 216 192 { ··· 223 195 return -ENOTSUPP; 224 196 225 197 if (rstc->shared) { 198 + if (WARN_ON(atomic_read(&rstc->triggered_count) != 0)) 199 + return -EINVAL; 200 + 226 201 if (atomic_inc_return(&rstc->deassert_count) != 1) 227 202 return 0; 228 203 }