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.

phy: Add new phy_notify_state() api

Peter Griffin <peter.griffin@linaro.org> says:

This series adds a new phy_notify_state() API to the phy subsystem. It is
designed to be used when some specific runtime configuration parameters
need to be changed when transitioning to the desired state which can't be
handled by phy_calibrate()or phy_power_{on|off}().

The first user of the new API is phy-samsung-ufs and phy-gs101-ufs which
need to issue some register writes when entering and exiting the hibern8
link state.

A separate patch will be sent for ufs-exynos driver to make use of this new
API in the hibern8 callbacks.

Link: https://patch.msgid.link/20251112-phy-notify-pmstate-v5-0-39df622d8fcb@linaro.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>

+119
+25
drivers/phy/phy-core.c
··· 521 521 EXPORT_SYMBOL_GPL(phy_notify_disconnect); 522 522 523 523 /** 524 + * phy_notify_state() - phy state notification 525 + * @phy: the PHY returned by phy_get() 526 + * @state: the PHY state 527 + * 528 + * Notify the PHY of a state transition. Used to notify and 529 + * configure the PHY accordingly. 530 + * 531 + * Returns: %0 if successful, a negative error code otherwise 532 + */ 533 + int phy_notify_state(struct phy *phy, union phy_notify state) 534 + { 535 + int ret; 536 + 537 + if (!phy || !phy->ops->notify_phystate) 538 + return 0; 539 + 540 + mutex_lock(&phy->mutex); 541 + ret = phy->ops->notify_phystate(phy, state); 542 + mutex_unlock(&phy->mutex); 543 + 544 + return ret; 545 + } 546 + EXPORT_SYMBOL_GPL(phy_notify_state); 547 + 548 + /** 524 549 * phy_configure() - Changes the phy parameters 525 550 * @phy: the phy returned by phy_get() 526 551 * @opts: New configuration to apply
+28
drivers/phy/samsung/phy-gs101-ufs.c
··· 108 108 END_UFS_PHY_CFG, 109 109 }; 110 110 111 + static const struct samsung_ufs_phy_cfg tensor_gs101_post_h8_enter[] = { 112 + PHY_TRSV_REG_CFG_GS101(0x262, 0x08, PWR_MODE_ANY), 113 + PHY_TRSV_REG_CFG_GS101(0x265, 0x0A, PWR_MODE_ANY), 114 + PHY_COMN_REG_CFG(0x1, 0x8, PWR_MODE_ANY), 115 + PHY_COMN_REG_CFG(0x0, 0x86, PWR_MODE_ANY), 116 + PHY_COMN_REG_CFG(0x8, 0x60, PWR_MODE_HS_ANY), 117 + PHY_TRSV_REG_CFG_GS101(0x222, 0x08, PWR_MODE_HS_ANY), 118 + PHY_TRSV_REG_CFG_GS101(0x246, 0x01, PWR_MODE_HS_ANY), 119 + END_UFS_PHY_CFG, 120 + }; 121 + 122 + static const struct samsung_ufs_phy_cfg tensor_gs101_pre_h8_exit[] = { 123 + PHY_COMN_REG_CFG(0x0, 0xC6, PWR_MODE_ANY), 124 + PHY_COMN_REG_CFG(0x1, 0x0C, PWR_MODE_ANY), 125 + PHY_TRSV_REG_CFG_GS101(0x262, 0x00, PWR_MODE_ANY), 126 + PHY_TRSV_REG_CFG_GS101(0x265, 0x00, PWR_MODE_ANY), 127 + PHY_COMN_REG_CFG(0x8, 0xE0, PWR_MODE_HS_ANY), 128 + PHY_TRSV_REG_CFG_GS101(0x246, 0x03, PWR_MODE_HS_ANY), 129 + PHY_TRSV_REG_CFG_GS101(0x222, 0x18, PWR_MODE_HS_ANY), 130 + END_UFS_PHY_CFG, 131 + }; 132 + 111 133 static const struct samsung_ufs_phy_cfg *tensor_gs101_ufs_phy_cfgs[CFG_TAG_MAX] = { 112 134 [CFG_PRE_INIT] = tensor_gs101_pre_init_cfg, 113 135 [CFG_PRE_PWR_HS] = tensor_gs101_pre_pwr_hs_config, 114 136 [CFG_POST_PWR_HS] = tensor_gs101_post_pwr_hs_config, 137 + }; 138 + 139 + static const struct samsung_ufs_phy_cfg *tensor_gs101_hibern8_cfgs[] = { 140 + [CFG_POST_HIBERN8_ENTER] = tensor_gs101_post_h8_enter, 141 + [CFG_PRE_HIBERN8_EXIT] = tensor_gs101_pre_h8_exit, 115 142 }; 116 143 117 144 static const char * const tensor_gs101_ufs_phy_clks[] = { ··· 197 170 198 171 const struct samsung_ufs_phy_drvdata tensor_gs101_ufs_phy = { 199 172 .cfgs = tensor_gs101_ufs_phy_cfgs, 173 + .cfgs_hibern8 = tensor_gs101_hibern8_cfgs, 200 174 .isol = { 201 175 .offset = TENSOR_GS101_PHY_CTRL, 202 176 .mask = TENSOR_GS101_PHY_CTRL_MASK,
+40
drivers/phy/samsung/phy-samsung-ufs.c
··· 217 217 return 0; 218 218 } 219 219 220 + static int samsung_ufs_phy_notify_state(struct phy *phy, 221 + union phy_notify state) 222 + { 223 + struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); 224 + const struct samsung_ufs_phy_cfg *cfg; 225 + int i, err = -EINVAL; 226 + 227 + if (!ufs_phy->cfgs_hibern8) 228 + return 0; 229 + 230 + if (state.ufs_state == PHY_UFS_HIBERN8_ENTER) 231 + cfg = ufs_phy->cfgs_hibern8[CFG_POST_HIBERN8_ENTER]; 232 + else if (state.ufs_state == PHY_UFS_HIBERN8_EXIT) 233 + cfg = ufs_phy->cfgs_hibern8[CFG_PRE_HIBERN8_EXIT]; 234 + else 235 + goto err_out; 236 + 237 + for_each_phy_cfg(cfg) { 238 + for_each_phy_lane(ufs_phy, i) { 239 + samsung_ufs_phy_config(ufs_phy, cfg, i); 240 + } 241 + } 242 + 243 + if (state.ufs_state == PHY_UFS_HIBERN8_EXIT) { 244 + for_each_phy_lane(ufs_phy, i) { 245 + if (ufs_phy->drvdata->wait_for_cdr) { 246 + err = ufs_phy->drvdata->wait_for_cdr(phy, i); 247 + if (err) 248 + goto err_out; 249 + } 250 + } 251 + } 252 + 253 + return 0; 254 + err_out: 255 + return err; 256 + } 257 + 220 258 static int samsung_ufs_phy_exit(struct phy *phy) 221 259 { 222 260 struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); ··· 271 233 .power_off = samsung_ufs_phy_power_off, 272 234 .calibrate = samsung_ufs_phy_calibrate, 273 235 .set_mode = samsung_ufs_phy_set_mode, 236 + .notify_phystate = samsung_ufs_phy_notify_state, 274 237 .owner = THIS_MODULE, 275 238 }; 276 239 ··· 326 287 phy->dev = dev; 327 288 phy->drvdata = drvdata; 328 289 phy->cfgs = drvdata->cfgs; 290 + phy->cfgs_hibern8 = drvdata->cfgs_hibern8; 329 291 memcpy(&phy->isol, &drvdata->isol, sizeof(phy->isol)); 330 292 331 293 if (!of_property_read_u32_index(dev->of_node, "samsung,pmu-syscon", 1,
+7
drivers/phy/samsung/phy-samsung-ufs.h
··· 92 92 CFG_TAG_MAX, 93 93 }; 94 94 95 + enum { 96 + CFG_POST_HIBERN8_ENTER, 97 + CFG_PRE_HIBERN8_EXIT, 98 + }; 99 + 95 100 struct samsung_ufs_phy_cfg { 96 101 u32 off_0; 97 102 u32 off_1; ··· 113 108 114 109 struct samsung_ufs_phy_drvdata { 115 110 const struct samsung_ufs_phy_cfg **cfgs; 111 + const struct samsung_ufs_phy_cfg **cfgs_hibern8; 116 112 struct samsung_ufs_phy_pmu_isol isol; 117 113 const char * const *clk_list; 118 114 int num_clks; ··· 130 124 struct clk_bulk_data *clks; 131 125 const struct samsung_ufs_phy_drvdata *drvdata; 132 126 const struct samsung_ufs_phy_cfg * const *cfgs; 127 + const struct samsung_ufs_phy_cfg * const *cfgs_hibern8; 133 128 struct samsung_ufs_phy_pmu_isol isol; 134 129 u8 lane_cnt; 135 130 int ufs_phy_state;
+19
include/linux/phy/phy.h
··· 53 53 PHY_MEDIA_DAC, 54 54 }; 55 55 56 + enum phy_ufs_state { 57 + PHY_UFS_HIBERN8_ENTER, 58 + PHY_UFS_HIBERN8_EXIT, 59 + }; 60 + 61 + union phy_notify { 62 + enum phy_ufs_state ufs_state; 63 + }; 64 + 56 65 /** 57 66 * union phy_configure_opts - Opaque generic phy configuration 58 67 * ··· 92 83 * @set_speed: set the speed of the phy (optional) 93 84 * @reset: resetting the phy 94 85 * @calibrate: calibrate the phy 86 + * @notify_phystate: notify and configure the phy for a particular state 95 87 * @release: ops to be performed while the consumer relinquishes the PHY 96 88 * @owner: the module owner containing the ops 97 89 */ ··· 142 132 int (*connect)(struct phy *phy, int port); 143 133 int (*disconnect)(struct phy *phy, int port); 144 134 135 + int (*notify_phystate)(struct phy *phy, union phy_notify state); 145 136 void (*release)(struct phy *phy); 146 137 struct module *owner; 147 138 }; ··· 266 255 int phy_calibrate(struct phy *phy); 267 256 int phy_notify_connect(struct phy *phy, int port); 268 257 int phy_notify_disconnect(struct phy *phy, int port); 258 + int phy_notify_state(struct phy *phy, union phy_notify state); 269 259 static inline int phy_get_bus_width(struct phy *phy) 270 260 { 271 261 return phy->attrs.bus_width; ··· 418 406 } 419 407 420 408 static inline int phy_notify_disconnect(struct phy *phy, int index) 409 + { 410 + if (!phy) 411 + return 0; 412 + return -ENOSYS; 413 + } 414 + 415 + static inline int phy_notify_state(struct phy *phy, union phy_notify state) 421 416 { 422 417 if (!phy) 423 418 return 0;