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

ata: libahci_platform: Introduce reset assertion/deassertion methods

Currently the ACHI-platform library supports only the assert and deassert
reset signals and ignores the platforms with self-deasserting reset lines.
That prone to having the platforms with self-deasserting reset method
misbehaviour when it comes to resuming from sleep state after the clocks
have been fully disabled. For such cases the controller needs to be fully
reset all over after the reference clocks are enabled and stable,
otherwise the controller state machine might be in an undetermined state.

The best solution would be to auto-detect which reset method is supported
by the particular platform and use it implicitly in the framework of the
ahci_platform_enable_resources()/ahci_platform_disable_resources()
methods. Alas it can't be implemented due to the AHCI-platform library
already supporting the shared reset control lines. As [1] says in such
case we have to use only one of the next methods:
+ reset_control_assert()/reset_control_deassert();
+ reset_control_reset()/reset_control_rearm().
If the driver had an exclusive control over the reset lines we could have
been able to manipulate the lines with no much limitation and just used
the combination of the methods above to cover all the possible
reset-control cases. Since the shared reset control has already been
advertised and couldn't be changed with no risk to breaking the platforms
relying on it, we have no choice but to make the platform drivers to
determine which reset methods the platform reset system supports.

In order to implement both types of reset control support we suggest to
introduce the new AHCI-platform flag: AHCI_PLATFORM_RST_TRIGGER, which
when passed to the ahci_platform_get_resources() method together with the
AHCI_PLATFORM_GET_RESETS flag will indicate that the reset lines are
self-deasserting thus the reset_control_reset()/reset_control_rearm() will
be used to control the reset state. Otherwise the
reset_control_deassert()/reset_control_assert() methods will be utilized.

[1] Documentation/driver-api/reset.rst

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>

authored by

Serge Semin and committed by
Damien Le Moal
f67f12ff 3f74cd04

+50 -6
+1
drivers/ata/ahci.h
··· 340 340 bool got_runtime_pm; /* Did we do pm_runtime_get? */ 341 341 unsigned int n_clks; 342 342 struct clk_bulk_data *clks; /* Optional */ 343 + unsigned int f_rsts; 343 344 struct reset_control *rsts; /* Optional */ 344 345 struct regulator **target_pwrs; /* Optional */ 345 346 struct regulator *ahci_regulator;/* Optional */
+45 -5
drivers/ata/libahci_platform.c
··· 123 123 EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); 124 124 125 125 /** 126 + * ahci_platform_deassert_rsts - Deassert/trigger platform resets 127 + * @hpriv: host private area to store config values 128 + * 129 + * This function deasserts or triggers all the reset lines found for 130 + * the AHCI device. 131 + * 132 + * RETURNS: 133 + * 0 on success otherwise a negative error code 134 + */ 135 + int ahci_platform_deassert_rsts(struct ahci_host_priv *hpriv) 136 + { 137 + if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER) 138 + return reset_control_reset(hpriv->rsts); 139 + 140 + return reset_control_deassert(hpriv->rsts); 141 + } 142 + EXPORT_SYMBOL_GPL(ahci_platform_deassert_rsts); 143 + 144 + /** 145 + * ahci_platform_assert_rsts - Assert/rearm platform resets 146 + * @hpriv: host private area to store config values 147 + * 148 + * This function asserts or rearms (for self-deasserting resets) all 149 + * the reset controls found for the AHCI device. 150 + * 151 + * RETURNS: 152 + * 0 on success otherwise a negative error code 153 + */ 154 + int ahci_platform_assert_rsts(struct ahci_host_priv *hpriv) 155 + { 156 + if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER) 157 + return reset_control_rearm(hpriv->rsts); 158 + 159 + return reset_control_assert(hpriv->rsts); 160 + } 161 + EXPORT_SYMBOL_GPL(ahci_platform_assert_rsts); 162 + 163 + /** 126 164 * ahci_platform_enable_regulators - Enable regulators 127 165 * @hpriv: host private area to store config values 128 166 * ··· 257 219 if (rc) 258 220 goto disable_regulator; 259 221 260 - rc = reset_control_deassert(hpriv->rsts); 222 + rc = ahci_platform_deassert_rsts(hpriv); 261 223 if (rc) 262 224 goto disable_clks; 263 225 264 226 rc = ahci_platform_enable_phys(hpriv); 265 227 if (rc) 266 - goto disable_resets; 228 + goto disable_rsts; 267 229 268 230 return 0; 269 231 270 - disable_resets: 271 - reset_control_assert(hpriv->rsts); 232 + disable_rsts: 233 + ahci_platform_assert_rsts(hpriv); 272 234 273 235 disable_clks: 274 236 ahci_platform_disable_clks(hpriv); ··· 295 257 { 296 258 ahci_platform_disable_phys(hpriv); 297 259 298 - reset_control_assert(hpriv->rsts); 260 + ahci_platform_assert_rsts(hpriv); 299 261 300 262 ahci_platform_disable_clks(hpriv); 301 263 ··· 486 448 rc = PTR_ERR(hpriv->rsts); 487 449 goto err_out; 488 450 } 451 + 452 + hpriv->f_rsts = flags & AHCI_PLATFORM_RST_TRIGGER; 489 453 } 490 454 491 455 /*
+4 -1
include/linux/ahci_platform.h
··· 23 23 void ahci_platform_disable_phys(struct ahci_host_priv *hpriv); 24 24 int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); 25 25 void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); 26 + int ahci_platform_deassert_rsts(struct ahci_host_priv *hpriv); 27 + int ahci_platform_assert_rsts(struct ahci_host_priv *hpriv); 26 28 int ahci_platform_enable_regulators(struct ahci_host_priv *hpriv); 27 29 void ahci_platform_disable_regulators(struct ahci_host_priv *hpriv); 28 30 int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); ··· 43 41 int ahci_platform_suspend(struct device *dev); 44 42 int ahci_platform_resume(struct device *dev); 45 43 46 - #define AHCI_PLATFORM_GET_RESETS 0x01 44 + #define AHCI_PLATFORM_GET_RESETS BIT(0) 45 + #define AHCI_PLATFORM_RST_TRIGGER BIT(1) 47 46 48 47 #endif /* _AHCI_PLATFORM_H */