diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2018-04-04 14:28:54 -0400 |
---|---|---|
committer | Bjorn Helgaas <helgaas@kernel.org> | 2018-04-04 14:28:54 -0400 |
commit | 34fe07b11dbf8d2a4d3676c8f7b1de7d1092bb9f (patch) | |
tree | 48f348cf6addb22485847ac5cf684ec873a3cf85 | |
parent | df25d407afa5e929449dc78f89588617d27d4983 (diff) | |
parent | da76ba50963b81413ffd3613f84ee9e592220b3d (diff) |
Merge branch 'lorenzo/pci/tegra'
* lorenzo/pci/tegra:
PCI: tegra: Add power management support
PCI: tegra: Add loadable kernel module support
PCI: tegra: Free resources on probe failure
-rw-r--r-- | drivers/pci/host/pci-tegra.c | 354 |
1 files changed, 278 insertions, 76 deletions
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index dd9b3bcc41c3..389e74be846c 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c | |||
@@ -18,10 +18,12 @@ | |||
18 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
19 | #include <linux/export.h> | 19 | #include <linux/export.h> |
20 | #include <linux/interrupt.h> | 20 | #include <linux/interrupt.h> |
21 | #include <linux/iopoll.h> | ||
21 | #include <linux/irq.h> | 22 | #include <linux/irq.h> |
22 | #include <linux/irqdomain.h> | 23 | #include <linux/irqdomain.h> |
23 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
24 | #include <linux/init.h> | 25 | #include <linux/init.h> |
26 | #include <linux/module.h> | ||
25 | #include <linux/msi.h> | 27 | #include <linux/msi.h> |
26 | #include <linux/of_address.h> | 28 | #include <linux/of_address.h> |
27 | #include <linux/of_pci.h> | 29 | #include <linux/of_pci.h> |
@@ -139,6 +141,8 @@ | |||
139 | #define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) | 141 | #define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) |
140 | #define AFI_INTR_EN_PRSNT_SENSE (1 << 8) | 142 | #define AFI_INTR_EN_PRSNT_SENSE (1 << 8) |
141 | 143 | ||
144 | #define AFI_PCIE_PME 0xf0 | ||
145 | |||
142 | #define AFI_PCIE_CONFIG 0x0f8 | 146 | #define AFI_PCIE_CONFIG 0x0f8 |
143 | #define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) | 147 | #define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) |
144 | #define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe | 148 | #define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe |
@@ -219,6 +223,8 @@ | |||
219 | #define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ | 223 | #define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ |
220 | #define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ | 224 | #define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ |
221 | 225 | ||
226 | #define PME_ACK_TIMEOUT 10000 | ||
227 | |||
222 | struct tegra_msi { | 228 | struct tegra_msi { |
223 | struct msi_controller chip; | 229 | struct msi_controller chip; |
224 | DECLARE_BITMAP(used, INT_PCI_MSI_NR); | 230 | DECLARE_BITMAP(used, INT_PCI_MSI_NR); |
@@ -230,8 +236,16 @@ struct tegra_msi { | |||
230 | }; | 236 | }; |
231 | 237 | ||
232 | /* used to differentiate between Tegra SoC generations */ | 238 | /* used to differentiate between Tegra SoC generations */ |
239 | struct tegra_pcie_port_soc { | ||
240 | struct { | ||
241 | u8 turnoff_bit; | ||
242 | u8 ack_bit; | ||
243 | } pme; | ||
244 | }; | ||
245 | |||
233 | struct tegra_pcie_soc { | 246 | struct tegra_pcie_soc { |
234 | unsigned int num_ports; | 247 | unsigned int num_ports; |
248 | const struct tegra_pcie_port_soc *ports; | ||
235 | unsigned int msi_base_shift; | 249 | unsigned int msi_base_shift; |
236 | u32 pads_pll_ctl; | 250 | u32 pads_pll_ctl; |
237 | u32 tx_ref_sel; | 251 | u32 tx_ref_sel; |
@@ -549,14 +563,25 @@ static int tegra_pcie_request_resources(struct tegra_pcie *pcie) | |||
549 | pci_add_resource(windows, &pcie->busn); | 563 | pci_add_resource(windows, &pcie->busn); |
550 | 564 | ||
551 | err = devm_request_pci_bus_resources(dev, windows); | 565 | err = devm_request_pci_bus_resources(dev, windows); |
552 | if (err < 0) | 566 | if (err < 0) { |
567 | pci_free_resource_list(windows); | ||
553 | return err; | 568 | return err; |
569 | } | ||
554 | 570 | ||
555 | pci_remap_iospace(&pcie->pio, pcie->io.start); | 571 | pci_remap_iospace(&pcie->pio, pcie->io.start); |
556 | 572 | ||
557 | return 0; | 573 | return 0; |
558 | } | 574 | } |
559 | 575 | ||
576 | static void tegra_pcie_free_resources(struct tegra_pcie *pcie) | ||
577 | { | ||
578 | struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); | ||
579 | struct list_head *windows = &host->windows; | ||
580 | |||
581 | pci_unmap_iospace(&pcie->pio); | ||
582 | pci_free_resource_list(windows); | ||
583 | } | ||
584 | |||
560 | static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) | 585 | static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) |
561 | { | 586 | { |
562 | struct tegra_pcie *pcie = pdev->bus->sysdata; | 587 | struct tegra_pcie *pcie = pdev->bus->sysdata; |
@@ -966,24 +991,35 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) | |||
966 | return 0; | 991 | return 0; |
967 | } | 992 | } |
968 | 993 | ||
969 | static void tegra_pcie_power_off(struct tegra_pcie *pcie) | 994 | static void tegra_pcie_disable_controller(struct tegra_pcie *pcie) |
970 | { | 995 | { |
971 | struct device *dev = pcie->dev; | ||
972 | const struct tegra_pcie_soc *soc = pcie->soc; | ||
973 | int err; | 996 | int err; |
974 | 997 | ||
975 | /* TODO: disable and unprepare clocks? */ | 998 | reset_control_assert(pcie->pcie_xrst); |
976 | 999 | ||
977 | if (soc->program_uphy) { | 1000 | if (pcie->soc->program_uphy) { |
978 | err = tegra_pcie_phy_power_off(pcie); | 1001 | err = tegra_pcie_phy_power_off(pcie); |
979 | if (err < 0) | 1002 | if (err < 0) |
980 | dev_err(dev, "failed to power off PHY(s): %d\n", err); | 1003 | dev_err(pcie->dev, "failed to power off PHY(s): %d\n", |
1004 | err); | ||
981 | } | 1005 | } |
1006 | } | ||
1007 | |||
1008 | static void tegra_pcie_power_off(struct tegra_pcie *pcie) | ||
1009 | { | ||
1010 | struct device *dev = pcie->dev; | ||
1011 | const struct tegra_pcie_soc *soc = pcie->soc; | ||
1012 | int err; | ||
982 | 1013 | ||
983 | reset_control_assert(pcie->pcie_xrst); | ||
984 | reset_control_assert(pcie->afi_rst); | 1014 | reset_control_assert(pcie->afi_rst); |
985 | reset_control_assert(pcie->pex_rst); | 1015 | reset_control_assert(pcie->pex_rst); |
986 | 1016 | ||
1017 | clk_disable_unprepare(pcie->pll_e); | ||
1018 | if (soc->has_cml_clk) | ||
1019 | clk_disable_unprepare(pcie->cml_clk); | ||
1020 | clk_disable_unprepare(pcie->afi_clk); | ||
1021 | clk_disable_unprepare(pcie->pex_clk); | ||
1022 | |||
987 | if (!dev->pm_domain) | 1023 | if (!dev->pm_domain) |
988 | tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); | 1024 | tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); |
989 | 1025 | ||
@@ -1192,6 +1228,30 @@ static int tegra_pcie_phys_get(struct tegra_pcie *pcie) | |||
1192 | return 0; | 1228 | return 0; |
1193 | } | 1229 | } |
1194 | 1230 | ||
1231 | static void tegra_pcie_phys_put(struct tegra_pcie *pcie) | ||
1232 | { | ||
1233 | struct tegra_pcie_port *port; | ||
1234 | struct device *dev = pcie->dev; | ||
1235 | int err, i; | ||
1236 | |||
1237 | if (pcie->legacy_phy) { | ||
1238 | err = phy_exit(pcie->phy); | ||
1239 | if (err < 0) | ||
1240 | dev_err(dev, "failed to teardown PHY: %d\n", err); | ||
1241 | return; | ||
1242 | } | ||
1243 | |||
1244 | list_for_each_entry(port, &pcie->ports, list) { | ||
1245 | for (i = 0; i < port->lanes; i++) { | ||
1246 | err = phy_exit(port->phys[i]); | ||
1247 | if (err < 0) | ||
1248 | dev_err(dev, "failed to teardown PHY#%u: %d\n", | ||
1249 | i, err); | ||
1250 | } | ||
1251 | } | ||
1252 | } | ||
1253 | |||
1254 | |||
1195 | static int tegra_pcie_get_resources(struct tegra_pcie *pcie) | 1255 | static int tegra_pcie_get_resources(struct tegra_pcie *pcie) |
1196 | { | 1256 | { |
1197 | struct device *dev = pcie->dev; | 1257 | struct device *dev = pcie->dev; |
@@ -1220,31 +1280,25 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) | |||
1220 | } | 1280 | } |
1221 | } | 1281 | } |
1222 | 1282 | ||
1223 | err = tegra_pcie_power_on(pcie); | ||
1224 | if (err) { | ||
1225 | dev_err(dev, "failed to power up: %d\n", err); | ||
1226 | return err; | ||
1227 | } | ||
1228 | |||
1229 | pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); | 1283 | pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); |
1230 | pcie->pads = devm_ioremap_resource(dev, pads); | 1284 | pcie->pads = devm_ioremap_resource(dev, pads); |
1231 | if (IS_ERR(pcie->pads)) { | 1285 | if (IS_ERR(pcie->pads)) { |
1232 | err = PTR_ERR(pcie->pads); | 1286 | err = PTR_ERR(pcie->pads); |
1233 | goto poweroff; | 1287 | goto phys_put; |
1234 | } | 1288 | } |
1235 | 1289 | ||
1236 | afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); | 1290 | afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); |
1237 | pcie->afi = devm_ioremap_resource(dev, afi); | 1291 | pcie->afi = devm_ioremap_resource(dev, afi); |
1238 | if (IS_ERR(pcie->afi)) { | 1292 | if (IS_ERR(pcie->afi)) { |
1239 | err = PTR_ERR(pcie->afi); | 1293 | err = PTR_ERR(pcie->afi); |
1240 | goto poweroff; | 1294 | goto phys_put; |
1241 | } | 1295 | } |
1242 | 1296 | ||
1243 | /* request configuration space, but remap later, on demand */ | 1297 | /* request configuration space, but remap later, on demand */ |
1244 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); | 1298 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); |
1245 | if (!res) { | 1299 | if (!res) { |
1246 | err = -EADDRNOTAVAIL; | 1300 | err = -EADDRNOTAVAIL; |
1247 | goto poweroff; | 1301 | goto phys_put; |
1248 | } | 1302 | } |
1249 | 1303 | ||
1250 | pcie->cs = *res; | 1304 | pcie->cs = *res; |
@@ -1255,14 +1309,14 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) | |||
1255 | pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); | 1309 | pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); |
1256 | if (IS_ERR(pcie->cfg)) { | 1310 | if (IS_ERR(pcie->cfg)) { |
1257 | err = PTR_ERR(pcie->cfg); | 1311 | err = PTR_ERR(pcie->cfg); |
1258 | goto poweroff; | 1312 | goto phys_put; |
1259 | } | 1313 | } |
1260 | 1314 | ||
1261 | /* request interrupt */ | 1315 | /* request interrupt */ |
1262 | err = platform_get_irq_byname(pdev, "intr"); | 1316 | err = platform_get_irq_byname(pdev, "intr"); |
1263 | if (err < 0) { | 1317 | if (err < 0) { |
1264 | dev_err(dev, "failed to get IRQ: %d\n", err); | 1318 | dev_err(dev, "failed to get IRQ: %d\n", err); |
1265 | goto poweroff; | 1319 | goto phys_put; |
1266 | } | 1320 | } |
1267 | 1321 | ||
1268 | pcie->irq = err; | 1322 | pcie->irq = err; |
@@ -1270,36 +1324,56 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) | |||
1270 | err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); | 1324 | err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); |
1271 | if (err) { | 1325 | if (err) { |
1272 | dev_err(dev, "failed to register IRQ: %d\n", err); | 1326 | dev_err(dev, "failed to register IRQ: %d\n", err); |
1273 | goto poweroff; | 1327 | goto phys_put; |
1274 | } | 1328 | } |
1275 | 1329 | ||
1276 | return 0; | 1330 | return 0; |
1277 | 1331 | ||
1278 | poweroff: | 1332 | phys_put: |
1279 | tegra_pcie_power_off(pcie); | 1333 | if (soc->program_uphy) |
1334 | tegra_pcie_phys_put(pcie); | ||
1280 | return err; | 1335 | return err; |
1281 | } | 1336 | } |
1282 | 1337 | ||
1283 | static int tegra_pcie_put_resources(struct tegra_pcie *pcie) | 1338 | static int tegra_pcie_put_resources(struct tegra_pcie *pcie) |
1284 | { | 1339 | { |
1285 | struct device *dev = pcie->dev; | ||
1286 | const struct tegra_pcie_soc *soc = pcie->soc; | 1340 | const struct tegra_pcie_soc *soc = pcie->soc; |
1287 | int err; | ||
1288 | 1341 | ||
1289 | if (pcie->irq > 0) | 1342 | if (pcie->irq > 0) |
1290 | free_irq(pcie->irq, pcie); | 1343 | free_irq(pcie->irq, pcie); |
1291 | 1344 | ||
1292 | tegra_pcie_power_off(pcie); | 1345 | if (soc->program_uphy) |
1293 | 1346 | tegra_pcie_phys_put(pcie); | |
1294 | if (soc->program_uphy) { | ||
1295 | err = phy_exit(pcie->phy); | ||
1296 | if (err < 0) | ||
1297 | dev_err(dev, "failed to teardown PHY: %d\n", err); | ||
1298 | } | ||
1299 | 1347 | ||
1300 | return 0; | 1348 | return 0; |
1301 | } | 1349 | } |
1302 | 1350 | ||
1351 | static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port) | ||
1352 | { | ||
1353 | struct tegra_pcie *pcie = port->pcie; | ||
1354 | const struct tegra_pcie_soc *soc = pcie->soc; | ||
1355 | int err; | ||
1356 | u32 val; | ||
1357 | u8 ack_bit; | ||
1358 | |||
1359 | val = afi_readl(pcie, AFI_PCIE_PME); | ||
1360 | val |= (0x1 << soc->ports[port->index].pme.turnoff_bit); | ||
1361 | afi_writel(pcie, val, AFI_PCIE_PME); | ||
1362 | |||
1363 | ack_bit = soc->ports[port->index].pme.ack_bit; | ||
1364 | err = readl_poll_timeout(pcie->afi + AFI_PCIE_PME, val, | ||
1365 | val & (0x1 << ack_bit), 1, PME_ACK_TIMEOUT); | ||
1366 | if (err) | ||
1367 | dev_err(pcie->dev, "PME Ack is not received on port: %d\n", | ||
1368 | port->index); | ||
1369 | |||
1370 | usleep_range(10000, 11000); | ||
1371 | |||
1372 | val = afi_readl(pcie, AFI_PCIE_PME); | ||
1373 | val &= ~(0x1 << soc->ports[port->index].pme.turnoff_bit); | ||
1374 | afi_writel(pcie, val, AFI_PCIE_PME); | ||
1375 | } | ||
1376 | |||
1303 | static int tegra_msi_alloc(struct tegra_msi *chip) | 1377 | static int tegra_msi_alloc(struct tegra_msi *chip) |
1304 | { | 1378 | { |
1305 | int msi; | 1379 | int msi; |
@@ -1436,15 +1510,13 @@ static const struct irq_domain_ops msi_domain_ops = { | |||
1436 | .map = tegra_msi_map, | 1510 | .map = tegra_msi_map, |
1437 | }; | 1511 | }; |
1438 | 1512 | ||
1439 | static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) | 1513 | static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) |
1440 | { | 1514 | { |
1441 | struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); | 1515 | struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); |
1442 | struct platform_device *pdev = to_platform_device(pcie->dev); | 1516 | struct platform_device *pdev = to_platform_device(pcie->dev); |
1443 | const struct tegra_pcie_soc *soc = pcie->soc; | ||
1444 | struct tegra_msi *msi = &pcie->msi; | 1517 | struct tegra_msi *msi = &pcie->msi; |
1445 | struct device *dev = pcie->dev; | 1518 | struct device *dev = pcie->dev; |
1446 | int err; | 1519 | int err; |
1447 | u32 reg; | ||
1448 | 1520 | ||
1449 | mutex_init(&msi->lock); | 1521 | mutex_init(&msi->lock); |
1450 | 1522 | ||
@@ -1477,6 +1549,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) | |||
1477 | /* setup AFI/FPCI range */ | 1549 | /* setup AFI/FPCI range */ |
1478 | msi->pages = __get_free_pages(GFP_KERNEL, 0); | 1550 | msi->pages = __get_free_pages(GFP_KERNEL, 0); |
1479 | msi->phys = virt_to_phys((void *)msi->pages); | 1551 | msi->phys = virt_to_phys((void *)msi->pages); |
1552 | host->msi = &msi->chip; | ||
1553 | |||
1554 | return 0; | ||
1555 | |||
1556 | err: | ||
1557 | irq_domain_remove(msi->domain); | ||
1558 | return err; | ||
1559 | } | ||
1560 | |||
1561 | static void tegra_pcie_enable_msi(struct tegra_pcie *pcie) | ||
1562 | { | ||
1563 | const struct tegra_pcie_soc *soc = pcie->soc; | ||
1564 | struct tegra_msi *msi = &pcie->msi; | ||
1565 | u32 reg; | ||
1480 | 1566 | ||
1481 | afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); | 1567 | afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); |
1482 | afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); | 1568 | afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); |
@@ -1497,20 +1583,29 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) | |||
1497 | reg = afi_readl(pcie, AFI_INTR_MASK); | 1583 | reg = afi_readl(pcie, AFI_INTR_MASK); |
1498 | reg |= AFI_INTR_MASK_MSI_MASK; | 1584 | reg |= AFI_INTR_MASK_MSI_MASK; |
1499 | afi_writel(pcie, reg, AFI_INTR_MASK); | 1585 | afi_writel(pcie, reg, AFI_INTR_MASK); |
1586 | } | ||
1500 | 1587 | ||
1501 | host->msi = &msi->chip; | 1588 | static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie) |
1589 | { | ||
1590 | struct tegra_msi *msi = &pcie->msi; | ||
1591 | unsigned int i, irq; | ||
1502 | 1592 | ||
1503 | return 0; | 1593 | free_pages(msi->pages, 0); |
1594 | |||
1595 | if (msi->irq > 0) | ||
1596 | free_irq(msi->irq, pcie); | ||
1597 | |||
1598 | for (i = 0; i < INT_PCI_MSI_NR; i++) { | ||
1599 | irq = irq_find_mapping(msi->domain, i); | ||
1600 | if (irq > 0) | ||
1601 | irq_dispose_mapping(irq); | ||
1602 | } | ||
1504 | 1603 | ||
1505 | err: | ||
1506 | irq_domain_remove(msi->domain); | 1604 | irq_domain_remove(msi->domain); |
1507 | return err; | ||
1508 | } | 1605 | } |
1509 | 1606 | ||
1510 | static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) | 1607 | static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) |
1511 | { | 1608 | { |
1512 | struct tegra_msi *msi = &pcie->msi; | ||
1513 | unsigned int i, irq; | ||
1514 | u32 value; | 1609 | u32 value; |
1515 | 1610 | ||
1516 | /* mask the MSI interrupt */ | 1611 | /* mask the MSI interrupt */ |
@@ -1528,19 +1623,6 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) | |||
1528 | afi_writel(pcie, 0, AFI_MSI_EN_VEC6); | 1623 | afi_writel(pcie, 0, AFI_MSI_EN_VEC6); |
1529 | afi_writel(pcie, 0, AFI_MSI_EN_VEC7); | 1624 | afi_writel(pcie, 0, AFI_MSI_EN_VEC7); |
1530 | 1625 | ||
1531 | free_pages(msi->pages, 0); | ||
1532 | |||
1533 | if (msi->irq > 0) | ||
1534 | free_irq(msi->irq, pcie); | ||
1535 | |||
1536 | for (i = 0; i < INT_PCI_MSI_NR; i++) { | ||
1537 | irq = irq_find_mapping(msi->domain, i); | ||
1538 | if (irq > 0) | ||
1539 | irq_dispose_mapping(irq); | ||
1540 | } | ||
1541 | |||
1542 | irq_domain_remove(msi->domain); | ||
1543 | |||
1544 | return 0; | 1626 | return 0; |
1545 | } | 1627 | } |
1546 | 1628 | ||
@@ -2035,8 +2117,22 @@ static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) | |||
2035 | } | 2117 | } |
2036 | } | 2118 | } |
2037 | 2119 | ||
2120 | static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) | ||
2121 | { | ||
2122 | struct tegra_pcie_port *port, *tmp; | ||
2123 | |||
2124 | list_for_each_entry_safe(port, tmp, &pcie->ports, list) | ||
2125 | tegra_pcie_port_disable(port); | ||
2126 | } | ||
2127 | |||
2128 | static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { | ||
2129 | { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, | ||
2130 | { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, | ||
2131 | }; | ||
2132 | |||
2038 | static const struct tegra_pcie_soc tegra20_pcie = { | 2133 | static const struct tegra_pcie_soc tegra20_pcie = { |
2039 | .num_ports = 2, | 2134 | .num_ports = 2, |
2135 | .ports = tegra20_pcie_ports, | ||
2040 | .msi_base_shift = 0, | 2136 | .msi_base_shift = 0, |
2041 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, | 2137 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, |
2042 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, | 2138 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, |
@@ -2050,8 +2146,15 @@ static const struct tegra_pcie_soc tegra20_pcie = { | |||
2050 | .program_uphy = true, | 2146 | .program_uphy = true, |
2051 | }; | 2147 | }; |
2052 | 2148 | ||
2149 | static const struct tegra_pcie_port_soc tegra30_pcie_ports[] = { | ||
2150 | { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, | ||
2151 | { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, | ||
2152 | { .pme.turnoff_bit = 16, .pme.ack_bit = 18 }, | ||
2153 | }; | ||
2154 | |||
2053 | static const struct tegra_pcie_soc tegra30_pcie = { | 2155 | static const struct tegra_pcie_soc tegra30_pcie = { |
2054 | .num_ports = 3, | 2156 | .num_ports = 3, |
2157 | .ports = tegra30_pcie_ports, | ||
2055 | .msi_base_shift = 8, | 2158 | .msi_base_shift = 8, |
2056 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, | 2159 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, |
2057 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, | 2160 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, |
@@ -2068,6 +2171,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { | |||
2068 | 2171 | ||
2069 | static const struct tegra_pcie_soc tegra124_pcie = { | 2172 | static const struct tegra_pcie_soc tegra124_pcie = { |
2070 | .num_ports = 2, | 2173 | .num_ports = 2, |
2174 | .ports = tegra20_pcie_ports, | ||
2071 | .msi_base_shift = 8, | 2175 | .msi_base_shift = 8, |
2072 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, | 2176 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, |
2073 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, | 2177 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, |
@@ -2083,6 +2187,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { | |||
2083 | 2187 | ||
2084 | static const struct tegra_pcie_soc tegra210_pcie = { | 2188 | static const struct tegra_pcie_soc tegra210_pcie = { |
2085 | .num_ports = 2, | 2189 | .num_ports = 2, |
2190 | .ports = tegra20_pcie_ports, | ||
2086 | .msi_base_shift = 8, | 2191 | .msi_base_shift = 8, |
2087 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, | 2192 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, |
2088 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, | 2193 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, |
@@ -2096,8 +2201,15 @@ static const struct tegra_pcie_soc tegra210_pcie = { | |||
2096 | .program_uphy = true, | 2201 | .program_uphy = true, |
2097 | }; | 2202 | }; |
2098 | 2203 | ||
2204 | static const struct tegra_pcie_port_soc tegra186_pcie_ports[] = { | ||
2205 | { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, | ||
2206 | { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, | ||
2207 | { .pme.turnoff_bit = 12, .pme.ack_bit = 14 }, | ||
2208 | }; | ||
2209 | |||
2099 | static const struct tegra_pcie_soc tegra186_pcie = { | 2210 | static const struct tegra_pcie_soc tegra186_pcie = { |
2100 | .num_ports = 3, | 2211 | .num_ports = 3, |
2212 | .ports = tegra186_pcie_ports, | ||
2101 | .msi_base_shift = 8, | 2213 | .msi_base_shift = 8, |
2102 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, | 2214 | .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, |
2103 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, | 2215 | .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, |
@@ -2209,6 +2321,12 @@ static const struct file_operations tegra_pcie_ports_ops = { | |||
2209 | .release = seq_release, | 2321 | .release = seq_release, |
2210 | }; | 2322 | }; |
2211 | 2323 | ||
2324 | static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) | ||
2325 | { | ||
2326 | debugfs_remove_recursive(pcie->debugfs); | ||
2327 | pcie->debugfs = NULL; | ||
2328 | } | ||
2329 | |||
2212 | static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) | 2330 | static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) |
2213 | { | 2331 | { |
2214 | struct dentry *file; | 2332 | struct dentry *file; |
@@ -2225,8 +2343,7 @@ static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) | |||
2225 | return 0; | 2343 | return 0; |
2226 | 2344 | ||
2227 | remove: | 2345 | remove: |
2228 | debugfs_remove_recursive(pcie->debugfs); | 2346 | tegra_pcie_debugfs_exit(pcie); |
2229 | pcie->debugfs = NULL; | ||
2230 | return -ENOMEM; | 2347 | return -ENOMEM; |
2231 | } | 2348 | } |
2232 | 2349 | ||
@@ -2244,6 +2361,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) | |||
2244 | 2361 | ||
2245 | pcie = pci_host_bridge_priv(host); | 2362 | pcie = pci_host_bridge_priv(host); |
2246 | host->sysdata = pcie; | 2363 | host->sysdata = pcie; |
2364 | platform_set_drvdata(pdev, pcie); | ||
2247 | 2365 | ||
2248 | pcie->soc = of_device_get_match_data(dev); | 2366 | pcie->soc = of_device_get_match_data(dev); |
2249 | INIT_LIST_HEAD(&pcie->ports); | 2367 | INIT_LIST_HEAD(&pcie->ports); |
@@ -2259,26 +2377,22 @@ static int tegra_pcie_probe(struct platform_device *pdev) | |||
2259 | return err; | 2377 | return err; |
2260 | } | 2378 | } |
2261 | 2379 | ||
2262 | err = tegra_pcie_enable_controller(pcie); | 2380 | err = tegra_pcie_msi_setup(pcie); |
2263 | if (err) | 2381 | if (err < 0) { |
2264 | goto put_resources; | 2382 | dev_err(dev, "failed to enable MSI support: %d\n", err); |
2265 | |||
2266 | err = tegra_pcie_request_resources(pcie); | ||
2267 | if (err) | ||
2268 | goto put_resources; | 2383 | goto put_resources; |
2384 | } | ||
2269 | 2385 | ||
2270 | /* setup the AFI address translations */ | 2386 | pm_runtime_enable(pcie->dev); |
2271 | tegra_pcie_setup_translations(pcie); | 2387 | err = pm_runtime_get_sync(pcie->dev); |
2272 | 2388 | if (err) { | |
2273 | if (IS_ENABLED(CONFIG_PCI_MSI)) { | 2389 | dev_err(dev, "fail to enable pcie controller: %d\n", err); |
2274 | err = tegra_pcie_enable_msi(pcie); | 2390 | goto teardown_msi; |
2275 | if (err < 0) { | ||
2276 | dev_err(dev, "failed to enable MSI support: %d\n", err); | ||
2277 | goto put_resources; | ||
2278 | } | ||
2279 | } | 2391 | } |
2280 | 2392 | ||
2281 | tegra_pcie_enable_ports(pcie); | 2393 | err = tegra_pcie_request_resources(pcie); |
2394 | if (err) | ||
2395 | goto pm_runtime_put; | ||
2282 | 2396 | ||
2283 | host->busnr = pcie->busn.start; | 2397 | host->busnr = pcie->busn.start; |
2284 | host->dev.parent = &pdev->dev; | 2398 | host->dev.parent = &pdev->dev; |
@@ -2289,7 +2403,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) | |||
2289 | err = pci_scan_root_bus_bridge(host); | 2403 | err = pci_scan_root_bus_bridge(host); |
2290 | if (err < 0) { | 2404 | if (err < 0) { |
2291 | dev_err(dev, "failed to register host: %d\n", err); | 2405 | dev_err(dev, "failed to register host: %d\n", err); |
2292 | goto disable_msi; | 2406 | goto free_resources; |
2293 | } | 2407 | } |
2294 | 2408 | ||
2295 | pci_bus_size_bridges(host->bus); | 2409 | pci_bus_size_bridges(host->bus); |
@@ -2308,20 +2422,108 @@ static int tegra_pcie_probe(struct platform_device *pdev) | |||
2308 | 2422 | ||
2309 | return 0; | 2423 | return 0; |
2310 | 2424 | ||
2311 | disable_msi: | 2425 | free_resources: |
2312 | if (IS_ENABLED(CONFIG_PCI_MSI)) | 2426 | tegra_pcie_free_resources(pcie); |
2313 | tegra_pcie_disable_msi(pcie); | 2427 | pm_runtime_put: |
2428 | pm_runtime_put_sync(pcie->dev); | ||
2429 | pm_runtime_disable(pcie->dev); | ||
2430 | teardown_msi: | ||
2431 | tegra_pcie_msi_teardown(pcie); | ||
2314 | put_resources: | 2432 | put_resources: |
2315 | tegra_pcie_put_resources(pcie); | 2433 | tegra_pcie_put_resources(pcie); |
2316 | return err; | 2434 | return err; |
2317 | } | 2435 | } |
2318 | 2436 | ||
2437 | static int tegra_pcie_remove(struct platform_device *pdev) | ||
2438 | { | ||
2439 | struct tegra_pcie *pcie = platform_get_drvdata(pdev); | ||
2440 | struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); | ||
2441 | struct tegra_pcie_port *port, *tmp; | ||
2442 | |||
2443 | if (IS_ENABLED(CONFIG_DEBUG_FS)) | ||
2444 | tegra_pcie_debugfs_exit(pcie); | ||
2445 | |||
2446 | pci_stop_root_bus(host->bus); | ||
2447 | pci_remove_root_bus(host->bus); | ||
2448 | tegra_pcie_free_resources(pcie); | ||
2449 | pm_runtime_put_sync(pcie->dev); | ||
2450 | pm_runtime_disable(pcie->dev); | ||
2451 | |||
2452 | if (IS_ENABLED(CONFIG_PCI_MSI)) | ||
2453 | tegra_pcie_msi_teardown(pcie); | ||
2454 | |||
2455 | tegra_pcie_put_resources(pcie); | ||
2456 | |||
2457 | list_for_each_entry_safe(port, tmp, &pcie->ports, list) | ||
2458 | tegra_pcie_port_free(port); | ||
2459 | |||
2460 | return 0; | ||
2461 | } | ||
2462 | |||
2463 | static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) | ||
2464 | { | ||
2465 | struct tegra_pcie *pcie = dev_get_drvdata(dev); | ||
2466 | struct tegra_pcie_port *port; | ||
2467 | |||
2468 | list_for_each_entry(port, &pcie->ports, list) | ||
2469 | tegra_pcie_pme_turnoff(port); | ||
2470 | |||
2471 | tegra_pcie_disable_ports(pcie); | ||
2472 | |||
2473 | if (IS_ENABLED(CONFIG_PCI_MSI)) | ||
2474 | tegra_pcie_disable_msi(pcie); | ||
2475 | |||
2476 | tegra_pcie_disable_controller(pcie); | ||
2477 | tegra_pcie_power_off(pcie); | ||
2478 | |||
2479 | return 0; | ||
2480 | } | ||
2481 | |||
2482 | static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) | ||
2483 | { | ||
2484 | struct tegra_pcie *pcie = dev_get_drvdata(dev); | ||
2485 | int err; | ||
2486 | |||
2487 | err = tegra_pcie_power_on(pcie); | ||
2488 | if (err) { | ||
2489 | dev_err(dev, "tegra pcie power on fail: %d\n", err); | ||
2490 | return err; | ||
2491 | } | ||
2492 | err = tegra_pcie_enable_controller(pcie); | ||
2493 | if (err) { | ||
2494 | dev_err(dev, "tegra pcie controller enable fail: %d\n", err); | ||
2495 | goto poweroff; | ||
2496 | } | ||
2497 | tegra_pcie_setup_translations(pcie); | ||
2498 | |||
2499 | if (IS_ENABLED(CONFIG_PCI_MSI)) | ||
2500 | tegra_pcie_enable_msi(pcie); | ||
2501 | |||
2502 | tegra_pcie_enable_ports(pcie); | ||
2503 | |||
2504 | return 0; | ||
2505 | |||
2506 | poweroff: | ||
2507 | tegra_pcie_power_off(pcie); | ||
2508 | |||
2509 | return err; | ||
2510 | } | ||
2511 | |||
2512 | static const struct dev_pm_ops tegra_pcie_pm_ops = { | ||
2513 | SET_RUNTIME_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume, NULL) | ||
2514 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pcie_pm_suspend, | ||
2515 | tegra_pcie_pm_resume) | ||
2516 | }; | ||
2517 | |||
2319 | static struct platform_driver tegra_pcie_driver = { | 2518 | static struct platform_driver tegra_pcie_driver = { |
2320 | .driver = { | 2519 | .driver = { |
2321 | .name = "tegra-pcie", | 2520 | .name = "tegra-pcie", |
2322 | .of_match_table = tegra_pcie_of_match, | 2521 | .of_match_table = tegra_pcie_of_match, |
2323 | .suppress_bind_attrs = true, | 2522 | .suppress_bind_attrs = true, |
2523 | .pm = &tegra_pcie_pm_ops, | ||
2324 | }, | 2524 | }, |
2325 | .probe = tegra_pcie_probe, | 2525 | .probe = tegra_pcie_probe, |
2526 | .remove = tegra_pcie_remove, | ||
2326 | }; | 2527 | }; |
2327 | builtin_platform_driver(tegra_pcie_driver); | 2528 | module_platform_driver(tegra_pcie_driver); |
2529 | MODULE_LICENSE("GPL"); | ||