2014-04-18 19:04:37

by Suman Tripathi

[permalink] [raw]
Subject: [PATCH 0/2] PHY: Add SATA Gen1/Gen2 support to the APM X-Gene SoC 15Gbps PHY driver

This patch adds the necessary code to support SATA Gen1/Gen2 for the APM X-Gene
SoC 15Gbps Multi-purpose PHY driver.

Suman Tripathi (2):
PHY: Add function set_rate to generic PHY framework
PHY: Add Gen1/Gen2 support for the APM SoC X-Gene SATA PHY driver

drivers/phy/phy-core.c | 29 ++++++++++++++++++++++
drivers/phy/phy-xgene.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/phy/phy.h | 8 +++++++
3 files changed, 101 insertions(+)

--
1.8.2.1


2014-04-18 19:04:48

by Suman Tripathi

[permalink] [raw]
Subject: [PATCH 2/2] PHY: Add Gen1/Gen2 support for the APM SoC X-Gene SATA PHY driver

This patch adds the necessary PHY code to support SATA Gen1/Gen2 for
the APM X-Gene SoC PHY driver.

Signed-off-by: Loc Ho <[email protected]>
Signed-off-by: Suman Tripathi <[email protected]>
---
drivers/phy/phy-xgene.c | 90 ++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 77 insertions(+), 13 deletions(-)

diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c
index 4aa1ccd..36e1d99 100644
--- a/drivers/phy/phy-xgene.c
+++ b/drivers/phy/phy-xgene.c
@@ -48,7 +48,7 @@
* at 0x1f23a000 (SATA Port 4/5). For such PHY, another resource is required
* to located the SDS/Ref PLL CMU module and its clock for that IP enabled.
*
- * Currently, this driver only supports Gen3 SATA mode with external clock.
+ * This driver supports Gen3/Gen2/Gen1 SATA mode with external clock.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -76,10 +76,6 @@
#define DEFAULT_SATA_TXCN2 { 0x0, 0x0, 0x0 }
#define DEFAULT_SATA_TXCP1 { 0xa, 0xa, 0xa }

-#define SATA_SPD_SEL_GEN3 0x7
-#define SATA_SPD_SEL_GEN2 0x3
-#define SATA_SPD_SEL_GEN1 0x1
-
#define SSC_DISABLE 0
#define SSC_ENABLE 1

