aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Lin <shawn.lin@rock-chips.com>2016-12-12 06:50:07 -0500
committerBjorn Helgaas <bhelgaas@google.com>2017-01-11 13:22:47 -0500
commit013dd3d5e1835c2cfa9c824e61465b61509afa54 (patch)
treec680a46bc383c1cece04b84df55a530ff3ac94cf
parent7ce7d89f48834cefece7804d38fc5d85382edf77 (diff)
PCI: rockchip: Add system PM support
Add system PM support for Rockchip's RC. For pre S3, the EP is configured into D3 state which guarantees the link state should be in L1. So we could send PME_Turn_Off message to the EP and wait for its ACK to make the link state into L2 or L3 without the aux-supply. This could help save more power which I think should be very important for mobile devices. As note that there is a 5s timeout for RC to wait for the PMA_ACK after sending PME_Turn_Off. Technically it should depend on the hierarchy of devices but seems PCIe core framework doesn't handle the L2/3 for S3 at all. So that means we should presume to set a default value for PME_ACK. From the bug report[1], we could find a statement that Microsoft Windows versions typically wait for 5 seconds. So we are prone to take 5s for this timeout here. [1] https://lists.launchpad.net/kernel-packages/msg123315.html Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Brian Norris <briannorris@chromium.org>
-rw-r--r--drivers/pci/host/pcie-rockchip.c106
1 files changed, 106 insertions, 0 deletions
diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
index f2dca7bb0b39..03923494825d 100644
--- a/drivers/pci/host/pcie-rockchip.c
+++ b/drivers/pci/host/pcie-rockchip.c
@@ -20,6 +20,7 @@
20#include <linux/gpio/consumer.h> 20#include <linux/gpio/consumer.h>
21#include <linux/init.h> 21#include <linux/init.h>
22#include <linux/interrupt.h> 22#include <linux/interrupt.h>
23#include <linux/iopoll.h>
23#include <linux/irq.h> 24#include <linux/irq.h>
24#include <linux/irqchip/chained_irq.h> 25#include <linux/irqchip/chained_irq.h>
25#include <linux/irqdomain.h> 26#include <linux/irqdomain.h>
@@ -55,6 +56,10 @@
55#define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040) 56#define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040)
56#define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0) 57#define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0)
57#define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080) 58#define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080)
59#define PCIE_CLIENT_DEBUG_OUT_0 (PCIE_CLIENT_BASE + 0x3c)
60#define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(5, 0)
61#define PCIE_CLIENT_DEBUG_LTSSM_L1 0x18
62#define PCIE_CLIENT_DEBUG_LTSSM_L2 0x19
58#define PCIE_CLIENT_BASIC_STATUS1 (PCIE_CLIENT_BASE + 0x48) 63#define PCIE_CLIENT_BASIC_STATUS1 (PCIE_CLIENT_BASE + 0x48)
59#define PCIE_CLIENT_LINK_STATUS_UP 0x00300000 64#define PCIE_CLIENT_LINK_STATUS_UP 0x00300000
60#define PCIE_CLIENT_LINK_STATUS_MASK 0x00300000 65#define PCIE_CLIENT_LINK_STATUS_MASK 0x00300000
@@ -167,9 +172,11 @@
167#define IB_ROOT_PORT_REG_SIZE_SHIFT 3 172#define IB_ROOT_PORT_REG_SIZE_SHIFT 3
168#define AXI_WRAPPER_IO_WRITE 0x6 173#define AXI_WRAPPER_IO_WRITE 0x6
169#define AXI_WRAPPER_MEM_WRITE 0x2 174#define AXI_WRAPPER_MEM_WRITE 0x2
175#define AXI_WRAPPER_NOR_MSG 0xc
170 176
171#define MAX_AXI_IB_ROOTPORT_REGION_NUM 3 177#define MAX_AXI_IB_ROOTPORT_REGION_NUM 3
172#define MIN_AXI_ADDR_BITS_PASSED 8 178#define MIN_AXI_ADDR_BITS_PASSED 8
179#define PCIE_RC_SEND_PME_OFF 0x11960
173#define ROCKCHIP_VENDOR_ID 0x1d87 180#define ROCKCHIP_VENDOR_ID 0x1d87
174#define PCIE_ECAM_BUS(x) (((x) & 0xff) << 20) 181#define PCIE_ECAM_BUS(x) (((x) & 0xff) << 20)
175#define PCIE_ECAM_DEV(x) (((x) & 0x1f) << 15) 182#define PCIE_ECAM_DEV(x) (((x) & 0x1f) << 15)
@@ -178,6 +185,9 @@
178#define PCIE_ECAM_ADDR(bus, dev, func, reg) \ 185#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
179 (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \ 186 (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
180 PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg)) 187 PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
188#define PCIE_LINK_IS_L2(x) \
189 (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == \
190 PCIE_CLIENT_DEBUG_LTSSM_L2)
181 191
182#define RC_REGION_0_ADDR_TRANS_H 0x00000000 192#define RC_REGION_0_ADDR_TRANS_H 0x00000000
183#define RC_REGION_0_ADDR_TRANS_L 0x00000000 193#define RC_REGION_0_ADDR_TRANS_L 0x00000000
@@ -211,7 +221,9 @@ struct rockchip_pcie {
211 u32 io_size; 221 u32 io_size;
212 int offset; 222 int offset;
213 phys_addr_t io_bus_addr; 223 phys_addr_t io_bus_addr;
224 void __iomem *msg_region;
214 u32 mem_size; 225 u32 mem_size;
226 phys_addr_t msg_bus_addr;
215 phys_addr_t mem_bus_addr; 227 phys_addr_t mem_bus_addr;
216}; 228};
217 229
@@ -1186,6 +1198,85 @@ static int rockchip_cfg_atu(struct rockchip_pcie *rockchip)
1186 } 1198 }
1187 } 1199 }
1188 1200
1201 /* assign message regions */
1202 rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1 + offset,
1203 AXI_WRAPPER_NOR_MSG,
1204 20 - 1, 0, 0);
1205
1206 rockchip->msg_bus_addr = rockchip->mem_bus_addr +
1207 ((reg_no + offset) << 20);
1208 return err;
1209}
1210
1211static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip)
1212{
1213 u32 value;
1214 int err;
1215
1216 /* send PME_TURN_OFF message */
1217 writel(0x0, rockchip->msg_region + PCIE_RC_SEND_PME_OFF);
1218
1219 /* read LTSSM and wait for falling into L2 link state */
1220 err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0,
1221 value, PCIE_LINK_IS_L2(value), 20,
1222 jiffies_to_usecs(5 * HZ));
1223 if (err) {
1224 dev_err(rockchip->dev, "PCIe link enter L2 timeout!\n");
1225 return err;
1226 }
1227
1228 return 0;
1229}
1230
1231static int rockchip_pcie_suspend_noirq(struct device *dev)
1232{
1233 struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
1234 int ret;
1235
1236 /* disable core and cli int since we don't need to ack PME_ACK */
1237 rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) |
1238 PCIE_CLIENT_INT_CLI, PCIE_CLIENT_INT_MASK);
1239 rockchip_pcie_write(rockchip, (u32)PCIE_CORE_INT, PCIE_CORE_INT_MASK);
1240
1241 ret = rockchip_pcie_wait_l2(rockchip);
1242 if (ret) {
1243 rockchip_pcie_enable_interrupts(rockchip);
1244 return ret;
1245 }
1246
1247 phy_power_off(rockchip->phy);
1248 phy_exit(rockchip->phy);
1249
1250 clk_disable_unprepare(rockchip->clk_pcie_pm);
1251 clk_disable_unprepare(rockchip->hclk_pcie);
1252 clk_disable_unprepare(rockchip->aclk_perf_pcie);
1253 clk_disable_unprepare(rockchip->aclk_pcie);
1254
1255 return ret;
1256}
1257
1258static int rockchip_pcie_resume_noirq(struct device *dev)
1259{
1260 struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
1261 int err;
1262
1263 clk_prepare_enable(rockchip->clk_pcie_pm);
1264 clk_prepare_enable(rockchip->hclk_pcie);
1265 clk_prepare_enable(rockchip->aclk_perf_pcie);
1266 clk_prepare_enable(rockchip->aclk_pcie);
1267
1268 err = rockchip_pcie_init_port(rockchip);
1269 if (err)
1270 return err;
1271
1272 err = rockchip_cfg_atu(rockchip);
1273 if (err)
1274 return err;
1275
1276 /* Need this to enter L1 again */
1277 rockchip_pcie_update_txcredit_mui(rockchip);
1278 rockchip_pcie_enable_interrupts(rockchip);
1279
1189 return 0; 1280 return 0;
1190} 1281}
1191 1282
@@ -1209,6 +1300,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
1209 if (!rockchip) 1300 if (!rockchip)
1210 return -ENOMEM; 1301 return -ENOMEM;
1211 1302
1303 platform_set_drvdata(pdev, rockchip);
1212 rockchip->dev = dev; 1304 rockchip->dev = dev;
1213 1305
1214 err = rockchip_pcie_parse_dt(rockchip); 1306 err = rockchip_pcie_parse_dt(rockchip);
@@ -1296,6 +1388,14 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
1296 err = rockchip_cfg_atu(rockchip); 1388 err = rockchip_cfg_atu(rockchip);
1297 if (err) 1389 if (err)
1298 goto err_vpcie; 1390 goto err_vpcie;
1391
1392 rockchip->msg_region = devm_ioremap(rockchip->dev,
1393 rockchip->msg_bus_addr, SZ_1M);
1394 if (!rockchip->msg_region) {
1395 err = -ENOMEM;
1396 goto err_vpcie;
1397 }
1398
1299 bus = pci_scan_root_bus(&pdev->dev, 0, &rockchip_pcie_ops, rockchip, &res); 1399 bus = pci_scan_root_bus(&pdev->dev, 0, &rockchip_pcie_ops, rockchip, &res);
1300 if (!bus) { 1400 if (!bus) {
1301 err = -ENOMEM; 1401 err = -ENOMEM;
@@ -1329,6 +1429,11 @@ err_aclk_pcie:
1329 return err; 1429 return err;
1330} 1430}
1331 1431
1432static const struct dev_pm_ops rockchip_pcie_pm_ops = {
1433 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq,
1434 rockchip_pcie_resume_noirq)
1435};
1436
1332static const struct of_device_id rockchip_pcie_of_match[] = { 1437static const struct of_device_id rockchip_pcie_of_match[] = {
1333 { .compatible = "rockchip,rk3399-pcie", }, 1438 { .compatible = "rockchip,rk3399-pcie", },
1334 {} 1439 {}
@@ -1338,6 +1443,7 @@ static struct platform_driver rockchip_pcie_driver = {
1338 .driver = { 1443 .driver = {
1339 .name = "rockchip-pcie", 1444 .name = "rockchip-pcie",
1340 .of_match_table = rockchip_pcie_of_match, 1445 .of_match_table = rockchip_pcie_of_match,
1446 .pm = &rockchip_pcie_pm_ops,
1341 }, 1447 },
1342 .probe = rockchip_pcie_probe, 1448 .probe = rockchip_pcie_probe,
1343 1449