aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Lin <shawn.lin@rock-chips.com>2016-09-01 03:44:54 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2016-09-10 07:18:33 -0400
commitfcffee3d54fcadcfa82b183c3fcdbd43e573339e (patch)
treeca8ca4c5a8b8f7fdf2de1b38b520a24b636cf63e
parentb11c821532b53976ff8af1b9c98d114facdfadcb (diff)
phy: add a driver for the Rockchip SoC internal PCIe PHY
This patch to add a generic PHY driver for rockchip PCIe PHY. Access the PHY via registers provided by GRF (general register files) module. Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
-rw-r--r--drivers/phy/Kconfig8
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-rockchip-pcie.c357
3 files changed, 366 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index f9bf9817e63f..46e5536c453e 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -388,6 +388,14 @@ config PHY_ROCKCHIP_DP
388 help 388 help
389 Enable this to support the Rockchip Display Port PHY. 389 Enable this to support the Rockchip Display Port PHY.
390 390
391config PHY_ROCKCHIP_PCIE
392 tristate "Rockchip PCIe PHY Driver"
393 depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
394 select GENERIC_PHY
395 select MFD_SYSCON
396 help
397 Enable this to support the Rockchip PCIe PHY.
398
391config PHY_ST_SPEAR1310_MIPHY 399config PHY_ST_SPEAR1310_MIPHY
392 tristate "ST SPEAR1310-MIPHY driver" 400 tristate "ST SPEAR1310-MIPHY driver"
393 select GENERIC_PHY 401 select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 74b44ef5f0a5..ce0e526059dd 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
42obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o 42obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
43obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o 43obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
44obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o 44obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
45obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
45obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o 46obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
46obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o 47obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
47obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o 48obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
diff --git a/drivers/phy/phy-rockchip-pcie.c b/drivers/phy/phy-rockchip-pcie.c
new file mode 100644
index 000000000000..a2b4c6b58aea
--- /dev/null
+++ b/drivers/phy/phy-rockchip-pcie.c
@@ -0,0 +1,357 @@
1/*
2 * Rockchip PCIe PHY driver
3 *
4 * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
5 * Copyright (C) 2016 ROCKCHIP, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/clk.h>
18#include <linux/delay.h>
19#include <linux/io.h>
20#include <linux/mfd/syscon.h>
21#include <linux/module.h>
22#include <linux/of.h>
23#include <linux/of_address.h>
24#include <linux/of_platform.h>
25#include <linux/phy/phy.h>
26#include <linux/platform_device.h>
27#include <linux/regmap.h>
28#include <linux/reset.h>
29
30/*
31 * The higher 16-bit of this register is used for write protection
32 * only if BIT(x + 16) set to 1 the BIT(x) can be written.
33 */
34#define HIWORD_UPDATE(val, mask, shift) \
35 ((val) << (shift) | (mask) << ((shift) + 16))
36
37#define PHY_MAX_LANE_NUM 4
38#define PHY_CFG_DATA_SHIFT 7
39#define PHY_CFG_ADDR_SHIFT 1
40#define PHY_CFG_DATA_MASK 0xf
41#define PHY_CFG_ADDR_MASK 0x3f
42#define PHY_CFG_RD_MASK 0x3ff
43#define PHY_CFG_WR_ENABLE 1
44#define PHY_CFG_WR_DISABLE 1
45#define PHY_CFG_WR_SHIFT 0
46#define PHY_CFG_WR_MASK 1
47#define PHY_CFG_PLL_LOCK 0x10
48#define PHY_CFG_CLK_TEST 0x10
49#define PHY_CFG_CLK_SCC 0x12
50#define PHY_CFG_SEPE_RATE BIT(3)
51#define PHY_CFG_PLL_100M BIT(3)
52#define PHY_PLL_LOCKED BIT(9)
53#define PHY_PLL_OUTPUT BIT(10)
54#define PHY_LANE_A_STATUS 0x30
55#define PHY_LANE_B_STATUS 0x31
56#define PHY_LANE_C_STATUS 0x32
57#define PHY_LANE_D_STATUS 0x33
58#define PHY_LANE_RX_DET_SHIFT 11
59#define PHY_LANE_RX_DET_TH 0x1
60#define PHY_LANE_IDLE_OFF 0x1
61#define PHY_LANE_IDLE_MASK 0x1
62#define PHY_LANE_IDLE_A_SHIFT 3
63#define PHY_LANE_IDLE_B_SHIFT 4
64#define PHY_LANE_IDLE_C_SHIFT 5
65#define PHY_LANE_IDLE_D_SHIFT 6
66
67struct rockchip_pcie_data {
68 unsigned int pcie_conf;
69 unsigned int pcie_status;
70 unsigned int pcie_laneoff;
71};
72
73struct rockchip_pcie_phy {
74 struct rockchip_pcie_data *phy_data;
75 struct regmap *reg_base;
76 struct reset_control *phy_rst;
77 struct clk *clk_pciephy_ref;
78};
79
80static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy,
81 u32 addr, u32 data)
82{
83 regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
84 HIWORD_UPDATE(data,
85 PHY_CFG_DATA_MASK,
86 PHY_CFG_DATA_SHIFT) |
87 HIWORD_UPDATE(addr,
88 PHY_CFG_ADDR_MASK,
89 PHY_CFG_ADDR_SHIFT));
90 udelay(1);
91 regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
92 HIWORD_UPDATE(PHY_CFG_WR_ENABLE,
93 PHY_CFG_WR_MASK,
94 PHY_CFG_WR_SHIFT));
95 udelay(1);
96 regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
97 HIWORD_UPDATE(PHY_CFG_WR_DISABLE,
98 PHY_CFG_WR_MASK,
99 PHY_CFG_WR_SHIFT));
100}
101
102static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy,
103 u32 addr)
104{
105 u32 val;
106
107 regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
108 HIWORD_UPDATE(addr,
109 PHY_CFG_RD_MASK,
110 PHY_CFG_ADDR_SHIFT));
111 regmap_read(rk_phy->reg_base,
112 rk_phy->phy_data->pcie_status,
113 &val);
114 return val;
115}
116
117static int rockchip_pcie_phy_power_off(struct phy *phy)
118{
119 struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
120 int err = 0;
121
122 err = reset_control_assert(rk_phy->phy_rst);
123 if (err) {
124 dev_err(&phy->dev, "assert phy_rst err %d\n", err);
125 return err;
126 }
127
128 return 0;
129}
130
131static int rockchip_pcie_phy_power_on(struct phy *phy)
132{
133 struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
134 int err = 0;
135 u32 status;
136 unsigned long timeout;
137
138 err = reset_control_deassert(rk_phy->phy_rst);
139 if (err) {
140 dev_err(&phy->dev, "deassert phy_rst err %d\n", err);
141 return err;
142 }
143
144 regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
145 HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
146 PHY_CFG_ADDR_MASK,
147 PHY_CFG_ADDR_SHIFT));
148
149 /*
150 * No documented timeout value for phy operation below,
151 * so we make it large enough here. And we use loop-break
152 * method which should not be harmful.
153 */
154 timeout = jiffies + msecs_to_jiffies(1000);
155
156 err = -EINVAL;
157 while (time_before(jiffies, timeout)) {
158 regmap_read(rk_phy->reg_base,
159 rk_phy->phy_data->pcie_status,
160 &status);
161 if (status & PHY_PLL_LOCKED) {
162 dev_dbg(&phy->dev, "pll locked!\n");
163 err = 0;
164 break;
165 }
166 msleep(20);
167 }
168
169 if (err) {
170 dev_err(&phy->dev, "pll lock timeout!\n");
171 goto err_pll_lock;
172 }
173</