mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-01-11 17:10:13 +00:00
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>
This commit is contained in:
commit
58e0f987c2
@ -520,6 +520,31 @@ int phy_notify_disconnect(struct phy *phy, int port)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_notify_disconnect);
|
||||
|
||||
/**
|
||||
* phy_notify_state() - phy state notification
|
||||
* @phy: the PHY returned by phy_get()
|
||||
* @state: the PHY state
|
||||
*
|
||||
* Notify the PHY of a state transition. Used to notify and
|
||||
* configure the PHY accordingly.
|
||||
*
|
||||
* Returns: %0 if successful, a negative error code otherwise
|
||||
*/
|
||||
int phy_notify_state(struct phy *phy, union phy_notify state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!phy || !phy->ops->notify_phystate)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
ret = phy->ops->notify_phystate(phy, state);
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_notify_state);
|
||||
|
||||
/**
|
||||
* phy_configure() - Changes the phy parameters
|
||||
* @phy: the phy returned by phy_get()
|
||||
|
||||
@ -108,12 +108,39 @@ static const struct samsung_ufs_phy_cfg tensor_gs101_post_pwr_hs_config[] = {
|
||||
END_UFS_PHY_CFG,
|
||||
};
|
||||
|
||||
static const struct samsung_ufs_phy_cfg tensor_gs101_post_h8_enter[] = {
|
||||
PHY_TRSV_REG_CFG_GS101(0x262, 0x08, PWR_MODE_ANY),
|
||||
PHY_TRSV_REG_CFG_GS101(0x265, 0x0A, PWR_MODE_ANY),
|
||||
PHY_COMN_REG_CFG(0x1, 0x8, PWR_MODE_ANY),
|
||||
PHY_COMN_REG_CFG(0x0, 0x86, PWR_MODE_ANY),
|
||||
PHY_COMN_REG_CFG(0x8, 0x60, PWR_MODE_HS_ANY),
|
||||
PHY_TRSV_REG_CFG_GS101(0x222, 0x08, PWR_MODE_HS_ANY),
|
||||
PHY_TRSV_REG_CFG_GS101(0x246, 0x01, PWR_MODE_HS_ANY),
|
||||
END_UFS_PHY_CFG,
|
||||
};
|
||||
|
||||
static const struct samsung_ufs_phy_cfg tensor_gs101_pre_h8_exit[] = {
|
||||
PHY_COMN_REG_CFG(0x0, 0xC6, PWR_MODE_ANY),
|
||||
PHY_COMN_REG_CFG(0x1, 0x0C, PWR_MODE_ANY),
|
||||
PHY_TRSV_REG_CFG_GS101(0x262, 0x00, PWR_MODE_ANY),
|
||||
PHY_TRSV_REG_CFG_GS101(0x265, 0x00, PWR_MODE_ANY),
|
||||
PHY_COMN_REG_CFG(0x8, 0xE0, PWR_MODE_HS_ANY),
|
||||
PHY_TRSV_REG_CFG_GS101(0x246, 0x03, PWR_MODE_HS_ANY),
|
||||
PHY_TRSV_REG_CFG_GS101(0x222, 0x18, PWR_MODE_HS_ANY),
|
||||
END_UFS_PHY_CFG,
|
||||
};
|
||||
|
||||
static const struct samsung_ufs_phy_cfg *tensor_gs101_ufs_phy_cfgs[CFG_TAG_MAX] = {
|
||||
[CFG_PRE_INIT] = tensor_gs101_pre_init_cfg,
|
||||
[CFG_PRE_PWR_HS] = tensor_gs101_pre_pwr_hs_config,
|
||||
[CFG_POST_PWR_HS] = tensor_gs101_post_pwr_hs_config,
|
||||
};
|
||||
|
||||
static const struct samsung_ufs_phy_cfg *tensor_gs101_hibern8_cfgs[] = {
|
||||
[CFG_POST_HIBERN8_ENTER] = tensor_gs101_post_h8_enter,
|
||||
[CFG_PRE_HIBERN8_EXIT] = tensor_gs101_pre_h8_exit,
|
||||
};
|
||||
|
||||
static const char * const tensor_gs101_ufs_phy_clks[] = {
|
||||
"ref_clk",
|
||||
};
|
||||
@ -170,6 +197,7 @@ static int gs101_phy_wait_for_cdr_lock(struct phy *phy, u8 lane)
|
||||
|
||||
const struct samsung_ufs_phy_drvdata tensor_gs101_ufs_phy = {
|
||||
.cfgs = tensor_gs101_ufs_phy_cfgs,
|
||||
.cfgs_hibern8 = tensor_gs101_hibern8_cfgs,
|
||||
.isol = {
|
||||
.offset = TENSOR_GS101_PHY_CTRL,
|
||||
.mask = TENSOR_GS101_PHY_CTRL_MASK,
|
||||
|
||||
@ -217,6 +217,44 @@ static int samsung_ufs_phy_set_mode(struct phy *generic_phy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int samsung_ufs_phy_notify_state(struct phy *phy,
|
||||
union phy_notify state)
|
||||
{
|
||||
struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
|
||||
const struct samsung_ufs_phy_cfg *cfg;
|
||||
int i, err = -EINVAL;
|
||||
|
||||
if (!ufs_phy->cfgs_hibern8)
|
||||
return 0;
|
||||
|
||||
if (state.ufs_state == PHY_UFS_HIBERN8_ENTER)
|
||||
cfg = ufs_phy->cfgs_hibern8[CFG_POST_HIBERN8_ENTER];
|
||||
else if (state.ufs_state == PHY_UFS_HIBERN8_EXIT)
|
||||
cfg = ufs_phy->cfgs_hibern8[CFG_PRE_HIBERN8_EXIT];
|
||||
else
|
||||
goto err_out;
|
||||
|
||||
for_each_phy_cfg(cfg) {
|
||||
for_each_phy_lane(ufs_phy, i) {
|
||||
samsung_ufs_phy_config(ufs_phy, cfg, i);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.ufs_state == PHY_UFS_HIBERN8_EXIT) {
|
||||
for_each_phy_lane(ufs_phy, i) {
|
||||
if (ufs_phy->drvdata->wait_for_cdr) {
|
||||
err = ufs_phy->drvdata->wait_for_cdr(phy, i);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int samsung_ufs_phy_exit(struct phy *phy)
|
||||
{
|
||||
struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
|
||||
@ -233,6 +271,7 @@ static const struct phy_ops samsung_ufs_phy_ops = {
|
||||
.power_off = samsung_ufs_phy_power_off,
|
||||
.calibrate = samsung_ufs_phy_calibrate,
|
||||
.set_mode = samsung_ufs_phy_set_mode,
|
||||
.notify_phystate = samsung_ufs_phy_notify_state,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
@ -287,6 +326,7 @@ static int samsung_ufs_phy_probe(struct platform_device *pdev)
|
||||
phy->dev = dev;
|
||||
phy->drvdata = drvdata;
|
||||
phy->cfgs = drvdata->cfgs;
|
||||
phy->cfgs_hibern8 = drvdata->cfgs_hibern8;
|
||||
memcpy(&phy->isol, &drvdata->isol, sizeof(phy->isol));
|
||||
|
||||
if (!of_property_read_u32_index(dev->of_node, "samsung,pmu-syscon", 1,
|
||||
|
||||
@ -92,6 +92,11 @@ enum {
|
||||
CFG_TAG_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
CFG_POST_HIBERN8_ENTER,
|
||||
CFG_PRE_HIBERN8_EXIT,
|
||||
};
|
||||
|
||||
struct samsung_ufs_phy_cfg {
|
||||
u32 off_0;
|
||||
u32 off_1;
|
||||
@ -108,6 +113,7 @@ struct samsung_ufs_phy_pmu_isol {
|
||||
|
||||
struct samsung_ufs_phy_drvdata {
|
||||
const struct samsung_ufs_phy_cfg **cfgs;
|
||||
const struct samsung_ufs_phy_cfg **cfgs_hibern8;
|
||||
struct samsung_ufs_phy_pmu_isol isol;
|
||||
const char * const *clk_list;
|
||||
int num_clks;
|
||||
@ -124,6 +130,7 @@ struct samsung_ufs_phy {
|
||||
struct clk_bulk_data *clks;
|
||||
const struct samsung_ufs_phy_drvdata *drvdata;
|
||||
const struct samsung_ufs_phy_cfg * const *cfgs;
|
||||
const struct samsung_ufs_phy_cfg * const *cfgs_hibern8;
|
||||
struct samsung_ufs_phy_pmu_isol isol;
|
||||
u8 lane_cnt;
|
||||
int ufs_phy_state;
|
||||
|
||||
@ -53,6 +53,15 @@ enum phy_media {
|
||||
PHY_MEDIA_DAC,
|
||||
};
|
||||
|
||||
enum phy_ufs_state {
|
||||
PHY_UFS_HIBERN8_ENTER,
|
||||
PHY_UFS_HIBERN8_EXIT,
|
||||
};
|
||||
|
||||
union phy_notify {
|
||||
enum phy_ufs_state ufs_state;
|
||||
};
|
||||
|
||||
/**
|
||||
* union phy_configure_opts - Opaque generic phy configuration
|
||||
*
|
||||
@ -83,6 +92,7 @@ union phy_configure_opts {
|
||||
* @set_speed: set the speed of the phy (optional)
|
||||
* @reset: resetting the phy
|
||||
* @calibrate: calibrate the phy
|
||||
* @notify_phystate: notify and configure the phy for a particular state
|
||||
* @release: ops to be performed while the consumer relinquishes the PHY
|
||||
* @owner: the module owner containing the ops
|
||||
*/
|
||||
@ -132,6 +142,7 @@ struct phy_ops {
|
||||
int (*connect)(struct phy *phy, int port);
|
||||
int (*disconnect)(struct phy *phy, int port);
|
||||
|
||||
int (*notify_phystate)(struct phy *phy, union phy_notify state);
|
||||
void (*release)(struct phy *phy);
|
||||
struct module *owner;
|
||||
};
|
||||
@ -255,6 +266,7 @@ int phy_reset(struct phy *phy);
|
||||
int phy_calibrate(struct phy *phy);
|
||||
int phy_notify_connect(struct phy *phy, int port);
|
||||
int phy_notify_disconnect(struct phy *phy, int port);
|
||||
int phy_notify_state(struct phy *phy, union phy_notify state);
|
||||
static inline int phy_get_bus_width(struct phy *phy)
|
||||
{
|
||||
return phy->attrs.bus_width;
|
||||
@ -412,6 +424,13 @@ static inline int phy_notify_disconnect(struct phy *phy, int index)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int phy_notify_state(struct phy *phy, union phy_notify state)
|
||||
{
|
||||
if (!phy)
|
||||
return 0;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int phy_configure(struct phy *phy,
|
||||
union phy_configure_opts *opts)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user