aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2014-05-28 10:49:13 -0400
committerThierry Reding <treding@nvidia.com>2014-07-18 05:20:03 -0400
commit077fb1580deace540c0d7ea629f4afdad5834fd9 (patch)
tree0f01247d01a6e974ab14061fa0349b8295ee3c3b /drivers/pci
parentcca8614d444de065b7642152d269c8392effbbc8 (diff)
PCI: tegra: Implement accurate power supply scheme
The current description of power supplies doesn't match the hardware. Instead it's designed to support the needs of current designs, which will break as soon as a new design appears that cannot be described using the current assumptions. In order to fully support all possible future designs, all power supply inputs to the PCIe block need to be accurately described and separately configurable. Acked-by: Bjorn Helgaas <bhelgaas@google.com> Signed-off-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Stephen Warren <swarren@nvidia.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/host/pci-tegra.c222
1 files changed, 161 insertions, 61 deletions
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 083cf37ca047..d697587dbb7c 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -233,7 +233,6 @@ struct tegra_pcie_soc_data {
233 bool has_pex_clkreq_en; 233 bool has_pex_clkreq_en;
234 bool has_pex_bias_ctrl; 234 bool has_pex_bias_ctrl;
235 bool has_intr_prsnt_sense; 235 bool has_intr_prsnt_sense;
236 bool has_avdd_supply;
237 bool has_cml_clk; 236 bool has_cml_clk;
238}; 237};
239 238
@@ -272,9 +271,8 @@ struct tegra_pcie {
272 unsigned int num_ports; 271 unsigned int num_ports;
273 u32 xbar_config; 272 u32 xbar_config;
274 273
275 struct regulator *pex_clk_supply; 274 struct regulator_bulk_data *supplies;
276 struct regulator *vdd_supply; 275 unsigned int num_supplies;
277 struct regulator *avdd_supply;
278 276
279 const struct tegra_pcie_soc_data *soc_data; 277 const struct tegra_pcie_soc_data *soc_data;
280}; 278};
@@ -894,7 +892,6 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
894 892
895static void tegra_pcie_power_off(struct tegra_pcie *pcie) 893static void tegra_pcie_power_off(struct tegra_pcie *pcie)
896{ 894{
897 const struct tegra_pcie_soc_data *soc = pcie->soc_data;
898 int err; 895 int err;
899 896
900 /* TODO: disable and unprepare clocks? */ 897 /* TODO: disable and unprepare clocks? */
@@ -905,23 +902,9 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
905 902
906 tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); 903 tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
907 904
908 if (soc->has_avdd_supply) { 905 err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies);
909 err = regulator_disable(pcie->avdd_supply);
910 if (err < 0)
911 dev_warn(pcie->dev,
912 "failed to disable AVDD regulator: %d\n",
913 err);
914 }
915
916 err = regulator_disable(pcie->pex_clk_supply);
917 if (err < 0) 906 if (err < 0)
918 dev_warn(pcie->dev, "failed to disable pex-clk regulator: %d\n", 907 dev_warn(pcie->dev, "failed to disable regulators: %d\n", err);
919 err);
920
921 err = regulator_disable(pcie->vdd_supply);
922 if (err < 0)
923 dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n",
924 err);
925} 908}
926 909
927static int tegra_pcie_power_on(struct tegra_pcie *pcie) 910static int tegra_pcie_power_on(struct tegra_pcie *pcie)
@@ -936,28 +919,9 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
936 tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); 919 tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
937 920
938 /* enable regulators */ 921 /* enable regulators */
939 err = regulator_enable(pcie->vdd_supply); 922 err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies);
940 if (err < 0) { 923 if (err < 0)
941 dev_err(pcie->dev, "failed to enable VDD regulator: %d\n", err); 924 dev_err(pcie->dev, "failed to enable regulators: %d\n", err);
942 return err;
943 }
944
945 err = regulator_enable(pcie->pex_clk_supply);
946 if (err < 0) {
947 dev_err(pcie->dev, "failed to enable pex-clk regulator: %d\n",
948 err);
949 return err;
950 }
951
952 if (soc->has_avdd_supply) {
953 err = regulator_enable(pcie->avdd_supply);
954 if (err < 0) {
955 dev_err(pcie->dev,
956 "failed to enable AVDD regulator: %d\n",
957 err);
958 return err;
959 }
960 }
961 925
962 err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, 926 err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
963 pcie->pex_clk, 927 pcie->pex_clk,
@@ -1394,14 +1358,157 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
1394 return -EINVAL; 1358 return -EINVAL;
1395} 1359}
1396 1360
1361/*
1362 * Check whether a given set of supplies is available in a device tree node.
1363 * This is used to check whether the new or the legacy device tree bindings
1364 * should be used.
1365 */
1366static bool of_regulator_bulk_available(struct device_node *np,
1367 struct regulator_bulk_data *supplies,
1368 unsigned int num_supplies)
1369{
1370 char property[32];
1371 unsigned int i;
1372
1373 for (i = 0; i < num_supplies; i++) {
1374 snprintf(property, 32, "%s-supply", supplies[i].supply);
1375
1376 if (of_find_property(np, property, NULL) == NULL)
1377 return false;
1378 }
1379
1380 return true;
1381}
1382
1383/*
1384 * Old versions of the device tree binding for this device used a set of power
1385 * supplies that didn't match the hardware inputs. This happened to work for a
1386 * number of cases but is not future proof. However to preserve backwards-
1387 * compatibility with old device trees, this function will try to use the old
1388 * set of supplies.
1389 */
1390static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
1391{
1392 struct device_node *np = pcie->dev->of_node;
1393
1394 if (of_device_is_compatible(np, "nvidia,tegra30-pcie"))
1395 pcie->num_supplies = 3;
1396 else if (of_device_is_compatible(np, "nvidia,tegra20-pcie"))
1397 pcie->num_supplies = 2;
1398
1399 if (pcie->num_supplies == 0) {
1400 dev_err(pcie->dev, "device %s not supported in legacy mode\n",
1401 np->full_name);
1402 return -ENODEV;
1403 }
1404
1405 pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
1406 sizeof(*pcie->supplies),
1407 GFP_KERNEL);
1408 if (!pcie->supplies)
1409 return -ENOMEM;
1410
1411 pcie->supplies[0].supply = "pex-clk";
1412 pcie->supplies[1].supply = "vdd";
1413
1414 if (pcie->num_supplies > 2)
1415 pcie->supplies[2].supply = "avdd";
1416
1417 return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
1418 pcie->supplies);
1419}
1420
1421/*
1422 * Obtains the list of regulators required for a particular generation of the
1423 * IP block.
1424 *
1425 * This would've been nice to do simply by providing static tables for use
1426 * with the regulator_bulk_*() API, but unfortunately Tegra30 is a bit quirky
1427 * in that it has two pairs or AVDD_PEX and VDD_PEX supplies (PEXA and PEXB)
1428 * and either seems to be optional depending on which ports are being used.
1429 */
1430static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
1431{
1432 struct device_node *np = pcie->dev->of_node;
1433 unsigned int i = 0;
1434
1435 if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
1436 bool need_pexa = false, need_pexb = false;
1437
1438 /* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */
1439 if (lane_mask & 0x0f)
1440 need_pexa = true;
1441
1442 /* VDD_PEXB and AVDD_PEXB supply lanes 4 to 5 */
1443 if (lane_mask & 0x30)
1444 need_pexb = true;
1445
1446 pcie->num_supplies = 4 + (need_pexa ? 2 : 0) +
1447 (need_pexb ? 2 : 0);
1448
1449 pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
1450 sizeof(*pcie->supplies),
1451 GFP_KERNEL);
1452 if (!pcie->supplies)
1453 return -ENOMEM;
1454
1455 pcie->supplies[i++].supply = "avdd-pex-pll";
1456 pcie->supplies[i++].supply = "hvdd-pex";
1457 pcie->supplies[i++].supply = "vddio-pex-ctl";
1458 pcie->supplies[i++].supply = "avdd-plle";
1459
1460 if (need_pexa) {
1461 pcie->supplies[i++].supply = "avdd-pexa";
1462 pcie->supplies[i++].supply = "vdd-pexa";
1463 }
1464
1465 if (need_pexb) {
1466 pcie->supplies[i++].supply = "avdd-pexb";
1467 pcie->supplies[i++].supply = "vdd-pexb";
1468 }
1469 } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
1470 pcie->num_supplies = 5;
1471
1472 pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
1473 sizeof(*pcie->supplies),
1474 GFP_KERNEL);
1475 if (!pcie->supplies)
1476 return -ENOMEM;
1477
1478 pcie->supplies[0].supply = "avdd-pex";
1479 pcie->supplies[1].supply = "vdd-pex";
1480 pcie->supplies[2].supply = "avdd-pex-pll";
1481 pcie->supplies[3].supply = "avdd-plle";
1482 pcie->supplies[4].supply = "vddio-pex-clk";
1483 }
1484
1485 if (of_regulator_bulk_available(pcie->dev->of_node, pcie->supplies,
1486 pcie->num_supplies))
1487 return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
1488 pcie->supplies);
1489
1490 /*
1491 * If not all regulators are available for this new scheme, assume
1492 * that the device tree complies with an older version of the device
1493 * tree binding.
1494 */
1495 dev_info(pcie->dev, "using legacy DT binding for power supplies\n");
1496
1497 devm_kfree(pcie->dev, pcie->supplies);
1498 pcie->num_supplies = 0;
1499
1500 return tegra_pcie_get_legacy_regulators(pcie);
1501}
1502
1397static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) 1503static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
1398{ 1504{
1399 const struct tegra_pcie_soc_data *soc = pcie->soc_data; 1505 const struct tegra_pcie_soc_data *soc = pcie->soc_data;
1400 struct device_node *np = pcie->dev->of_node, *port; 1506 struct device_node *np = pcie->dev->of_node, *port;
1401 struct of_pci_range_parser parser; 1507 struct of_pci_range_parser parser;
1402 struct of_pci_range range; 1508 struct of_pci_range range;
1509 u32 lanes = 0, mask = 0;
1510 unsigned int lane = 0;
1403 struct resource res; 1511 struct resource res;
1404 u32 lanes = 0;
1405 int err; 1512 int err;
1406 1513
1407 if (of_pci_range_parser_init(&parser, np)) { 1514 if (of_pci_range_parser_init(&parser, np)) {
@@ -1409,20 +1516,6 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
1409 return -EINVAL; 1516 return -EINVAL;
1410 } 1517 }
1411 1518
1412 pcie->vdd_supply = devm_regulator_get(pcie->dev, "vdd");
1413 if (IS_ERR(pcie->vdd_supply))
1414 return PTR_ERR(pcie->vdd_supply);
1415
1416 pcie->pex_clk_supply = devm_regulator_get(pcie->dev, "pex-clk");
1417 if (IS_ERR(pcie->pex_clk_supply))
1418 return PTR_ERR(pcie->pex_clk_supply);
1419
1420 if (soc->has_avdd_supply) {
1421 pcie->avdd_supply = devm_regulator_get(pcie->dev, "avdd");
1422 if (IS_ERR(pcie->avdd_supply))
1423 return PTR_ERR(pcie->avdd_supply);
1424 }
1425
1426 for_each_of_pci_range(&parser, &range) { 1519 for_each_of_pci_range(&parser, &range) {
1427 of_pci_range_to_resource(&range, np, &res); 1520 of_pci_range_to_resource(&range, np, &res);
1428 1521
@@ -1490,8 +1583,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
1490 1583
1491 lanes |= value << (index << 3); 1584 lanes |= value << (index << 3);
1492 1585
1493 if (!of_device_is_available(port)) 1586 if (!of_device_is_available(port)) {
1587 lane += value;
1494 continue; 1588 continue;
1589 }
1590
1591 mask |= ((1 << value) - 1) << lane;
1592 lane += value;
1495 1593
1496 rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL); 1594 rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL);
1497 if (!rp) 1595 if (!rp)
@@ -1522,6 +1620,10 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
1522 return err; 1620 return err;
1523 } 1621 }
1524 1622
1623 err = tegra_pcie_get_regulators(pcie, mask);
1624 if (err < 0)
1625 return err;
1626
1525 return 0; 1627 return 0;
1526} 1628}
1527 1629
@@ -1615,7 +1717,6 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = {
1615 .has_pex_clkreq_en = false, 1717 .has_pex_clkreq_en = false,
1616 .has_pex_bias_ctrl = false, 1718 .has_pex_bias_ctrl = false,
1617 .has_intr_prsnt_sense = false, 1719 .has_intr_prsnt_sense = false,
1618 .has_avdd_supply = false,
1619 .has_cml_clk = false, 1720 .has_cml_clk = false,
1620}; 1721};
1621 1722
@@ -1627,7 +1728,6 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = {
1627 .has_pex_clkreq_en = true, 1728 .has_pex_clkreq_en = true,
1628 .has_pex_bias_ctrl = true, 1729 .has_pex_bias_ctrl = true,
1629 .has_intr_prsnt_sense = true, 1730 .has_intr_prsnt_sense = true,
1630 .has_avdd_supply = true,
1631 .has_cml_clk = true, 1731 .has_cml_clk = true,
1632}; 1732};
1633 1733