@@ -331,6 +327,14 @@
(((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
#define RXTX_REG7_RX_WORD_MODE_SET(dst, src) \
(((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG7_RX_DATA_RATE_RD(src) ((0x00000600 & (u32) (src)) >> 9)
+#define RXTX_REG7_RESETB_RXD_SET(dst, src) \
+ (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define RXTX_REG7_RESETB_RXA_SET(dst, src) \
+ (((dst) & ~0x00000080) | (((u32) (src) << 7) & 0x00000080))
+#define RXTX_REG7_LOOP_BACK_ENA_CTLE_MASK 0x00004000
+#define RXTX_REG7_LOOP_BACK_ENA_CTLE_SET(dst, src) \
+ (((dst) & ~0x00004000) | (((u32) (src) << 14) & 0x00004000))
#define RXTX_REG8 0x010
#define RXTX_REG8_CDR_LOOP_ENA_SET(dst, src) \
(((dst) & ~0x00004000) | (((u32) (src) << 14) & 0x00004000))
@@ -342,14 +346,6 @@
(((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
#define RXTX_REG8_SD_DISABLE_SET(dst, src) \
(((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
-#define RXTX_REG7 0x00e
-#define RXTX_REG7_RESETB_RXD_SET(dst, src) \
- (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
-#define RXTX_REG7_RESETB_RXA_SET(dst, src) \
- (((dst) & ~0x00000080) | (((u32) (src) << 7) & 0x00000080))
-#define RXTX_REG7_LOOP_BACK_ENA_CTLE_MASK 0x00004000
-#define RXTX_REG7_LOOP_BACK_ENA_CTLE_SET(dst, src) \
- (((dst) & ~0x00004000) | (((u32) (src) << 14) & 0x00004000))
#define RXTX_REG11 0x016
#define RXTX_REG11_PHASE_ADJUST_LIMIT_SET(dst, src) \
(((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
@@ -1615,8 +1611,76 @@ static int xgene_phy_hw_init(struct phy *phy)
return 0;
}

+static void xgene_phy_sata_force_gen(struct xgene_phy_ctx *ctx,
+ int lane, bool is_gen3)
+{
+ u32 val;
+
+ serdes_rd(ctx, lane, RXTX_REG38, &val);
+ val = RXTX_REG38_CUSTOMER_PINMODE_INV_SET(val, is_gen3 ? 0x0 : 0x400);
+ serdes_wr(ctx, lane, RXTX_REG38, val);
+
+ /* Set boost control value */
+ serdes_rd(ctx, lane, RXTX_REG1, &val);
+ val = RXTX_REG1_RXACVCM_SET(val, 0x7);
+ val = RXTX_REG1_CTLE_EQ_SET(val,
+ ctx->sata_param.txboostgain[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ serdes_wr(ctx, lane, RXTX_REG1, val);
+
+ serdes_rd(ctx, lane, RXTX_REG125, &val);
+ val = RXTX_REG125_PQ_REG_SET(val,
+ ctx->sata_param.txeyetuning[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ serdes_wr(ctx, lane, RXTX_REG125, val);
+
+ serdes_rd(ctx, lane, RXTX_REG61, &val);
+ val = RXTX_REG61_SPD_SEL_CDR_SET(val,
+ ctx->sata_param.txspeed[ctx->sata_param.speed[lane]]);
+ serdes_wr(ctx, lane, RXTX_REG61, val);
+}
+
+static int xgene_phy_set_rate(struct phy *phy, int lane, u64 rate)
+{
+ struct xgene_phy_ctx *ctx = phy_get_drvdata(phy);
+ u32 val;
+
+ if (lane >= MAX_LANE)
+ return -EINVAL;
+
+ if (ctx->mode == MODE_SATA) {
+ /*
+ * Check if the previous rate is same
+ * as the current link up rate. If so
+ * skip the operation.
+ */
+ serdes_rd(ctx, lane, RXTX_REG7, &val);
+ if (2 - (RXTX_REG7_RX_DATA_RATE_RD(val)) ==
+ ctx->sata_param.speed[lane])
+ return 1;
+
+ switch (rate) {
+ case 1500000000ULL:
+ ctx->sata_param.speed[lane] = 0;
+ xgene_phy_sata_force_gen(ctx, lane, false);
+ break;
+ case 3000000000ULL:
+ ctx->sata_param.speed[lane] = 1;
+ xgene_phy_sata_force_gen(ctx, lane, false);
+ break;
+ default:
+ ctx->sata_param.speed[lane] = 2;
+ xgene_phy_sata_force_gen(ctx, lane, true);
+ break;
+ }
+ }
+
+ return 0;
+}
+
static const struct phy_ops xgene_phy_ops = {
.init = xgene_phy_hw_init,
+ .set_rate = xgene_phy_set_rate,
.owner = THIS_MODULE,
};

--
1.8.2.1

2014-04-18 19:05:24

by Suman Tripathi

[permalink] [raw]
Subject: [PATCH 1/2] PHY: Add function set_rate to generic PHY framework

This patch adds function set_rate to the generic PHY framework operation
structure. This function can be called to instruct the PHY underlying
layer at specified lane to configure for specified rate.

Signed-off-by: Loc Ho <[email protected]>
Signed-off-by: Suman Tripathi <[email protected]>
---
drivers/phy/phy-core.c | 27 +++++++++++++++++++++++++++
include/linux/phy/phy.h | 8 ++++++++
2 files changed, 35 insertions(+)

diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 645c867..51f82e0 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -258,6 +258,33 @@ int phy_power_off(struct phy *phy)
EXPORT_SYMBOL_GPL(phy_power_off);

/**
+ * phy_set_rate - set an specified lane at an specified rate
+ * @phy: phy instance to be set
+ * @lane: zero-based lane index
+ * @rate: operating rate in bps (bits per second)
+ *
+ * Returns 0 if successful, -ENOTSUPP if not supported
+ * -EINVAL if parameter out of range.
+ */
+int phy_set_rate(struct phy *phy, int lane, u64 rate)
+{
+ int ret = 0;
+
+ mutex_lock(&phy->mutex);
+ if (phy->ops->set_rate) {
+ ret = phy->ops->set_rate(phy, lane, rate);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy set rate failed --> %d\n",
+ ret);
+ }
+ }
+ mutex_unlock(&phy->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_set_rate);
+
+/**
* of_phy_get() - lookup and obtain a reference to a phy by phandle
* @dev: device that requests this phy
* @index: the index of the phy
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index e273e5a..2dfcb34 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -27,6 +27,7 @@ struct phy;
* @exit: operation to be performed while exiting
* @power_on: powering on the phy
* @power_off: powering off the phy
+ * @set_rate: set the rate
* @owner: the module owner containing the ops
*/
struct phy_ops {
@@ -34,6 +35,7 @@ struct phy_ops {
int (*exit)(struct phy *phy);
int (*power_on)(struct phy *phy);
int (*power_off)(struct phy *phy);
+ int (*set_rate)(struct phy *phy, int lane, u64 rate);
struct module *owner;
};

@@ -137,6 +139,7 @@ int phy_init(struct phy *phy);
int phy_exit(struct phy *phy);
int phy_power_on(struct phy *phy);
int phy_power_off(struct phy *phy);
+int phy_set_rate(struct phy *phy, int lane, u64 rate);
static inline int phy_get_bus_width(struct phy *phy)
{
return phy->attrs.bus_width;
@@ -217,6 +220,11 @@ static inline int phy_power_off(struct phy *phy)
return -ENOSYS;
}

+static inline int phy_set_rate(struct phy *phy, int lane, u64 rate)
+{
+ return -ENOSYS;
+}
+
static inline int phy_get_bus_width(struct phy *phy)
{
return -ENOSYS;
--
1.8.2.1