summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2022-03-16 19:52:02 +0300
committerTom Rini <trini@konsulko.com>2022-03-16 19:52:02 +0300
commit297e6eb8dcf9d90aaf9b0d146cdd502403003d04 (patch)
treea08774cdaa4a72af892d4c7a57b3e1307734ad89 /drivers
parentc24b4e4fb8810b496e5f303ffd2641293f4c4b50 (diff)
parent0ac03fbab51c72fa978569a831c001c4ddad8e2a (diff)
downloadu-boot-297e6eb8dcf9d90aaf9b0d146cdd502403003d04.tar.xz
Merge tag 'xilinx-for-v2022.07-rc1' of https://source.denx.de/u-boot/custodians/u-boot-microblaze into next
Xilinx changes for v2022.07-rc1 microblaze: - Add support for reserved memory xilinx: - Update FRU code with MAC reading zynqmp: - Remove double AMS setting - DT updates (mostly for SOMs) - Add support for zcu106 rev 1.0 zynq: - Update nand binding nand: - Aligned zynq_nand to upstream DT binding net: - Add support for ethernet-phy-id mmc: - Workaround CD in zynq_sdhci driver also for ZynqMP - Add support for dynamic/run-time SD config for SOMs gpio: - Add driver for slg7xl45106 firmware: - Add support for dynamic SD config power-domain: - Update zynqmp driver with the latest firmware video: - Add skeleton driver for DP and DPDMA i2c: - Fix i2c to work with QEMU pinctrl: - Add driver for zynqmp pinctrl driver
Diffstat (limited to 'drivers')
-rw-r--r--drivers/core/ofnode.c36
-rw-r--r--drivers/dma/Kconfig7
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/xilinx_dpdma.c43
-rw-r--r--drivers/firmware/firmware-zynqmp.c51
-rw-r--r--drivers/gpio/Kconfig8
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio_slg7xl45106.c115
-rw-r--r--drivers/i2c/i2c-cdns.c18
-rw-r--r--drivers/mmc/zynq_sdhci.c110
-rw-r--r--drivers/mtd/nand/raw/zynq_nand.c9
-rw-r--r--drivers/net/phy/Kconfig8
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/ethernet_id.c69
-rw-r--r--drivers/net/phy/phy.c11
-rw-r--r--drivers/pinctrl/Kconfig10
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl-zynqmp.c644
-rw-r--r--drivers/power/domain/zynqmp-power-domain.c29
-rw-r--r--drivers/video/Kconfig8
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/zynqmp_dpsub.c66
22 files changed, 1206 insertions, 41 deletions
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 709bea272a..8042847f3c 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -898,6 +898,42 @@ int ofnode_read_pci_vendev(ofnode node, u16 *vendor, u16 *device)
return -ENOENT;
}
+int ofnode_read_eth_phy_id(ofnode node, u16 *vendor, u16 *device)
+{
+ const char *list, *end;
+ int len;
+
+ list = ofnode_get_property(node, "compatible", &len);
+
+ if (!list)
+ return -ENOENT;
+
+ end = list + len;
+ while (list < end) {
+ len = strlen(list);
+
+ if (len >= strlen("ethernet-phy-idVVVV,DDDD")) {
+ char *s = strstr(list, "ethernet-phy-id");
+
+ /*
+ * check if the string is something like
+ * ethernet-phy-idVVVV,DDDD
+ */
+ if (s && s[19] == '.') {
+ s += strlen("ethernet-phy-id");
+ *vendor = simple_strtol(s, NULL, 16);
+ s += 5;
+ *device = simple_strtol(s, NULL, 16);
+
+ return 0;
+ }
+ }
+ list += (len + 1);
+ }
+
+ return -ENOENT;
+}
+
int ofnode_read_addr_cells(ofnode node)
{
if (ofnode_is_np(node)) {
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 9cacea88d0..0af5460421 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -68,6 +68,13 @@ config APBH_DMA
help
Enable APBH DMA driver.
+config XILINX_DPDMA
+ bool "Enable ZynqMP Display Port DMA driver"
+ depends on DMA && ZYNQMP_POWER_DOMAIN
+ help
+ Enable support for Xilinx ZynqMP Display DMA driver. Currently
+ this file is used as placeholder for driver. The main reason is
+ to record compatible string and calling power domain driver.
if APBH_DMA
config APBH_DMA_BURST
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index afab324461..a75572fe5d 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -13,5 +13,6 @@ obj-$(CONFIG_SANDBOX_DMA) += sandbox-dma-test.o
obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o
obj-$(CONFIG_TI_EDMA3) += ti-edma3.o
obj-$(CONFIG_DMA_LPC32XX) += lpc32xx_dma.o
+obj-$(CONFIG_XILINX_DPDMA) += xilinx_dpdma.o
obj-y += ti/
diff --git a/drivers/dma/xilinx_dpdma.c b/drivers/dma/xilinx_dpdma.c
new file mode 100644
index 0000000000..d4ee21dfc0
--- /dev/null
+++ b/drivers/dma/xilinx_dpdma.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Xilinx Inc.
+ */
+
+#include <common.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <dma.h>
+#include <dma-uclass.h>
+#include <errno.h>
+#include <dm/device_compat.h>
+
+/**
+ * struct zynqmp_dpdma_priv - Private structure
+ * @dev: Device uclass for video_ops
+ */
+struct zynqmp_dpdma_priv {
+ struct udevice *dev;
+};
+
+static int zynqmp_dpdma_probe(struct udevice *dev)
+{
+ /* Only placeholder for power domain driver */
+ return 0;
+}
+
+static const struct dma_ops zynqmp_dpdma_ops = {
+};
+
+static const struct udevice_id zynqmp_dpdma_ids[] = {
+ { .compatible = "xlnx,zynqmp-dpdma" },
+ { }
+};
+
+U_BOOT_DRIVER(zynqmp_dpdma) = {
+ .name = "zynqmp_dpdma",
+ .id = UCLASS_DMA,
+ .of_match = zynqmp_dpdma_ids,
+ .ops = &zynqmp_dpdma_ops,
+ .probe = zynqmp_dpdma_probe,
+ .priv_auto = sizeof(struct zynqmp_dpdma_priv),
+};
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
index 8d8492d99f..8916c55896 100644
--- a/drivers/firmware/firmware-zynqmp.c
+++ b/drivers/firmware/firmware-zynqmp.c
@@ -140,6 +140,57 @@ unsigned int zynqmp_firmware_version(void)
return pm_api_version;
};
+int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value)
+{
+ int ret;
+
+ ret = xilinx_pm_request(PM_IOCTL, node, IOCTL_SET_SD_CONFIG,
+ config, value, NULL);
+ if (ret)
+ printf("%s: node %d: set_sd_config %d failed\n",
+ __func__, node, config);
+
+ return ret;
+}
+
+int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id)
+{
+ int ret;
+ u32 *bit_mask;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ /* Input arguments validation */
+ if (id >= 64 || (api_id != PM_IOCTL && api_id != PM_QUERY_DATA))
+ return -EINVAL;
+
+ /* Check feature check API version */
+ ret = xilinx_pm_request(PM_FEATURE_CHECK, PM_FEATURE_CHECK, 0, 0, 0,
+ ret_payload);
+ if (ret)
+ return ret;
+
+ /* Check if feature check version 2 is supported or not */
+ if ((ret_payload[1] & FIRMWARE_VERSION_MASK) == PM_API_VERSION_2) {
+ /*
+ * Call feature check for IOCTL/QUERY API to get IOCTL ID or
+ * QUERY ID feature status.
+ */
+
+ ret = xilinx_pm_request(PM_FEATURE_CHECK, api_id, 0, 0, 0,
+ ret_payload);
+ if (ret)
+ return ret;
+
+ bit_mask = &ret_payload[2];
+ if ((bit_mask[(id / 32)] & BIT((id % 32))) == 0)
+ return -EOPNOTSUPP;
+ } else {
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
/**
* Send a configuration object to the PMU firmware.
*
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 12972cdb4e..c3f110933a 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -558,4 +558,12 @@ config SL28CPLD_GPIO
help
Support GPIO access on Kontron sl28cpld board management controllers.
+config SLG7XL45106_I2C_GPO
+ bool "slg7xl45106 i2c gpo expander"
+ depends on DM_GPIO
+ help
+ Support for slg7xl45106 i2c gpo expander. It is an i2c based
+ 8-bit gpo expander, all gpo lines are controlled by writing
+ value into data register.
+
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 44f015397f..b39dde12ab 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -73,3 +73,4 @@ obj-$(CONFIG_NOMADIK_GPIO) += nmk_gpio.o
obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o
obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o
obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o
+obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o
diff --git a/drivers/gpio/gpio_slg7xl45106.c b/drivers/gpio/gpio_slg7xl45106.c
new file mode 100644
index 0000000000..2cbf7488ad
--- /dev/null
+++ b/drivers/gpio/gpio_slg7xl45106.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * slg7xl45106_i2c_gpo driver
+ *
+ * Copyright (C) 2021 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/arch/hardware.h>
+
+#define SLG7XL45106_REG 0xdb
+
+static int slg7xl45106_i2c_gpo_direction_input(struct udevice *dev,
+ unsigned int offset)
+{
+ return 0;
+}
+
+static int slg7xl45106_i2c_gpo_xlate(struct udevice *dev,
+ struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ desc->offset = (unsigned int)args->args[0];
+
+ return 0;
+}
+
+static int slg7xl45106_i2c_gpo_set_value(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ int ret;
+ u8 val;
+
+ ret = dm_i2c_read(dev, SLG7XL45106_REG, &val, 1);
+ if (ret)
+ return ret;
+
+ if (value)
+ val |= BIT(offset);
+ else
+ val &= ~BIT(offset);
+
+ return dm_i2c_write(dev, SLG7XL45106_REG, &val, 1);
+}
+
+static int slg7xl45106_i2c_gpo_direction_output(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ return slg7xl45106_i2c_gpo_set_value(dev, offset, value);
+}
+
+static int slg7xl45106_i2c_gpo_get_value(struct udevice *dev,
+ unsigned int offset)
+{
+ int ret;
+ u8 val;
+
+ ret = dm_i2c_read(dev, SLG7XL45106_REG, &val, 1);
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(offset));
+}
+
+static int slg7xl45106_i2c_gpo_get_function(struct udevice *dev,
+ unsigned int offset)
+{
+ return GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops slg7xl45106_i2c_gpo_ops = {
+ .direction_input = slg7xl45106_i2c_gpo_direction_input,
+ .direction_output = slg7xl45106_i2c_gpo_direction_output,
+ .get_value = slg7xl45106_i2c_gpo_get_value,
+ .set_value = slg7xl45106_i2c_gpo_set_value,
+ .get_function = slg7xl45106_i2c_gpo_get_function,
+ .xlate = slg7xl45106_i2c_gpo_xlate,
+};
+
+static int slg7xl45106_i2c_gpo_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const void *label_ptr;
+
+ label_ptr = dev_read_prop(dev, "label", NULL);
+ if (label_ptr) {
+ uc_priv->bank_name = strdup(label_ptr);
+ if (!uc_priv->bank_name)
+ return -ENOMEM;
+ } else {
+ uc_priv->bank_name = dev->name;
+ }
+
+ uc_priv->gpio_count = 8;
+
+ return 0;
+}
+
+static const struct udevice_id slg7xl45106_i2c_gpo_ids[] = {
+ { .compatible = "dlg,slg7xl45106",},
+ { }
+};
+
+U_BOOT_DRIVER(slg7xl45106_i2c_gpo) = {
+ .name = "slg7xl45106_i2c_gpo",
+ .id = UCLASS_GPIO,
+ .ops = &slg7xl45106_i2c_gpo_ops,
+ .of_match = slg7xl45106_i2c_gpo_ids,
+ .probe = slg7xl45106_i2c_gpo_probe,
+};
diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
index a650dd69b8..0da9f6f35a 100644
--- a/drivers/i2c/i2c-cdns.c
+++ b/drivers/i2c/i2c-cdns.c
@@ -251,24 +251,32 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
u8 *cur_data = data;
struct cdns_i2c_regs *regs = i2c_bus->regs;
u32 ret;
+ bool start = 1;
/* Set the controller in Master transmit mode and clear FIFO */
setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO);
clrbits_le32(&regs->control, CDNS_I2C_CONTROL_RW);
- /* Check message size against FIFO depth, and set hold bus bit
- * if it is greater than FIFO depth
+ /*
+ * For sequential data load hold the bus.
*/
- if (len > CDNS_I2C_FIFO_DEPTH)
+ if (len > 1)
setbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
/* Clear the interrupts in status register */
writel(CDNS_I2C_INTERRUPTS_MASK, &regs->interrupt_status);
- writel(addr, &regs->address);
+ /* In case of Probe (i.e no data), start the transfer */
+ if (!len)
+ writel(addr, &regs->address);
while (len-- && !is_arbitration_lost(regs)) {
writel(*(cur_data++), &regs->data);
+ /* Trigger write only after loading data */
+ if (start) {
+ writel(addr, &regs->address);
+ start = 0;
+ }
if (len && readl(&regs->transfer_size) == CDNS_I2C_FIFO_DEPTH) {
ret = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
CDNS_I2C_INTERRUPT_ARBLOST);
@@ -375,7 +383,6 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
curr_recv_count = recv_count;
}
} else if (recv_count && !hold_quirk && !curr_recv_count) {
- writel(addr, &regs->address);
if (recv_count > CDNS_I2C_TRANSFER_SIZE) {
writel(CDNS_I2C_TRANSFER_SIZE,
&regs->transfer_size);
@@ -384,6 +391,7 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
writel(recv_count, &regs->transfer_size);
curr_recv_count = recv_count;
}
+ writel(addr, &regs->address);
}
}
diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c
index 5cea4c695e..d96f5d543f 100644
--- a/drivers/mmc/zynq_sdhci.c
+++ b/drivers/mmc/zynq_sdhci.c
@@ -12,9 +12,12 @@
#include <linux/delay.h>
#include "mmc_private.h"
#include <log.h>
+#include <reset.h>
#include <dm/device_compat.h>
#include <linux/err.h>
#include <linux/libfdt.h>
+#include <asm/types.h>
+#include <linux/math64.h>
#include <asm/cache.h>
#include <malloc.h>
#include <sdhci.h>
@@ -61,6 +64,7 @@ struct arasan_sdhci_priv {
u8 deviceid;
u8 bank;
u8 no_1p8;
+ struct reset_ctl_bulk resets;
};
/* For Versal platforms zynqmp_mmio_write() won't be available */
@@ -243,7 +247,7 @@ static int arasan_sdhci_execute_tuning(struct mmc *mmc, u8 opcode)
char tuning_loop_counter = SDHCI_TUNING_LOOP_COUNT;
u8 node_id = priv->deviceid ? NODE_SD_1 : NODE_SD_0;
- debug("%s\n", __func__);
+ dev_dbg(mmc->dev, "%s\n", __func__);
host = priv->host;
@@ -703,6 +707,87 @@ static const struct sdhci_ops arasan_ops = {
};
#endif
+#if defined(CONFIG_ARCH_ZYNQMP)
+static int sdhci_zynqmp_set_dynamic_config(struct arasan_sdhci_priv *priv,
+ struct udevice *dev)
+{
+ int ret;
+ u32 node_id = priv->deviceid ? NODE_SD_1 : NODE_SD_0;
+ struct clk clk;
+ unsigned long clock, mhz;
+
+ ret = xilinx_pm_request(PM_REQUEST_NODE, node_id, ZYNQMP_PM_CAPABILITY_ACCESS,
+ ZYNQMP_PM_MAX_QOS, ZYNQMP_PM_REQUEST_ACK_NO, NULL);
+ if (ret) {
+ dev_err(dev, "Request node failed for %d\n", node_id);
+ return ret;
+ }
+
+ ret = reset_get_bulk(dev, &priv->resets);
+ if (ret == -ENOTSUPP || ret == -ENOENT) {
+ dev_err(dev, "Reset not found\n");
+ return 0;
+ } else if (ret) {
+ dev_err(dev, "Reset failed\n");
+ return ret;
+ }
+
+ ret = reset_assert_bulk(&priv->resets);
+ if (ret) {
+ dev_err(dev, "Reset assert failed\n");
+ return ret;
+ }
+
+ ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_FIXED, 0);
+ if (ret) {
+ dev_err(dev, "SD_CONFIG_FIXED failed\n");
+ return ret;
+ }
+
+ ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_EMMC_SEL,
+ dev_read_bool(dev, "non-removable"));
+ if (ret) {
+ dev_err(dev, "SD_CONFIG_EMMC_SEL failed\n");
+ return ret;
+ }
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to get clock\n");
+ return ret;
+ }
+
+ clock = clk_get_rate(&clk);
+ if (IS_ERR_VALUE(clock)) {
+ dev_err(dev, "failed to get rate\n");
+ return clock;
+ }
+
+ mhz = DIV64_U64_ROUND_UP(clock, 1000000);
+
+ ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_BASECLK, mhz);
+ if (ret) {
+ dev_err(dev, "SD_CONFIG_BASECLK failed\n");
+ return ret;
+ }
+
+ ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_8BIT,
+ (dev_read_u32_default(dev, "bus-width", 1) == 8));
+ if (ret) {
+ dev_err(dev, "SD_CONFIG_8BIT failed\n");
+ return ret;
+ }
+
+ ret = reset_deassert_bulk(&priv->resets);
+ if (ret) {
+ dev_err(dev, "Reset release failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
static int arasan_sdhci_probe(struct udevice *dev)
{
struct arasan_sdhci_plat *plat = dev_get_plat(dev);
@@ -715,6 +800,18 @@ static int arasan_sdhci_probe(struct udevice *dev)
host = priv->host;
+#if defined(CONFIG_ARCH_ZYNQMP)
+ if (device_is_compatible(dev, "xlnx,zynqmp-8.9a")) {
+ ret = zynqmp_pm_is_function_supported(PM_IOCTL,
+ IOCTL_SET_SD_CONFIG);
+ if (!ret) {
+ ret = sdhci_zynqmp_set_dynamic_config(priv, dev);
+ if (ret)
+ return ret;
+ }
+ }
+#endif
+
ret = clk_get_by_index(dev, 0, &clk);
if (ret < 0) {
dev_err(dev, "failed to get clock\n");
@@ -727,7 +824,7 @@ static int arasan_sdhci_probe(struct udevice *dev)
return clock;
}
- debug("%s: CLK %ld\n", __func__, clock);
+ dev_dbg(dev, "%s: CLK %ld\n", __func__, clock);
ret = clk_enable(&clk);
if (ret) {
@@ -769,12 +866,13 @@ static int arasan_sdhci_probe(struct udevice *dev)
* causing sd card timeout error. Workaround this by adding a wait for
* 1000msec till the card detect state gets stable.
*/
- if (IS_ENABLED(CONFIG_ARCH_VERSAL)) {
- u32 timeout = 1000;
+ if (IS_ENABLED(CONFIG_ARCH_ZYNQMP) || IS_ENABLED(CONFIG_ARCH_VERSAL)) {
+ u32 timeout = 1000000;
while (((sdhci_readl(host, SDHCI_PRESENT_STATE) &
- SDHCI_CARD_STATE_STABLE) == 0) && timeout--) {
- mdelay(1);
+ SDHCI_CARD_STATE_STABLE) == 0) && timeout) {
+ udelay(1);
+ timeout--;
}
if (!timeout) {
dev_err(dev, "Sdhci card detect state not stable\n");
diff --git a/drivers/mtd/nand/raw/zynq_nand.c b/drivers/mtd/nand/raw/zynq_nand.c
index d792528370..10e9cd18b0 100644
--- a/drivers/mtd/nand/raw/zynq_nand.c
+++ b/drivers/mtd/nand/raw/zynq_nand.c
@@ -1086,10 +1086,13 @@ static int zynq_nand_probe(struct udevice *dev)
int is_16bit_bw;
smc->reg = (struct zynq_nand_smc_regs *)dev_read_addr(dev);
- of_nand = dev_read_subnode(dev, "flash@e1000000");
+ of_nand = dev_read_subnode(dev, "nand-controller@0,0");
if (!ofnode_valid(of_nand)) {
- printf("Failed to find nand node in dt\n");
- return -ENODEV;
+ of_nand = dev_read_subnode(dev, "flash@e1000000");
+ if (!ofnode_valid(of_nand)) {
+ printf("Failed to find nand node in dt\n");
+ return -ENODEV;
+ }
}
if (!ofnode_is_available(of_nand)) {
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 4f8d33ce8f..74339a25ca 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -307,6 +307,14 @@ config PHY_XILINX_GMII2RGMII
as bridge between MAC connected over GMII and external phy that
is connected over RGMII interface.
+config PHY_ETHERNET_ID
+ bool "Read ethernet PHY id"
+ depends on DM_GPIO
+ default y if ZYNQ_GEM
+ help
+ Enable this config to read ethernet phy id from the phy node of DT
+ and create a phy device using id.
+
config PHY_FIXED
bool "Fixed-Link PHY"
depends on DM_ETH
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 77f7f60621..b28440bc4e 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_PHY_TI_DP83867) += dp83867.o
obj-$(CONFIG_PHY_TI_DP83869) += dp83869.o
obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o
obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
+obj-$(CONFIG_PHY_ETHERNET_ID) += ethernet_id.o
obj-$(CONFIG_PHY_VITESSE) += vitesse.o
obj-$(CONFIG_PHY_MSCC) += mscc.o
obj-$(CONFIG_PHY_FIXED) += fixed.o
diff --git a/drivers/net/phy/ethernet_id.c b/drivers/net/phy/ethernet_id.c
new file mode 100644
index 0000000000..5617ac3ad6
--- /dev/null
+++ b/drivers/net/phy/ethernet_id.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Xilinx ethernet phy reset driver
+ *
+ * Copyright (C) 2022 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <dm/device_compat.h>
+#include <phy.h>
+#include <linux/delay.h>
+#include <asm/gpio.h>
+
+struct phy_device *phy_connect_phy_id(struct mii_dev *bus, struct udevice *dev,
+ phy_interface_t interface)
+{
+ struct phy_device *phydev;
+ struct ofnode_phandle_args phandle_args;
+ struct gpio_desc gpio;
+ ofnode node;
+ u32 id, assert, deassert;
+ u16 vendor, device;
+ int ret;
+
+ if (dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0,
+ &phandle_args))
+ return NULL;
+
+ if (!ofnode_valid(phandle_args.node))
+ return NULL;
+
+ node = phandle_args.node;
+
+ ret = ofnode_read_eth_phy_id(node, &vendor, &device);
+ if (ret) {
+ dev_err(dev, "Failed to read eth PHY id, err: %d\n", ret);
+ return NULL;
+ }
+
+ ret = gpio_request_by_name_nodev(node, "reset-gpios", 0, &gpio,
+ GPIOD_ACTIVE_LOW);
+ if (!ret) {
+ assert = ofnode_read_u32_default(node, "reset-assert-us", 0);
+ deassert = ofnode_read_u32_default(node,
+ "reset-deassert-us", 0);
+ ret = dm_gpio_set_value(&gpio, 1);
+ if (ret) {
+ dev_err(dev, "Failed assert gpio, err: %d\n", ret);
+ return NULL;
+ }
+
+ udelay(assert);
+
+ ret = dm_gpio_set_value(&gpio, 0);
+ if (ret) {
+ dev_err(dev, "Failed deassert gpio, err: %d\n", ret);
+ return NULL;
+ }
+
+ udelay(deassert);
+ }
+
+ id = vendor << 16 | device;
+ phydev = phy_device_create(bus, 0, id, false, interface);
+ if (phydev)
+ phydev->node = node;
+
+ return phydev;
+}
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index c9fc20855b..fffa10f68b 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -659,9 +659,9 @@ static struct phy_driver *get_phy_driver(struct phy_device *phydev,
return generic_for_interface(interface);
}
-static struct phy_device *phy_device_create(struct mii_dev *bus, int addr,
- u32 phy_id, bool is_c45,
- phy_interface_t interface)
+struct phy_device *phy_device_create(struct mii_dev *bus, int addr,
+ u32 phy_id, bool is_c45,
+ phy_interface_t interface)
{
struct phy_device *dev;
@@ -1047,6 +1047,11 @@ struct phy_device *phy_connect(struct mii_dev *bus, int addr,
phydev = phy_device_create(bus, 0, PHY_NCSI_ID, false, interface);
#endif
+#ifdef CONFIG_PHY_ETHERNET_ID
+ if (!phydev)
+ phydev = phy_connect_phy_id(bus, dev, interface);
+#endif
+
#ifdef CONFIG_PHY_XILINX_GMII2RGMII
if (!phydev)
phydev = phy_connect_gmii2rgmii(bus, dev, interface);
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 03946245c7..d7477d7c33 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -318,6 +318,16 @@ config PINCTRL_K210
Support pin multiplexing on the K210. The "FPIOA" can remap any
supported function to any multifunctional IO pin. It can also perform
basic GPIO functions, such as reading the current value of a pin.
+
+config PINCTRL_ZYNQMP
+ bool "Xilinx ZynqMP pin control driver"
+ depends on DM && PINCTRL_GENERIC && ARCH_ZYNQMP
+ default y
+ help
+ Support pin multiplexing control on Xilinx ZynqMP. The driver uses
+ Generic Pinctrl framework and is compatible with the Linux driver,
+ i.e. it uses the same device tree configuration.
+
endif
source "drivers/pinctrl/broadcom/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index fd736a7f64..ddddd13433 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_PINCTRL_STI) += pinctrl-sti.o
obj-$(CONFIG_PINCTRL_STM32) += pinctrl_stm32.o
obj-$(CONFIG_$(SPL_)PINCTRL_STMFX) += pinctrl-stmfx.o
obj-y += broadcom/
+obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o
diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c
new file mode 100644
index 0000000000..7c5a02db1b
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-zynqmp.c
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx pinctrl driver for ZynqMP
+ *
+ * Author(s): Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
+ * Michal Simek <michal.simek@xilinx.com>
+ *
+ * Copyright (C) 2021 Xilinx, Inc. All rights reserved.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <zynqmp_firmware.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <dm/pinctrl.h>
+#include <linux/compat.h>
+#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
+
+#define PINCTRL_GET_FUNC_GROUPS_RESP_LEN 12
+#define PINCTRL_GET_PIN_GROUPS_RESP_LEN 12
+#define NUM_GROUPS_PER_RESP 6
+#define NA_GROUP -1
+#define RESERVED_GROUP -2
+#define MAX_GROUP_PIN 50
+#define MAX_PIN_GROUPS 50
+#define MAX_GROUP_NAME_LEN 32
+#define MAX_FUNC_NAME_LEN 16
+
+#define DRIVE_STRENGTH_2MA 2
+#define DRIVE_STRENGTH_4MA 4
+#define DRIVE_STRENGTH_8MA 8
+#define DRIVE_STRENGTH_12MA 12
+
+/*
+ * This driver works with very simple configuration that has the same name
+ * for group and function. This way it is compatible with the Linux Kernel
+ * driver.
+ */
+struct zynqmp_pinctrl_priv {
+ u32 npins;
+ u32 nfuncs;
+ u32 ngroups;
+ struct zynqmp_pmux_function *funcs;
+ struct zynqmp_pctrl_group *groups;
+};
+
+/**
+ * struct zynqmp_pinctrl_config - pinconfig parameters
+ * @slew: Slew rate slow or fast
+ * @bias: Bias enabled or disabled
+ * @pull_ctrl: Pull control pull up or pull down
+ * @input_type: CMOS or Schmitt
+ * @drive_strength: Drive strength 2mA/4mA/8mA/12mA
+ * @volt_sts: Voltage status 1.8V or 3.3V
+ * @tri_state: Tristate enabled or disabled
+ *
+ * This structure holds information about pin control config
+ * option that can be set for each pin.
+ */
+struct zynqmp_pinctrl_config {
+ u32 slew;
+ u32 bias;
+ u32 pull_ctrl;
+ u32 input_type;
+ u32 drive_strength;
+ u32 volt_sts;
+ u32 tri_state;
+};
+
+/**
+ * enum zynqmp_pin_config_param - possible pin configuration parameters
+ * @PIN_CONFIG_IOSTANDARD: if the pin can select an IO standard,
+ * the argument to this parameter (on a
+ * custom format) tells the driver which
+ * alternative IO standard to use
+ * @PIN_CONFIG_SCHMITTCMOS: this parameter (on a custom format) allows
+ * to select schmitt or cmos input for MIO pins
+ */
+enum zynqmp_pin_config_param {
+ PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1,
+ PIN_CONFIG_SCHMITTCMOS,
+};
+
+/**
+ * struct zynqmp_pmux_function - a pinmux function
+ * @name: Name of the pinmux function
+ * @groups: List of pingroups for this function
+ * @ngroups: Number of entries in @groups
+ *
+ * This structure holds information about pin control function
+ * and function group names supporting that function.
+ */
+struct zynqmp_pmux_function {
+ char name[MAX_FUNC_NAME_LEN];
+ const char * const *groups;
+ unsigned int ngroups;
+};
+
+/**
+ * struct zynqmp_pctrl_group - Pin control group info
+ * @name: Group name
+ * @pins: Group pin numbers
+ * @npins: Number of pins in group
+ */
+struct zynqmp_pctrl_group {
+ const char *name;
+ unsigned int pins[MAX_GROUP_PIN];
+ unsigned int npins;
+};
+
+static char pin_name[PINNAME_SIZE];
+
+/**
+ * zynqmp_pm_query_data() - Get query data from firmware
+ * @qid: Value of enum pm_query_id
+ * @arg1: Argument 1
+ * @arg2: Argument 2
+ * @out: Returned output value
+ *
+ * Return: Returns status, either success or error+reason
+ */
+static int zynqmp_pm_query_data(enum pm_query_id qid, u32 arg1, u32 arg2, u32 *out)
+{
+ int ret;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ ret = xilinx_pm_request(PM_QUERY_DATA, qid, arg1, arg2, 0, ret_payload);
+ if (ret)
+ return ret;
+
+ *out = ret_payload[1];
+
+ return ret;
+}
+
+static int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param, u32 *value)
+{
+ int ret;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ /* Get config for the pin */
+ ret = xilinx_pm_request(PM_PINCTRL_CONFIG_PARAM_GET, pin, param, 0, 0, ret_payload);
+ if (ret) {
+ printf("%s failed\n", __func__);
+ return ret;
+ }
+
+ *value = ret_payload[1];
+
+ return ret;
+}
+
+static int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param, u32 value)
+{
+ int ret;
+
+ /* Request the pin first */
+ ret = xilinx_pm_request(PM_PINCTRL_REQUEST, pin, 0, 0, 0, NULL);
+ if (ret) {
+ printf("%s: pin request failed\n", __func__);
+ return ret;
+ }
+
+ /* Set config for the pin */
+ ret = xilinx_pm_request(PM_PINCTRL_CONFIG_PARAM_SET, pin, param, value, 0, NULL);
+ if (ret) {
+ printf("%s failed\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int zynqmp_pinctrl_get_function_groups(u32 fid, u32 index, u16 *groups)
+{
+ int ret;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ ret = xilinx_pm_request(PM_QUERY_DATA, PM_QID_PINCTRL_GET_FUNCTION_GROUPS,
+ fid, index, 0, ret_payload);
+ if (ret) {
+ printf("%s failed\n", __func__);
+ return ret;
+ }
+
+ memcpy(groups, &ret_payload[1], PINCTRL_GET_FUNC_GROUPS_RESP_LEN);
+
+ return ret;
+}
+
+static int zynqmp_pinctrl_prepare_func_groups(u32 fid,
+ struct zynqmp_pmux_function *func,
+ struct zynqmp_pctrl_group *groups)
+{
+ const char **fgroups;
+ char name[MAX_GROUP_NAME_LEN];
+ u16 resp[NUM_GROUPS_PER_RESP] = {0};
+ int ret, index, i;
+
+ fgroups = kcalloc(func->ngroups, sizeof(*fgroups), GFP_KERNEL);
+ if (!fgroups)
+ return -ENOMEM;
+
+ for (index = 0; index < func->ngroups; index += NUM_GROUPS_PER_RESP) {
+ ret = zynqmp_pinctrl_get_function_groups(fid, index, resp);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < NUM_GROUPS_PER_RESP; i++) {
+ if (resp[i] == (u16)NA_GROUP)
+ goto done;
+ if (resp[i] == (u16)RESERVED_GROUP)
+ continue;
+
+ snprintf(name, MAX_GROUP_NAME_LEN, "%s_%d_grp",
+ func->name, index + i);
+ fgroups[index + i] = strdup(name);
+
+ snprintf(name, MAX_GROUP_NAME_LEN, "%s_%d_grp",
+ func->name, index + i);
+ groups[resp[i]].name = strdup(name);
+ }
+ }
+done:
+ func->groups = fgroups;
+
+ return ret;
+}
+
+static int zynqmp_pinctrl_get_pin_groups(u32 pin, u32 index, u16 *groups)
+{
+ int ret;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ ret = xilinx_pm_request(PM_QUERY_DATA, PM_QID_PINCTRL_GET_PIN_GROUPS,
+ pin, index, 0, ret_payload);
+ if (ret) {
+ printf("%s failed to get pin groups\n", __func__);
+ return ret;
+ }
+
+ memcpy(groups, &ret_payload[1], PINCTRL_GET_PIN_GROUPS_RESP_LEN);
+
+ return ret;
+}
+
+static void zynqmp_pinctrl_group_add_pin(struct zynqmp_pctrl_group *group,
+ unsigned int pin)
+{
+ group->pins[group->npins++] = pin;
+}
+
+static int zynqmp_pinctrl_create_pin_groups(struct zynqmp_pctrl_group *groups,
+ unsigned int pin)
+{
+ u16 resp[NUM_GROUPS_PER_RESP] = {0};
+ int ret, i, index = 0;
+
+ do {
+ ret = zynqmp_pinctrl_get_pin_groups(pin, index, resp);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < NUM_GROUPS_PER_RESP; i++) {
+ if (resp[i] == (u16)NA_GROUP)
+ goto done;
+ if (resp[i] == (u16)RESERVED_GROUP)
+ continue;
+ zynqmp_pinctrl_group_add_pin(&groups[resp[i]], pin);
+ }
+ index += NUM_GROUPS_PER_RESP;
+ } while (index <= MAX_PIN_GROUPS);
+
+done:
+ return ret;
+}
+
+static int zynqmp_pinctrl_probe(struct udevice *dev)
+{
+ struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+ int ret, i;
+ u32 pin;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ /* Get number of pins first */
+ ret = zynqmp_pm_query_data(PM_QID_PINCTRL_GET_NUM_PINS, 0, 0, &priv->npins);
+ if (ret) {
+ printf("%s failed to get no of pins\n", __func__);
+ return ret;
+ }
+
+ /* Get number of functions available */
+ ret = zynqmp_pm_query_data(PM_QID_PINCTRL_GET_NUM_FUNCTIONS, 0, 0, &priv->nfuncs);
+ if (ret) {
+ printf("%s failed to get no of functions\n", __func__);
+ return ret;
+ }
+
+ /* Allocating structures for functions and its groups */
+ priv->funcs = kzalloc(sizeof(*priv->funcs) * priv->nfuncs, GFP_KERNEL);
+ if (!priv->funcs)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->nfuncs; i++) {
+ /* Get function name for the function and fill */
+ xilinx_pm_request(PM_QUERY_DATA, PM_QID_PINCTRL_GET_FUNCTION_NAME,
+ i, 0, 0, ret_payload);
+
+ memcpy((void *)priv->funcs[i].name, ret_payload, MAX_FUNC_NAME_LEN);
+
+ /* And fill number of groups available for certain function */
+ xilinx_pm_request(PM_QUERY_DATA, PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS,
+ i, 0, 0, ret_payload);
+
+ priv->funcs[i].ngroups = ret_payload[1];
+ priv->ngroups += priv->funcs[i].ngroups;
+ }
+
+ /* Prepare all groups */
+ priv->groups = kzalloc(sizeof(*priv->groups) * priv->ngroups,
+ GFP_KERNEL);
+ if (!priv->groups)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->nfuncs; i++) {
+ ret = zynqmp_pinctrl_prepare_func_groups(i, &priv->funcs[i],
+ priv->groups);
+ if (ret) {
+ printf("Failed to prepare_func_groups\n");
+ return ret;
+ }
+ }
+
+ for (pin = 0; pin < priv->npins; pin++) {
+ ret = zynqmp_pinctrl_create_pin_groups(priv->groups, pin);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int zynqmp_pinctrl_get_functions_count(struct udevice *dev)
+{
+ struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+ return priv->nfuncs;
+}
+
+static const char *zynqmp_pinctrl_get_function_name(struct udevice *dev,
+ unsigned int selector)
+{
+ struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+ return priv->funcs[selector].name;
+}
+
+static int zynqmp_pinmux_set(struct udevice *dev, unsigned int selector,
+ unsigned int func_selector)
+{
+ int ret;
+
+ /* Request the pin first */
+ ret = xilinx_pm_request(PM_PINCTRL_REQUEST, selector, 0, 0, 0, NULL);
+ if (ret) {
+ printf("%s: pin request failed\n", __func__);
+ return ret;
+ }
+
+ /* Set the pin function */
+ ret = xilinx_pm_request(PM_PINCTRL_SET_FUNCTION, selector, func_selector,
+ 0, 0, NULL);
+ if (ret) {
+ printf("%s: Failed to set pinmux function\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int zynqmp_pinmux_group_set(struct udevice *dev, unsigned int selector,
+ unsigned int func_selector)
+{
+ int i;
+ struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+ const struct zynqmp_pctrl_group *pgrp = &priv->groups[selector];
+
+ for (i = 0; i < pgrp->npins; i++)
+ zynqmp_pinmux_set(dev, pgrp->pins[i], func_selector);
+
+ return 0;
+}
+
+static int zynqmp_pinconf_set(struct udevice *dev, unsigned int pin,
+ unsigned int param, unsigned int arg)
+{
+ int ret = 0;
+ unsigned int value;
+
+ switch (param) {
+ case PIN_CONFIG_SLEW_RATE:
+ param = PM_PINCTRL_CONFIG_SLEW_RATE;
+ ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ param = PM_PINCTRL_CONFIG_PULL_CTRL;
+ arg = PM_PINCTRL_BIAS_PULL_UP;
+ ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ param = PM_PINCTRL_CONFIG_PULL_CTRL;
+ arg = PM_PINCTRL_BIAS_PULL_DOWN;
+ ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ param = PM_PINCTRL_CONFIG_BIAS_STATUS;
+ arg = PM_PINCTRL_BIAS_DISABLE;
+ ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+ break;
+ case PIN_CONFIG_SCHMITTCMOS:
+ param = PM_PINCTRL_CONFIG_SCHMITT_CMOS;
+ ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+ break;
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ param = PM_PINCTRL_CONFIG_SCHMITT_CMOS;
+ ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ switch (arg) {
+ case DRIVE_STRENGTH_2MA:
+ value = PM_PINCTRL_DRIVE_STRENGTH_2MA;
+ break;
+ case DRIVE_STRENGTH_4MA:
+ value = PM_PINCTRL_DRIVE_STRENGTH_4MA;
+ break;
+ case DRIVE_STRENGTH_8MA:
+ value = PM_PINCTRL_DRIVE_STRENGTH_8MA;
+ break;
+ case DRIVE_STRENGTH_12MA:
+ value = PM_PINCTRL_DRIVE_STRENGTH_12MA;
+ break;
+ default:
+ /* Invalid drive strength */
+ dev_warn(dev, "Invalid drive strength for pin %d\n", pin);
+ return -EINVAL;
+ }
+
+ param = PM_PINCTRL_CONFIG_DRIVE_STRENGTH;
+ ret = zynqmp_pm_pinctrl_set_config(pin, param, value);
+ break;
+ case PIN_CONFIG_IOSTANDARD:
+ param = PM_PINCTRL_CONFIG_VOLTAGE_STATUS;
+ ret = zynqmp_pm_pinctrl_get_config(pin, param, &value);
+ if (arg != value)
+ dev_warn(dev, "Invalid IO Standard requested for pin %d\n",
+ pin);
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ param = PM_PINCTRL_CONFIG_VOLTAGE_STATUS;
+ ret = zynqmp_pm_pinctrl_get_config(pin, param, &value);
+ if (arg != value)
+ dev_warn(dev, "Invalid IO Standard requested for pin %d\n",
+ pin);
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ case PIN_CONFIG_LOW_POWER_MODE:
+ /*
+ * This cases are mentioned in dts but configurable
+ * registers are unknown. So falling through to ignore
+ * boot time warnings as of now.
+ */
+ ret = 0;
+ break;
+ default:
+ dev_warn(dev, "unsupported configuration parameter '%u'\n",
+ param);
+ ret = -ENOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int zynqmp_pinconf_group_set(struct udevice *dev,
+ unsigned int group_selector,
+ unsigned int param, unsigned int arg)
+{
+ int i;
+ struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+ const struct zynqmp_pctrl_group *pgrp = &priv->groups[group_selector];
+
+ for (i = 0; i < pgrp->npins; i++)
+ zynqmp_pinconf_set(dev, pgrp->pins[i], param, arg);
+
+ return 0;
+}
+
+static int zynqmp_pinctrl_get_pins_count(struct udevice *dev)
+{
+ struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+ return priv->npins;
+}
+
+static const char *zynqmp_pinctrl_get_pin_name(struct udevice *dev,
+ unsigned int selector)
+{
+ snprintf(pin_name, PINNAME_SIZE, "MIO%d", selector);
+
+ return pin_name;
+}
+
+static int zynqmp_pinctrl_get_pin_muxing(struct udevice *dev,
+ unsigned int selector,
+ char *buf,
+ int size)
+{
+ struct zynqmp_pinctrl_config pinmux;
+
+ zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_SLEW_RATE,
+ &pinmux.slew);
+ zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_BIAS_STATUS,
+ &pinmux.bias);
+ zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_PULL_CTRL,
+ &pinmux.pull_ctrl);
+ zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_SCHMITT_CMOS,
+ &pinmux.input_type);
+ zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_DRIVE_STRENGTH,
+ &pinmux.drive_strength);
+ zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_VOLTAGE_STATUS,
+ &pinmux.volt_sts);
+
+ switch (pinmux.drive_strength) {
+ case PM_PINCTRL_DRIVE_STRENGTH_2MA:
+ pinmux.drive_strength = DRIVE_STRENGTH_2MA;
+ break;
+ case PM_PINCTRL_DRIVE_STRENGTH_4MA:
+ pinmux.drive_strength = DRIVE_STRENGTH_4MA;
+ break;
+ case PM_PINCTRL_DRIVE_STRENGTH_8MA:
+ pinmux.drive_strength = DRIVE_STRENGTH_8MA;
+ break;
+ case PM_PINCTRL_DRIVE_STRENGTH_12MA:
+ pinmux.drive_strength = DRIVE_STRENGTH_12MA;
+ break;
+ default:
+ /* Invalid drive strength */
+ dev_warn(dev, "Invalid drive strength\n");
+ return -EINVAL;
+ }
+
+ snprintf(buf, size, "slew:%s\tbias:%s\tpull:%s\tinput:%s\tdrive:%dmA\tvolt:%s",
+ pinmux.slew ? "slow" : "fast",
+ pinmux.bias ? "enabled" : "disabled",
+ pinmux.pull_ctrl ? "up" : "down",
+ pinmux.input_type ? "schmitt" : "cmos",
+ pinmux.drive_strength,
+ pinmux.volt_sts ? "1.8" : "3.3");
+
+ return 0;
+}
+
+static int zynqmp_pinctrl_get_groups_count(struct udevice *dev)
+{
+ struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+ return priv->ngroups;
+}
+
+static const char *zynqmp_pinctrl_get_group_name(struct udevice *dev,
+ unsigned int selector)
+{
+ struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+ return priv->groups[selector].name;
+}
+
+static const struct pinconf_param zynqmp_conf_params[] = {
+ { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 },
+ { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+ { "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 },
+ { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+ { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 },
+ { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+ { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 },
+ { "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 },
+ { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
+ { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
+ { "drive-strength-microamp", PIN_CONFIG_DRIVE_STRENGTH_UA, 0 },
+ { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 },
+ { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
+ { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
+ { "input-schmitt", PIN_CONFIG_INPUT_SCHMITT, 0 },
+ { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
+ { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
+ { "low-power-disable", PIN_CONFIG_LOW_POWER_MODE, 0 },
+ { "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 },
+ { "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 },
+ { "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 },
+ { "output-high", PIN_CONFIG_OUTPUT, 1, },
+ { "output-low", PIN_CONFIG_OUTPUT, 0, },
+ { "power-source", PIN_CONFIG_POWER_SOURCE, 0 },
+ { "sleep-hardware-state", PIN_CONFIG_SLEEP_HARDWARE_STATE, 0 },
+ { "slew-rate", PIN_CONFIG_SLEW_RATE, 0 },
+ { "skew-delay", PIN_CONFIG_SKEW_DELAY, 0 },
+ /* zynqmp specific */
+ {"io-standard", PIN_CONFIG_IOSTANDARD, IO_STANDARD_LVCMOS18},
+ {"schmitt-cmos", PIN_CONFIG_SCHMITTCMOS, PM_PINCTRL_INPUT_TYPE_SCHMITT},
+};
+
+static struct pinctrl_ops zynqmp_pinctrl_ops = {
+ .get_pins_count = zynqmp_pinctrl_get_pins_count,
+ .get_pin_name = zynqmp_pinctrl_get_pin_name,
+ .get_pin_muxing = zynqmp_pinctrl_get_pin_muxing,
+ .set_state = pinctrl_generic_set_state,
+ .get_groups_count = zynqmp_pinctrl_get_groups_count,
+ .get_group_name = zynqmp_pinctrl_get_group_name,
+ .get_functions_count = zynqmp_pinctrl_get_functions_count,
+ .get_function_name = zynqmp_pinctrl_get_function_name,
+ .pinmux_group_set = zynqmp_pinmux_group_set,
+ .pinmux_set = zynqmp_pinmux_set,
+ .pinconf_params = zynqmp_conf_params,
+ .pinconf_group_set = zynqmp_pinconf_group_set,
+ .pinconf_set = zynqmp_pinconf_set,
+ .pinconf_num_params = ARRAY_SIZE(zynqmp_conf_params),
+};
+
+static const struct udevice_id zynqmp_pinctrl_ids[] = {
+ { .compatible = "xlnx,zynqmp-pinctrl" },
+ { }
+};
+
+U_BOOT_DRIVER(pinctrl_zynqmp) = {
+ .name = "zynqmp-pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = zynqmp_pinctrl_ids,
+ .priv_auto = sizeof(struct zynqmp_pinctrl_priv),
+ .ops = &zynqmp_pinctrl_ops,
+ .probe = zynqmp_pinctrl_probe,
+};
diff --git a/drivers/power/domain/zynqmp-power-domain.c b/drivers/power/domain/zynqmp-power-domain.c
index 5383d09896..6943658be4 100644
--- a/drivers/power/domain/zynqmp-power-domain.c
+++ b/drivers/power/domain/zynqmp-power-domain.c
@@ -5,6 +5,7 @@
#include <common.h>
#include <dm.h>
+#include <dm/device_compat.h>
#include <log.h>
#include <malloc.h>
#include <misc.h>
@@ -13,25 +14,6 @@
#include <zynqmp_firmware.h>
-#define NODE_ID_LOCATION 5
-
-static unsigned int xpm_configobject[] = {
- /* HEADER */
- 2, /* Number of remaining words in the header */
- 1, /* Number of sections included in config object */
- PM_CONFIG_OBJECT_TYPE_OVERLAY, /* Type of Config object as overlay */
- /* SLAVE SECTION */
-
- PM_CONFIG_SLAVE_SECTION_ID, /* Section ID */
- 1, /* Number of slaves */
-
- 0, /* Node ID which will be changed below */
- PM_SLAVE_FLAG_IS_SHAREABLE,
- PM_CONFIG_IPI_PSU_CORTEXA53_0_MASK |
- PM_CONFIG_IPI_PSU_CORTEXR5_0_MASK |
- PM_CONFIG_IPI_PSU_CORTEXR5_1_MASK, /* IPI Mask */
-};
-
static int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
const u32 qos, const enum zynqmp_pm_request_ack ack)
{
@@ -41,12 +23,9 @@ static int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
static int zynqmp_power_domain_request(struct power_domain *power_domain)
{
- /* Record power domain id */
- xpm_configobject[NODE_ID_LOCATION] = power_domain->id;
-
- zynqmp_pmufw_load_config_object(xpm_configobject, sizeof(xpm_configobject));
+ dev_dbg(power_domain->dev, "Request for id: %ld\n", power_domain->id);
- return 0;
+ return zynqmp_pmufw_node(power_domain->id);
}
static int zynqmp_power_domain_free(struct power_domain *power_domain)
@@ -57,6 +36,8 @@ static int zynqmp_power_domain_free(struct power_domain *power_domain)
static int zynqmp_power_domain_on(struct power_domain *power_domain)
{
+ dev_dbg(power_domain->dev, "Domain ON for id: %ld\n", power_domain->id);
+
return zynqmp_pm_request_node(power_domain->id,
ZYNQMP_PM_CAPABILITY_ACCESS,
ZYNQMP_PM_MAX_QOS,
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index ff8e11f648..646fec7026 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -680,6 +680,14 @@ config VIDEO_SEPS525
Enable support for the Syncoam PM-OLED display driver (RGB 160x128).
Currently driver is supporting only SPI interface.
+config VIDEO_ZYNQMP_DPSUB
+ bool "Enable video support for ZynqMP Display Port"
+ depends on DM_VIDEO && ZYNQMP_POWER_DOMAIN
+ help
+ Enable support for Xilinx ZynqMP Display Port. Currently this file
+ is used as placeholder for driver. The main reason is to record
+ compatible string and calling power domain driver.
+
source "drivers/video/nexell/Kconfig"
config VIDEO
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 4038395b12..2530791eb4 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_VIDEO_TEGRA20) += tegra.o
obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
obj-$(CONFIG_VIDEO_VESA) += vesa.o
obj-$(CONFIG_VIDEO_SEPS525) += seps525.o
+obj-$(CONFIG_VIDEO_ZYNQMP_DPSUB) += zynqmp_dpsub.o
obj-y += bridge/
obj-y += sunxi/
diff --git a/drivers/video/zynqmp_dpsub.c b/drivers/video/zynqmp_dpsub.c
new file mode 100644
index 0000000000..4ead663cd5
--- /dev/null
+++ b/drivers/video/zynqmp_dpsub.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Xilinx Inc.
+ */
+
+#include <common.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <errno.h>
+#include <video.h>
+#include <dm/device_compat.h>
+
+#define WIDTH 640
+#define HEIGHT 480
+
+/**
+ * struct zynqmp_dpsub_priv - Private structure
+ * @dev: Device uclass for video_ops
+ */
+struct zynqmp_dpsub_priv {
+ struct udevice *dev;
+};
+
+static int zynqmp_dpsub_probe(struct udevice *dev)
+{
+ struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct zynqmp_dpsub_priv *priv = dev_get_priv(dev);
+
+ uc_priv->bpix = VIDEO_BPP16;
+ uc_priv->xsize = WIDTH;
+ uc_priv->ysize = HEIGHT;
+ uc_priv->rot = 0;
+
+ priv->dev = dev;
+
+ /* Only placeholder for power domain driver */
+ return 0;
+}
+
+static int zynqmp_dpsub_bind(struct udevice *dev)
+{
+ struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+
+ plat->size = WIDTH * HEIGHT * 16;
+
+ return 0;
+}
+
+static const struct video_ops zynqmp_dpsub_ops = {
+};
+
+static const struct udevice_id zynqmp_dpsub_ids[] = {
+ { .compatible = "xlnx,zynqmp-dpsub-1.7" },
+ { }
+};
+
+U_BOOT_DRIVER(zynqmp_dpsub_video) = {
+ .name = "zynqmp_dpsub_video",
+ .id = UCLASS_VIDEO,
+ .of_match = zynqmp_dpsub_ids,
+ .ops = &zynqmp_dpsub_ops,
+ .plat_auto = sizeof(struct video_uc_plat),
+ .bind = zynqmp_dpsub_bind,
+ .probe = zynqmp_dpsub_probe,
+ .priv_auto = sizeof(struct zynqmp_dpsub_priv),
+};