2019-05-29 21:17:58 +07:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
2015-01-15 21:32:35 +07:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef UFS_QCOM_PHY_I_H_
|
|
|
|
#define UFS_QCOM_PHY_I_H_
|
|
|
|
|
2015-01-15 21:32:36 +07:00
|
|
|
#include <linux/module.h>
|
2015-01-15 21:32:35 +07:00
|
|
|
#include <linux/clk.h>
|
2018-09-04 17:17:18 +07:00
|
|
|
#include <linux/phy/phy.h>
|
2015-01-15 21:32:36 +07:00
|
|
|
#include <linux/regulator/consumer.h>
|
2019-03-22 00:17:59 +07:00
|
|
|
#include <linux/reset.h>
|
2015-01-15 21:32:35 +07:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/delay.h>
|
2018-12-21 17:13:18 +07:00
|
|
|
#include <linux/iopoll.h>
|
2015-01-15 21:32:36 +07:00
|
|
|
|
|
|
|
#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \
|
|
|
|
{ \
|
|
|
|
.reg_offset = reg, \
|
|
|
|
.cfg_value = val, \
|
|
|
|
}
|
|
|
|
|
2015-01-15 21:32:35 +07:00
|
|
|
#define UFS_QCOM_PHY_NAME_LEN 30
|
|
|
|
|
2015-01-15 21:32:36 +07:00
|
|
|
enum {
|
|
|
|
MASK_SERDES_START = 0x1,
|
|
|
|
MASK_PCS_READY = 0x1,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
OFFSET_SERDES_START = 0x0,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ufs_qcom_phy_stored_attributes {
|
|
|
|
u32 att;
|
|
|
|
u32 value;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-01-15 21:32:35 +07:00
|
|
|
struct ufs_qcom_phy_calibration {
|
|
|
|
u32 reg_offset;
|
|
|
|
u32 cfg_value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ufs_qcom_phy_vreg {
|
|
|
|
const char *name;
|
|
|
|
struct regulator *reg;
|
|
|
|
int max_uA;
|
|
|
|
int min_uV;
|
|
|
|
int max_uV;
|
|
|
|
bool enabled;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ufs_qcom_phy {
|
|
|
|
struct list_head list;
|
|
|
|
struct device *dev;
|
|
|
|
void __iomem *mmio;
|
|
|
|
void __iomem *dev_ref_clk_ctrl_mmio;
|
|
|
|
struct clk *tx_iface_clk;
|
|
|
|
struct clk *rx_iface_clk;
|
|
|
|
bool is_iface_clk_enabled;
|
|
|
|
struct clk *ref_clk_src;
|
|
|
|
struct clk *ref_clk_parent;
|
|
|
|
struct clk *ref_clk;
|
|
|
|
bool is_ref_clk_enabled;
|
|
|
|
bool is_dev_ref_clk_enabled;
|
|
|
|
struct ufs_qcom_phy_vreg vdda_pll;
|
|
|
|
struct ufs_qcom_phy_vreg vdda_phy;
|
|
|
|
struct ufs_qcom_phy_vreg vddp_ref_clk;
|
|
|
|
unsigned int quirks;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If UFS link is put into Hibern8 and if UFS PHY analog hardware is
|
|
|
|
* power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8
|
|
|
|
* exit might fail even after powering on UFS PHY analog hardware.
|
|
|
|
* Enabling this quirk will help to solve above issue by doing
|
|
|
|
* custom PHY settings just before PHY analog power collapse.
|
|
|
|
*/
|
|
|
|
#define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0)
|
|
|
|
|
|
|
|
u8 host_ctrl_rev_major;
|
|
|
|
u16 host_ctrl_rev_minor;
|
|
|
|
u16 host_ctrl_rev_step;
|
|
|
|
|
|
|
|
char name[UFS_QCOM_PHY_NAME_LEN];
|
|
|
|
struct ufs_qcom_phy_calibration *cached_regs;
|
|
|
|
int cached_regs_table_size;
|
|
|
|
struct ufs_qcom_phy_specific_ops *phy_spec_ops;
|
2017-10-12 13:19:34 +07:00
|
|
|
|
|
|
|
enum phy_mode mode;
|
2019-03-22 00:17:59 +07:00
|
|
|
struct reset_control *ufs_reset;
|
2015-01-15 21:32:35 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a
|
|
|
|
* specific implementation per phy. Each UFS phy, should implement
|
|
|
|
* those functions according to its spec and requirements
|
|
|
|
* @start_serdes: pointer to a function that starts the serdes
|
|
|
|
* @is_physical_coding_sublayer_ready: pointer to a function that
|
|
|
|
* checks pcs readiness. returns 0 for success and non-zero for error.
|
|
|
|
* @set_tx_lane_enable: pointer to a function that enable tx lanes
|
|
|
|
* @power_control: pointer to a function that controls analog rail of phy
|
|
|
|
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
|
|
|
|
*/
|
|
|
|
struct ufs_qcom_phy_specific_ops {
|
phy: ufs-qcom: Refactor all init steps into phy_poweron
The phy code was using implicit sequencing between the PHY driver
and the UFS driver to implement certain hardware requirements.
Specifically, the PHY reset register in the UFS controller needs
to be deasserted before serdes start occurs in the PHY.
Before this change, the code was doing this by utilizing the two
phy callbacks, phy_init() and phy_poweron(), as "init step 1" and
"init step 2", where the UFS driver would deassert reset between
these two steps.
This makes it challenging to power off the regulators in suspend,
as regulators are initialized in init, not in poweron(), but only
poweroff() is called during suspend, not exit().
For UFS, move the actual firing up of the PHY to phy_poweron() and
phy_poweroff() callbacks, rather than init()/exit(). UFS calls
phy_poweroff() during suspend, so now all clocks and regulators for
the phy can be powered down during suspend.
QMP is a little tricky because the PHY is also shared with PCIe and
USB3, which have their own definitions for init() and poweron(). Rename
the meaty functions to _enable() and _disable() to disentangle from the
PHY core names, and then create two different ops structures: one for
UFS and one for the other PHY types.
In phy-qcom-ufs, remove the 'is_powered_on' and 'is_started' guards,
as the generic PHY code does the reference counting. The
14/20nm-specific init functions get collapsed into the generic power_on()
function, with the addition of a calibrate() callback specific to 14/20nm.
Signed-off-by: Evan Green <evgreen@chromium.org>
Reviewed-by: Stephen Boyd <swboyd@chromium.org>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
2019-03-22 00:18:00 +07:00
|
|
|
int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B);
|
2015-01-15 21:32:35 +07:00
|
|
|
void (*start_serdes)(struct ufs_qcom_phy *phy);
|
|
|
|
int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
|
|
|
|
void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);
|
|
|
|
void (*power_control)(struct ufs_qcom_phy *phy, bool val);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy);
|
|
|
|
int ufs_qcom_phy_power_on(struct phy *generic_phy);
|
|
|
|
int ufs_qcom_phy_power_off(struct phy *generic_phy);
|
2016-11-08 17:07:42 +07:00
|
|
|
int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common);
|
|
|
|
int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common);
|
2015-01-15 21:32:35 +07:00
|
|
|
int ufs_qcom_phy_remove(struct phy *generic_phy,
|
|
|
|
struct ufs_qcom_phy *ufs_qcom_phy);
|
|
|
|
struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
|
|
|
|
struct ufs_qcom_phy *common_cfg,
|
2015-07-15 14:33:51 +07:00
|
|
|
const struct phy_ops *ufs_qcom_phy_gen_ops,
|
2015-01-15 21:32:35 +07:00
|
|
|
struct ufs_qcom_phy_specific_ops *phy_spec_ops);
|
|
|
|
int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
|
|
|
struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A,
|
|
|
|
struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B,
|
|
|
|
bool is_rate_B);
|
|
|
|
#endif
|