ARM: tegra: rework PCIe regulators

This branch reworks the set of regulators that the Tegra PCIe driver
 uses, so that the driver and DT bindings more correctly model what's
 really going on in HW. For backwards-compatibility the driver will
 fallback to using the old set of regulators if the new ones can't be
 found.
 
 I've made this a separate branch in case it needs to be pulled into the
 PCIe tree to resolve any conflicts.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABAgAGBQJTyRXmAAoJEN0jrNd/PrOhpcIP/1bhFxw7a0HjG1Cwtj27uJKo
 2S9z28sFbVERmOTy4Sfy/bf3EEBNZajgYJ0kOIJbrVRkpGV6BU/3nNVmqR9G1jOY
 9wVr7e9Z0lWQ8r8e9jXIRpTOO1PUFfx3AhyjD1kT5bUsI8m5dcDmryyLqsvh2UDn
 F1S2JpAeylVSFzZLspqnuc1HgG6V4xMxt7JCKQqQo4uTTs2LVWpLWRqQEOhpsmVW
 WDSzovSXBThm4wXvZlrTij7HuOqYbwG3wLpzJMVVfhysRZDfIO8i7hK2kAQ8+3O6
 0yS8HsfzrjhGvNgbGUt+hGTYg+omHp3i0RJf/AxhOIOrA5fIs4pOTC2HSqq+kG4x
 K2OWCUboaTbMpJ/+TwcY83Ohk/r+Qj3Ay9loyIbQ5e2ORbkmpvbBALiQLTFwswaf
 zPsuwSXW8imVnPsduo+7qnvq2sbQ45Wy30wZMPRKYSfQzNY40AK0hzvRtW1BfMHY
 3P35z+9uIygiOr4KlwvbnTjL/nNWa5aVO8CYDfAxDa81SUJn/4vGkJNLNw6z1zo8
 Jvem210R6G0dca257NFWZ9w7hB9bfX3AVR8ZovDkg7tiOOlaOwq8HJrVU8oM3dtu
 /ztRVYKo/XqRtKyCcUnCtoF6CswlhfT4u4JOmo/5KwD9VBmxQPWHEkUwMl9j6eu3
 5dNKF79ROlQ1Idk0jjDZ
 =48cL
 -----END PGP SIGNATURE-----

Merge tag 'tegra-for-3.17-pcie-regulators' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into next/drivers

Merge "ARM: tegra: rework PCIe regulators" from Thierry Reding:

This branch reworks the set of regulators that the Tegra PCIe driver
uses, so that the driver and DT bindings more correctly model what's
really going on in HW. For backwards-compatibility the driver will
fallback to using the old set of regulators if the new ones can't be
found.

I've made this a separate branch in case it needs to be pulled into the
PCIe tree to resolve any conflicts.

* tag 'tegra-for-3.17-pcie-regulators' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux:
  ARM: tegra: Remove legacy PCIe power supply properties
  PCI: tegra: Remove deprecated power supply properties
  PCI: tegra: Implement accurate power supply scheme
  ARM: tegra: Add new PCIe regulator properties
  PCI: tegra: Overhaul regulator usage

Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2014-07-19 12:35:45 -07:00
commit b776eec135
7 changed files with 222 additions and 76 deletions

View File

@ -14,9 +14,6 @@ Required properties:
- interrupt-names: Must include the following entries:
"intr": The Tegra interrupt that is asserted for controller interrupts
"msi": The Tegra interrupt that is asserted when an MSI is received
- pex-clk-supply: Supply voltage for internal reference clock
- vdd-supply: Power supply for controller (1.05V)
- avdd-supply: Power supply for controller (1.05V) (not required for Tegra20)
- bus-range: Range of bus numbers associated with this controller
- #address-cells: Address representation for root ports (must be 3)
- cell 0 specifies the bus and device numbers of the root port:
@ -60,6 +57,33 @@ Required properties:
- afi
- pcie_x
Power supplies for Tegra20:
- avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
- vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
supply 1.05 V.
- avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
supply 1.05 V.
- vddio-pex-clk-supply: Power supply for PCIe clock. Must supply 3.3 V.
Power supplies for Tegra30:
- Required:
- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
supply 1.05 V.
- avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
supply 1.05 V.
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
supply 1.8 V.
- hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
Must supply 3.3 V.
- Optional:
- If lanes 0 to 3 are used:
- avdd-pexa-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
- vdd-pexa-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
- If lanes 4 or 5 are used:
- avdd-pexb-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
- vdd-pexb-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
Root ports are defined as subnodes of the PCIe controller node.
Required properties:

