Skip to content

Commit 58e0f98

Browse files
committed
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>
2 parents 42690b8 + a1af5d2 commit 58e0f98

5 files changed

Lines changed: 119 additions & 0 deletions

File tree

drivers/phy/phy-core.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,31 @@ int phy_notify_disconnect(struct phy *phy, int port)
520520
}
521521
EXPORT_SYMBOL_GPL(phy_notify_disconnect);
522522

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+
523548
/**
524549
* phy_configure() - Changes the phy parameters
525550
* @phy: the phy returned by phy_get()

drivers/phy/samsung/phy-gs101-ufs.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,39 @@ static const struct samsung_ufs_phy_cfg tensor_gs101_post_pwr_hs_config[] = {
108108
END_UFS_PHY_CFG,
109109
};
110110

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+
111133
static const struct samsung_ufs_phy_cfg *tensor_gs101_ufs_phy_cfgs[CFG_TAG_MAX] = {
112134
[CFG_PRE_INIT] = tensor_gs101_pre_init_cfg,
113135
[CFG_PRE_PWR_HS] = tensor_gs101_pre_pwr_hs_config,
114136
[CFG_POST_PWR_HS] = tensor_gs101_post_pwr_hs_config,
115137
};
116138

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,
142+
};
143+
117144
static const char * const tensor_gs101_ufs_phy_clks[] = {
118145
"ref_clk",
119146
};
@@ -170,6 +197,7 @@ static int gs101_phy_wait_for_cdr_lock(struct phy *phy, u8 lane)
170197

171198
const struct samsung_ufs_phy_drvdata tensor_gs101_ufs_phy = {
172199
.cfgs = tensor_gs101_ufs_phy_cfgs,
200+
.cfgs_hibern8 = tensor_gs101_hibern8_cfgs,
173201
.isol = {
174202
.offset = TENSOR_GS101_PHY_CTRL,
175203
.mask = TENSOR_GS101_PHY_CTRL_MASK,

drivers/phy/samsung/phy-samsung-ufs.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,44 @@ static int samsung_ufs_phy_set_mode(struct phy *generic_phy,
217217
return 0;
218218
}
219219

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+
220258
static int samsung_ufs_phy_exit(struct phy *phy)
221259
{
222260
struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
@@ -233,6 +271,7 @@ static const struct phy_ops samsung_ufs_phy_ops = {
233271
.power_off = samsung_ufs_phy_power_off,
234272
.calibrate = samsung_ufs_phy_calibrate,
235273
.set_mode = samsung_ufs_phy_set_mode,
274+
.notify_phystate = samsung_ufs_phy_notify_state,
236275
.owner = THIS_MODULE,
237276
};
238277

@@ -287,6 +326,7 @@ static int samsung_ufs_phy_probe(struct platform_device *pdev)
287326
phy->dev = dev;
288327
phy->drvdata = drvdata;
289328
phy->cfgs = drvdata->cfgs;
329+
phy->cfgs_hibern8 = drvdata->cfgs_hibern8;
290330
memcpy(&phy->isol, &drvdata->isol, sizeof(phy->isol));
291331

292332
if (!of_property_read_u32_index(dev->of_node, "samsung,pmu-syscon", 1,

drivers/phy/samsung/phy-samsung-ufs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ enum {
9292
CFG_TAG_MAX,
9393
};
9494

95+
enum {
96+
CFG_POST_HIBERN8_ENTER,
97+
CFG_PRE_HIBERN8_EXIT,
98+
};
99+
95100
struct samsung_ufs_phy_cfg {
96101
u32 off_0;
97102
u32 off_1;
@@ -108,6 +113,7 @@ struct samsung_ufs_phy_pmu_isol {
108113

109114
struct samsung_ufs_phy_drvdata {
110115
const struct samsung_ufs_phy_cfg **cfgs;
116+
const struct samsung_ufs_phy_cfg **cfgs_hibern8;
111117
struct samsung_ufs_phy_pmu_isol isol;
112118
const char * const *clk_list;
113119
int num_clks;
@@ -124,6 +130,7 @@ struct samsung_ufs_phy {
124130
struct clk_bulk_data *clks;
125131
const struct samsung_ufs_phy_drvdata *drvdata;
126132
const struct samsung_ufs_phy_cfg * const *cfgs;
133+
const struct samsung_ufs_phy_cfg * const *cfgs_hibern8;
127134
struct samsung_ufs_phy_pmu_isol isol;
128135
u8 lane_cnt;
129136
int ufs_phy_state;

include/linux/phy/phy.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ enum phy_media {
5353
PHY_MEDIA_DAC,
5454
};
5555

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+
5665
/**
5766
* union phy_configure_opts - Opaque generic phy configuration
5867
*
@@ -83,6 +92,7 @@ union phy_configure_opts {
8392
* @set_speed: set the speed of the phy (optional)
8493
* @reset: resetting the phy
8594
* @calibrate: calibrate the phy
95+
* @notify_phystate: notify and configure the phy for a particular state
8696
* @release: ops to be performed while the consumer relinquishes the PHY
8797
* @owner: the module owner containing the ops
8898
*/
@@ -132,6 +142,7 @@ struct phy_ops {
132142
int (*connect)(struct phy *phy, int port);
133143
int (*disconnect)(struct phy *phy, int port);
134144

145+
int (*notify_phystate)(struct phy *phy, union phy_notify state);
135146
void (*release)(struct phy *phy);
136147
struct module *owner;
137148
};
@@ -255,6 +266,7 @@ int phy_reset(struct phy *phy);
255266
int phy_calibrate(struct phy *phy);
256267
int phy_notify_connect(struct phy *phy, int port);
257268
int phy_notify_disconnect(struct phy *phy, int port);
269+
int phy_notify_state(struct phy *phy, union phy_notify state);
258270
static inline int phy_get_bus_width(struct phy *phy)
259271
{
260272
return phy->attrs.bus_width;
@@ -412,6 +424,13 @@ static inline int phy_notify_disconnect(struct phy *phy, int index)
412424
return -ENOSYS;
413425
}
414426

427+
static inline int phy_notify_state(struct phy *phy, union phy_notify state)
428+
{
429+
if (!phy)
430+
return 0;
431+
return -ENOSYS;
432+
}
433+
415434
static inline int phy_configure(struct phy *phy,
416435
union phy_configure_opts *opts)
417436
{

0 commit comments

Comments
 (0)