* [PATCH V15 04/13] PCI: dwc: Move config space capability search API
From: Vidya Sagar @ 2019-08-09 4:46 UTC (permalink / raw)
To: lorenzo.pieralisi, bhelgaas, robh+dt, mark.rutland,
thierry.reding, jonathanh, kishon, catalin.marinas, will.deacon,
jingoohan1, gustavo.pimentel
Cc: digetx, mperttunen, linux-pci, devicetree, linux-tegra,
linux-kernel, linux-arm-kernel, kthota, mmaddireddy, vidyas,
sagar.tv
In-Reply-To: <20190809044609.20401-1-vidyas@nvidia.com>
Move PCIe config space capability search API to common DesignWare file
as this can be used by both host and ep mode codes.
Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
Acked-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Reviewed-by: Thierry Reding <treding@nvidia.com>
---
V15:
* None
V14:
* None
V13:
* None
V12:
* None
V11:
* None
V10:
* None
V9:
* None
V8:
* Changed comment to explicitly state their mere resemblance to standard APIs
but not their operation and place of use.
V7:
* Exported dw_pcie_find_capability() API
V6:
* None
V5:
* Removed redundant APIs in pcie-designware-ep.c file after moving them
to pcie-designware.c file based on Bjorn's comments.
V4:
* Rebased to linux-next top of the tree
V3:
* None
V2:
* Removed dw_pcie_find_next_ext_capability() API from here and made a
separate patch for that
.../pci/controller/dwc/pcie-designware-ep.c | 37 +-----------------
drivers/pci/controller/dwc/pcie-designware.c | 39 +++++++++++++++++++
drivers/pci/controller/dwc/pcie-designware.h | 2 +
3 files changed, 43 insertions(+), 35 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 2bf5a35c0570..65f479250087 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -40,39 +40,6 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
__dw_pcie_ep_reset_bar(pci, bar, 0);
}
-static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
- u8 cap)
-{
- u8 cap_id, next_cap_ptr;
- u16 reg;
-
- if (!cap_ptr)
- return 0;
-
- reg = dw_pcie_readw_dbi(pci, cap_ptr);
- cap_id = (reg & 0x00ff);
-
- if (cap_id > PCI_CAP_ID_MAX)
- return 0;
-
- if (cap_id == cap)
- return cap_ptr;
-
- next_cap_ptr = (reg & 0xff00) >> 8;
- return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
-}
-
-static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap)
-{
- u8 next_cap_ptr;
- u16 reg;
-
- reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
- next_cap_ptr = (reg & 0x00ff);
-
- return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
-}
-
static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
struct pci_epf_header *hdr)
{
@@ -612,9 +579,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
return -ENOMEM;
}
- ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI);
+ ep->msi_cap = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI);
- ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX);
+ ep->msix_cap = dw_pcie_find_capability(pci, PCI_CAP_ID_MSIX);
offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
if (offset) {
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 7d25102c304c..7818b4febb08 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -14,6 +14,45 @@
#include "pcie-designware.h"
+/*
+ * These interfaces resemble the pci_find_*capability() interfaces, but these
+ * are for configuring host controllers, which are bridges *to* PCI devices but
+ * are not PCI devices themselves.
+ */
+static u8 __dw_pcie_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
+ u8 cap)
+{
+ u8 cap_id, next_cap_ptr;
+ u16 reg;
+
+ if (!cap_ptr)
+ return 0;
+
+ reg = dw_pcie_readw_dbi(pci, cap_ptr);
+ cap_id = (reg & 0x00ff);
+
+ if (cap_id > PCI_CAP_ID_MAX)
+ return 0;
+
+ if (cap_id == cap)
+ return cap_ptr;
+
+ next_cap_ptr = (reg & 0xff00) >> 8;
+ return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap);
+}
+
+u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap)
+{
+ u8 next_cap_ptr;
+ u16 reg;
+
+ reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
+ next_cap_ptr = (reg & 0x00ff);
+
+ return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap);
+}
+EXPORT_SYMBOL_GPL(dw_pcie_find_capability);
+
int dw_pcie_read(void __iomem *addr, int size, u32 *val)
{
if (!IS_ALIGNED((uintptr_t)addr, size)) {
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index ffed084a0b4f..d8c66a6827dc 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -251,6 +251,8 @@ struct dw_pcie {
#define to_dw_pcie_from_ep(endpoint) \
container_of((endpoint), struct dw_pcie, ep)
+u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap);
+
int dw_pcie_read(void __iomem *addr, int size, u32 *val);
int dw_pcie_write(void __iomem *addr, int size, u32 val);
--
2.17.1
^ permalink raw reply related
* [PATCH V15 03/13] PCI: dwc: Perform dbi regs write lock towards the end
From: Vidya Sagar @ 2019-08-09 4:45 UTC (permalink / raw)
To: lorenzo.pieralisi, bhelgaas, robh+dt, mark.rutland,
thierry.reding, jonathanh, kishon, catalin.marinas, will.deacon,
jingoohan1, gustavo.pimentel
Cc: digetx, mperttunen, linux-pci, devicetree, linux-tegra,
linux-kernel, linux-arm-kernel, kthota, mmaddireddy, vidyas,
sagar.tv
In-Reply-To: <20190809044609.20401-1-vidyas@nvidia.com>
Some of DesignWare core's DBI registers (a.k.a configuration space
registers) are write-protected with a lock without enabling which they are
read-only by default. These write-protected registers are implementation
specific. Tegra194's BAR-0 register which is at offset 0x10 in the
configuration space is an example. Current implementation in
dw_pcie_setup_rc() API attempts to unlock those write-protected registers
whenever they are updated and lock them back again for writing. This patch
attempts to unlock all such write-protected registers for writing in the
beginning of the API once and lock them back again towards the end to avoid
bloating the API with multiple unlock/lock sequences for all those
write-protected registers.
Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
Reviewed-by: Thierry Reding <treding@nvidia.com>
Acked-by: Jingoo Han <jingoohan1@gmail.com>
---
V15:
* None
V14:
* None
V13:
* None
V12:
* Modified commit message to make it explicit that write-protected registers are
implementation specific.
V11:
* None
V10:
* None
V9:
* None
V8:
* None
V7:
* None
V6:
* Moved write enable to the beginning of the API and write disable to the end
V5:
* None
V4:
* None
V3:
* None
V2:
* None
drivers/pci/controller/dwc/pcie-designware-host.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index f93252d0da5b..d3156446ff27 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -628,6 +628,12 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
u32 val, ctrl, num_ctrls;
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ /*
+ * Enable DBI read-only registers for writing/updating configuration.
+ * Write permission gets disabled towards the end of this function.
+ */
+ dw_pcie_dbi_ro_wr_en(pci);
+
dw_pcie_setup(pci);
if (!pp->ops->msi_host_init) {
@@ -650,12 +656,10 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);
/* Setup interrupt pins */
- dw_pcie_dbi_ro_wr_en(pci);
val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE);
val &= 0xffff00ff;
val |= 0x00000100;
dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val);
- dw_pcie_dbi_ro_wr_dis(pci);
/* Setup bus numbers */
val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS);
@@ -687,15 +691,13 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
- /* Enable write permission for the DBI read-only register */
- dw_pcie_dbi_ro_wr_en(pci);
/* Program correct class for RC */
dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
- /* Better disable write permission right after the update */
- dw_pcie_dbi_ro_wr_dis(pci);
dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
+
+ dw_pcie_dbi_ro_wr_dis(pci);
}
EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
--
2.17.1
^ permalink raw reply related
* [PATCH V15 02/13] PCI: Disable MSI for Tegra root ports
From: Vidya Sagar @ 2019-08-09 4:45 UTC (permalink / raw)
To: lorenzo.pieralisi, bhelgaas, robh+dt, mark.rutland,
thierry.reding, jonathanh, kishon, catalin.marinas, will.deacon,
jingoohan1, gustavo.pimentel
Cc: digetx, mperttunen, linux-pci, devicetree, linux-tegra,
linux-kernel, linux-arm-kernel, kthota, mmaddireddy, vidyas,
sagar.tv
In-Reply-To: <20190809044609.20401-1-vidyas@nvidia.com>
Tegra PCIe rootports don't generate MSI interrupts for PME and AER events.
Since PCIe spec (Ref: r4.0 sec 7.7.1.2 and 7.7.2.2) doesn't support using
a mix of INTx and MSI/MSI-X, MSI needs to be disabled to avoid root ports
service drivers registering their respective ISRs with MSI interrupt and
to let only INTx be used for all events.
Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
Reviewed-by: Thierry Reding <treding@nvidia.com>
---
V15:
* None
V14:
* None
V13:
* None
V12:
* None
V11:
* Included older Tegra chips to extend the quirk as this issue is present in
older Tegra chips as well.
V10:
* None
V9:
* None
V8:
* Changed quirk macro to consider class code as well to avoid this quirk
getting applied to Tegra194 when it is operating in endpoint mode. Also
quoted relevant sections from PCIe spec in comments.
V7:
* This is a new patch
drivers/pci/quirks.c | 53 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 208aacf39329..168782c5d23b 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2592,6 +2592,59 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
PCI_DEVICE_ID_NVIDIA_NVENET_15,
nvenet_msi_disable);
+/*
+ * PCIe spec r4.0 sec 7.7.1.2 and sec 7.7.2.2 say that if MSI/MSI-X is enabled,
+ * then the device can't use INTx interrupts. Tegra's PCIe root ports don't
+ * generate MSI interrupts for PME and AER events instead only INTx interrupts
+ * are generated. Though Tegra's PCIe root ports can generate MSI interrupts
+ * for other events, since PCIe specificiation doesn't support using a mix of
+ * INTx and MSI/MSI-X, it is required to disable MSI interrupts to avoid port
+ * service drivers registering their respective ISRs for MSIs.
+ */
+static void pci_quirk_nvidia_tegra_disable_rp_msi(struct pci_dev *dev)
+{
+ dev->no_msi = 1;
+}
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x1ad0,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x1ad1,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x1ad2,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e12,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e13,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0fae,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0faf,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x10e5,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x10e6,
+ PCI_CLASS_BRIDGE_PCI, 8,
+ pci_quirk_nvidia_tegra_disable_rp_msi);
+
/*
* Some versions of the MCP55 bridge from Nvidia have a legacy IRQ routing
* config register. This register controls the routing of legacy
--
2.17.1
^ permalink raw reply related
* [PATCH V15 01/13] PCI: Add #defines for some of PCIe spec r4.0 features
From: Vidya Sagar @ 2019-08-09 4:45 UTC (permalink / raw)
To: lorenzo.pieralisi, bhelgaas, robh+dt, mark.rutland,
thierry.reding, jonathanh, kishon, catalin.marinas, will.deacon,
jingoohan1, gustavo.pimentel
Cc: digetx, mperttunen, linux-pci, devicetree, linux-tegra,
linux-kernel, linux-arm-kernel, kthota, mmaddireddy, vidyas,
sagar.tv
In-Reply-To: <20190809044609.20401-1-vidyas@nvidia.com>
Add #defines only for the Data Link Feature and Physical Layer 16.0 GT/s
features as defined in PCIe spec r4.0, sec 7.7.4 for Data Link Feature and
sec 7.7.5 for Physical Layer 16.0 GT/s.
Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
Reviewed-by: Thierry Reding <treding@nvidia.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
---
V15:
* None
V14:
* Added 'Acked-by: Bjorn Helgaas <bhelgaas@google.com>'
V13:
* Updated commit message to include references from spec
* Removed unused defines and moved some from pcie-tegra194.c file
* Addressed review comments from Bjorn
V12:
* None
V11:
* None
V10:
* None
V9:
* None
V8:
* None
V7:
* None
V6:
* None
V5:
* None
V4:
* None
V3:
* Updated commit message and description to explicitly mention that defines are
added only for some of the features and not all.
V2:
* None
include/uapi/linux/pci_regs.h | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index f28e562d7ca8..d28d0319d932 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -713,7 +713,9 @@
#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
#define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */
#define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */
-#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM
+#define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */
+#define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */
+#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_16GT
#define PCI_EXT_CAP_DSN_SIZEOF 12
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
@@ -1053,4 +1055,14 @@
#define PCI_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */
#define PCI_L1SS_CTL2 0x0c /* Control 2 Register */
+/* Data Link Feature */
+#define PCI_DLF_CAP 0x04 /* Capabilities Register */
+#define PCI_DLF_EXCHANGE_ENABLE 0x80000000 /* Data Link Feature Exchange Enable */
+
+/* Physical Layer 16.0 GT/s */
+#define PCI_PL_16GT_LE_CTRL 0x20 /* Lane Equalization Control Register */
+#define PCI_PL_16GT_LE_CTRL_DSP_TX_PRESET_MASK 0x0000000F
+#define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK 0x000000F0
+#define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT 4
+
#endif /* LINUX_PCI_REGS_H */
--
2.17.1
^ permalink raw reply related
* [PATCH V15 00/13] PCI: tegra: Add Tegra194 PCIe support
From: Vidya Sagar @ 2019-08-09 4:45 UTC (permalink / raw)
To: lorenzo.pieralisi, bhelgaas, robh+dt, mark.rutland,
thierry.reding, jonathanh, kishon, catalin.marinas, will.deacon,
jingoohan1, gustavo.pimentel
Cc: devicetree, mmaddireddy, kthota, linux-pci, linux-kernel,
mperttunen, linux-tegra, digetx, vidyas, linux-arm-kernel,
sagar.tv
Tegra194 has six PCIe controllers based on Synopsys DesignWare core.
There are two Universal PHY (UPHY) blocks with each supporting 12(HSIO:
Hisg Speed IO) and 8(NVHS: NVIDIA High Speed) lanes respectively.
Controllers:0~4 use UPHY lanes from HSIO brick whereas Controller:5 uses
UPHY lanes from NVHS brick. Lane mapping in HSIO UPHY brick to each PCIe
controller (0~4) is controlled in XBAR module by BPMP-FW. Since PCIe
core has PIPE interface, a glue module called PIPE-to-UPHY (P2U) is used
to connect each UPHY lane (applicable to both HSIO and NVHS UPHY bricks)
to PCIe controller
This patch series
- Adds support for P2U PHY driver
- Adds support for PCIe host controller
- Adds device tree nodes each PCIe controllers
- Enables nodes applicable to p2972-0000 platform
- Adds helper APIs in Designware core driver to get capability regs offset
- Adds defines for new feature registers of PCIe spec revision 4
- Makes changes in DesignWare core driver to get Tegra194 PCIe working
Testing done on P2972-0000 platform
- Able to get PCIe link up with on-board Marvel eSATA controller
- Able to get PCIe link up with NVMe cards connected to M.2 Key-M slot
- Able to do data transfers with both SATA drives and NVMe cards
- Able to perform suspend-resume sequence
Note
- Enabling x8 slot on P2972-0000 platform requires pinmux driver for Tegra194.
It is being worked on currently and hence Controller:5 (i.e. x8 slot) is
disabled in this patch series. A future patch series would enable this.
- This series is based on top of the following series
Jisheng's patches to add support to .remove() in Designware sub-system
https://patchwork.kernel.org/project/linux-pci/list/?series=98559
(Update: Jisheng's patches are now accepted and applied for v5.2)
My patches made on top of Jisheng's patches to export various symbols
http://patchwork.ozlabs.org/project/linux-pci/list/?series=115671
(Update: My above patch series is accepted and applied for v5.3)
Another patch of mine to enable BPMP-FW resume in noirq phase
http://patchwork.ozlabs.org/patch/1140973/
(This is already accepted)
V15:
* Refactored pcie-tegra194.c code to call only tegra_bpmp_transfer() API
in both .probe() path and .resume_noirq() path.
V14:
* Addressed Lorenzo's review comments in pcie-tegra194.c file (Patch 13/13)
* Added a new patch to export dw_pcie_wait_for_link() API
V13:
* Addressed Bjorn's review comments for adding Gen-4 specific defines to pci_regs.h header file
V12:
* Modified the commit message of patch-3 in this series to address review
comments from Lorenzo
V11:
* Removed device-tree patches from the series as they are applied to relevant
Tegra specific trees by Thierry Reding.
* Included older Tegra chips to extend quirk that disables MSI interrupt being
used for Tegra PCIe root ports.
* Addressed review comments in P2U driver file.
V10:
* Used _relaxed() versions of readl() & writel()
V9:
* Made the drivers dependent on ARCH_TEGRA_194_SOC directly
* Addressed review comments from Dmitry
V8:
* Changed P2U driver file name from pcie-p2u-tegra194.c to phy-tegra194-p2u.c
* Addressed review comments from Thierry and Rob
V7:
* Took care of review comments from Rob
* Added a quirk to disable MSI for root ports
* Removed using pcie_pme_disable_msi() API in host controller driver
V6:
* Removed patch that exports pcie_bus_config symbol
* Took care of review comments from Thierry and Rob
V5:
* Removed redundant APIs in pcie-designware-ep.c file after moving them
to pcie-designware.c file based on Bjorn's review comments
V4:
* Rebased on top of linux-next top of the tree
* Addressed Gustavo's comments and added his Ack for some of the changes.
V3:
* Addressed review comments from Thierry
V2:
* Addressed review comments from Bjorn, Thierry, Jonathan, Rob & Kishon
* Added more patches in v2 series
Vidya Sagar (13):
PCI: Add #defines for some of PCIe spec r4.0 features
PCI: Disable MSI for Tegra root ports
PCI: dwc: Perform dbi regs write lock towards the end
PCI: dwc: Move config space capability search API
PCI: dwc: Add ext config space capability search API
PCI: dwc: Export dw_pcie_wait_for_link() API
dt-bindings: PCI: designware: Add binding for CDM register check
PCI: dwc: Add support to enable CDM register check
dt-bindings: Add PCIe supports-clkreq property
dt-bindings: PCI: tegra: Add device tree support for Tegra194
dt-bindings: PHY: P2U: Add Tegra194 P2U block
phy: tegra: Add PCIe PIPE2UPHY support
PCI: tegra: Add Tegra194 PCIe support
.../bindings/pci/designware-pcie.txt | 5 +
.../bindings/pci/nvidia,tegra194-pcie.txt | 155 ++
Documentation/devicetree/bindings/pci/pci.txt | 5 +
.../bindings/phy/phy-tegra194-p2u.txt | 28 +
drivers/pci/controller/dwc/Kconfig | 10 +
drivers/pci/controller/dwc/Makefile | 1 +
.../pci/controller/dwc/pcie-designware-ep.c | 37 +-
.../pci/controller/dwc/pcie-designware-host.c | 14 +-
drivers/pci/controller/dwc/pcie-designware.c | 88 +
drivers/pci/controller/dwc/pcie-designware.h | 12 +
drivers/pci/controller/dwc/pcie-tegra194.c | 1620 +++++++++++++++++
drivers/pci/quirks.c | 53 +
drivers/phy/tegra/Kconfig | 7 +
drivers/phy/tegra/Makefile | 1 +
drivers/phy/tegra/phy-tegra194-p2u.c | 120 ++
include/uapi/linux/pci_regs.h | 14 +-
16 files changed, 2128 insertions(+), 42 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pci/nvidia,tegra194-pcie.txt
create mode 100644 Documentation/devicetree/bindings/phy/phy-tegra194-p2u.txt
create mode 100644 drivers/pci/controller/dwc/pcie-tegra194.c
create mode 100644 drivers/phy/tegra/phy-tegra194-p2u.c
--
2.17.1
^ permalink raw reply
* Re: [PATCH 2/2] regulator: qcom-rpmh: Add support for SM8150
From: Vinod Koul @ 2019-08-09 4:44 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Mark Brown, linux-arm-msm, Andy Gross, Liam Girdwood, Rob Herring,
Mark Rutland, linux-kernel, devicetree
In-Reply-To: <20190809033255.GL26807@tuxbook-pro>
On 08-08-19, 20:32, Bjorn Andersson wrote:
> On Thu 08 Aug 02:33 PDT 2019, Vinod Koul wrote:
> > diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c
> [..]
> > +static const struct rpmh_vreg_hw_data pmic5_bob = {
> > + .regulator_type = VRM,
> > + .ops = &rpmh_regulator_vrm_bypass_ops,
> > + .voltage_range = REGULATOR_LINEAR_RANGE(300000, 0, 135, 32000),
> > + .n_voltages = 135,
>
> There are 136 voltages in [0,135]
Oops, will send an update
>
> > + .pmic_mode_map = pmic_mode_map_pmic4_bob,
> > + .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
> > +};
> > +
> [..]
> > @@ -755,6 +890,18 @@ static const struct of_device_id rpmh_regulator_match_table[] = {
> > .compatible = "qcom,pm8005-rpmh-regulators",
> > .data = pm8005_vreg_data,
> > },
> > + {
> > + .compatible = "qcom,pm8150-rpmh-regulators",
> > + .data = pm8150_vreg_data,
> > + },
> > + {
> > + .compatible = "qcom,pm8150l-rpmh-regulators",
> > + .data = pm8150l_vreg_data,
> > + },
> > + {
> > + .compatible = "qcom,pm8009-rpmh-regulators",
> > + .data = pm8009_vreg_data,
> > + },
>
> Sort order...
Yes will sort all entries.
> > {}
> > };
> > MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);
>
> Apart from these nits this looks good.
Thanks :)
--
~Vinod
^ permalink raw reply
* Re: [PATCH 1/2] regulator: dt-bindings: Add PM8150x compatibles
From: Vinod Koul @ 2019-08-09 4:43 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Mark Brown, linux-arm-msm, Andy Gross, Liam Girdwood, Rob Herring,
Mark Rutland, linux-kernel, devicetree
In-Reply-To: <20190809032915.GK26807@tuxbook-pro>
On 08-08-19, 20:29, Bjorn Andersson wrote:
> On Thu 08 Aug 02:33 PDT 2019, Vinod Koul wrote:
>
> > Add PM8150, PM8150L and PM8009 compatibles for these PMICs found
> > in some Qualcomm platforms.
> >
> > Signed-off-by: Vinod Koul <vkoul@kernel.org>
> > ---
> > .../devicetree/bindings/regulator/qcom,rpmh-regulator.txt | 8 +++++++-
> > 1 file changed, 7 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
> > index 14d2eee96b3d..1a9cab50503a 100644
> > --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
> > +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
> > @@ -25,6 +25,9 @@ Supported regulator node names:
> > PM8998: smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
> > PMI8998: bob
> > PM8005: smps1 - smps4
> > + PM8150: smps1 - smps10, ldo1 - ldo18
> > + PM8150L: smps1 - smps8, ldo1 - ldo11, bob, flash, rgb
> > + PM8009: smps1 - smps2, ld01 - ldo7
>
> Please maintain the sort order.
Ah yes, Mark has applied the patch, I will send a sort order patch. Even
in previous entries, PM8005 should be the first one..
>
> Apart from that
>
> Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Thanks for the review.
--
~Vinod
^ permalink raw reply
* Re: [PATCH 2/2] regulator: qcom-rpmh: Add support for SM8150
From: Bjorn Andersson @ 2019-08-09 3:32 UTC (permalink / raw)
To: Vinod Koul
Cc: Mark Brown, linux-arm-msm, Andy Gross, Liam Girdwood, Rob Herring,
Mark Rutland, linux-kernel, devicetree
In-Reply-To: <20190808093343.5600-2-vkoul@kernel.org>
On Thu 08 Aug 02:33 PDT 2019, Vinod Koul wrote:
> diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c
[..]
> +static const struct rpmh_vreg_hw_data pmic5_bob = {
> + .regulator_type = VRM,
> + .ops = &rpmh_regulator_vrm_bypass_ops,
> + .voltage_range = REGULATOR_LINEAR_RANGE(300000, 0, 135, 32000),
> + .n_voltages = 135,
There are 136 voltages in [0,135]
> + .pmic_mode_map = pmic_mode_map_pmic4_bob,
> + .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
> +};
> +
[..]
> @@ -755,6 +890,18 @@ static const struct of_device_id rpmh_regulator_match_table[] = {
> .compatible = "qcom,pm8005-rpmh-regulators",
> .data = pm8005_vreg_data,
> },
> + {
> + .compatible = "qcom,pm8150-rpmh-regulators",
> + .data = pm8150_vreg_data,
> + },
> + {
> + .compatible = "qcom,pm8150l-rpmh-regulators",
> + .data = pm8150l_vreg_data,
> + },
> + {
> + .compatible = "qcom,pm8009-rpmh-regulators",
> + .data = pm8009_vreg_data,
> + },
Sort order...
> {}
> };
> MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);
Apart from these nits this looks good.
Regards,
Bjorn
^ permalink raw reply
* Re: [PATCH 1/2] regulator: dt-bindings: Add PM8150x compatibles
From: Bjorn Andersson @ 2019-08-09 3:29 UTC (permalink / raw)
To: Vinod Koul
Cc: Mark Brown, linux-arm-msm, Andy Gross, Liam Girdwood, Rob Herring,
Mark Rutland, linux-kernel, devicetree
In-Reply-To: <20190808093343.5600-1-vkoul@kernel.org>
On Thu 08 Aug 02:33 PDT 2019, Vinod Koul wrote:
> Add PM8150, PM8150L and PM8009 compatibles for these PMICs found
> in some Qualcomm platforms.
>
> Signed-off-by: Vinod Koul <vkoul@kernel.org>
> ---
> .../devicetree/bindings/regulator/qcom,rpmh-regulator.txt | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
> index 14d2eee96b3d..1a9cab50503a 100644
> --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
> +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
> @@ -25,6 +25,9 @@ Supported regulator node names:
> PM8998: smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
> PMI8998: bob
> PM8005: smps1 - smps4
> + PM8150: smps1 - smps10, ldo1 - ldo18
> + PM8150L: smps1 - smps8, ldo1 - ldo11, bob, flash, rgb
> + PM8009: smps1 - smps2, ld01 - ldo7
Please maintain the sort order.
Apart from that
Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Regards,
Bjorn
>
> ========================
> First Level Nodes - PMIC
> @@ -35,7 +38,10 @@ First Level Nodes - PMIC
> Value type: <string>
> Definition: Must be one of: "qcom,pm8998-rpmh-regulators",
> "qcom,pmi8998-rpmh-regulators" or
> - "qcom,pm8005-rpmh-regulators".
> + "qcom,pm8005-rpmh-regulators" or
> + "qcom,pm8150-rpmh-regulators" or
> + "qcom,pm8150l-rpmh-regulators" or
> + "qcom,pm8009-rpmh-regulators".
>
> - qcom,pmic-id
> Usage: required
> --
> 2.20.1
>
^ permalink raw reply
* [PATCH] ARM: dts: imx7d: cl-som-imx7: make ethernet work again
From: André Draszik @ 2019-08-09 3:12 UTC (permalink / raw)
To: linux-kernel
Cc: André Draszik, Ilya Ledvich, Igor Grinberg, Rob Herring,
Mark Rutland, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, NXP Linux Team, devicetree, linux-arm-kernel
Recent changes to the atheros at803x driver caused
ethernet to stop working on this board.
In particular commit 6d4cd041f0af
("net: phy: at803x: disable delay only for RGMII mode")
and commit cd28d1d6e52e
("net: phy: at803x: Disable phy delay for RGMII mode")
fix the AR8031 driver to configure the phy's (RX/TX)
delays as per the 'phy-mode' in the device tree.
This now prevents ethernet from working on this board.
It used to work before those commits, because the
AR8031 comes out of reset with RX delay enabled, and
the at803x driver didn't touch the delay configuration
at all when "rgmii" mode was selected, and because
arch/arm/mach-imx/mach-imx7d.c:ar8031_phy_fixup()
unconditionally enables TX delay.
Since above commits ar8031_phy_fixup() also has no
effect anymore, and the end-result is that all delays
are disabled in the phy, no ethernet.
Update the device tree to restore functionality.
Signed-off-by: André Draszik <git@andred.net>
CC: Ilya Ledvich <ilya@compulab.co.il>
CC: Igor Grinberg <grinberg@compulab.co.il>
CC: Rob Herring <robh+dt@kernel.org>
CC: Mark Rutland <mark.rutland@arm.com>
CC: Shawn Guo <shawnguo@kernel.org>
CC: Sascha Hauer <s.hauer@pengutronix.de>
CC: Pengutronix Kernel Team <kernel@pengutronix.de>
CC: Fabio Estevam <festevam@gmail.com>
CC: NXP Linux Team <linux-imx@nxp.com>
CC: devicetree@vger.kernel.org
CC: linux-arm-kernel@lists.infradead.org
---
arch/arm/boot/dts/imx7d-cl-som-imx7.dts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/imx7d-cl-som-imx7.dts b/arch/arm/boot/dts/imx7d-cl-som-imx7.dts
index e61567437d73..62d5e9a4a781 100644
--- a/arch/arm/boot/dts/imx7d-cl-som-imx7.dts
+++ b/arch/arm/boot/dts/imx7d-cl-som-imx7.dts
@@ -44,7 +44,7 @@
<&clks IMX7D_ENET1_TIME_ROOT_CLK>;
assigned-clock-parents = <&clks IMX7D_PLL_ENET_MAIN_100M_CLK>;
assigned-clock-rates = <0>, <100000000>;
- phy-mode = "rgmii";
+ phy-mode = "rgmii-id";
phy-handle = <ðphy0>;
fsl,magic-packet;
status = "okay";
@@ -70,7 +70,7 @@
<&clks IMX7D_ENET2_TIME_ROOT_CLK>;
assigned-clock-parents = <&clks IMX7D_PLL_ENET_MAIN_100M_CLK>;
assigned-clock-rates = <0>, <100000000>;
- phy-mode = "rgmii";
+ phy-mode = "rgmii-id";
phy-handle = <ðphy1>;
fsl,magic-packet;
status = "okay";
--
2.20.1
^ permalink raw reply related
* Re: [RFC PATCH 09/11] devfreq: exynos-bus: Add interconnect functionality to exynos-bus
From: Chanwoo Choi @ 2019-08-09 2:17 UTC (permalink / raw)
To: Artur Świgoń, devicetree, linux-arm-kernel,
linux-samsung-soc, linux-kernel, linux-pm, dri-devel
Cc: krzk, myungjoo.ham, inki.dae, sw0312.kim, georgi.djakov,
m.szyprowski
In-Reply-To: <7171e3c56b0dca8a81058e77dd76bd1e684e7778.camel@partner.samsung.com>
Hi,
We need to discuss how to change or do refactoring on v2.
Actually, I don't know the your opinion and how to do it on v2.
You have to reply the answer and then after finished the discussion,
I recommend that you would rework and resend the v2 patches.
On 19. 8. 8. 오후 10:18, Artur Świgoń wrote:
> Hi,
>
> Thank you for your remarks. I will take them into account while preparing RFCv2.
>
> On Mon, 2019-07-29 at 10:52 +0900, Chanwoo Choi wrote:
>> Hi,
>>
>> On 19. 7. 23. 오후 9:20, Artur Świgoń wrote:
>>> This patch adds interconnect functionality to the exynos-bus devfreq
>>> driver.
>>>
>>> The SoC topology is a graph (or, more specifically, a tree) and most of its
>>> edges are taken from the devfreq parent-child hierarchy (cf.
>>> Documentation/devicetree/bindings/devfreq/exynos-bus.txt). The previous
>>> patch adds missing edges to the DT (under the name 'parent'). Due to
>>> unspecified relative probing order, -EPROBE_DEFER may be propagated to
>>> guarantee that a child is probed before its parent.
>>>
>>> Each bus is now an interconnect provider and an interconnect node as well
>>> (cf. Documentation/interconnect/interconnect.rst), i.e. every bus registers
>>> itself as a node. Node IDs are not hardcoded but rather assigned at
>>> runtime, in probing order (subject to the above-mentioned exception
>>> regarding relative order). This approach allows for using this driver with
>>> various Exynos SoCs.
>>>
>>> The devfreq target() callback provided by exynos-bus now selects either the
>>> frequency calculated by the devfreq governor or the frequency requested via
>>> the interconnect API for the given node, whichever is higher.
>>
>> Basically, I agree to support the QoS requirement between devices.
>> But, I think that need to consider the multiple cases.
>>
>>
>> 1. When changing the devfreq governor by user,
>> For example of the connection between bus_dmc/leftbus/display on patch8,
>> there are possible multiple cases with various devfreq governor
>> which is changed on the runtime by user through sysfs interface.
>>
>> If users changes the devfreq governor as following:
>> Before,
>> - bus_dmc (simple_ondemand, available frequency 100/200/300/400 MHz)
>> --> bus_leftbus(simple_ondemand, available frequency 100/200/300/400 MHz)
>> ----> bus_display(passive)
>>
>> After changed governor of bus_dmc,
>> if the min_freq by interconnect requirement is 400Mhz,
>> - bus_dmc (powersave) : min_freq and max_freq and cur_freq is 100MHz
>> --> bus_leftbus(simple_ondemand) : cur_freq is 400Mhz
>> ----> bus_display(passive)
>>
>> The final frequency is 400MHz of bus_dmc
>> even if the min_freq/max_freq/cur_freq is 100MHz.
>> It cannot show the correct min_freq/max_freq through
>> devfreq sysfs interface.
>>
>>
>> 2. When disabling the some frequency by devfreq-thermal throttling,
>> This patch checks the min_freq of interconnect requirement
>> in the exynos_bus_target() and exynos_bus_passive_target().
>> Also, it cannot show the correct min_freq/max_freq through
>> devfreq sysfs interface.
>>
>> For example of bus_dmc bus,
>> - The available frequencies are 100MHz, 200MHz, 300MHz, 400MHz
>> - Disable 400MHz by devfreq-thermal throttling
>> - min_freq is 100MHz
>> - max_freq is 300MHz
>> - min_freq of interconnect is 400MHz
>>
>> In result, the final frequency is 400MHz by exynos_bus_target()
>> There are no problem for working. But, the user cannot know
>> reason why cur_freq is 400MHz even if max_freq is 300MHz.
>>
>> Basically, update_devfreq() considers the all constraints
>> of min_freq/max_freq to decide the proper target frequency.
>>
>>
>> 3.
>> I think that the exynos_bus_passive_target() is used for devfreq device
>> using 'passive' governor. The frequency already depends on the parent device.
>>
>> If already the parent devfreq device like bus_leftbus consider
>> the minimum frequency of QoS requirement like interconnect,
>> it is not necessary. The next frequency of devfreq device
>> with 'passive' governor, it will apply the QoS requirement
>> without any additional code.
>>
>>>
>>> Please note that it is not an error when CONFIG_INTERCONNECT is 'n', in
>>> which case all interconnect API functions are no-op.
>>>
>>> Signed-off-by: Artur Świgoń <a.swigon@partner.samsung.com>
>>> ---
>>> drivers/devfreq/exynos-bus.c | 145 +++++++++++++++++++++++++++++++++++
>>> 1 file changed, 145 insertions(+)
>>>
>>> diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
>>> index 412511ca7703..12fb7c84ae50 100644
>>> --- a/drivers/devfreq/exynos-bus.c
>>> +++ b/drivers/devfreq/exynos-bus.c
>>> @@ -14,6 +14,7 @@
>>> #include <linux/devfreq-event.h>
>>> #include <linux/device.h>
>>> #include <linux/export.h>
>>> +#include <linux/interconnect-provider.h>
>>> #include <linux/module.h>
>>> #include <linux/of.h>
>>> #include <linux/pm_opp.h>
>>> @@ -23,6 +24,8 @@
>>> #define DEFAULT_SATURATION_RATIO 40
>>> #define DEFAULT_VOLTAGE_TOLERANCE 2
>>>
>>> +#define icc_units_to_hz(x) ((x) * 1000UL / 8)
>>> +
>>> struct exynos_bus {
>>> struct device *dev;
>>>
>>> @@ -31,12 +34,17 @@ struct exynos_bus {
>>> unsigned int edev_count;
>>> struct mutex lock;
>>>
>>> + unsigned long min_freq;
>>> unsigned long curr_freq;
>>>
>>> struct regulator *regulator;
>>> struct clk *clk;
>>> unsigned int voltage_tolerance;
>>> unsigned int ratio;
>>> +
>>> + /* One provider per bus, one node per provider */
>>> + struct icc_provider provider;
>>> + struct icc_node *node;
>>> };
>>>
>>> /*
>>> @@ -61,6 +69,13 @@ exynos_bus_ops_edev(enable_edev);
>>> exynos_bus_ops_edev(disable_edev);
>>> exynos_bus_ops_edev(set_event);
>>>
>>> +static int exynos_bus_next_id(void)
>>> +{
>>> + static int exynos_bus_node_id;
>>> +
>>> + return exynos_bus_node_id++;
>>> +}
>>> +
>>> static int exynos_bus_get_event(struct exynos_bus *bus,
>>> struct devfreq_event_data *edata)
>>> {
>>> @@ -98,6 +113,8 @@ static int exynos_bus_target(struct device *dev, unsigned
>>> long *freq, u32 flags)
>>> unsigned long old_freq, new_freq, new_volt, tol;
>>> int ret = 0;
>>>
>>> + *freq = max(*freq, bus->min_freq);
>>> +
>>> /* Get new opp-bus instance according to new bus clock */
>>> new_opp = devfreq_recommended_opp(dev, freq, flags);
>>> if (IS_ERR(new_opp)) {
>>> @@ -208,6 +225,8 @@ static int exynos_bus_passive_target(struct device *dev,
>>> unsigned long *freq,
>>> unsigned long old_freq, new_freq;
>>> int ret = 0;
>>>
>>> + *freq = max(*freq, bus->min_freq);
>>> +
>>> /* Get new opp-bus instance according to new bus clock */
>>> new_opp = devfreq_recommended_opp(dev, freq, flags);
>>> if (IS_ERR(new_opp)) {
>>> @@ -251,6 +270,35 @@ static void exynos_bus_passive_exit(struct device *dev)
>>> clk_disable_unprepare(bus->clk);
>>> }
>>>
>>> +static int exynos_bus_icc_set(struct icc_node *src, struct icc_node *dst)
>>> +{
>>> + struct exynos_bus *src_bus = src->data, *dst_bus = dst->data;
>>> +
>>> + src_bus->min_freq = icc_units_to_hz(src->peak_bw);
>>> + dst_bus->min_freq = icc_units_to_hz(dst->peak_bw);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int exynos_bus_icc_aggregate(struct icc_node *node, u32 avg_bw,
>>> + u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
>>> +{
>>> + *agg_peak = *agg_avg = peak_bw;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static struct icc_node *exynos_bus_icc_xlate(struct of_phandle_args *spec,
>>> + void *data)
>>> +{
>>> + struct exynos_bus *bus = data;
>>> +
>>> + if (spec->np != bus->dev->of_node)
>>> + return ERR_PTR(-EINVAL);
>>> +
>>> + return bus->node;
>>> +}
>>> +
>>> static int exynos_bus_parent_parse_of(struct device_node *np,
>>> struct exynos_bus *bus)
>>> {
>>> @@ -469,6 +517,95 @@ static int exynos_bus_profile_init_passive(struct
>>> exynos_bus *bus,
>>> return ret;
>>> }
>>>
>>> +static int exynos_bus_icc_connect(struct exynos_bus *bus)
>>> +{
>>> + struct device_node *np = bus->dev->of_node;
>>> + struct devfreq *parent_devfreq;
>>> + struct icc_node *parent_node = NULL;
>>> + struct of_phandle_args args;
>>> + int ret = 0;
>>> +
>>> + parent_devfreq = devfreq_get_devfreq_by_phandle(bus->dev, 0);
>>> + if (!IS_ERR(parent_devfreq)) {
>>> + struct exynos_bus *parent_bus;
>>> +
>>> + parent_bus = dev_get_drvdata(parent_devfreq->dev.parent);
>>> + parent_node = parent_bus->node;
>>> + } else {
>>> + /* Look for parent in DT */
>>> + int num = of_count_phandle_with_args(np, "parent",
>>> + "#interconnect-cells");
>>> + if (num != 1)
>>> + goto out;
>>> +
>>> + ret = of_parse_phandle_with_args(np, "parent",
>>> + "#interconnect-cells",
>>> + 0, &args);
>>> + if (ret < 0)
>>> + goto out;
>>> +
>>> + of_node_put(args.np);
>>> +
>>> + parent_node = of_icc_get_from_provider(&args);
>>> + if (IS_ERR(parent_node)) {
>>> + /* May be -EPROBE_DEFER */
>>> + ret = PTR_ERR(parent_node);
>>> + goto out;
>>> + }
>>> + }
>>> +
>>> + ret = icc_link_create(bus->node, parent_node->id);
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static int exynos_bus_icc_init(struct exynos_bus *bus)
>>> +{
>>> + struct device *dev = bus->dev;
>>> + struct icc_provider *provider = &bus->provider;
>>> + struct icc_node *node;
>>> + int id, ret;
>>> +
>>> + /* Initialize the interconnect provider */
>>> + provider->set = exynos_bus_icc_set;
>>> + provider->aggregate = exynos_bus_icc_aggregate;
>>> + provider->xlate = exynos_bus_icc_xlate;
>>> + provider->dev = dev;
>>> + provider->data = bus;
>>> +
>>> + ret = icc_provider_add(provider);
>>> + if (ret < 0)
>>> + goto out;
>>> +
>>> + id = exynos_bus_next_id();
>>> + node = icc_node_create(id);
>>> + if (IS_ERR(node)) {
>>> + ret = PTR_ERR(node);
>>> + goto err_node;
>>> + }
>>> +
>>> + bus->node = node;
>>> + node->name = dev->of_node->name;
>>> + node->data = bus;
>>> + icc_node_add(node, provider);
>>> +
>>> + ret = exynos_bus_icc_connect(bus);
>>> + if (ret < 0)
>>> + goto err_connect;
>>> +
>>> +out:
>>> + return ret;
>>> +
>>> +err_connect:
>>> + icc_node_del(node);
>>> + icc_node_destroy(id);
>>> +err_node:
>>> + icc_provider_del(provider);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> static int exynos_bus_probe(struct platform_device *pdev)
>>> {
>>> struct device *dev = &pdev->dev;
>>> @@ -517,6 +654,14 @@ static int exynos_bus_probe(struct platform_device
>>> *pdev)
>>> goto err;
>>> }
>>>
>>> + /*
>>> + * Initialize interconnect provider. A return value of -ENOTSUPP means
>>> + * that CONFIG_INTERCONNECT is disabled.
>>> + */
>>> + ret = exynos_bus_icc_init(bus);
>>> + if (ret < 0 && ret != -ENOTSUPP)
>>> + goto err;
>>> +
>>> max_state = bus->devfreq->profile->max_state;
>>> min_freq = (bus->devfreq->profile->freq_table[0] / 1000);
>>> max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000);
>>>
>>
>>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
^ permalink raw reply
* [PATCH v8 21/21] arm64: dts: tegra210-p3450: Jetson Nano SC7 timings
From: Sowjanya Komatineni @ 2019-08-08 23:47 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch adds Jetson Nano platform specific SC7 timing configuration
in the device tree.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
index 9d17ec707bce..b525e69c172a 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
@@ -382,6 +382,13 @@
pmc@7000e400 {
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <0>;
+ nvidia,cpu-pwr-good-time = <0>;
+ nvidia,cpu-pwr-off-time = <0>;
+ nvidia,core-pwr-good-time = <4587 3876>;
+ nvidia,core-pwr-off-time = <39065>;
+ nvidia,core-power-req-active-high;
+ nvidia,sys-clock-req-active-high;
};
hda@70030000 {
--
2.7.4
^ permalink raw reply related
* [PATCH v8 20/21] arm64: dts: tegra210-p2180: Jetson TX1 SC7 timings
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch has Jetson TX1 platform specific SC7 timing configuration
in device tree.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
index 27723829d033..cb58f79deb48 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
@@ -279,6 +279,13 @@
pmc@7000e400 {
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <0>;
+ nvidia,cpu-pwr-good-time = <0>;
+ nvidia,cpu-pwr-off-time = <0>;
+ nvidia,core-pwr-good-time = <4587 3876>;
+ nvidia,core-pwr-off-time = <39065>;
+ nvidia,core-power-req-active-high;
+ nvidia,sys-clock-req-active-high;
};
/* eMMC */
--
2.7.4
^ permalink raw reply related
* [PATCH v8 19/21] soc/tegra: pmc: Configure deep sleep control settings
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
Tegra210 and prior Tegra chips have deep sleep entry and wakeup related
timings which are platform specific that should be configured before
entering into deep sleep.
Below are the timing specific configurations for deep sleep entry and
wakeup.
- Core rail power-on stabilization timer
- OSC clock stabilization timer after SOC rail power is stabilized.
- Core power off time is the minimum wake delay to keep the system
in deep sleep state irrespective of any quick wake event.
These values depends on the discharge time of regulators and turn OFF
time of the PMIC to allow the complete system to finish entering into
deep sleep state.
These values vary based on the platform design and are specified
through the device tree.
This patch has implementation to configure these timings which are must
to have for proper deep sleep and wakeup operations.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/soc/tegra/pmc.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index e013ada7e4e9..9a78d8417367 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -88,6 +88,8 @@
#define PMC_CPUPWRGOOD_TIMER 0xc8
#define PMC_CPUPWROFF_TIMER 0xcc
+#define PMC_COREPWRGOOD_TIMER 0x3c
+#define PMC_COREPWROFF_TIMER 0xe0
#define PMC_PWR_DET_VALUE 0xe4
@@ -2277,7 +2279,7 @@ static const struct tegra_pmc_regs tegra20_pmc_regs = {
static void tegra20_pmc_init(struct tegra_pmc *pmc)
{
- u32 value;
+ u32 value, osc, pmu, off;
/* Always enable CPU power request */
value = tegra_pmc_readl(pmc, PMC_CNTRL);
@@ -2303,6 +2305,15 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc)
value = tegra_pmc_readl(pmc, PMC_CNTRL);
value |= PMC_CNTRL_SYSCLK_OE;
tegra_pmc_writel(pmc, value, PMC_CNTRL);
+
+ osc = DIV_ROUND_UP(pmc->core_osc_time * 8192, 1000000);
+ pmu = DIV_ROUND_UP(pmc->core_pmu_time * 32768, 1000000);
+ off = DIV_ROUND_UP(pmc->core_off_time * 32768, 1000000);
+ if (osc && pmu)
+ tegra_pmc_writel(pmc, ((osc << 8) & 0xff00) | (pmu & 0xff),
+ PMC_COREPWRGOOD_TIMER);
+ if (off)
+ tegra_pmc_writel(pmc, off, PMC_COREPWROFF_TIMER);
}
static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
--
2.7.4
^ permalink raw reply related
* [PATCH v8 18/21] soc/tegra: pmc: Configure core power request polarity
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch configures polarity of the core power request signal
in PMC control register based on the device tree property.
PMC asserts and de-asserts power request signal based on it polarity
when it need to power-up and power-down the core rail during SC7.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/soc/tegra/pmc.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 3aa71c28a10a..e013ada7e4e9 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -56,6 +56,7 @@
#define PMC_CNTRL_SIDE_EFFECT_LP0 BIT(14) /* LP0 when CPU pwr gated */
#define PMC_CNTRL_SYSCLK_OE BIT(11) /* system clock enable */
#define PMC_CNTRL_SYSCLK_POLARITY BIT(10) /* sys clk polarity */
+#define PMC_CNTRL_PWRREQ_POLARITY BIT(8)
#define PMC_CNTRL_MAIN_RST BIT(4)
#define PMC_WAKE_MASK 0x0c
@@ -2290,6 +2291,11 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc)
else
value |= PMC_CNTRL_SYSCLK_POLARITY;
+ if (pmc->corereq_high)
+ value &= ~PMC_CNTRL_PWRREQ_POLARITY;
+ else
+ value |= PMC_CNTRL_PWRREQ_POLARITY;
+
/* configure the output polarity while the request is tristated */
tegra_pmc_writel(pmc, value, PMC_CNTRL);
--
2.7.4
^ permalink raw reply related
* [PATCH v8 17/21] arm64: tegra: Enable wake from deep sleep on RTC alarm
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch updates device tree for RTC and PMC to allow system wake
from deep sleep on RTC alarm.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
arch/arm64/boot/dts/nvidia/tegra210.dtsi | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 659753118e96..30a7c48385a2 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -768,7 +768,8 @@
rtc@7000e000 {
compatible = "nvidia,tegra210-rtc", "nvidia,tegra20-rtc";
reg = <0x0 0x7000e000 0x0 0x100>;
- interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&pmc>;
clocks = <&tegra_car TEGRA210_CLK_RTC>;
clock-names = "rtc";
};
@@ -778,6 +779,8 @@
reg = <0x0 0x7000e400 0x0 0x400>;
clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>;
clock-names = "pclk", "clk32k_in";
+ #interrupt-cells = <2>;
+ interrupt-controller;
powergates {
pd_audio: aud {
--
2.7.4
^ permalink raw reply related
* [PATCH v8 16/21] soc/tegra: pmc: Add pmc wake support for tegra210
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch implements PMC wakeup sequence for Tegra210 and defines
common used RTC alarm wake event.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/soc/tegra/pmc.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 91c84d0e66ae..3aa71c28a10a 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -58,6 +58,11 @@
#define PMC_CNTRL_SYSCLK_POLARITY BIT(10) /* sys clk polarity */
#define PMC_CNTRL_MAIN_RST BIT(4)
+#define PMC_WAKE_MASK 0x0c
+#define PMC_WAKE_LEVEL 0x10
+#define PMC_WAKE_STATUS 0x14
+#define PMC_SW_WAKE_STATUS 0x18
+
#define DPD_SAMPLE 0x020
#define DPD_SAMPLE_ENABLE BIT(0)
#define DPD_SAMPLE_DISABLE (0 << 0)
@@ -87,6 +92,11 @@
#define PMC_SCRATCH41 0x140
+#define PMC_WAKE2_MASK 0x160
+#define PMC_WAKE2_LEVEL 0x164
+#define PMC_WAKE2_STATUS 0x168
+#define PMC_SW_WAKE2_STATUS 0x16c
+
#define PMC_SENSOR_CTRL 0x1b0
#define PMC_SENSOR_CTRL_SCRATCH_WRITE BIT(2)
#define PMC_SENSOR_CTRL_ENABLE_RST BIT(1)
@@ -1922,6 +1932,43 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = {
.alloc = tegra_pmc_irq_alloc,
};
+static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
+ unsigned int offset, bit;
+ u32 value;
+
+ if (data->hwirq == ULONG_MAX)
+ return 0;
+
+ offset = data->hwirq / 32;
+ bit = data->hwirq % 32;
+
+ /* clear wake status */
+ tegra_pmc_writel(pmc, 0, PMC_SW_WAKE_STATUS);
+ tegra_pmc_writel(pmc, 0, PMC_SW_WAKE2_STATUS);
+
+ tegra_pmc_writel(pmc, 0, PMC_WAKE_STATUS);
+ tegra_pmc_writel(pmc, 0, PMC_WAKE2_STATUS);
+
+ /* enable PMC wake */
+ if (data->hwirq >= 32)
+ offset = PMC_WAKE2_MASK;
+ else
+ offset = PMC_WAKE_MASK;
+
+ value = tegra_pmc_readl(pmc, offset);
+
+ if (on)
+ value |= 1 << bit;
+ else
+ value &= ~(1 << bit);
+
+ tegra_pmc_writel(pmc, value, offset);
+
+ return 0;
+}
+
static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
{
struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
@@ -1954,6 +2001,49 @@ static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
return 0;
}
+static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
+ unsigned int offset, bit;
+ u32 value;
+
+ if (data->hwirq == ULONG_MAX)
+ return 0;
+
+ offset = data->hwirq / 32;
+ bit = data->hwirq % 32;
+
+ if (data->hwirq >= 32)
+ offset = PMC_WAKE2_LEVEL;
+ else
+ offset = PMC_WAKE_LEVEL;
+
+ value = tegra_pmc_readl(pmc, offset);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_LEVEL_HIGH:
+ value |= 1 << bit;
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_LEVEL_LOW:
+ value &= ~(1 << bit);
+ break;
+
+ case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING:
+ value ^= 1 << bit;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ tegra_pmc_writel(pmc, value, offset);
+
+ return 0;
+}
+
static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
{
struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
@@ -2540,6 +2630,10 @@ static const struct pinctrl_pin_desc tegra210_pin_descs[] = {
TEGRA210_IO_PAD_TABLE(TEGRA_IO_PIN_DESC)
};
+static const struct tegra_wake_event tegra210_wake_events[] = {
+ TEGRA_WAKE_IRQ("rtc", 16, 2),
+};
+
static const struct tegra_pmc_soc tegra210_pmc_soc = {
.num_powergates = ARRAY_SIZE(tegra210_powergates),
.powergates = tegra210_powergates,
@@ -2557,10 +2651,14 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
.regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
+ .irq_set_wake = tegra210_pmc_irq_set_wake,
+ .irq_set_type = tegra210_pmc_irq_set_type,
.reset_sources = tegra210_reset_sources,
.num_reset_sources = ARRAY_SIZE(tegra210_reset_sources),
.reset_levels = NULL,
.num_reset_levels = 0,
+ .num_wake_events = ARRAY_SIZE(tegra210_wake_events),
+ .wake_events = tegra210_wake_events,
};
#define TEGRA186_IO_PAD_TABLE(_pad) \
--
2.7.4
^ permalink raw reply related
* [PATCH v8 15/21] soc/tegra: pmc: Allow to support more tegras wake
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch allows to create separate irq_set_wake and irq_set_type
implementations for different tegra designs PMC that has different
wake models which require difference wake registers and different
programming sequence.
AOWAKE model support is available for Tegra186 and Tegra194 only
and it resides within PMC and supports tiered wake architecture.
Tegra210 and prior tegra designs uses PMC directly to receive wake
events and coordinate the wake sequence.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/soc/tegra/pmc.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 9f9c1c677cf4..91c84d0e66ae 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -226,6 +226,8 @@ struct tegra_pmc_soc {
void (*setup_irq_polarity)(struct tegra_pmc *pmc,
struct device_node *np,
bool invert);
+ int (*irq_set_wake)(struct irq_data *data, unsigned int on);
+ int (*irq_set_type)(struct irq_data *data, unsigned int type);
const char * const *reset_sources;
unsigned int num_reset_sources;
@@ -1920,7 +1922,7 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = {
.alloc = tegra_pmc_irq_alloc,
};
-static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
+static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
{
struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
unsigned int offset, bit;
@@ -1952,7 +1954,7 @@ static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
return 0;
}
-static int tegra_pmc_irq_set_type(struct irq_data *data, unsigned int type)
+static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
{
struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
u32 value;
@@ -2006,8 +2008,8 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
pmc->irq.irq_unmask = irq_chip_unmask_parent;
pmc->irq.irq_eoi = irq_chip_eoi_parent;
pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent;
- pmc->irq.irq_set_type = tegra_pmc_irq_set_type;
- pmc->irq.irq_set_wake = tegra_pmc_irq_set_wake;
+ pmc->irq.irq_set_type = pmc->soc->irq_set_type;
+ pmc->irq.irq_set_wake = pmc->soc->irq_set_wake;
pmc->domain = irq_domain_add_hierarchy(parent, 0, 96, pmc->dev->of_node,
&tegra_pmc_irq_domain_ops, pmc);
@@ -2680,6 +2682,8 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
.regs = &tegra186_pmc_regs,
.init = NULL,
.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
+ .irq_set_wake = tegra186_pmc_irq_set_wake,
+ .irq_set_type = tegra186_pmc_irq_set_type,
.reset_sources = tegra186_reset_sources,
.num_reset_sources = ARRAY_SIZE(tegra186_reset_sources),
.reset_levels = tegra186_reset_levels,
--
2.7.4
^ permalink raw reply related
* [PATCH v8 14/21] clk: tegra210: Add suspend and resume support
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch adds support for clk: tegra210: suspend-resume.
All the CAR controller settings are lost on suspend when core
power goes off.
This patch has implementation for saving and restoring all PLLs
and clocks context during system suspend and resume to have the
clocks back to same state for normal operation.
Clock driver suspend and resume are registered as syscore_ops as clocks
restore need to happen before the other drivers resume to have all their
clocks back to the same state as before suspend.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/clk/tegra/clk-tegra210.c | 103 +++++++++++++++++++++++++++++++++++++--
drivers/clk/tegra/clk.c | 64 ++++++++++++++++++++++++
drivers/clk/tegra/clk.h | 3 ++
3 files changed, 166 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 998bf60b219a..8dd6f4f4debb 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -9,13 +9,13 @@
#include <linux/clkdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/clk/tegra.h>
#include <dt-bindings/clock/tegra210-car.h>
#include <dt-bindings/reset/tegra210-car.h>
-#include <linux/iopoll.h>
#include <linux/sizes.h>
#include <soc/tegra/pmc.h>
@@ -220,11 +220,15 @@
#define CLK_M_DIVISOR_SHIFT 2
#define CLK_M_DIVISOR_MASK 0x3
+#define CLK_MASK_ARM 0x44
+#define MISC_CLK_ENB 0x48
+
#define RST_DFLL_DVCO 0x2f4
#define DVFS_DFLL_RESET_SHIFT 0
#define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8
#define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac
+#define CPU_SOFTRST_CTRL 0x380
#define LVL2_CLK_GATE_OVRA 0xf8
#define LVL2_CLK_GATE_OVRC 0x3a0
@@ -2825,6 +2829,7 @@ static int tegra210_enable_pllu(void)
struct tegra_clk_pll_freq_table *fentry;
struct tegra_clk_pll pllu;
u32 reg;
+ int ret;
for (fentry = pll_u_freq_table; fentry->input_rate; fentry++) {
if (fentry->input_rate == pll_ref_freq)
@@ -2853,9 +2858,14 @@ static int tegra210_enable_pllu(void)
reg |= PLL_ENABLE;
writel(reg, clk_base + PLLU_BASE);
- readl_relaxed_poll_timeout_atomic(clk_base + PLLU_BASE, reg,
- reg & PLL_BASE_LOCK, 2, 1000);
- if (!(reg & PLL_BASE_LOCK)) {
+ /*
+ * During clocks resume, same PLLU init and enable sequence get
+ * executed. So, readx_poll_timeout_atomic can't be used here as it
+ * uses ktime_get() and timekeeping resume doesn't happen by that
+ * time. So, using tegra210_wait_for_mask for PLL LOCK.
+ */
+ ret = tegra210_wait_for_mask(&pllu, PLLU_BASE, PLL_BASE_LOCK);
+ if (ret) {
pr_err("Timed out waiting for PLL_U to lock\n");
return -ETIMEDOUT;
}
@@ -3288,6 +3298,84 @@ static void tegra210_disable_cpu_clock(u32 cpu)
}
#ifdef CONFIG_PM_SLEEP
+/*
+ * This array lists mask values for each peripheral clk bank
+ * to mask out reserved bits during the clocks state restore
+ * on SC7 resume to prevent accidental writes to these reserved
+ * bits.
+ */
+static u32 periph_clk_rsvd_mask[TEGRA210_CAR_BANK_COUNT] = {
+ 0x23282006,
+ 0x782e0c18,
+ 0x0c012c05,
+ 0x003e7304,
+ 0x86c04800,
+ 0xc0199000,
+ 0x03e03800,
+};
+
+#define car_readl(_base, _off) readl_relaxed(clk_base + (_base) + ((_off) * 4))
+#define car_writel(_val, _base, _off) \
+ writel_relaxed(_val, clk_base + (_base) + ((_off) * 4))
+
+static u32 spare_reg_ctx, misc_clk_enb_ctx, clk_msk_arm_ctx;
+static u32 cpu_softrst_ctx[3];
+
+static int tegra210_clk_suspend(void)
+{
+ unsigned int i;
+
+ clk_save_context();
+
+ /*
+ * Save the bootloader configured clock registers SPARE_REG0,
+ * MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL.
+ */
+ spare_reg_ctx = readl_relaxed(clk_base + SPARE_REG0);
+ misc_clk_enb_ctx = readl_relaxed(clk_base + MISC_CLK_ENB);
+ clk_msk_arm_ctx = readl_relaxed(clk_base + CLK_MASK_ARM);
+
+ for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
+ cpu_softrst_ctx[i] = car_readl(CPU_SOFTRST_CTRL, i);
+
+ tegra_clk_periph_suspend();
+ return 0;
+}
+
+static void tegra210_clk_resume(void)
+{
+ unsigned int i;
+
+ tegra_clk_osc_resume(clk_base);
+
+ /*
+ * Restore the bootloader configured clock registers SPARE_REG0,
+ * MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL from saved context.
+ */
+ writel_relaxed(spare_reg_ctx, clk_base + SPARE_REG0);
+ writel_relaxed(misc_clk_enb_ctx, clk_base + MISC_CLK_ENB);
+ writel_relaxed(clk_msk_arm_ctx, clk_base + CLK_MASK_ARM);
+
+ for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
+ car_writel(cpu_softrst_ctx[i], CPU_SOFTRST_CTRL, i);
+
+ fence_udelay(5, clk_base);
+
+ /* enable all the clocks before changing the clock sources */
+ tegra_clk_periph_force_on(periph_clk_rsvd_mask);
+
+ /* wait for all writes to happen to have all the clocks enabled */
+ wmb();
+ fence_udelay(2, clk_base);
+
+ /* restore PLLs and all peripheral clock rates */
+ tegra210_init_pllu();
+ clk_restore_context();
+
+ /* restore all peripheral clocks enable and reset state */
+ tegra_clk_periph_resume();
+}
+
static void tegra210_cpu_clock_suspend(void)
{
/* switch coresite to clk_m, save off original source */
@@ -3303,6 +3391,11 @@ static void tegra210_cpu_clock_resume(void)
}
#endif
+static struct syscore_ops tegra_clk_syscore_ops = {
+ .suspend = tegra210_clk_suspend,
+ .resume = tegra210_clk_resume,
+};
+
static struct tegra_cpu_car_ops tegra210_cpu_car_ops = {
.wait_for_reset = tegra210_wait_cpu_in_reset,
.disable_clock = tegra210_disable_cpu_clock,
@@ -3587,5 +3680,7 @@ static void __init tegra210_clock_init(struct device_node *np)
tegra210_mbist_clk_init();
tegra_cpu_car_ops = &tegra210_cpu_car_ops;
+
+ register_syscore_ops(&tegra_clk_syscore_ops);
}
CLK_OF_DECLARE(tegra210, "nvidia,tegra210-car", tegra210_clock_init);
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index eb08047fd02f..368a576132f6 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -67,6 +67,7 @@ struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
int *periph_clk_enb_refcnt;
static int periph_banks;
+static u32 *periph_state_ctx;
static struct clk **clks;
static int clk_num;
static struct clk_onecell_data clk_data;
@@ -213,6 +214,61 @@ void tegra_clk_set_pllp_out_cpu(bool enable)
writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
}
+void tegra_clk_periph_force_on(u32 *clks_rsvd_mask)
+{
+ unsigned int i;
+
+ for (i = 0; i < periph_banks; i++)
+ writel_relaxed(~clks_rsvd_mask[i],
+ clk_base + periph_regs[i].enb_reg);
+}
+
+void tegra_clk_periph_suspend(void)
+{
+ unsigned int i, idx;
+
+ idx = 0;
+ for (i = 0; i < periph_banks; i++, idx++)
+ periph_state_ctx[idx] =
+ readl_relaxed(clk_base + periph_regs[i].enb_reg);
+
+ for (i = 0; i < periph_banks; i++, idx++)
+ periph_state_ctx[idx] =
+ readl_relaxed(clk_base + periph_regs[i].rst_reg);
+}
+
+void tegra_clk_periph_resume(void)
+{
+ unsigned int i, idx;
+
+ idx = 0;
+ for (i = 0; i < periph_banks; i++, idx++)
+ writel_relaxed(periph_state_ctx[idx],
+ clk_base + periph_regs[i].enb_reg);
+ /*
+ * All non-boot peripherals will be in reset state on resume.
+ * Wait for 5us of reset propagation delay before de-asserting
+ * the peripherals based on the saved context.
+ */
+ fence_udelay(5, clk_base);
+
+ for (i = 0; i < periph_banks; i++, idx++)
+ writel_relaxed(periph_state_ctx[idx],
+ clk_base + periph_regs[i].rst_reg);
+
+ fence_udelay(2, clk_base);
+}
+
+static int tegra_clk_periph_ctx_init(int banks)
+{
+ periph_state_ctx = kcalloc(2 * banks, sizeof(*periph_state_ctx),
+ GFP_KERNEL);
+ if (!periph_state_ctx)
+ return -ENOMEM;
+
+ return 0;
+}
+
struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
{
clk_base = regs;
@@ -234,6 +290,14 @@ struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
clk_num = num;
+ if (IS_ENABLED(CONFIG_PM_SLEEP)) {
+ if (tegra_clk_periph_ctx_init(banks)) {
+ kfree(periph_clk_enb_refcnt);
+ kfree(clks);
+ return NULL;
+ }
+ }
+
return clks;
}
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 560e2bcb3d7d..9a17cad28d72 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -843,6 +843,9 @@ int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
u8 frac_width, u8 flags);
void tegra_clk_osc_resume(void __iomem *clk_base);
void tegra_clk_set_pllp_out_cpu(bool enable);
+void tegra_clk_periph_force_on(u32 *clks_rsvd_mask);
+void tegra_clk_periph_suspend(void);
+void tegra_clk_periph_resume(void);
/* Combined read fence with delay */
--
2.7.4
^ permalink raw reply related
* [PATCH v8 13/21] clk: tegra210: Use fence_udelay during PLLU init
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch uses fence_udelay rather than udelay during PLLU
initialization to ensure writes to clock registers happens before
waiting for specified delay.
Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/clk/tegra/clk-tegra210.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 4721ee030d1c..998bf60b219a 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -2841,7 +2841,7 @@ static int tegra210_enable_pllu(void)
reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]);
reg &= ~BIT(pllu.params->iddq_bit_idx);
writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]);
- udelay(5);
+ fence_udelay(5, clk_base);
reg = readl_relaxed(clk_base + PLLU_BASE);
reg &= ~GENMASK(20, 0);
@@ -2849,7 +2849,7 @@ static int tegra210_enable_pllu(void)
reg |= fentry->n << 8;
reg |= fentry->p << 16;
writel(reg, clk_base + PLLU_BASE);
- udelay(1);
+ fence_udelay(1, clk_base);
reg |= PLL_ENABLE;
writel(reg, clk_base + PLLU_BASE);
@@ -2895,12 +2895,12 @@ static int tegra210_init_pllu(void)
reg = readl_relaxed(clk_base + XUSB_PLL_CFG0);
reg &= ~XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK;
writel_relaxed(reg, clk_base + XUSB_PLL_CFG0);
- udelay(1);
+ fence_udelay(1, clk_base);
reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0);
reg |= PLLU_HW_PWRDN_CFG0_SEQ_ENABLE;
writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0);
- udelay(1);
+ fence_udelay(1, clk_base);
reg = readl_relaxed(clk_base + PLLU_BASE);
reg &= ~PLLU_BASE_CLKENABLE_USB;
--
2.7.4
^ permalink raw reply related
* [PATCH v8 12/21] cpufreq: tegra124: Add suspend and resume support
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch adds suspend and resume pm ops for cpufreq driver.
PLLP is the safe clock source for CPU during system suspend and
resume as PLLP rate is below the CPU Fmax at Vmin.
CPUFreq driver suspend switches the CPU clock source to PLLP and
disables the DFLL clock.
During system resume, warmboot code powers up the CPU with PLLP
clock source. So CPUFreq driver resume enabled DFLL clock and
switches CPU back to DFLL clock source.
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/cpufreq/tegra124-cpufreq.c | 60 ++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
index 4f0c637b3b49..e979a3370988 100644
--- a/drivers/cpufreq/tegra124-cpufreq.c
+++ b/drivers/cpufreq/tegra124-cpufreq.c
@@ -6,6 +6,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -128,8 +129,67 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
return ret;
}
+static int __maybe_unused tegra124_cpufreq_suspend(struct device *dev)
+{
+ struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
+ int err;
+
+ /*
+ * PLLP rate 408Mhz is below the CPU Fmax at Vmin and is safe to
+ * use during suspend and resume. So, switch the CPU clock source
+ * to PLLP and disable DFLL.
+ */
+ err = clk_set_parent(priv->cpu_clk, priv->pllp_clk);
+ if (err < 0) {
+ dev_err(dev, "failed to reparent to PLLP: %d\n", err);
+ return err;
+ }
+
+ /* disable DFLL clock */
+ clk_disable_unprepare(priv->dfll_clk);
+
+ return 0;
+}
+
+static int __maybe_unused tegra124_cpufreq_resume(struct device *dev)
+{
+ struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
+ int err;
+
+ /*
+ * Warmboot code powers up the CPU with PLLP clock source.
+ * Enable DFLL clock and switch CPU clock source back to DFLL.
+ */
+ err = clk_prepare_enable(priv->dfll_clk);
+ if (err < 0) {
+ dev_err(dev, "failed to enable DFLL clock for CPU: %d\n", err);
+ goto disable_cpufreq;
+ }
+
+ err = clk_set_parent(priv->cpu_clk, priv->dfll_clk);
+ if (err < 0) {
+ dev_err(dev, "failed to reparent to DFLL clock: %d\n", err);
+ goto disable_dfll;
+ }
+
+ return 0;
+
+disable_dfll:
+ clk_disable_unprepare(priv->dfll_clk);
+disable_cpufreq:
+ disable_cpufreq();
+
+ return err;
+}
+
+static const struct dev_pm_ops tegra124_cpufreq_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra124_cpufreq_suspend,
+ tegra124_cpufreq_resume)
+};
+
static struct platform_driver tegra124_cpufreq_platdrv = {
.driver.name = "cpufreq-tegra124",
+ .driver.pm = &tegra124_cpufreq_pm_ops,
.probe = tegra124_cpufreq_probe,
};
--
2.7.4
^ permalink raw reply related
* [PATCH v8 11/21] clk: tegra: clk-dfll: Add suspend and resume support
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch implements DFLL suspend and resume operation.
During system suspend entry, CPU clock will switch CPU to safe
clock source of PLLP and disables DFLL clock output.
DFLL driver suspend confirms DFLL disable state and errors out on
being active.
DFLL is re-initialized during the DFLL driver resume as it goes
through complete reset during suspend entry.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/clk/tegra/clk-dfll.c | 56 ++++++++++++++++++++++++++++++
drivers/clk/tegra/clk-dfll.h | 2 ++
drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 1 +
3 files changed, 59 insertions(+)
diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index f8688c2ddf1a..eb298a5d7be9 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -1487,6 +1487,7 @@ static int dfll_init(struct tegra_dfll *td)
td->last_unrounded_rate = 0;
pm_runtime_enable(td->dev);
+ pm_runtime_irq_safe(td->dev);
pm_runtime_get_sync(td->dev);
dfll_set_mode(td, DFLL_DISABLED);
@@ -1513,6 +1514,61 @@ static int dfll_init(struct tegra_dfll *td)
return ret;
}
+/**
+ * tegra_dfll_suspend - check DFLL is disabled
+ * @dev: DFLL device *
+ *
+ * DFLL clock should be disabled by the CPUFreq driver. So, make
+ * sure it is disabled and disable all clocks needed by the DFLL.
+ */
+int tegra_dfll_suspend(struct device *dev)
+{
+ struct tegra_dfll *td = dev_get_drvdata(dev);
+
+ if (dfll_is_running(td)) {
+ dev_err(td->dev, "dfll is enabled while shouldn't be\n");
+ return -EBUSY;
+ }
+
+ reset_control_assert(td->dvco_rst);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_suspend);
+
+/**
+ * tegra_dfll_resume - reinitialize DFLL on resume
+ * @dev: DFLL instance
+ *
+ * DFLL is disabled and reset during suspend and resume.
+ * So, reinitialize the DFLL IP block back for use.
+ * DFLL clock is enabled later in closed loop mode by CPUFreq
+ * driver before switching its clock source to DFLL output.
+ */
+int tegra_dfll_resume(struct device *dev)
+{
+ struct tegra_dfll *td = dev_get_drvdata(dev);
+
+ reset_control_deassert(td->dvco_rst);
+
+ pm_runtime_get_sync(td->dev);
+
+ dfll_set_mode(td, DFLL_DISABLED);
+ dfll_set_default_params(td);
+
+ if (td->soc->init_clock_trimmers)
+ td->soc->init_clock_trimmers();
+
+ dfll_set_open_loop_config(td);
+
+ dfll_init_out_if(td);
+
+ pm_runtime_put_sync(td->dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_resume);
+
/*
* DT data fetch
*/
diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
index 1b14ebe7268b..fb209eb5f365 100644
--- a/drivers/clk/tegra/clk-dfll.h
+++ b/drivers/clk/tegra/clk-dfll.h
@@ -42,5 +42,7 @@ int tegra_dfll_register(struct platform_device *pdev,
struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev);
int tegra_dfll_runtime_suspend(struct device *dev);
int tegra_dfll_runtime_resume(struct device *dev);
+int tegra_dfll_suspend(struct device *dev);
+int tegra_dfll_resume(struct device *dev);
#endif /* __DRIVERS_CLK_TEGRA_CLK_DFLL_H */
diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
index e84b6d52cbbd..2ac2679d696d 100644
--- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -631,6 +631,7 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
static const struct dev_pm_ops tegra124_dfll_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
tegra_dfll_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_dfll_suspend, tegra_dfll_resume)
};
static struct platform_driver tegra124_dfll_fcpu_driver = {
--
2.7.4
^ permalink raw reply related
* [PATCH v8 10/21] clk: tegra: clk-super: Add restore-context support
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch implements restore_context for clk_super_mux and clk_super.
During system supend, core power goes off the and context of Tegra
CAR registers is lost.
So on system resume, context of super clock registers are restored
to have them in same state as before suspend.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/clk/tegra/clk-super.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
index e2a1e95a8db7..74c9e913e41c 100644
--- a/drivers/clk/tegra/clk-super.c
+++ b/drivers/clk/tegra/clk-super.c
@@ -124,9 +124,18 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
return err;
}
+static void clk_super_mux_restore_context(struct clk_hw *hw)
+{
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ int parent_id = clk_hw_get_parent_index(hw, parent);
+
+ clk_super_set_parent(hw, parent_id);
+}
+
static const struct clk_ops tegra_clk_super_mux_ops = {
.get_parent = clk_super_get_parent,
.set_parent = clk_super_set_parent,
+ .restore_context = clk_super_mux_restore_context,
};
static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -162,12 +171,24 @@ static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
return super->div_ops->set_rate(div_hw, rate, parent_rate);
}
+static void clk_super_restore_context(struct clk_hw *hw)
+{
+ struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+ struct clk_hw *div_hw = &super->frac_div.hw;
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ int parent_id = clk_hw_get_parent_index(hw, parent);
+
+ super->div_ops->restore_context(div_hw);
+ clk_super_set_parent(hw, parent_id);
+}
+
const struct clk_ops tegra_clk_super_ops = {
.get_parent = clk_super_get_parent,
.set_parent = clk_super_set_parent,
.set_rate = clk_super_set_rate,
.round_rate = clk_super_round_rate,
.recalc_rate = clk_super_recalc_rate,
+ .restore_context = clk_super_restore_context,
};
struct clk *tegra_clk_register_super_mux(const char *name,
--
2.7.4
^ permalink raw reply related
* [PATCH v8 09/21] clk: tegra: clk-super: Fix to enable PLLP branches to CPU
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch has a fix to enable PLLP branches to CPU before changing
the CPU cluster clock source to PLLP for Gen5 Super clock and
disables PLLP branches to CPU when not in use.
During system suspend entry and exit, CPU source will be switched
to PLLP and this needs PLLP branches to be enabled to CPU prior to
the switch.
On system resume, warmboot code enables PLLP branches to CPU and
powers up the CPU with PLLP clock source.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/clk/tegra/clk-super.c | 14 ++++++++++++++
drivers/clk/tegra/clk-tegra-super-gen4.c | 7 ++++++-
drivers/clk/tegra/clk.c | 14 ++++++++++++++
drivers/clk/tegra/clk.h | 5 +++++
4 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
index 39ef31b46df5..e2a1e95a8db7 100644
--- a/drivers/clk/tegra/clk-super.c
+++ b/drivers/clk/tegra/clk-super.c
@@ -28,6 +28,9 @@
#define super_state_to_src_shift(m, s) ((m->width * s))
#define super_state_to_src_mask(m) (((1 << m->width) - 1))
+#define CCLK_SRC_PLLP_OUT0 4
+#define CCLK_SRC_PLLP_OUT4 5
+
static u8 clk_super_get_parent(struct clk_hw *hw)
{
struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
@@ -97,12 +100,23 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
if (index == mux->div2_index)
index = mux->pllx_index;
}
+
+ /* enable PLLP branches to CPU before selecting PLLP source */
+ if ((mux->flags & TEGRA210_CPU_CLK) &&
+ (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4))
+ tegra_clk_set_pllp_out_cpu(true);
+
val &= ~((super_state_to_src_mask(mux)) << shift);
val |= (index & (super_state_to_src_mask(mux))) << shift;
writel_relaxed(val, mux->reg);
udelay(2);
+ /* disable PLLP branches to CPU if not used */
+ if ((mux->flags & TEGRA210_CPU_CLK) &&
+ index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4)
+ tegra_clk_set_pllp_out_cpu(false);
+
out:
if (mux->lock)
spin_unlock_irqrestore(mux->lock, flags);
diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c
index cdfe7c9697e1..98538f79b0c4 100644
--- a/drivers/clk/tegra/clk-tegra-super-gen4.c
+++ b/drivers/clk/tegra/clk-tegra-super-gen4.c
@@ -180,7 +180,7 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
gen_info->num_cclk_g_parents,
CLK_SET_RATE_PARENT,
clk_base + CCLKG_BURST_POLICY,
- 0, 4, 8, 0, NULL);
+ TEGRA210_CPU_CLK, 4, 8, 0, NULL);
} else {
clk = tegra_clk_register_super_mux("cclk_g",
gen_info->cclk_g_parents,
@@ -196,6 +196,11 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks);
if (dt_clk) {
if (gen_info->gen == gen5) {
+ /*
+ * TEGRA210_CPU_CLK flag is not needed for cclk_lp as cluster
+ * switching is not currently supported on Tegra210 and also
+ * cpu_lp is not used.
+ */
clk = tegra_clk_register_super_mux("cclk_lp",
gen_info->cclk_lp_parents,
gen_info->num_cclk_lp_parents,
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index 573e3c967ae1..eb08047fd02f 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -23,6 +23,7 @@
#define CLK_OUT_ENB_W 0x364
#define CLK_OUT_ENB_X 0x280
#define CLK_OUT_ENB_Y 0x298
+#define CLK_ENB_PLLP_OUT_CPU BIT(31)
#define CLK_OUT_ENB_SET_L 0x320
#define CLK_OUT_ENB_CLR_L 0x324
#define CLK_OUT_ENB_SET_H 0x328
@@ -199,6 +200,19 @@ const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
}
}
+void tegra_clk_set_pllp_out_cpu(bool enable)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + CLK_OUT_ENB_Y);
+ if (enable)
+ val |= CLK_ENB_PLLP_OUT_CPU;
+ else
+ val &= ~CLK_ENB_PLLP_OUT_CPU;
+
+ writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
+}
+
struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
{
clk_base = regs;
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 8a9af45b6084..560e2bcb3d7d 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -677,6 +677,9 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
* Flags:
* TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
* that this is LP cluster clock.
+ * TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5
+ * super mux parent using PLLP branches. To use PLLP branches to CPU, need
+ * to configure additional bit PLLP_OUT_CPU in the clock registers.
*/
struct tegra_clk_super_mux {
struct clk_hw hw;
@@ -693,6 +696,7 @@ struct tegra_clk_super_mux {
#define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
#define TEGRA_DIVIDER_2 BIT(0)
+#define TEGRA210_CPU_CLK BIT(1)
extern const struct clk_ops tegra_clk_super_ops;
struct clk *tegra_clk_register_super_mux(const char *name,
@@ -838,6 +842,7 @@ int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
u8 frac_width, u8 flags);
void tegra_clk_osc_resume(void __iomem *clk_base);
+void tegra_clk_set_pllp_out_cpu(bool enable);
/* Combined read fence with delay */
--
2.7.4
^ permalink raw reply related
* [PATCH v8 08/21] clk: tegra: periph: Add restore_context support
From: Sowjanya Komatineni @ 2019-08-08 23:46 UTC (permalink / raw)
To: thierry.reding, jonathanh, tglx, jason, marc.zyngier,
linus.walleij, stefan, mark.rutland
Cc: pdeschrijver, pgaikwad, sboyd, linux-clk, linux-gpio, jckuo,
josephl, talho, skomatineni, linux-tegra, linux-kernel,
mperttunen, spatra, robh+dt, digetx, devicetree, rjw,
viresh.kumar, linux-pm
In-Reply-To: <1565308020-31952-1-git-send-email-skomatineni@nvidia.com>
This patch implements restore_context support for clk-periph and
clk-sdmmc-mux clock operations to restore clock parent and rates
on system resume.
During system suspend, core power goes off and looses the context
of the Tegra clock controller registers.
So on system resume, clocks parent and rate are restored back to
the context before suspend based on cached data.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
drivers/clk/tegra/clk-periph.c | 18 ++++++++++++++++++
drivers/clk/tegra/clk-sdmmc-mux.c | 12 ++++++++++++
2 files changed, 30 insertions(+)
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
index 58437da25156..c9d28cbadccc 100644
--- a/drivers/clk/tegra/clk-periph.c
+++ b/drivers/clk/tegra/clk-periph.c
@@ -3,6 +3,7 @@
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*/
+#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/export.h>
#include <linux/slab.h>
@@ -99,6 +100,20 @@ static void clk_periph_disable(struct clk_hw *hw)
gate_ops->disable(gate_hw);
}
+static void clk_periph_restore_context(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *div_ops = periph->div_ops;
+ struct clk_hw *div_hw = &periph->divider.hw;
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ int parent_id = clk_hw_get_parent_index(hw, parent);
+
+ if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
+ div_ops->restore_context(div_hw);
+
+ clk_periph_set_parent(hw, parent_id);
+}
+
const struct clk_ops tegra_clk_periph_ops = {
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
@@ -108,6 +123,7 @@ const struct clk_ops tegra_clk_periph_ops = {
.is_enabled = clk_periph_is_enabled,
.enable = clk_periph_enable,
.disable = clk_periph_disable,
+ .restore_context = clk_periph_restore_context,
};
static const struct clk_ops tegra_clk_periph_nodiv_ops = {
@@ -116,6 +132,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
.is_enabled = clk_periph_is_enabled,
.enable = clk_periph_enable,
.disable = clk_periph_disable,
+ .restore_context = clk_periph_restore_context,
};
static const struct clk_ops tegra_clk_periph_no_gate_ops = {
@@ -124,6 +141,7 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = {
.recalc_rate = clk_periph_recalc_rate,
.round_rate = clk_periph_round_rate,
.set_rate = clk_periph_set_rate,
+ .restore_context = clk_periph_restore_context,
};
static struct clk *_tegra_clk_register_periph(const char *name,
diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c
index a5cd3e31dbae..8db48966b100 100644
--- a/drivers/clk/tegra/clk-sdmmc-mux.c
+++ b/drivers/clk/tegra/clk-sdmmc-mux.c
@@ -194,6 +194,17 @@ static void clk_sdmmc_mux_disable(struct clk_hw *hw)
gate_ops->disable(gate_hw);
}
+static void clk_sdmmc_mux_restore_context(struct clk_hw *hw)
+{
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+ unsigned long rate = clk_hw_get_rate(hw);
+ int parent_id = clk_hw_get_parent_index(hw, parent);
+
+ clk_sdmmc_mux_set_parent(hw, parent_id);
+ clk_sdmmc_mux_set_rate(hw, rate, parent_rate);
+}
+
static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
.get_parent = clk_sdmmc_mux_get_parent,
.set_parent = clk_sdmmc_mux_set_parent,
@@ -203,6 +214,7 @@ static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
.is_enabled = clk_sdmmc_mux_is_enabled,
.enable = clk_sdmmc_mux_enable,
.disable = clk_sdmmc_mux_disable,
+ .restore_context = clk_sdmmc_mux_restore_context,
};
struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
--
2.7.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox