summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2019-08-13 01:47:53 +0300
committerTom Rini <trini@konsulko.com>2019-08-13 01:47:53 +0300
commit9c6115822e894ead72fa4c094bf718eaabb9e103 (patch)
tree4f8843a5cfbe2895a168d96dee111dd31e443b2a /drivers
parent5939afc9611e8ba4a86b96e67670b765ee27668e (diff)
parent0805fe151d8c47cfbcfddf71c0891ed4f3c10b56 (diff)
downloadu-boot-9c6115822e894ead72fa4c094bf718eaabb9e103.tar.xz
Merge branch '2019-08-11-ti-imports'
- More DaVinci updates and fixes - PCIe support on am65x - Watchdog converted to DM - Assorted other bugfixes
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/clk-uclass.c13
-rw-r--r--drivers/core/fdtaddr.c17
-rw-r--r--drivers/core/read.c20
-rw-r--r--drivers/firmware/ti_sci.c13
-rw-r--r--drivers/pci/Kconfig6
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/pcie_dw_ti.c725
-rw-r--r--drivers/phy/Kconfig9
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-ti-am654.c411
-rw-r--r--drivers/power/domain/ti-sci-power-domain.c8
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/omap_wdt.c121
14 files changed, 1347 insertions, 8 deletions
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index cee4d912b0..c66b6f3c4e 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -518,6 +518,19 @@ int clk_get_by_id(ulong id, struct clk **clkp)
return -ENOENT;
}
+bool clk_is_match(const struct clk *p, const struct clk *q)
+{
+ /* trivial case: identical struct clk's or both NULL */
+ if (p == q)
+ return true;
+
+ /* same device, id and data */
+ if (p->dev == q->dev && p->id == q->id && p->data == q->data)
+ return true;
+
+ return false;
+}
+
UCLASS_DRIVER(clk) = {
.id = UCLASS_CLK,
.name = "clk",
diff --git a/drivers/core/fdtaddr.c b/drivers/core/fdtaddr.c
index c2873861da..6850003a28 100644
--- a/drivers/core/fdtaddr.c
+++ b/drivers/core/fdtaddr.c
@@ -129,6 +129,23 @@ fdt_addr_t devfdt_get_addr_name(struct udevice *dev, const char *name)
#endif
}
+fdt_addr_t devfdt_get_addr_size_name(struct udevice *dev, const char *name,
+ fdt_size_t *size)
+{
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+ int index;
+
+ index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev),
+ "reg-names", name);
+ if (index < 0)
+ return index;
+
+ return devfdt_get_addr_size_index(dev, index, size);
+#else
+ return FDT_ADDR_T_NONE;
+#endif
+}
+
fdt_addr_t devfdt_get_addr(struct udevice *dev)
{
return devfdt_get_addr_index(dev, 0);
diff --git a/drivers/core/read.c b/drivers/core/read.c
index 1a044b05e8..8b5502de11 100644
--- a/drivers/core/read.c
+++ b/drivers/core/read.c
@@ -82,6 +82,15 @@ fdt_addr_t dev_read_addr_index(struct udevice *dev, int index)
return devfdt_get_addr_index(dev, index);
}
+fdt_addr_t dev_read_addr_size_index(struct udevice *dev, int index,
+ fdt_size_t *size)
+{
+ if (ofnode_is_np(dev_ofnode(dev)))
+ return ofnode_get_addr_size_index(dev_ofnode(dev), index, size);
+ else
+ return devfdt_get_addr_size_index(dev, index, size);
+}
+
void *dev_remap_addr_index(struct udevice *dev, int index)
{
fdt_addr_t addr = dev_read_addr_index(dev, index);
@@ -102,6 +111,17 @@ fdt_addr_t dev_read_addr_name(struct udevice *dev, const char *name)
return dev_read_addr_index(dev, index);
}
+fdt_addr_t dev_read_addr_size_name(struct udevice *dev, const char *name,
+ fdt_size_t *size)
+{
+ int index = dev_read_stringlist_search(dev, "reg-names", name);
+
+ if (index < 0)
+ return FDT_ADDR_T_NONE;
+ else
+ return dev_read_addr_size_index(dev, index, size);
+}
+
void *dev_remap_addr_name(struct udevice *dev, const char *name)
{
fdt_addr_t addr = dev_read_addr_name(dev, name);
diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
index 1fd29f2cdf..62b1dc2006 100644
--- a/drivers/firmware/ti_sci.c
+++ b/drivers/firmware/ti_sci.c
@@ -3170,6 +3170,7 @@ devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle,
u32 resource_subtype;
u16 resource_type;
struct ti_sci_resource *res;
+ bool valid_set = false;
int sets, i, ret;
u32 *temp;
@@ -3209,12 +3210,15 @@ devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle,
&res->desc[i].start,
&res->desc[i].num);
if (ret) {
- dev_err(dev, "type %d subtype %d not allocated for host %d\n",
+ dev_dbg(dev, "type %d subtype %d not allocated for host %d\n",
resource_type, resource_subtype,
handle_to_ti_sci_info(handle)->host_id);
- return ERR_PTR(ret);
+ res->desc[i].start = 0;
+ res->desc[i].num = 0;
+ continue;
}
+ valid_set = true;
dev_dbg(dev, "res type = %d, subtype = %d, start = %d, num = %d\n",
resource_type, resource_subtype, res->desc[i].start,
res->desc[i].num);
@@ -3226,7 +3230,10 @@ devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle,
return ERR_PTR(-ENOMEM);
}
- return res;
+ if (valid_set)
+ return res;
+
+ return ERR_PTR(-EINVAL);
}
/* Description for K2G */
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 3fe38f7315..bdfc0c1796 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -145,4 +145,10 @@ config PCI_MVEBU
Say Y here if you want to enable PCIe controller support on
Armada XP/38x SoCs.
+config PCI_KEYSTONE
+ bool "TI Keystone PCIe controller"
+ depends on DM_PCI
+ help
+ Say Y here if you want to enable PCI controller support on AM654 SoC.
+
endif
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index b5ebd50c85..e54a98b8c9 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -38,3 +38,4 @@ obj-$(CONFIG_PCIE_LAYERSCAPE_GEN4) += pcie_layerscape_gen4.o \
pcie_layerscape_gen4_fixup.o
obj-$(CONFIG_PCI_XILINX) += pcie_xilinx.o
obj-$(CONFIG_PCIE_INTEL_FPGA) += pcie_intel_fpga.o
+obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o
diff --git a/drivers/pci/pcie_dw_ti.c b/drivers/pci/pcie_dw_ti.c
new file mode 100644
index 0000000000..b37fc2de7f
--- /dev/null
+++ b/drivers/pci/pcie_dw_ti.c
@@ -0,0 +1,725 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Texas Instruments, Inc
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <pci.h>
+#include <generic-phy.h>
+#include <power-domain.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm-generic/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define PCIE_VENDORID_MASK GENMASK(15, 0)
+#define PCIE_DEVICEID_SHIFT 16
+
+/* PCI DBICS registers */
+#define PCIE_CONFIG_BAR0 0x10
+#define PCIE_LINK_STATUS_REG 0x80
+#define PCIE_LINK_STATUS_SPEED_OFF 16
+#define PCIE_LINK_STATUS_SPEED_MASK (0xf << PCIE_LINK_STATUS_SPEED_OFF)
+#define PCIE_LINK_STATUS_WIDTH_OFF 20
+#define PCIE_LINK_STATUS_WIDTH_MASK (0xf << PCIE_LINK_STATUS_WIDTH_OFF)
+
+#define PCIE_LINK_CAPABILITY 0x7c
+#define PCIE_LINK_CTL_2 0xa0
+#define TARGET_LINK_SPEED_MASK 0xf
+#define LINK_SPEED_GEN_1 0x1
+#define LINK_SPEED_GEN_2 0x2
+#define LINK_SPEED_GEN_3 0x3
+
+#define PCIE_MISC_CONTROL_1_OFF 0x8bc
+#define PCIE_DBI_RO_WR_EN BIT(0)
+
+#define PLR_OFFSET 0x700
+#define PCIE_PORT_DEBUG0 (PLR_OFFSET + 0x28)
+#define PORT_LOGIC_LTSSM_STATE_MASK 0x1f
+#define PORT_LOGIC_LTSSM_STATE_L0 0x11
+
+#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80c
+#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
+
+#define PCIE_LINK_UP_TIMEOUT_MS 100
+
+/*
+ * iATU Unroll-specific register definitions
+ * From 4.80 core version the address translation will be made by unroll.
+ * The registers are offset from atu_base
+ */
+#define PCIE_ATU_UNR_REGION_CTRL1 0x00
+#define PCIE_ATU_UNR_REGION_CTRL2 0x04
+#define PCIE_ATU_UNR_LOWER_BASE 0x08
+#define PCIE_ATU_UNR_UPPER_BASE 0x0c
+#define PCIE_ATU_UNR_LIMIT 0x10
+#define PCIE_ATU_UNR_LOWER_TARGET 0x14
+#define PCIE_ATU_UNR_UPPER_TARGET 0x18
+
+#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
+#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
+#define PCIE_ATU_TYPE_MEM (0x0 << 0)
+#define PCIE_ATU_TYPE_IO (0x2 << 0)
+#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
+#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
+#define PCIE_ATU_ENABLE (0x1 << 31)
+#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
+#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
+#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
+#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
+
+/* Register address builder */
+#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((region) << 9)
+
+/* Offsets from App base */
+#define PCIE_CMD_STATUS 0x04
+#define LTSSM_EN_VAL BIT(0)
+
+/* Parameters for the waiting for iATU enabled routine */
+#define LINK_WAIT_MAX_IATU_RETRIES 5
+#define LINK_WAIT_IATU 10000
+
+#define AM654_PCIE_DEV_TYPE_MASK 0x3
+#define EP 0x0
+#define LEG_EP 0x1
+#define RC 0x2
+
+/**
+ * struct pcie_dw_ti - TI DW PCIe controller state
+ *
+ * @app_base: The base address of application register space
+ * @dbics_base: The base address of dbics register space
+ * @cfg_base: The base address of configuration space
+ * @atu_base: The base address of ATU space
+ * @cfg_size: The size of the configuration space which is needed
+ * as it gets written into the PCIE_ATU_LIMIT register
+ * @first_busno: This driver supports multiple PCIe controllers.
+ * first_busno stores the bus number of the PCIe root-port
+ * number which may vary depending on the PCIe setup
+ * (PEX switches etc).
+ */
+struct pcie_dw_ti {
+ void *app_base;
+ void *dbi_base;
+ void *cfg_base;
+ void *atu_base;
+ fdt_size_t cfg_size;
+ int first_busno;
+ struct udevice *dev;
+
+ /* IO and MEM PCI regions */
+ struct pci_region io;
+ struct pci_region mem;
+};
+
+enum dw_pcie_device_mode {
+ DW_PCIE_UNKNOWN_TYPE,
+ DW_PCIE_EP_TYPE,
+ DW_PCIE_LEG_EP_TYPE,
+ DW_PCIE_RC_TYPE,
+};
+
+static int pcie_dw_get_link_speed(struct pcie_dw_ti *pci)
+{
+ return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) &
+ PCIE_LINK_STATUS_SPEED_MASK) >> PCIE_LINK_STATUS_SPEED_OFF;
+}
+
+static int pcie_dw_get_link_width(struct pcie_dw_ti *pci)
+{
+ return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) &
+ PCIE_LINK_STATUS_WIDTH_MASK) >> PCIE_LINK_STATUS_WIDTH_OFF;
+}
+
+static void dw_pcie_writel_ob_unroll(struct pcie_dw_ti *pci, u32 index, u32 reg,
+ u32 val)
+{
+ u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
+ void __iomem *base = pci->atu_base;
+
+ writel(val, base + offset + reg);
+}
+
+static u32 dw_pcie_readl_ob_unroll(struct pcie_dw_ti *pci, u32 index, u32 reg)
+{
+ u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
+ void __iomem *base = pci->atu_base;
+
+ return readl(base + offset + reg);
+}
+
+/**
+ * pcie_dw_prog_outbound_atu_unroll() - Configure ATU for outbound accesses
+ *
+ * @pcie: Pointer to the PCI controller state
+ * @index: ATU region index
+ * @type: ATU accsess type
+ * @cpu_addr: the physical address for the translation entry
+ * @pci_addr: the pcie bus address for the translation entry
+ * @size: the size of the translation entry
+ */
+static void pcie_dw_prog_outbound_atu_unroll(struct pcie_dw_ti *pci, int index,
+ int type, u64 cpu_addr,
+ u64 pci_addr, u32 size)
+{
+ u32 retries, val;
+
+ debug("ATU programmed with: index: %d, type: %d, cpu addr: %8llx, pci addr: %8llx, size: %8x\n",
+ index, type, cpu_addr, pci_addr, size);
+
+ dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE,
+ lower_32_bits(cpu_addr));
+ dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE,
+ upper_32_bits(cpu_addr));
+ dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT,
+ lower_32_bits(cpu_addr + size - 1));
+ dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
+ lower_32_bits(pci_addr));
+ dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
+ upper_32_bits(pci_addr));
+ dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
+ type);
+ dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
+ PCIE_ATU_ENABLE);
+
+ /*
+ * Make sure ATU enable takes effect before any subsequent config
+ * and I/O accesses.
+ */
+ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
+ val = dw_pcie_readl_ob_unroll(pci, index,
+ PCIE_ATU_UNR_REGION_CTRL2);
+ if (val & PCIE_ATU_ENABLE)
+ return;
+
+ udelay(LINK_WAIT_IATU);
+ }
+ dev_err(pci->dev, "outbound iATU is not being enabled\n");
+}
+
+/**
+ * set_cfg_address() - Configure the PCIe controller config space access
+ *
+ * @pcie: Pointer to the PCI controller state
+ * @d: PCI device to access
+ * @where: Offset in the configuration space
+ *
+ * Configures the PCIe controller to access the configuration space of
+ * a specific PCIe device and returns the address to use for this
+ * access.
+ *
+ * Return: Address that can be used to access the configation space
+ * of the requested device / offset
+ */
+static uintptr_t set_cfg_address(struct pcie_dw_ti *pcie,
+ pci_dev_t d, uint where)
+{
+ int bus = PCI_BUS(d) - pcie->first_busno;
+ uintptr_t va_address;
+ u32 atu_type;
+
+ /* Use dbi_base for own configuration read and write */
+ if (!bus) {
+ va_address = (uintptr_t)pcie->dbi_base;
+ goto out;
+ }
+
+ if (bus == 1)
+ /* For local bus, change TLP Type field to 4. */
+ atu_type = PCIE_ATU_TYPE_CFG0;
+ else
+ /* Otherwise, change TLP Type field to 5. */
+ atu_type = PCIE_ATU_TYPE_CFG1;
+
+ /*
+ * Not accessing root port configuration space?
+ * Region #0 is used for Outbound CFG space access.
+ * Direction = Outbound
+ * Region Index = 0
+ */
+ d = PCI_MASK_BUS(d);
+ d = PCI_ADD_BUS(bus, d);
+ pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1,
+ atu_type, (u64)pcie->cfg_base,
+ d << 8, pcie->cfg_size);
+
+ va_address = (uintptr_t)pcie->cfg_base;
+
+out:
+ va_address += where & ~0x3;
+
+ return va_address;
+}
+
+/**
+ * pcie_dw_addr_valid() - Check for valid bus address
+ *
+ * @d: The PCI device to access
+ * @first_busno: Bus number of the PCIe controller root complex
+ *
+ * Return 1 (true) if the PCI device can be accessed by this controller.
+ *
+ * Return: 1 on valid, 0 on invalid
+ */
+static int pcie_dw_addr_valid(pci_dev_t d, int first_busno)
+{
+ if ((PCI_BUS(d) == first_busno) && (PCI_DEV(d) > 0))
+ return 0;
+ if ((PCI_BUS(d) == first_busno + 1) && (PCI_DEV(d) > 0))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * pcie_dw_ti_read_config() - Read from configuration space
+ *
+ * @bus: Pointer to the PCI bus
+ * @bdf: Identifies the PCIe device to access
+ * @offset: The offset into the device's configuration space
+ * @valuep: A pointer at which to store the read value
+ * @size: Indicates the size of access to perform
+ *
+ * Read a value of size @size from offset @offset within the configuration
+ * space of the device identified by the bus, device & function numbers in @bdf
+ * on the PCI bus @bus.
+ *
+ * Return: 0 on success
+ */
+static int pcie_dw_ti_read_config(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
+{
+ struct pcie_dw_ti *pcie = dev_get_priv(bus);
+ uintptr_t va_address;
+ ulong value;
+
+ debug("PCIE CFG read: bdf=%2x:%2x:%2x ",
+ PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
+
+ if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) {
+ debug("- out of range\n");
+ *valuep = pci_get_ff(size);
+ return 0;
+ }
+
+ va_address = set_cfg_address(pcie, bdf, offset);
+
+ value = readl(va_address);
+
+ debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value);
+ *valuep = pci_conv_32_to_size(value, offset, size);
+
+ pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1,
+ PCIE_ATU_TYPE_IO, pcie->io.phys_start,
+ pcie->io.bus_start, pcie->io.size);
+
+ return 0;
+}
+
+/**
+ * pcie_dw_ti_write_config() - Write to configuration space
+ *
+ * @bus: Pointer to the PCI bus
+ * @bdf: Identifies the PCIe device to access
+ * @offset: The offset into the device's configuration space
+ * @value: The value to write
+ * @size: Indicates the size of access to perform
+ *
+ * Write the value @value of size @size from offset @offset within the
+ * configuration space of the device identified by the bus, device & function
+ * numbers in @bdf on the PCI bus @bus.
+ *
+ * Return: 0 on success
+ */
+static int pcie_dw_ti_write_config(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
+{
+ struct pcie_dw_ti *pcie = dev_get_priv(bus);
+ uintptr_t va_address;
+ ulong old;
+
+ debug("PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ",
+ PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
+ debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value);
+
+ if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) {
+ debug("- out of range\n");
+ return 0;
+ }
+
+ va_address = set_cfg_address(pcie, bdf, offset);
+
+ old = readl(va_address);
+ value = pci_conv_size_to_32(old, value, offset, size);
+ writel(value, va_address);
+
+ pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1,
+ PCIE_ATU_TYPE_IO, pcie->io.phys_start,
+ pcie->io.bus_start, pcie->io.size);
+
+ return 0;
+}
+
+static inline void dw_pcie_dbi_write_enable(struct pcie_dw_ti *pci, bool en)
+{
+ u32 val;
+
+ val = readl(pci->dbi_base + PCIE_MISC_CONTROL_1_OFF);
+ if (en)
+ val |= PCIE_DBI_RO_WR_EN;
+ else
+ val &= ~PCIE_DBI_RO_WR_EN;
+ writel(val, pci->dbi_base + PCIE_MISC_CONTROL_1_OFF);
+}
+
+/**
+ * pcie_dw_configure() - Configure link capabilities and speed
+ *
+ * @regs_base: A pointer to the PCIe controller registers
+ * @cap_speed: The capabilities and speed to configure
+ *
+ * Configure the link capabilities and speed in the PCIe root complex.
+ */
+static void pcie_dw_configure(struct pcie_dw_ti *pci, u32 cap_speed)
+{
+ u32 val;
+
+ dw_pcie_dbi_write_enable(pci, true);
+
+ val = readl(pci->dbi_base + PCIE_LINK_CAPABILITY);
+ val &= ~TARGET_LINK_SPEED_MASK;
+ val |= cap_speed;
+ writel(val, pci->dbi_base + PCIE_LINK_CAPABILITY);
+
+ val = readl(pci->dbi_base + PCIE_LINK_CTL_2);
+ val &= ~TARGET_LINK_SPEED_MASK;
+ val |= cap_speed;
+ writel(val, pci->dbi_base + PCIE_LINK_CTL_2);
+
+ dw_pcie_dbi_write_enable(pci, false);
+}
+
+/**
+ * is_link_up() - Return the link state
+ *
+ * @regs_base: A pointer to the PCIe DBICS registers
+ *
+ * Return: 1 (true) for active line and 0 (false) for no link
+ */
+static int is_link_up(struct pcie_dw_ti *pci)
+{
+ u32 val;
+
+ val = readl(pci->dbi_base + PCIE_PORT_DEBUG0);
+ val &= PORT_LOGIC_LTSSM_STATE_MASK;
+
+ return (val == PORT_LOGIC_LTSSM_STATE_L0);
+}
+
+/**
+ * wait_link_up() - Wait for the link to come up
+ *
+ * @regs_base: A pointer to the PCIe controller registers
+ *
+ * Return: 1 (true) for active line and 0 (false) for no link (timeout)
+ */
+static int wait_link_up(struct pcie_dw_ti *pci)
+{
+ unsigned long timeout;
+
+ timeout = get_timer(0) + PCIE_LINK_UP_TIMEOUT_MS;
+ while (!is_link_up(pci)) {
+ if (get_timer(0) > timeout)
+ return 0;
+ };
+
+ return 1;
+}
+
+static int pcie_dw_ti_pcie_link_up(struct pcie_dw_ti *pci, u32 cap_speed)
+{
+ u32 val;
+
+ if (is_link_up(pci)) {
+ printf("PCI Link already up before configuration!\n");
+ return 1;
+ }
+
+ /* DW pre link configurations */
+ pcie_dw_configure(pci, cap_speed);
+
+ /* Initiate link training */
+ val = readl(pci->app_base + PCIE_CMD_STATUS);
+ val |= LTSSM_EN_VAL;
+ writel(val, pci->app_base + PCIE_CMD_STATUS);
+
+ /* Check that link was established */
+ if (!wait_link_up(pci))
+ return 0;
+
+ /*
+ * Link can be established in Gen 1. still need to wait
+ * till MAC nagaotiation is completed
+ */
+ udelay(100);
+
+ return 1;
+}
+
+/**
+ * pcie_dw_setup_host() - Setup the PCIe controller for RC opertaion
+ *
+ * @pcie: Pointer to the PCI controller state
+ *
+ * Configure the host BARs of the PCIe controller root port so that
+ * PCI(e) devices may access the system memory.
+ */
+static void pcie_dw_setup_host(struct pcie_dw_ti *pci)
+{
+ u32 val;
+
+ /* setup RC BARs */
+ writel(PCI_BASE_ADDRESS_MEM_TYPE_64,
+ pci->dbi_base + PCI_BASE_ADDRESS_0);
+ writel(0x0, pci->dbi_base + PCI_BASE_ADDRESS_1);
+
+ /* setup interrupt pins */
+ dw_pcie_dbi_write_enable(pci, true);
+ val = readl(pci->dbi_base + PCI_INTERRUPT_LINE);
+ val &= 0xffff00ff;
+ val |= 0x00000100;
+ writel(val, pci->dbi_base + PCI_INTERRUPT_LINE);
+ dw_pcie_dbi_write_enable(pci, false);
+
+ /* setup bus numbers */
+ val = readl(pci->dbi_base + PCI_PRIMARY_BUS);
+ val &= 0xff000000;
+ val |= 0x00ff0100;
+ writel(val, pci->dbi_base + PCI_PRIMARY_BUS);
+
+ /* setup command register */
+ val = readl(pci->dbi_base + PCI_COMMAND);
+ val &= 0xffff0000;
+ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
+ writel(val, pci->dbi_base + PCI_COMMAND);
+
+ /* Enable write permission for the DBI read-only register */
+ dw_pcie_dbi_write_enable(pci, true);
+ /* program correct class for RC */
+ writew(PCI_CLASS_BRIDGE_PCI, pci->dbi_base + PCI_CLASS_DEVICE);
+ /* Better disable write permission right after the update */
+ dw_pcie_dbi_write_enable(pci, false);
+
+ val = readl(pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+ val |= PORT_LOGIC_SPEED_CHANGE;
+ writel(val, pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+}
+
+static int pcie_am654_set_mode(struct pcie_dw_ti *pci,
+ enum dw_pcie_device_mode mode)
+{
+ struct regmap *syscon;
+ u32 val;
+ u32 mask;
+ int ret;
+
+ syscon = syscon_regmap_lookup_by_phandle(pci->dev,
+ "ti,syscon-pcie-mode");
+ if (IS_ERR(syscon))
+ return 0;
+
+ mask = AM654_PCIE_DEV_TYPE_MASK;
+
+ switch (mode) {
+ case DW_PCIE_RC_TYPE:
+ val = RC;
+ break;
+ case DW_PCIE_EP_TYPE:
+ val = EP;
+ break;
+ default:
+ dev_err(pci->dev, "INVALID device type %d\n", mode);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(syscon, 0, mask, val);
+ if (ret) {
+ dev_err(pci->dev, "failed to set pcie mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pcie_dw_init_id(struct pcie_dw_ti *pci)
+{
+ struct regmap *devctrl_regs;
+ unsigned int id;
+ int ret;
+
+ devctrl_regs = syscon_regmap_lookup_by_phandle(pci->dev,
+ "ti,syscon-pcie-id");
+ if (IS_ERR(devctrl_regs))
+ return PTR_ERR(devctrl_regs);
+
+ ret = regmap_read(devctrl_regs, 0, &id);
+ if (ret)
+ return ret;
+
+ dw_pcie_dbi_write_enable(pci, true);
+ writew(id & PCIE_VENDORID_MASK, pci->dbi_base + PCI_VENDOR_ID);
+ writew(id >> PCIE_DEVICEID_SHIFT, pci->dbi_base + PCI_DEVICE_ID);
+ dw_pcie_dbi_write_enable(pci, false);
+
+ return 0;
+}
+
+/**
+ * pcie_dw_ti_probe() - Probe the PCIe bus for active link
+ *
+ * @dev: A pointer to the device being operated on
+ *
+ * Probe for an active link on the PCIe bus and configure the controller
+ * to enable this port.
+ *
+ * Return: 0 on success, else -ENODEV
+ */
+static int pcie_dw_ti_probe(struct udevice *dev)
+{
+ struct pcie_dw_ti *pci = dev_get_priv(dev);
+ struct udevice *ctlr = pci_get_controller(dev);
+ struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+ struct power_domain pci_pwrdmn;
+ struct phy phy0, phy1;
+ int ret;
+
+ ret = power_domain_get_by_index(dev, &pci_pwrdmn, 0);
+ if (ret) {
+ dev_err(dev, "failed to get power domain\n");
+ return ret;
+ }
+
+ ret = power_domain_on(&pci_pwrdmn);
+ if (ret) {
+ dev_err(dev, "Power domain on failed\n");
+ return ret;
+ }
+
+ ret = generic_phy_get_by_name(dev, "pcie-phy0", &phy0);
+ if (ret) {
+ dev_err(dev, "Unable to get phy0");
+ return ret;
+ }
+ generic_phy_reset(&phy0);
+ generic_phy_init(&phy0);
+ generic_phy_power_on(&phy0);
+
+ ret = generic_phy_get_by_name(dev, "pcie-phy1", &phy1);
+ if (ret) {
+ dev_err(dev, "Unable to get phy1");
+ return ret;
+ }
+ generic_phy_reset(&phy1);
+ generic_phy_init(&phy1);
+ generic_phy_power_on(&phy1);
+
+ pci->first_busno = dev->seq;
+ pci->dev = dev;
+
+ pcie_dw_setup_host(pci);
+ pcie_dw_init_id(pci);
+
+ if (device_is_compatible(dev, "ti,am654-pcie-rc"))
+ pcie_am654_set_mode(pci, DW_PCIE_RC_TYPE);
+
+ if (!pcie_dw_ti_pcie_link_up(pci, LINK_SPEED_GEN_2)) {
+ printf("PCIE-%d: Link down\n", dev->seq);
+ return -ENODEV;
+ }
+
+ printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev->seq,
+ pcie_dw_get_link_speed(pci),
+ pcie_dw_get_link_width(pci),
+ hose->first_busno);
+
+ /* Store the IO and MEM windows settings for future use by the ATU */
+ pci->io.phys_start = hose->regions[0].phys_start; /* IO base */
+ pci->io.bus_start = hose->regions[0].bus_start; /* IO_bus_addr */
+ pci->io.size = hose->regions[0].size; /* IO size */
+
+ pci->mem.phys_start = hose->regions[1].phys_start; /* MEM base */
+ pci->mem.bus_start = hose->regions[1].bus_start; /* MEM_bus_addr */
+ pci->mem.size = hose->regions[1].size; /* MEM size */
+
+ pcie_dw_prog_outbound_atu_unroll(pci, PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_TYPE_MEM,
+ pci->mem.phys_start,
+ pci->mem.bus_start, pci->mem.size);
+
+ return 0;
+}
+
+/**
+ * pcie_dw_ti_ofdata_to_platdata() - Translate from DT to device state
+ *
+ * @dev: A pointer to the device being operated on
+ *
+ * Translate relevant data from the device tree pertaining to device @dev into
+ * state that the driver will later make use of. This state is stored in the
+ * device's private data structure.
+ *
+ * Return: 0 on success, else -EINVAL
+ */
+static int pcie_dw_ti_ofdata_to_platdata(struct udevice *dev)
+{
+ struct pcie_dw_ti *pcie = dev_get_priv(dev);
+
+ /* Get the controller base address */
+ pcie->dbi_base = (void *)dev_read_addr_name(dev, "dbics");
+ if ((fdt_addr_t)pcie->dbi_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Get the config space base address and size */
+ pcie->cfg_base = (void *)dev_read_addr_size_name(dev, "config",
+ &pcie->cfg_size);
+ if ((fdt_addr_t)pcie->cfg_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Get the iATU base address and size */
+ pcie->atu_base = (void *)dev_read_addr_name(dev, "atu");
+ if ((fdt_addr_t)pcie->atu_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Get the app base address and size */
+ pcie->app_base = (void *)dev_read_addr_name(dev, "app");
+ if ((fdt_addr_t)pcie->app_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct dm_pci_ops pcie_dw_ti_ops = {
+ .read_config = pcie_dw_ti_read_config,
+ .write_config = pcie_dw_ti_write_config,
+};
+
+static const struct udevice_id pcie_dw_ti_ids[] = {
+ { .compatible = "ti,am654-pcie-rc" },
+ { }
+};
+
+U_BOOT_DRIVER(pcie_dw_ti) = {
+ .name = "pcie_dw_ti",
+ .id = UCLASS_PCI,
+ .of_match = pcie_dw_ti_ids,
+ .ops = &pcie_dw_ti_ops,
+ .ofdata_to_platdata = pcie_dw_ti_ofdata_to_platdata,
+ .probe = pcie_dw_ti_probe,
+ .priv_auto_alloc_size = sizeof(struct pcie_dw_ti),
+};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 8209ca7323..3942f035eb 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -108,6 +108,15 @@ config SPL_PIPE3_PHY
This PHY is found on omap devices supporting SATA such as dra7, am57x
and omap5
+config AM654_PHY
+ tristate "TI AM654 SERDES support"
+ depends on PHY && ARCH_K3
+ select REGMAP
+ select SYSCON
+ help
+ This option enables support for TI AM654 SerDes PHY used for
+ PCIe.
+
config STI_USB_PHY
bool "STMicroelectronics USB2 picoPHY driver for STiH407 family"
depends on PHY && ARCH_STI
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index b9f5195e1c..3157f1b7ee 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_BCM6358_USBH_PHY) += bcm6358-usbh-phy.o
obj-$(CONFIG_BCM6368_USBH_PHY) += bcm6368-usbh-phy.o
obj-$(CONFIG_PHY_SANDBOX) += sandbox-phy.o
obj-$(CONFIG_$(SPL_)PIPE3_PHY) += ti-pipe3-phy.o
+obj-$(CONFIG_AM654_PHY) += phy-ti-am654.o
obj-$(CONFIG_STI_USB_PHY) += sti_usb_phy.o
obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
obj-$(CONFIG_PHY_RCAR_GEN3) += phy-rcar-gen3.o
diff --git a/drivers/phy/phy-ti-am654.c b/drivers/phy/phy-ti-am654.c
new file mode 100644
index 0000000000..39490124ea
--- /dev/null
+++ b/drivers/phy/phy-ti-am654.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ * PCIe SERDES driver for AM654x SoC
+ *
+ * Copyright (C) 2018 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dt-bindings/phy/phy.h>
+#include <generic-phy.h>
+#include <asm/io.h>
+#include <asm/arch/sys_proto.h>
+#include <power-domain.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#define CMU_R07C 0x7c
+#define CMU_MASTER_CDN_O BIT(24)
+
+#define COMLANE_R138 0xb38
+#define CONFIG_VERSION_REG_MASK GENMASK(23, 16)
+#define CONFIG_VERSION_REG_SHIFT 16
+#define VERSION 0x70
+
+#define COMLANE_R190 0xb90
+#define L1_MASTER_CDN_O BIT(9)
+
+#define COMLANE_R194 0xb94
+#define CMU_OK_I_0 BIT(19)
+
+#define SERDES_CTRL 0x1fd0
+#define POR_EN BIT(29)
+
+#define WIZ_LANEXCTL_STS 0x1fe0
+#define TX0_ENABLE_OVL BIT(31)
+#define TX0_ENABLE_MASK GENMASK(30, 29)
+#define TX0_ENABLE_SHIFT 29
+#define TX0_DISABLE_STATE 0x0
+#define TX0_SLEEP_STATE 0x1
+#define TX0_SNOOZE_STATE 0x2
+#define TX0_ENABLE_STATE 0x3
+#define RX0_ENABLE_OVL BIT(15)
+#define RX0_ENABLE_MASK GENMASK(14, 13)
+#define RX0_ENABLE_SHIFT 13
+#define RX0_DISABLE_STATE 0x0
+#define RX0_SLEEP_STATE 0x1
+#define RX0_SNOOZE_STATE 0x2
+#define RX0_ENABLE_STATE 0x3
+
+#define WIZ_PLL_CTRL 0x1ff4
+#define PLL_ENABLE_OVL BIT(31)
+#define PLL_ENABLE_MASK GENMASK(30, 29)
+#define PLL_ENABLE_SHIFT 29
+#define PLL_DISABLE_STATE 0x0
+#define PLL_SLEEP_STATE 0x1
+#define PLL_SNOOZE_STATE 0x2
+#define PLL_ENABLE_STATE 0x3
+#define PLL_OK BIT(28)
+
+#define PLL_LOCK_TIME 1000 /* in milliseconds */
+#define SLEEP_TIME 100 /* in microseconds */
+
+#define LANE_USB3 0x0
+#define LANE_PCIE0_LANE0 0x1
+
+#define LANE_PCIE1_LANE0 0x0
+#define LANE_PCIE0_LANE1 0x1
+
+#define SERDES_NUM_CLOCKS 3
+
+/* SERDES control MMR bit offsets */
+#define SERDES_CTL_LANE_FUNC_SEL_SHIFT 0
+#define SERDES_CTL_LANE_FUNC_SEL_MASK GENMASK(1, 0)
+#define SERDES_CTL_CLK_SEL_SHIFT 4
+#define SERDES_CTL_CLK_SEL_MASK GENMASK(7, 4)
+
+/**
+ * struct serdes_am654_mux_clk_data - clock controller information structure
+ */
+struct serdes_am654_mux_clk_data {
+ struct regmap *regmap;
+ struct clk_bulk parents;
+};
+
+static int serdes_am654_mux_clk_probe(struct udevice *dev)
+{
+ struct serdes_am654_mux_clk_data *data = dev_get_priv(dev);
+ struct udevice *syscon;
+ struct regmap *regmap;
+ int ret;
+
+ debug("%s(dev=%s)\n", __func__, dev->name);
+
+ if (!data)
+ return -ENOMEM;
+
+ ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
+ "ti,serdes-clk", &syscon);
+ if (ret) {
+ dev_err(dev, "unable to find syscon device\n");
+ return ret;
+ }
+
+ regmap = syscon_get_regmap(syscon);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Fail to get Syscon regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ data->regmap = regmap;
+
+ ret = clk_get_bulk(dev, &data->parents);
+ if (ret) {
+ dev_err(dev, "Failed to obtain parent clocks\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mux_table[SERDES_NUM_CLOCKS][3] = {
+ /*
+ * The entries represent values for selecting between
+ * {left input, external reference clock, right input}
+ * Only one of Left Output or Right Output should be used since
+ * both left and right output clock uses the same bits and modifying
+ * one clock will impact the other.
+ */
+ { BIT(2), 0, BIT(0) }, /* Mux of CMU refclk */
+ { -1, BIT(3), BIT(1) }, /* Mux of Left Output */
+ { BIT(1), BIT(3) | BIT(1), -1 }, /* Mux of Right Output */
+};
+
+static int serdes_am654_mux_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct serdes_am654_mux_clk_data *data = dev_get_priv(clk->dev);
+ u32 val;
+ int i;
+
+ debug("%s(clk=%s, parent=%s)\n", __func__, clk->dev->name,
+ parent->dev->name);
+
+ /*
+ * Since we have the same device-tree node represent both the
+ * clock and serdes device, we have two devices associated with
+ * the serdes node. assigned-clocks for this node is processed twice,
+ * once for the clock device and another time for the serdes
+ * device. When it is processed for the clock device, it is before
+ * the probe for clock device has been called. We ignore this case
+ * and rely on assigned-clocks to be processed correctly for the
+ * serdes case.
+ */
+ if (!data->regmap)
+ return 0;
+
+ for (i = 0; i < data->parents.count; i++) {
+ if (clk_is_match(&data->parents.clks[i], parent))
+ break;
+ }
+
+ if (i >= data->parents.count)
+ return -EINVAL;
+
+ val = mux_table[clk->id][i];
+ val <<= SERDES_CTL_CLK_SEL_SHIFT;
+
+ regmap_update_bits(data->regmap, 0, SERDES_CTL_CLK_SEL_MASK, val);
+
+ return 0;
+}
+
+static struct clk_ops serdes_am654_mux_clk_ops = {
+ .set_parent = serdes_am654_mux_clk_set_parent,
+};
+
+U_BOOT_DRIVER(serdes_am654_mux_clk) = {
+ .name = "ti-serdes-am654-mux-clk",
+ .id = UCLASS_CLK,
+ .probe = serdes_am654_mux_clk_probe,
+ .priv_auto_alloc_size = sizeof(struct serdes_am654_mux_clk_data),
+ .ops = &serdes_am654_mux_clk_ops,
+};
+
+struct serdes_am654 {
+ struct regmap *regmap;
+ struct regmap *serdes_ctl;
+};
+
+static int serdes_am654_enable_pll(struct serdes_am654 *phy)
+{
+ u32 mask = PLL_ENABLE_OVL | PLL_ENABLE_MASK;
+ u32 val = PLL_ENABLE_OVL | (PLL_ENABLE_STATE << PLL_ENABLE_SHIFT);
+
+ regmap_update_bits(phy->regmap, WIZ_PLL_CTRL, mask, val);
+
+ return regmap_read_poll_timeout(phy->regmap, WIZ_PLL_CTRL, val,
+ val & PLL_OK, 1000, PLL_LOCK_TIME);
+}
+
+static void serdes_am654_disable_pll(struct serdes_am654 *phy)
+{
+ u32 mask = PLL_ENABLE_OVL | PLL_ENABLE_MASK;
+
+ regmap_update_bits(phy->regmap, WIZ_PLL_CTRL, mask, 0);
+}
+
+static int serdes_am654_enable_txrx(struct serdes_am654 *phy)
+{
+ u32 mask;
+ u32 val;
+
+ /* Enable TX */
+ mask = TX0_ENABLE_OVL | TX0_ENABLE_MASK;
+ val = TX0_ENABLE_OVL | (TX0_ENABLE_STATE << TX0_ENABLE_SHIFT);
+ regmap_update_bits(phy->regmap, WIZ_LANEXCTL_STS, mask, val);
+
+ /* Enable RX */
+ mask = RX0_ENABLE_OVL | RX0_ENABLE_MASK;
+ val = RX0_ENABLE_OVL | (RX0_ENABLE_STATE << RX0_ENABLE_SHIFT);
+ regmap_update_bits(phy->regmap, WIZ_LANEXCTL_STS, mask, val);
+
+ return 0;
+}
+
+static int serdes_am654_disable_txrx(struct serdes_am654 *phy)
+{
+ u32 mask;
+
+ /* Disable TX */
+ mask = TX0_ENABLE_OVL | TX0_ENABLE_MASK;
+ regmap_update_bits(phy->regmap, WIZ_LANEXCTL_STS, mask, 0);
+
+ /* Disable RX */
+ mask = RX0_ENABLE_OVL | RX0_ENABLE_MASK;
+ regmap_update_bits(phy->regmap, WIZ_LANEXCTL_STS, mask, 0);
+
+ return 0;
+}
+
+static int serdes_am654_power_on(struct phy *x)
+{
+ struct serdes_am654 *phy = dev_get_priv(x->dev);
+ int ret;
+ u32 val;
+
+ ret = serdes_am654_enable_pll(phy);
+ if (ret) {
+ dev_err(x->dev, "Failed to enable PLL\n");
+ return ret;
+ }
+
+ ret = serdes_am654_enable_txrx(phy);
+ if (ret) {
+ dev_err(x->dev, "Failed to enable TX RX\n");
+ return ret;
+ }
+
+ return regmap_read_poll_timeout(phy->regmap, COMLANE_R194, val,
+ val & CMU_OK_I_0, SLEEP_TIME,
+ PLL_LOCK_TIME);
+}
+
+static int serdes_am654_power_off(struct phy *x)
+{
+ struct serdes_am654 *phy = dev_get_priv(x->dev);
+
+ serdes_am654_disable_txrx(phy);
+ serdes_am654_disable_pll(phy);
+
+ return 0;
+}
+
+static int serdes_am654_init(struct phy *x)
+{
+ struct serdes_am654 *phy = dev_get_priv(x->dev);
+ u32 mask;
+ u32 val;
+
+ mask = CONFIG_VERSION_REG_MASK;
+ val = VERSION << CONFIG_VERSION_REG_SHIFT;
+ regmap_update_bits(phy->regmap, COMLANE_R138, mask, val);
+
+ val = CMU_MASTER_CDN_O;
+ regmap_update_bits(phy->regmap, CMU_R07C, val, val);
+
+ val = L1_MASTER_CDN_O;
+ regmap_update_bits(phy->regmap, COMLANE_R190, val, val);
+
+ return 0;
+}
+
+static int serdes_am654_reset(struct phy *x)
+{
+ struct serdes_am654 *phy = dev_get_priv(x->dev);
+ u32 val;
+
+ val = POR_EN;
+ regmap_update_bits(phy->regmap, SERDES_CTRL, val, val);
+ mdelay(1);
+ regmap_update_bits(phy->regmap, SERDES_CTRL, val, 0);
+
+ return 0;
+}
+
+static int serdes_am654_of_xlate(struct phy *x,
+ struct ofnode_phandle_args *args)
+{
+ struct serdes_am654 *phy = dev_get_priv(x->dev);
+
+ if (args->args_count != 2) {
+ dev_err(phy->dev, "Invalid DT PHY argument count: %d\n",
+ args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args[0] != PHY_TYPE_PCIE) {
+ dev_err(phy->dev, "Unrecognized PHY type: %d\n",
+ args->args[0]);
+ return -EINVAL;
+ }
+
+ x->id = args->args[0] | (args->args[1] << 16);
+
+ /* Setup mux mode using second argument */
+ regmap_update_bits(phy->serdes_ctl, 0, SERDES_CTL_LANE_FUNC_SEL_MASK,
+ args->args[1]);
+
+ return 0;
+}
+
+static int serdes_am654_bind(struct udevice *dev)
+{
+ int ret;
+
+ ret = device_bind_driver_to_node(dev->parent,
+ "ti-serdes-am654-mux-clk",
+ dev_read_name(dev), dev->node,
+ NULL);
+ if (ret) {
+ dev_err(dev, "%s: not able to bind clock driver\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int serdes_am654_probe(struct udevice *dev)
+{
+ struct serdes_am654 *phy = dev_get_priv(dev);
+ struct power_domain serdes_pwrdmn;
+ struct regmap *serdes_ctl;
+ struct regmap *map;
+ int ret;
+
+ ret = regmap_init_mem(dev_ofnode(dev), &map);
+ if (ret)
+ return ret;
+
+ phy->regmap = map;
+
+ serdes_ctl = syscon_regmap_lookup_by_phandle(dev, "ti,serdes-clk");
+ if (IS_ERR(serdes_ctl)) {
+ dev_err(dev, "unable to find syscon device\n");
+ return PTR_ERR(serdes_ctl);
+ }
+
+ phy->serdes_ctl = serdes_ctl;
+
+ ret = power_domain_get_by_index(dev, &serdes_pwrdmn, 0);
+ if (ret) {
+ dev_err(dev, "failed to get power domain\n");
+ return ret;
+ }
+
+ ret = power_domain_on(&serdes_pwrdmn);
+ if (ret) {
+ dev_err(dev, "Power domain on failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id serdes_am654_phy_ids[] = {
+ {
+ .compatible = "ti,phy-am654-serdes",
+ },
+};
+
+static const struct phy_ops serdes_am654_phy_ops = {
+ .reset = serdes_am654_reset,
+ .init = serdes_am654_init,
+ .power_on = serdes_am654_power_on,
+ .power_off = serdes_am654_power_off,
+ .of_xlate = serdes_am654_of_xlate,
+};
+
+U_BOOT_DRIVER(am654_serdes_phy) = {
+ .name = "am654_serdes_phy",
+ .id = UCLASS_PHY,
+ .of_match = serdes_am654_phy_ids,
+ .bind = serdes_am654_bind,
+ .ops = &serdes_am654_phy_ops,
+ .probe = serdes_am654_probe,
+ .priv_auto_alloc_size = sizeof(struct serdes_am654),
+};
diff --git a/drivers/power/domain/ti-sci-power-domain.c b/drivers/power/domain/ti-sci-power-domain.c
index b9cd37b612..4c4351d2d9 100644
--- a/drivers/power/domain/ti-sci-power-domain.c
+++ b/drivers/power/domain/ti-sci-power-domain.c
@@ -68,8 +68,8 @@ static int ti_sci_power_domain_on(struct power_domain *pd)
ret = dops->get_device(sci, pd->id);
if (ret)
- dev_err(power_domain->dev, "%s: get_device failed (%d)\n",
- __func__, ret);
+ dev_err(pd->dev, "%s: get_device(%lu) failed (%d)\n",
+ __func__, pd->id, ret);
return ret;
}
@@ -85,8 +85,8 @@ static int ti_sci_power_domain_off(struct power_domain *pd)
ret = dops->put_device(sci, pd->id);
if (ret)
- dev_err(power_domain->dev, "%s: put_device failed (%d)\n",
- __func__, ret);
+ dev_err(pd->dev, "%s: put_device(%lu) failed (%d)\n",
+ __func__, pd->id, ret);
return ret;
}
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 1e5d14c4ff..a66a9bcbe2 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -28,7 +28,6 @@ config OMAP_WATCHDOG
bool "TI OMAP watchdog driver"
depends on ARCH_OMAP2PLUS
select HW_WATCHDOG
- default y if AM33XX
help
Say Y here to enable the OMAP3+ watchdog driver.
@@ -113,6 +112,14 @@ config WDT_MTK
The watchdog timer is stopped when initialized.
It performs full SoC reset.
+config WDT_OMAP3
+ bool "TI OMAP watchdog timer support"
+ depends on WDT && ARCH_OMAP2PLUS
+ default y if AM33XX
+ help
+ This enables OMAP3+ watchdog timer driver, which can be
+ found on some TI chipsets and inline with driver model.
+
config WDT_ORION
bool "Orion watchdog timer support"
depends on WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 414ba2430a..955caef815 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o
obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o
obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o
obj-$(CONFIG_WDT_MTK) += mtk_wdt.o
+obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o
obj-$(CONFIG_WDT_SP805) += sp805_wdt.o
obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 343adb00c7..d5857be867 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -42,10 +42,14 @@
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/arch/cpu.h>
+#include <wdt.h>
+#include <dm.h>
+#include <errno.h>
/* Hardware timeout in seconds */
#define WDT_HW_TIMEOUT 60
+#if !CONFIG_IS_ENABLED(WDT)
static unsigned int wdt_trgr_pattern = 0x1234;
void hw_watchdog_reset(void)
@@ -134,3 +138,120 @@ void hw_watchdog_init(void)
while ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WSPR)
;
}
+
+void watchdog_reset(void)
+{
+ hw_watchdog_reset();
+}
+
+#else
+
+static int omap3_wdt_reset(struct udevice *dev)
+{
+ struct omap3_wdt_priv *priv = dev_get_priv(dev);
+
+ priv->wdt_trgr_pattern = 0x1234;
+/*
+ * Somebody just triggered watchdog reset and write to WTGR register
+ * is in progress. It is resetting right now, no need to trigger it
+ * again
+ */
+ if ((readl(&priv->regs->wdtwwps)) & WDT_WWPS_PEND_WTGR)
+ return 0;
+
+ priv->wdt_trgr_pattern = ~(priv->wdt_trgr_pattern);
+ writel(priv->wdt_trgr_pattern, &priv->regs->wdtwtgr);
+/*
+ * Don't wait for posted write to complete, i.e. don't check
+ * WDT_WWPS_PEND_WTGR bit in WWPS register. There is no writes to
+ * WTGR register outside of this func, and if entering it
+ * we see WDT_WWPS_PEND_WTGR bit set, it means watchdog reset
+ * was just triggered. This prevents us from wasting time in busy
+ * polling of WDT_WWPS_PEND_WTGR bit.
+ */
+ return 0;
+}
+
+static int omap3_wdt_stop(struct udevice *dev)
+{
+ struct omap3_wdt_priv *priv = dev_get_priv(dev);
+
+/* disable watchdog */
+ writel(0xAAAA, &priv->regs->wdtwspr);
+ while (readl(&priv->regs->wdtwwps) != 0x0)
+ ;
+ writel(0x5555, &priv->regs->wdtwspr);
+ while (readl(&priv->regs->wdtwwps) != 0x0)
+ ;
+ return 0;
+}
+
+static int omap3_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
+{
+ struct omap3_wdt_priv *priv = dev_get_priv(dev);
+ u32 pre_margin = GET_WLDR_VAL(timeout_ms);
+/*
+ * Make sure the watchdog is disabled. This is unfortunately required
+ * because writing to various registers with the watchdog running has no
+ * effect.
+ */
+ omap3_wdt_stop(dev);
+
+/* initialize prescaler */
+ while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WCLR)
+ ;
+
+ writel(WDT_WCLR_PRE | (PTV << WDT_WCLR_PTV_OFF), &priv->regs->wdtwclr);
+ while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WCLR)
+ ;
+/* just count up at 32 KHz */
+ while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WLDR)
+ ;
+
+ writel(pre_margin, &priv->regs->wdtwldr);
+ while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WLDR)
+ ;
+/* Sequence to enable the watchdog */
+ writel(0xBBBB, &priv->regs->wdtwspr);
+ while ((readl(&priv->regs->wdtwwps)) & WDT_WWPS_PEND_WSPR)
+ ;
+
+ writel(0x4444, &priv->regs->wdtwspr);
+ while ((readl(&priv->regs->wdtwwps)) & WDT_WWPS_PEND_WSPR)
+ ;
+
+ return 0;
+}
+
+static int omap3_wdt_probe(struct udevice *dev)
+{
+ struct omap3_wdt_priv *priv = dev_get_priv(dev);
+
+ priv->regs = (struct wd_timer *)devfdt_get_addr(dev);
+ if (!priv->regs)
+ return -EINVAL;
+
+ debug("%s: Probing wdt%u\n", __func__, dev->seq);
+ return 0;
+}
+
+static const struct wdt_ops omap3_wdt_ops = {
+ .start = omap3_wdt_start,
+ .stop = omap3_wdt_stop,
+ .reset = omap3_wdt_reset,
+};
+
+static const struct udevice_id omap3_wdt_ids[] = {
+ { .compatible = "ti,omap3-wdt" },
+ { }
+};
+
+U_BOOT_DRIVER(omap3_wdt) = {
+ .name = "omap3_wdt",
+ .id = UCLASS_WDT,
+ .of_match = omap3_wdt_ids,
+ .ops = &omap3_wdt_ops,
+ .probe = omap3_wdt_probe,
+ .priv_auto_alloc_size = sizeof(struct omap3_wdt_priv),
+};
+#endif /* !CONFIG_IS_ENABLED(WDT) */