View File

@ -562,10 +562,14 @@
};
pcie-controller@80003000 {
pex-clk-supply = <&pci_clk_reg>;
vdd-supply = <&pci_vdd_reg>;
status = "okay";
avdd-pex-supply = <&pci_vdd_reg>;
vdd-pex-supply = <&pci_vdd_reg>;
avdd-pex-pll-supply = <&pci_vdd_reg>;
avdd-plle-supply = <&pci_vdd_reg>;
vddio-pex-clk-supply = <&pci_clk_reg>;
pci@1,0 {
status = "okay";
};

View File

@ -473,8 +473,11 @@
};
pcie-controller@80003000 {
pex-clk-supply = <&pci_clk_reg>;
vdd-supply = <&pci_vdd_reg>;
avdd-pex-supply = <&pci_vdd_reg>;
vdd-pex-supply = <&pci_vdd_reg>;
avdd-pex-pll-supply = <&pci_vdd_reg>;
avdd-plle-supply = <&pci_vdd_reg>;
vddio-pex-clk-supply = <&pci_clk_reg>;
};
usb@c5008000 {

View File

@ -318,8 +318,12 @@
pcie-controller@80003000 {
status = "okay";
pex-clk-supply = <&pci_clk_reg>;
vdd-supply = <&pci_vdd_reg>;
avdd-pex-supply = <&pci_vdd_reg>;
vdd-pex-supply = <&pci_vdd_reg>;
avdd-pex-pll-supply = <&pci_vdd_reg>;
avdd-plle-supply = <&pci_vdd_reg>;
vddio-pex-clk-supply = <&pci_clk_reg>;
pci@1,0 {
status = "okay";

View File

@ -17,9 +17,15 @@
pcie-controller@00003000 {
status = "okay";
pex-clk-supply = <&sys_3v3_pexs_reg>;
vdd-supply = <&ldo1_reg>;
avdd-supply = <&ldo2_reg>;
avdd-pexa-supply = <&ldo1_reg>;
vdd-pexa-supply = <&ldo1_reg>;
avdd-pexb-supply = <&ldo1_reg>;
vdd-pexb-supply = <&ldo1_reg>;
avdd-pex-pll-supply = <&ldo1_reg>;
avdd-plle-supply = <&ldo1_reg>;
vddio-pex-ctl-supply = <&sys_3v3_reg>;
hvdd-pex-supply = <&sys_3v3_pexs_reg>;
pci@1,0 {
status = "okay";

View File

@ -38,9 +38,14 @@
pcie-controller@00003000 {
status = "okay";
pex-clk-supply = <&pex_hvdd_3v3_reg>;
vdd-supply = <&ldo1_reg>;
avdd-supply = <&ldo2_reg>;
/* AVDD_PEXA and VDD_PEXA inputs are grounded on Cardhu. */
avdd-pexb-supply = <&ldo1_reg>;
vdd-pexb-supply = <&ldo1_reg>;
avdd-pex-pll-supply = <&ldo1_reg>;
hvdd-pex-supply = <&pex_hvdd_3v3_reg>;
vddio-pex-ctl-supply = <&sys_3v3_reg>;
avdd-plle-supply = <&ldo2_reg>;
pci@1,0 {
nvidia,num-lanes = <4>;

View File

@ -233,7 +233,6 @@ struct tegra_pcie_soc_data {
bool has_pex_clkreq_en;
bool has_pex_bias_ctrl;
bool has_intr_prsnt_sense;
bool has_avdd_supply;
bool has_cml_clk;
};
@ -272,9 +271,8 @@ struct tegra_pcie {
unsigned int num_ports;
u32 xbar_config;
struct regulator *pex_clk_supply;
struct regulator *vdd_supply;
struct regulator *avdd_supply;
struct regulator_bulk_data *supplies;
unsigned int num_supplies;
const struct tegra_pcie_soc_data *soc_data;
};
@ -894,7 +892,6 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
static void tegra_pcie_power_off(struct tegra_pcie *pcie)
{
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
int err;
/* TODO: disable and unprepare clocks? */
@ -905,23 +902,9 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
if (soc->has_avdd_supply) {
err = regulator_disable(pcie->avdd_supply);
if (err < 0)
dev_warn(pcie->dev,
"failed to disable AVDD regulator: %d\n",
err);
}
err = regulator_disable(pcie->pex_clk_supply);
err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies);
if (err < 0)
dev_warn(pcie->dev, "failed to disable pex-clk regulator: %d\n",
err);
err = regulator_disable(pcie->vdd_supply);
if (err < 0)
dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n",
err);
dev_warn(pcie->dev, "failed to disable regulators: %d\n", err);
}
static int tegra_pcie_power_on(struct tegra_pcie *pcie)
@ -936,28 +919,9 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
/* enable regulators */
err = regulator_enable(pcie->vdd_supply);
if (err < 0) {
dev_err(pcie->dev, "failed to enable VDD regulator: %d\n", err);
return err;
}
err = regulator_enable(pcie->pex_clk_supply);
if (err < 0) {
dev_err(pcie->dev, "failed to enable pex-clk regulator: %d\n",
err);
return err;
}
if (soc->has_avdd_supply) {
err = regulator_enable(pcie->avdd_supply);
if (err < 0) {
dev_err(pcie->dev,
"failed to enable AVDD regulator: %d\n",
err);
return err;
}
}
err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies);
if (err < 0)
dev_err(pcie->dev, "failed to enable regulators: %d\n", err);
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
pcie->pex_clk,
@ -1394,14 +1358,157 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
return -EINVAL;
}
/*
* Check whether a given set of supplies is available in a device tree node.
* This is used to check whether the new or the legacy device tree bindings
* should be used.
*/
static bool of_regulator_bulk_available(struct device_node *np,
struct regulator_bulk_data *supplies,
unsigned int num_supplies)
{
char property[32];
unsigned int i;
for (i = 0; i < num_supplies; i++) {
snprintf(property, 32, "%s-supply", supplies[i].supply);
if (of_find_property(np, property, NULL) == NULL)
return false;
}
return true;
}
/*
* Old versions of the device tree binding for this device used a set of power
* supplies that didn't match the hardware inputs. This happened to work for a
* number of cases but is not future proof. However to preserve backwards-
* compatibility with old device trees, this function will try to use the old
* set of supplies.
*/
static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
{
struct device_node *np = pcie->dev->of_node;
if (of_device_is_compatible(np, "nvidia,tegra30-pcie"))
pcie->num_supplies = 3;
else if (of_device_is_compatible(np, "nvidia,tegra20-pcie"))
pcie->num_supplies = 2;
if (pcie->num_supplies == 0) {
dev_err(pcie->dev, "device %s not supported in legacy mode\n",
np->full_name);
return -ENODEV;
}
pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
sizeof(*pcie->supplies),
GFP_KERNEL);
if (!pcie->supplies)
return -ENOMEM;
pcie->supplies[0].supply = "pex-clk";
pcie->supplies[1].supply = "vdd";
if (pcie->num_supplies > 2)
pcie->supplies[2].supply = "avdd";
return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
pcie->supplies);
}
/*
* Obtains the list of regulators required for a particular generation of the
* IP block.
*
* This would've been nice to do simply by providing static tables for use
* with the regulator_bulk_*() API, but unfortunately Tegra30 is a bit quirky
* in that it has two pairs or AVDD_PEX and VDD_PEX supplies (PEXA and PEXB)
* and either seems to be optional depending on which ports are being used.
*/
static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
{
struct device_node *np = pcie->dev->of_node;
unsigned int i = 0;
if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
bool need_pexa = false, need_pexb = false;
/* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */
if (lane_mask & 0x0f)
need_pexa = true;
/* VDD_PEXB and AVDD_PEXB supply lanes 4 to 5 */
if (lane_mask & 0x30)
need_pexb = true;
pcie->num_supplies = 4 + (need_pexa ? 2 : 0) +
(need_pexb ? 2 : 0);
pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
sizeof(*pcie->supplies),
GFP_KERNEL);
if (!pcie->supplies)
return -ENOMEM;
pcie->supplies[i++].supply = "avdd-pex-pll";
pcie->supplies[i++].supply = "hvdd-pex";
pcie->supplies[i++].supply = "vddio-pex-ctl";
pcie->supplies[i++].supply = "avdd-plle";
if (need_pexa) {
pcie->supplies[i++].supply = "avdd-pexa";
pcie->supplies[i++].supply = "vdd-pexa";
}
if (need_pexb) {
pcie->supplies[i++].supply = "avdd-pexb";
pcie->supplies[i++].supply = "vdd-pexb";
}
} else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
pcie->num_supplies = 5;
pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
sizeof(*pcie->supplies),
GFP_KERNEL);
if (!pcie->supplies)
return -ENOMEM;
pcie->supplies[0].supply = "avdd-pex";
pcie->supplies[1].supply = "vdd-pex";
pcie->supplies[2].supply = "avdd-pex-pll";
pcie->supplies[3].supply = "avdd-plle";
pcie->supplies[4].supply = "vddio-pex-clk";
}
if (of_regulator_bulk_available(pcie->dev->of_node, pcie->supplies,
pcie->num_supplies))
return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
pcie->supplies);
/*
* If not all regulators are available for this new scheme, assume
* that the device tree complies with an older version of the device
* tree binding.
*/
dev_info(pcie->dev, "using legacy DT binding for power supplies\n");
devm_kfree(pcie->dev, pcie->supplies);
pcie->num_supplies = 0;
return tegra_pcie_get_legacy_regulators(pcie);
}
static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
{
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
struct device_node *np = pcie->dev->of_node, *port;
struct of_pci_range_parser parser;
struct of_pci_range range;
u32 lanes = 0, mask = 0;
unsigned int lane = 0;
struct resource res;
u32 lanes = 0;
int err;
if (of_pci_range_parser_init(&parser, np)) {
@ -1409,20 +1516,6 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
return -EINVAL;
}
pcie->vdd_supply = devm_regulator_get(pcie->dev, "vdd");
if (IS_ERR(pcie->vdd_supply))
return PTR_ERR(pcie->vdd_supply);
pcie->pex_clk_supply = devm_regulator_get(pcie->dev, "pex-clk");
if (IS_ERR(pcie->pex_clk_supply))
return PTR_ERR(pcie->pex_clk_supply);
if (soc->has_avdd_supply) {
pcie->avdd_supply = devm_regulator_get(pcie->dev, "avdd");
if (IS_ERR(pcie->avdd_supply))
return PTR_ERR(pcie->avdd_supply);
}
for_each_of_pci_range(&parser, &range) {
of_pci_range_to_resource(&range, np, &res);
@ -1490,8 +1583,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
lanes |= value << (index << 3);
if (!of_device_is_available(port))
if (!of_device_is_available(port)) {
lane += value;
continue;
}
mask |= ((1 << value) - 1) << lane;
lane += value;
rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL);
if (!rp)
@ -1522,6 +1620,10 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
return err;
}
err = tegra_pcie_get_regulators(pcie, mask);
if (err < 0)
return err;
return 0;
}
@ -1615,7 +1717,6 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = {
.has_pex_clkreq_en = false,
.has_pex_bias_ctrl = false,
.has_intr_prsnt_sense = false,
.has_avdd_supply = false,
.has_cml_clk = false,
};
@ -1627,7 +1728,6 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = {
.has_pex_clkreq_en = true,
.has_pex_bias_ctrl = true,
.has_intr_prsnt_sense = true,
.has_avdd_supply = true,
.has_cml_clk = true,
};