From 952b2e60de6d6a80f35878193649b49ae2e14df9 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:00:56 +0100 Subject: [PATCH 01/39] fru: ops: Clear fru table before storing data Fill fru table with 0's before using it, to avoid junk data. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/e5f15caf9c2102316e39f300d7c9c1ecb6be8439.1645624855.git.michal.simek@xilinx.com --- board/xilinx/common/fru_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/board/xilinx/common/fru_ops.c b/board/xilinx/common/fru_ops.c index 6ed63bb7ee..a0a1441a8e 100644 --- a/board/xilinx/common/fru_ops.c +++ b/board/xilinx/common/fru_ops.c @@ -222,7 +222,7 @@ int fru_capture(unsigned long addr) } hdr = (struct fru_common_hdr *)addr; - + memset((void *)&fru_data, 0, sizeof(fru_data)); memcpy((void *)&fru_data, (void *)hdr, sizeof(struct fru_common_hdr)); From 90e8f2db60a1e49526f053876925741682da96ab Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:00:57 +0100 Subject: [PATCH 02/39] fru: ops: Return error from checksum if data is all zero's fru_checksum function is simply adding all the bytes and returning the sum. If the data passed to this function is all zero's then it will return 0, and the functions calling this api will assume that checksum is correct. Ideally this is not good. Fix this by returning error if all the data is 0's. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/ac0366fe55c60a818a3f9ed33d96826c817d5520.1645624855.git.michal.simek@xilinx.com --- board/xilinx/common/fru_ops.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/board/xilinx/common/fru_ops.c b/board/xilinx/common/fru_ops.c index a0a1441a8e..058e750c44 100644 --- a/board/xilinx/common/fru_ops.c +++ b/board/xilinx/common/fru_ops.c @@ -39,12 +39,20 @@ static int fru_check_language(u8 code) u8 fru_checksum(u8 *addr, u8 len) { u8 checksum = 0; + u8 cnt = len; while (len--) { + if (*addr == 0) + cnt--; + checksum += *addr; addr++; } + /* If all data bytes are 0's return error */ + if (!cnt) + return EINVAL; + return checksum; } From ff8ee707fb0b5aab49832dd124a3c8cc9b3e5d41 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:00:58 +0100 Subject: [PATCH 03/39] xilinx: common: Optimise updating ethaddr from eeprom In board_late_init_xilinx() eth*addr are updated from the values read from eeprom. Ideally the MAC addresses are updated sequencially. So if any MAC address is invalid, it means there are no further valid values. So optimise this logic by replacing continue with break. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/efef0d07add5d5777396ea111ad75411dc402db3.1645624855.git.michal.simek@xilinx.com --- board/xilinx/common/board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/board/xilinx/common/board.c b/board/xilinx/common/board.c index 0068cb8792..db089c4a0b 100644 --- a/board/xilinx/common/board.c +++ b/board/xilinx/common/board.c @@ -416,7 +416,7 @@ int board_late_init_xilinx(void) for (i = 0; i < EEPROM_HDR_NO_OF_MAC_ADDR; i++) { if (!desc->mac_addr[i]) - continue; + break; if (is_valid_ethaddr((const u8 *)desc->mac_addr[i])) ret |= eth_env_set_enetaddr_by_index("eth", From 7a036b674fc808a71b8069250ac7331d4ceb29a0 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:00:59 +0100 Subject: [PATCH 04/39] fru: ops: Add support to read mac addresses from multirecord Add support to read MAC addresses from mac address multirecord. Check if multi record is found, then jump to mac address multirecord by comparing the record type field. If it matches mac address multirecord(0xD2), then copy mac addresses. Copy these read MAC address in xilinx_read_eeprom_fru so that they are updated to eth*addr in board_late_init_xilinx(). Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/18f31bc528820934854ea5fd9dc581778fc1e40c.1645624855.git.michal.simek@xilinx.com --- board/xilinx/common/board.c | 9 ++++++++ board/xilinx/common/fru.h | 21 +++++++++++++++++++ board/xilinx/common/fru_ops.c | 39 +++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/board/xilinx/common/board.c b/board/xilinx/common/board.c index db089c4a0b..0769189dcf 100644 --- a/board/xilinx/common/board.c +++ b/board/xilinx/common/board.c @@ -171,6 +171,7 @@ static int xilinx_read_eeprom_fru(struct udevice *dev, char *name, { int i, ret, eeprom_size; u8 *fru_content; + u8 id = 0; /* FIXME this is shortcut - if eeprom type is wrong it will fail */ eeprom_size = i2c_eeprom_size(dev); @@ -218,6 +219,14 @@ static int xilinx_read_eeprom_fru(struct udevice *dev, char *name, sizeof(desc->revision)); strncpy(desc->serial, (char *)fru_data.brd.serial_number, sizeof(desc->serial)); + + while (id < EEPROM_HDR_NO_OF_MAC_ADDR) { + if (is_valid_ethaddr((const u8 *)fru_data.mac.macid[id])) + memcpy(&desc->mac_addr[id], + (char *)fru_data.mac.macid[id], ETH_ALEN); + id++; + } + desc->header = EEPROM_HEADER_MAGIC; end: diff --git a/board/xilinx/common/fru.h b/board/xilinx/common/fru.h index e7284709dd..59f6b722cf 100644 --- a/board/xilinx/common/fru.h +++ b/board/xilinx/common/fru.h @@ -6,6 +6,7 @@ #ifndef __FRU_H #define __FRU_H +#include struct fru_common_hdr { u8 version; @@ -19,6 +20,7 @@ struct fru_common_hdr { }; #define FRU_BOARD_MAX_LEN 32 +#define FRU_MAX_NO_OF_MAC_ADDR 4 struct __packed fru_board_info_header { u8 ver; @@ -56,9 +58,24 @@ struct fru_board_data { u8 uuid[FRU_BOARD_MAX_LEN]; }; +struct fru_multirec_hdr { + u8 rec_type; + u8 type; + u8 len; + u8 csum; + u8 hdr_csum; +}; + +struct fru_multirec_mac { + u8 xlnx_iana_id[3]; + u8 ver; + u8 macid[FRU_MAX_NO_OF_MAC_ADDR][ETH_ALEN]; +}; + struct fru_table { struct fru_common_hdr hdr; struct fru_board_data brd; + struct fru_multirec_mac mac; bool captured; }; @@ -69,6 +86,10 @@ struct fru_table { #define FRU_LANG_CODE_ENGLISH 0 #define FRU_LANG_CODE_ENGLISH_1 25 #define FRU_TYPELEN_EOF 0xC1 +#define FRU_MULTIREC_TYPE_OEM 0xD2 +#define FRU_MULTIREC_MAC_OFFSET 4 +#define FRU_LAST_REC BIT(7) +#define FRU_DUT_MACID 0x31 /* This should be minimum of fields */ #define FRU_BOARD_AREA_TOTAL_FIELDS 5 diff --git a/board/xilinx/common/fru_ops.c b/board/xilinx/common/fru_ops.c index 058e750c44..49846ae3d6 100644 --- a/board/xilinx/common/fru_ops.c +++ b/board/xilinx/common/fru_ops.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -218,10 +219,43 @@ static int fru_parse_board(unsigned long addr) return 0; } +static int fru_parse_multirec(unsigned long addr) +{ + struct fru_multirec_hdr mrc; + u8 checksum = 0; + u8 hdr_len = sizeof(struct fru_multirec_hdr); + int mac_len = 0; + + debug("%s: multirec addr %lx\n", __func__, addr); + + do { + memcpy(&mrc.rec_type, (void *)addr, hdr_len); + + checksum = fru_checksum((u8 *)addr, hdr_len); + if (checksum) { + debug("%s header CRC error\n", __func__); + return -EINVAL; + } + + if (mrc.rec_type == FRU_MULTIREC_TYPE_OEM) { + struct fru_multirec_mac *mac = (void *)addr + hdr_len; + + if (mac->ver == FRU_DUT_MACID) { + mac_len = mrc.len - FRU_MULTIREC_MAC_OFFSET; + memcpy(&fru_data.mac.macid, mac->macid, mac_len); + } + } + addr += mrc.len + hdr_len; + } while (!(mrc.type & FRU_LAST_REC)); + + return 0; +} + int fru_capture(unsigned long addr) { struct fru_common_hdr *hdr; u8 checksum = 0; + unsigned long multirec_addr = addr; checksum = fru_checksum((u8 *)addr, sizeof(struct fru_common_hdr)); if (checksum) { @@ -243,6 +277,11 @@ int fru_capture(unsigned long addr) env_set_hex("fru_addr", addr); + if (hdr->off_multirec) { + multirec_addr += fru_cal_area_len(hdr->off_multirec); + fru_parse_multirec(multirec_addr); + } + return 0; } From 2569b51e324e451739c0dd293ff40643914f34e9 Mon Sep 17 00:00:00 2001 From: T Karthik Reddy Date: Wed, 23 Feb 2022 15:01:37 +0100 Subject: [PATCH 05/39] Revert "board: zynqmp: Fix for wrong AMS setting by ROM" This reverts commit dfbe492edef421de09617dc26805fc428440924e. Analog bus control register should be programmed in SPL only. This commit 3414712ba8a ("arm64: zynqmp: Writing correct value to ANALOG_BUS") is programming the same. So revert this commit. Signed-off-by: T Karthik Reddy Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/215bc936e36f88d2e7c4422ec68cad6d40cb8f68.1645624892.git.michal.simek@xilinx.com --- board/xilinx/zynqmp/zynqmp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index 70b3c81f12..bc2090941d 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -399,9 +399,6 @@ static void print_secure_boot(void) status & ZYNQMP_CSU_STATUS_ENCRYPTED ? "" : "not "); } -#define PS_SYSMON_ANALOG_BUS_VAL 0x3210 -#define PS_SYSMON_ANALOG_BUS_REG 0xFFA50914 - int board_init(void) { #if defined(CONFIG_ZYNQMP_FIRMWARE) @@ -429,9 +426,6 @@ int board_init(void) printf("EL Level:\tEL%d\n", current_el()); - /* Bug in ROM sets wrong value in this register */ - writel(PS_SYSMON_ANALOG_BUS_VAL, PS_SYSMON_ANALOG_BUS_REG); - #if CONFIG_IS_ENABLED(FPGA) && defined(CONFIG_FPGA_ZYNQMPPL) zynqmppl.name = zynqmp_get_silicon_idcode_name(); printf("Chip ID:\t%s\n", zynqmppl.name); From 4173a42685e1e3c6793517b03ddabacc3774f7da Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:02:51 +0100 Subject: [PATCH 06/39] dm: pinctrl: Use explicit values for enums Based on discussion at https://lore.kernel.org/r/20200318125003.GA2727094@kroah.com we got recommendation to use explicit values for all enums. So, add explicit values to all pinctrl related enums for readability. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Reviewed-by: Simon Glass Link: https://lore.kernel.org/r/dcdb20e7252ea7465e9f984d815e9624c30e9558.1645624969.git.michal.simek@xilinx.com --- include/dm/pinctrl.h | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/include/dm/pinctrl.h b/include/dm/pinctrl.h index 8b869c4fbf..0c461e56bb 100644 --- a/include/dm/pinctrl.h +++ b/include/dm/pinctrl.h @@ -453,30 +453,30 @@ struct pinctrl_ops { * presented using the packed format. */ enum pin_config_param { - PIN_CONFIG_BIAS_BUS_HOLD, - PIN_CONFIG_BIAS_DISABLE, - PIN_CONFIG_BIAS_HIGH_IMPEDANCE, - PIN_CONFIG_BIAS_PULL_DOWN, - PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, - PIN_CONFIG_BIAS_PULL_UP, - PIN_CONFIG_DRIVE_OPEN_DRAIN, - PIN_CONFIG_DRIVE_OPEN_SOURCE, - PIN_CONFIG_DRIVE_PUSH_PULL, - PIN_CONFIG_DRIVE_STRENGTH, - PIN_CONFIG_DRIVE_STRENGTH_UA, - PIN_CONFIG_INPUT_DEBOUNCE, - PIN_CONFIG_INPUT_ENABLE, - PIN_CONFIG_INPUT_SCHMITT, - PIN_CONFIG_INPUT_SCHMITT_ENABLE, - PIN_CONFIG_LOW_POWER_MODE, - PIN_CONFIG_OUTPUT_ENABLE, - PIN_CONFIG_OUTPUT, - PIN_CONFIG_POWER_SOURCE, - PIN_CONFIG_SLEEP_HARDWARE_STATE, - PIN_CONFIG_SLEW_RATE, - PIN_CONFIG_SKEW_DELAY, - PIN_CONFIG_END = 0x7F, - PIN_CONFIG_MAX = 0xFF, + PIN_CONFIG_BIAS_BUS_HOLD = 0, + PIN_CONFIG_BIAS_DISABLE = 1, + PIN_CONFIG_BIAS_HIGH_IMPEDANCE = 2, + PIN_CONFIG_BIAS_PULL_DOWN = 3, + PIN_CONFIG_BIAS_PULL_PIN_DEFAULT = 4, + PIN_CONFIG_BIAS_PULL_UP = 5, + PIN_CONFIG_DRIVE_OPEN_DRAIN = 6, + PIN_CONFIG_DRIVE_OPEN_SOURCE = 7, + PIN_CONFIG_DRIVE_PUSH_PULL = 8, + PIN_CONFIG_DRIVE_STRENGTH = 9, + PIN_CONFIG_DRIVE_STRENGTH_UA = 10, + PIN_CONFIG_INPUT_DEBOUNCE = 11, + PIN_CONFIG_INPUT_ENABLE = 12, + PIN_CONFIG_INPUT_SCHMITT = 13, + PIN_CONFIG_INPUT_SCHMITT_ENABLE = 14, + PIN_CONFIG_LOW_POWER_MODE = 15, + PIN_CONFIG_OUTPUT_ENABLE = 16, + PIN_CONFIG_OUTPUT = 17, + PIN_CONFIG_POWER_SOURCE = 18, + PIN_CONFIG_SLEEP_HARDWARE_STATE = 19, + PIN_CONFIG_SLEW_RATE = 20, + PIN_CONFIG_SKEW_DELAY = 21, + PIN_CONFIG_END = 127, /* 0x7F */ + PIN_CONFIG_MAX = 255, /* 0xFF */ }; #if CONFIG_IS_ENABLED(PINCTRL_GENERIC) From c2b74edf156ca60af26e87fdfce13025b4312857 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 15:10:33 +0100 Subject: [PATCH 07/39] mtd: nand: Update driver to match new DT binding New binding changed node name from flash@e1000000 to nand-controller@0,0 which should be reflected in the driver. Both names are supported for backward compatibility. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/61f6edd965e0c0b179184823d5283c6c61a1eb35.1645625433.git.michal.simek@xilinx.com --- drivers/mtd/nand/raw/zynq_nand.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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)) { From 03a8e826e0a05e7d255074d2c1a53feb84ca6b26 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 23 Feb 2022 15:10:34 +0100 Subject: [PATCH 08/39] ARM: dts: zynq: add NAND flash controller node Recently, a driver for the ARM Primecell PL35x static memory controller (including NAND controller) was added in linux. Add the corresponding device tree node. Also update cfi-flash registers and location in DT. Signed-off-by: Michael Walle Signed-off-by: Amit Kumar Mahapatra Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/20210616155437.27378-3-michael@walle.cc Link: https://lore.kernel.org/r/ee81d3846a1ce93f240d61537d404796e5599c1c.1645625433.git.michal.simek@xilinx.com --- arch/arm/dts/bitmain-antminer-s9.dts | 2 +- arch/arm/dts/zynq-7000.dtsi | 57 +++++++++++++++------------- arch/arm/dts/zynq-zc770-xm011.dts | 2 +- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/arch/arm/dts/bitmain-antminer-s9.dts b/arch/arm/dts/bitmain-antminer-s9.dts index 0694350555..408862bef0 100644 --- a/arch/arm/dts/bitmain-antminer-s9.dts +++ b/arch/arm/dts/bitmain-antminer-s9.dts @@ -50,7 +50,7 @@ ps-clk-frequency = <33333333>; }; -&nand0 { +&nfc0 { status = "okay"; }; diff --git a/arch/arm/dts/zynq-7000.dtsi b/arch/arm/dts/zynq-7000.dtsi index 4dda753671..9495911397 100644 --- a/arch/arm/dts/zynq-7000.dtsi +++ b/arch/arm/dts/zynq-7000.dtsi @@ -246,33 +246,6 @@ #size-cells = <0>; }; - smcc: memory-controller@e000e000 { - #address-cells = <1>; - #size-cells = <1>; - status = "disabled"; - clock-names = "memclk", "apb_pclk"; - clocks = <&clkc 11>, <&clkc 44>; - compatible = "arm,pl353-smc-r2p1", "arm,primecell"; - interrupt-parent = <&intc>; - interrupts = <0 18 4>; - ranges ; - reg = <0xe000e000 0x1000>; - nand0: flash@e1000000 { - status = "disabled"; - compatible = "arm,pl353-nand-r2p1"; - reg = <0xe1000000 0x1000000>; - #address-cells = <1>; - #size-cells = <1>; - }; - nor0: flash@e2000000 { - status = "disabled"; - compatible = "cfi-flash"; - reg = <0xe2000000 0x2000000>; - #address-cells = <1>; - #size-cells = <1>; - }; - }; - gem0: ethernet@e000b000 { compatible = "cdns,zynq-gem", "cdns,gem"; reg = <0xe000b000 0x1000>; @@ -295,6 +268,36 @@ #size-cells = <0>; }; + smcc: memory-controller@e000e000 { + compatible = "arm,pl353-smc-r2p1", "arm,primecell"; + reg = <0xe000e000 0x0001000>; + status = "disabled"; + clock-names = "memclk", "apb_pclk"; + clocks = <&clkc 11>, <&clkc 44>; + ranges = <0x0 0x0 0xe1000000 0x1000000 /* Nand CS region */ + 0x1 0x0 0xe2000000 0x2000000 /* SRAM/NOR CS0 region */ + 0x2 0x0 0xe4000000 0x2000000>; /* SRAM/NOR CS1 region */ + #address-cells = <2>; + #size-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <0 18 4>; + + nfc0: nand-controller@0,0 { + compatible = "arm,pl353-nand-r2p1"; + reg = <0 0 0x1000000>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <1>; + }; + nor0: flash@1,0 { + status = "disabled"; + compatible = "cfi-flash"; + reg = <1 0 0x2000000>; + #address-cells = <1>; + #size-cells = <1>; + }; + }; + sdhci0: mmc@e0100000 { compatible = "arasan,sdhci-8.9a"; status = "disabled"; diff --git a/arch/arm/dts/zynq-zc770-xm011.dts b/arch/arm/dts/zynq-zc770-xm011.dts index b6e3e255d7..0ef2ae1744 100644 --- a/arch/arm/dts/zynq-zc770-xm011.dts +++ b/arch/arm/dts/zynq-zc770-xm011.dts @@ -47,7 +47,7 @@ }; }; -&nand0 { +&nfc0 { status = "okay"; }; From c252b2774782360fd8785230072bb6c262a06709 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:13:30 +0100 Subject: [PATCH 09/39] mmc: zynq_sdhci: Fix timeout issue In the workaround added with 'commit b6f44082d5cd ("mmc: zynq_sdhci: Wait till sd card detect state is stable")' the timeout variable has post decrement. Whenever timeout happens, this post decrement is making timeout=0xffffffff, so timeout error print and return statement are never reached. Fix it by decrementing it inside the while loop. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/61fc1160ada0dd622cd29e381a74af7bf3d9a200.1645625609.git.michal.simek@xilinx.com --- drivers/mmc/zynq_sdhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index 5cea4c695e..f4d69a2f70 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -773,8 +773,9 @@ static int arasan_sdhci_probe(struct udevice *dev) u32 timeout = 1000; while (((sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_STATE_STABLE) == 0) && timeout--) { + SDHCI_CARD_STATE_STABLE) == 0) && timeout) { mdelay(1); + timeout--; } if (!timeout) { dev_err(dev, "Sdhci card detect state not stable\n"); From 8d32bca20535dea2f10d910b6458aec75d0c3fd9 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:13:31 +0100 Subject: [PATCH 10/39] mmc: zynq_sdhci: Change granularity of timeout to 1us The timeout used in 'commit b6f44082d5cd ("mmc: zynq_sdhci: Wait till sd card detect state is stable")' workaround is 1000ms at a granularity of 1msec. Change it to 1usec, to not waste time incase the cd is stable. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/f008d2bcf864702a01564789f14f9cdecb8acd45.1645625609.git.michal.simek@xilinx.com --- drivers/mmc/zynq_sdhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index f4d69a2f70..7d62d05eda 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -770,11 +770,11 @@ static int arasan_sdhci_probe(struct udevice *dev) * 1000msec till the card detect state gets stable. */ if (IS_ENABLED(CONFIG_ARCH_VERSAL)) { - u32 timeout = 1000; + u32 timeout = 1000000; while (((sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_STATE_STABLE) == 0) && timeout) { - mdelay(1); + udelay(1); timeout--; } if (!timeout) { From 980e55518fd9962ee4768a0f2c2f9382a2e11f8e Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:13:32 +0100 Subject: [PATCH 11/39] mmc: zynq_sdhci: Enable card detect workaround for ZynqMP Card detect state stable issue is observed on few ZynqMP boards(SOM), so enable the workaround 'commit b6f44082d5cd ("mmc: zynq_sdhci: Wait till sd card detect state is stable")' for ZynqMP platforms also. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/0bf6154c79f24227d786efc5e2c1f506185b2bce.1645625609.git.michal.simek@xilinx.com --- drivers/mmc/zynq_sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index 7d62d05eda..33d00b06c7 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -769,7 +769,7 @@ 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)) { + if (IS_ENABLED(CONFIG_ARCH_ZYNQMP) || IS_ENABLED(CONFIG_ARCH_VERSAL)) { u32 timeout = 1000000; while (((sdhci_readl(host, SDHCI_PRESENT_STATE) & From 15efe0f3a57fc783526769fdd245bc2ebdbe2588 Mon Sep 17 00:00:00 2001 From: T Karthik Reddy Date: Wed, 23 Feb 2022 16:21:30 +0100 Subject: [PATCH 12/39] gpio: slg7xl45106: Add support for slg7xl45106 i2c gpo expander slg7xl45106 is i2c based 8-bit gpo expander, gpo pins are set and get by writing and reading corresponding gpo bit value into its data register. Signed-off-by: T Karthik Reddy Signed-off-by: Michal Simek Reviewed-by: Heiko Schocher Reviewed-by: Simon Glass Link: https://lore.kernel.org/r/839f475cc75c97ffb3496a4caa93de2faabdbca2.1645629688.git.michal.simek@xilinx.com --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 8 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio_slg7xl45106.c | 115 ++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 drivers/gpio/gpio_slg7xl45106.c diff --git a/MAINTAINERS b/MAINTAINERS index 0f39bc6bc9..bb6ea06933 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -636,6 +636,7 @@ F: arch/arm/mach-zynqmp/ F: drivers/clk/clk_zynqmp.c F: driver/firmware/firmware-zynqmp.c F: drivers/fpga/zynqpl.c +F: drivers/gpio/gpio_slg7xl45106.c F: drivers/gpio/zynq_gpio.c F: drivers/gpio/zynqmp_gpio_modepin.c F: drivers/i2c/i2c-cdns.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 522dfc195e..a9e9b30f6c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -550,4 +550,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 33f7d41b7d..b2e78b0570 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -72,3 +72,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 +#include +#include +#include +#include +#include +#include + +#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, +}; From fda7cbfefd44448dc275508942b59fb38480c156 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 1 Mar 2022 09:10:59 +0100 Subject: [PATCH 13/39] power: zynqmp: Use zynqmp_pmufw_node() from firmware Remove private xpm_configobject[] and use zynqmp_pmufw_node() which provides the same functionality. Also add debug messages for easier debugging. Fixes: e0283cbdfd49 ("power: zynqmp: Add power domain driver for ZynqMP") Signed-off-by: Michal Simek Reviewed-by: Jaehoon Chung Link: https://lore.kernel.org/r/bddf11459b9b9e849fac9a50db2f1a5fdfae4119.1646122254.git.michal.simek@xilinx.com --- drivers/power/domain/zynqmp-power-domain.c | 29 ++++------------------ 1 file changed, 5 insertions(+), 24 deletions(-) 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 #include +#include #include #include #include @@ -13,25 +14,6 @@ #include -#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; + dev_dbg(power_domain->dev, "Request for id: %ld\n", power_domain->id); - zynqmp_pmufw_load_config_object(xpm_configobject, sizeof(xpm_configobject)); - - 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, From d7b5cc89d3299a4fb6d1b2dff183c7d490d1da86 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 1 Mar 2022 09:13:20 +0100 Subject: [PATCH 14/39] microblaze: Do not place u-boot to reserved memory location Microblaze can also have reserved space in DT which u-boot has to avoid to placing self to that location. The same change was done in Zynqmp by commit ce39ee28ec31 ("zynqmp: Do not place u-boot to reserved memory location"). This feature was tested with this memory description on kc705: memory { device_type = "memory"; reg = <0x80000000 0x40000000>; }; reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges; alloc@b00000000 { reg = <0xb0000000 0x10000000>; no-map; }; alloc@a8000000 { reg = <0xa8000000 0x00010000>; no-map; }; }; And in U-Boot log you can check u-boot relocation address and reserved locations. U-Boot 2022.01-03974-gb1b4c2dea9b9 (Feb 25 2022 - 11:59:48 +0100) Model: Xilinx MicroBlaze DRAM: 1 GiB Flash: 128 MiB Loading Environment from nowhere... OK In: serial Out: serial Err: serial Model: Xilinx MicroBlaze Net: AXI EMAC: 40c00000, phyaddr 7, interface gmii eth0: ethernet@40c00000 U-BOOT for microblaze-generic U-Boot-mONStR> bdi ... DRAM bank = 0x00000000 -> start = 0x80000000 -> size = 0x40000000 ... relocaddr = 0xaff69000 ... lmb_dump_all: memory.cnt = 0x1 memory[0] [0x80000000-0xbfffffff], 0x40000000 bytes flags: 0 reserved.cnt = 0x3 reserved[0] [0xa8000000-0xa800ffff], 0x00010000 bytes flags: 4 reserved[1] [0xafe87bb0-0xafffffff], 0x00178450 bytes flags: 0 reserved[2] [0xb0000000-0xbfffffff], 0x10000000 bytes flags: 4 Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/ea0a8ccce723478eb518f6fdceb91d4f129efb68.1646122398.git.michal.simek@xilinx.com --- arch/microblaze/include/asm/system.h | 2 ++ .../microblaze-generic/microblaze-generic.c | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/arch/microblaze/include/asm/system.h b/arch/microblaze/include/asm/system.h index 3107748d3b..050a8b4076 100644 --- a/arch/microblaze/include/asm/system.h +++ b/arch/microblaze/include/asm/system.h @@ -23,6 +23,8 @@ #endif #include +#define MMU_SECTION_SIZE (1 * 1024 * 1024) + #define prepare_to_switch() do { } while (0) /* diff --git a/board/xilinx/microblaze-generic/microblaze-generic.c b/board/xilinx/microblaze-generic/microblaze-generic.c index a427ac94a1..f58ecd1590 100644 --- a/board/xilinx/microblaze-generic/microblaze-generic.c +++ b/board/xilinx/microblaze-generic/microblaze-generic.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,6 +38,25 @@ int dram_init(void) return 0; }; +ulong board_get_usable_ram_top(ulong total_size) +{ + phys_size_t size; + phys_addr_t reg; + struct lmb lmb; + + /* found enough not-reserved memory to relocated U-Boot */ + lmb_init(&lmb); + lmb_add(&lmb, gd->ram_base, gd->ram_size); + boot_fdt_add_mem_rsv_regions(&lmb, (void *)gd->fdt_blob); + size = ALIGN(CONFIG_SYS_MALLOC_LEN + total_size, MMU_SECTION_SIZE); + reg = lmb_alloc(&lmb, size, MMU_SECTION_SIZE); + + if (!reg) + reg = gd->ram_top - size; + + return reg + size; +} + int board_late_init(void) { ulong max_size; From c36dc2449b1f902aed49548b4f058ec51ec42f08 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 16:17:37 +0100 Subject: [PATCH 15/39] arm64: zynqmp: Move usb hub from i2c to usb node Based on upstream discussion based on link below usb hub should be placed to usb node directly as child node. Based on this Linux driver was updated and the same change should be also reflected in kv260 board. Link: https://lore.kernel.org/all/CAL_JsqJZBbu+UXqUNdZwg-uv0PAsNg55026PTwhKr5wQtxCjVQ@mail.gmail.com/ Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/aa18df1978f161b933e6cdc6cd99c807b5f74398.1645629459.git.michal.simek@xilinx.com --- arch/arm/dts/zynqmp-sck-kv-g-revB.dts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts index df054e152a..01b14ebcb6 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts @@ -36,11 +36,7 @@ label = "ina260-u14"; reg = <0x40>; }; - usbhub: usb5744@2d { /* u43 */ - compatible = "microchip,usb5744"; - reg = <0x2d>; - reset-gpios = <&gpio 44 GPIO_ACTIVE_HIGH>; - }; + /* u43 - 0x2d - USB hub */ /* u27 - 0xe0 - STDP4320 DP/HDMI splitter */ }; @@ -111,6 +107,13 @@ pinctrl-0 = <&pinctrl_usb0_default>; phy-names = "usb3-phy"; phys = <&psgtr 2 PHY_TYPE_USB3 0 1>; + + usb5744: usb-hub { /* u43 */ + status = "okay"; + compatible = "microchip,usb5744"; + i2c-bus = <&i2c1>; + reset-gpios = <&gpio 44 GPIO_ACTIVE_HIGH>; + }; }; &dwc3_0 { From 59e1bdd48d059563287a4424f3d6ef9218c49581 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 16:17:38 +0100 Subject: [PATCH 16/39] arm64: zynqmp: Setup clock for DP and DPDMA Clocks are coming from shared HW design where these frequencies should be aligned with PLL setup. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/04454c50d0d13e450976942085d763ab5aa38f98.1645629459.git.michal.simek@xilinx.com --- arch/arm/dts/zynqmp-clk-ccf.dtsi | 4 ++++ arch/arm/dts/zynqmp-sck-kv-g-revA.dts | 2 ++ arch/arm/dts/zynqmp-sck-kv-g-revB.dts | 2 ++ 3 files changed, 8 insertions(+) diff --git a/arch/arm/dts/zynqmp-clk-ccf.dtsi b/arch/arm/dts/zynqmp-clk-ccf.dtsi index 664e65896d..86b99070c4 100644 --- a/arch/arm/dts/zynqmp-clk-ccf.dtsi +++ b/arch/arm/dts/zynqmp-clk-ccf.dtsi @@ -279,10 +279,14 @@ &zynqmp_dpdma { clocks = <&zynqmp_clk DPDMA_REF>; + assigned-clocks = <&zynqmp_clk DPDMA_REF>; /* apll */ }; &zynqmp_dpsub { clocks = <&zynqmp_clk TOPSW_LSBUS>, <&zynqmp_clk DP_AUDIO_REF>, <&zynqmp_clk DP_VIDEO_REF>; + assigned-clocks = <&zynqmp_clk DP_STC_REF>, + <&zynqmp_clk DP_AUDIO_REF>, + <&zynqmp_clk DP_VIDEO_REF>; /* rpll, rpll, vpll */ }; diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revA.dts b/arch/arm/dts/zynqmp-sck-kv-g-revA.dts index 22602d8c33..34fb592d4f 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revA.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revA.dts @@ -115,10 +115,12 @@ status = "disabled"; phy-names = "dp-phy0", "dp-phy1"; phys = <&psgtr 1 PHY_TYPE_DP 0 0>, <&psgtr 0 PHY_TYPE_DP 1 0>; + assigned-clock-rates = <27000000>, <25000000>, <300000000>; }; &zynqmp_dpdma { status = "okay"; + assigned-clock-rates = <600000000>; }; &usb0 { diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts index 01b14ebcb6..35247b0bbd 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts @@ -95,10 +95,12 @@ status = "disabled"; phy-names = "dp-phy0", "dp-phy1"; phys = <&psgtr 1 PHY_TYPE_DP 0 0>, <&psgtr 0 PHY_TYPE_DP 1 0>; + assigned-clock-rates = <27000000>, <25000000>, <300000000>; }; &zynqmp_dpdma { status = "okay"; + assigned-clock-rates = <600000000>; }; &usb0 { From a3efa53c01100fc1aa015c37a258a50142cca708 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 16:17:39 +0100 Subject: [PATCH 17/39] arm64: zynqmp: Use assigned-clock-rates for setting up clock in SOM With limited low level configuration done via psu-init only IPs connected on SOM are initialized and configured. All IPs connected to carrier card are not initialized. There is a need to do proper reset, pin configuration and also clock setting. The patch targets the last part which is setting up proper clock for USBs and SDs. Also setup proper bus width for SD cards. Signed-off-by: Michal Simek Signed-off-by: Sai Krishna Potthuri Link: https://lore.kernel.org/r/d9f80b2551bd246c3d7ecb09b516806c8dc83ed9.1645629459.git.michal.simek@xilinx.com --- arch/arm/dts/zynqmp-clk-ccf.dtsi | 4 ++++ arch/arm/dts/zynqmp-sck-kv-g-revA.dts | 2 ++ arch/arm/dts/zynqmp-sck-kv-g-revB.dts | 3 +++ arch/arm/dts/zynqmp-sm-k26-revA.dts | 1 + 4 files changed, 10 insertions(+) diff --git a/arch/arm/dts/zynqmp-clk-ccf.dtsi b/arch/arm/dts/zynqmp-clk-ccf.dtsi index 86b99070c4..7b09d75151 100644 --- a/arch/arm/dts/zynqmp-clk-ccf.dtsi +++ b/arch/arm/dts/zynqmp-clk-ccf.dtsi @@ -215,10 +215,12 @@ &sdhci0 { clocks = <&zynqmp_clk SDIO0_REF>, <&zynqmp_clk LPD_LSBUS>; + assigned-clocks = <&zynqmp_clk SDIO0_REF>; }; &sdhci1 { clocks = <&zynqmp_clk SDIO1_REF>, <&zynqmp_clk LPD_LSBUS>; + assigned-clocks = <&zynqmp_clk SDIO1_REF>; }; &spi0 { @@ -255,10 +257,12 @@ &usb0 { clocks = <&zynqmp_clk USB0_BUS_REF>, <&zynqmp_clk USB3_DUAL_REF>; + assigned-clocks = <&zynqmp_clk USB0_BUS_REF>, <&zynqmp_clk USB3_DUAL_REF>; }; &usb1 { clocks = <&zynqmp_clk USB1_BUS_REF>, <&zynqmp_clk USB3_DUAL_REF>; + assigned-clocks = <&zynqmp_clk USB1_BUS_REF>, <&zynqmp_clk USB3_DUAL_REF>; }; &watchdog0 { diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revA.dts b/arch/arm/dts/zynqmp-sck-kv-g-revA.dts index 34fb592d4f..f58ad69be3 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revA.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revA.dts @@ -154,6 +154,8 @@ no-1-8-v; disable-wp; xlnx,mio-bank = <1>; + assigned-clock-rates = <187498123>; + bus-width = <8>; }; &gem3 { /* required by spec */ diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts index 35247b0bbd..7236e03a5a 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts @@ -109,6 +109,7 @@ pinctrl-0 = <&pinctrl_usb0_default>; phy-names = "usb3-phy"; phys = <&psgtr 2 PHY_TYPE_USB3 0 1>; + assigned-clock-rates = <250000000>, <20000000>; usb5744: usb-hub { /* u43 */ status = "okay"; @@ -140,6 +141,8 @@ clk-phase-sd-hs = <126>, <60>; clk-phase-uhs-sdr25 = <120>, <60>; clk-phase-uhs-ddr50 = <126>, <48>; + assigned-clock-rates = <187498123>; + bus-width = <8>; }; &gem3 { /* required by spec */ diff --git a/arch/arm/dts/zynqmp-sm-k26-revA.dts b/arch/arm/dts/zynqmp-sm-k26-revA.dts index 5f55df28f3..e9baf4cb41 100644 --- a/arch/arm/dts/zynqmp-sm-k26-revA.dts +++ b/arch/arm/dts/zynqmp-sm-k26-revA.dts @@ -189,6 +189,7 @@ disable-wp; bus-width = <8>; xlnx,mio-bank = <0>; + assigned-clock-rates = <187498123>; }; &spi1 { /* MIO6, 9-11 */ From ff7944829497ce94feb62268d0652487ea873ac2 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 16:17:40 +0100 Subject: [PATCH 18/39] arm64: zynqmp: Switch to ethernet-phy-id in kv260 Use ethernet-phy-id compatible string to properly describe phy reset on kv260 boards. Previous description wasn't correct because reset was done for mdio bus to operate and it was in this case used for different purpose which was eth phy reset. With ethernet-phy-id phy reset happens only for the phy via phy framework. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/73b64f1a2b873b4e26bd2b365364bdf313794ae2.1645629459.git.michal.simek@xilinx.com --- arch/arm/dts/zynqmp-sck-kv-g-revA.dts | 6 ++++-- arch/arm/dts/zynqmp-sck-kv-g-revB.dts | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revA.dts b/arch/arm/dts/zynqmp-sck-kv-g-revA.dts index f58ad69be3..9445dace73 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revA.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revA.dts @@ -168,16 +168,18 @@ mdio: mdio { #address-cells = <1>; #size-cells = <0>; - reset-gpios = <&gpio 38 GPIO_ACTIVE_LOW>; - reset-delay-us = <2>; phy0: ethernet-phy@1 { #phy-cells = <1>; reg = <1>; + compatible = "ethernet-phy-id2000.a231"; ti,rx-internal-delay = ; ti,tx-internal-delay = ; ti,fifo-depth = ; ti,dp83867-rxctrl-strap-quirk; + reset-assert-us = <100>; + reset-deassert-us = <280>; + reset-gpios = <&gpio 38 GPIO_ACTIVE_LOW>; }; }; }; diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts index 7236e03a5a..6ea950a13f 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts @@ -155,16 +155,18 @@ mdio: mdio { #address-cells = <1>; #size-cells = <0>; - reset-gpios = <&gpio 38 GPIO_ACTIVE_LOW>; - reset-delay-us = <2>; phy0: ethernet-phy@1 { #phy-cells = <1>; reg = <1>; + compatible = "ethernet-phy-id2000.a231"; ti,rx-internal-delay = ; ti,tx-internal-delay = ; ti,fifo-depth = ; ti,dp83867-rxctrl-strap-quirk; + reset-assert-us = <100>; + reset-deassert-us = <280>; + reset-gpios = <&gpio 38 GPIO_ACTIVE_LOW>; }; }; }; From 8b82a3a7feb06e25b4b731dc5485a1da315d2a93 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 16:17:41 +0100 Subject: [PATCH 19/39] arm64: zynqmp: Enable DP driver for SOMs The main reason is to send pmufw cfg overlay from U-Boot to PMUFW to enable access to DP. Overlay is sent when cls command is called and for that IP has to be enabled in carrier cards. And IP needs to be also enabled in SOM dt because with DTB reselection new DT is not parsed in pre reloc U-Boot instance. It is called from board_f via embedded_dtb_select(). That's why bind function is not able to allocate memory and it ends up with error: "Video device 'display@fd4a0000' cannot allocate frame buffer memory -ensure the device is set up before relocation" To avoid this situation DP is placed also to SOM where bind function is called and frame buffer memory is allocated and just reused after DTB reselection. Result is the same. There could be a problem in Linux with different DP configurations but that's need to be solved there because console should be on from u-boot already. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/c4f31641f917fddb09d976f56875057c658f264c.1645629459.git.michal.simek@xilinx.com --- arch/arm/dts/zynqmp-sck-kv-g-revB.dts | 2 +- arch/arm/dts/zynqmp-sm-k26-revA.dts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts index 6ea950a13f..28a3f3c219 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts @@ -92,7 +92,7 @@ }; &zynqmp_dpsub { - status = "disabled"; + status = "okay"; phy-names = "dp-phy0", "dp-phy1"; phys = <&psgtr 1 PHY_TYPE_DP 0 0>, <&psgtr 0 PHY_TYPE_DP 1 0>; assigned-clock-rates = <27000000>, <25000000>, <300000000>; diff --git a/arch/arm/dts/zynqmp-sm-k26-revA.dts b/arch/arm/dts/zynqmp-sm-k26-revA.dts index e9baf4cb41..d242f8712b 100644 --- a/arch/arm/dts/zynqmp-sm-k26-revA.dts +++ b/arch/arm/dts/zynqmp-sm-k26-revA.dts @@ -317,3 +317,7 @@ &ams_pl { status = "okay"; }; + +&zynqmp_dpsub { + status = "okay"; +}; From 2f6e1dde380d566dd81cc410bdc2ea010bbcac78 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 16:17:42 +0100 Subject: [PATCH 20/39] arm64: zynqmp: Fix level of gpio reset for usb on kv260 boards Active level is low that's why it should be fixed. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/86b3a96ce990b0ee0adab221146b5a5c751bd4a9.1645629459.git.michal.simek@xilinx.com --- arch/arm/dts/zynqmp-sck-kv-g-revA.dts | 2 +- arch/arm/dts/zynqmp-sck-kv-g-revB.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revA.dts b/arch/arm/dts/zynqmp-sck-kv-g-revA.dts index 9445dace73..85994bef7c 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revA.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revA.dts @@ -131,7 +131,7 @@ phys = <&psgtr 2 PHY_TYPE_USB3 0 1>; usbhub: usb5744 { /* u43 */ compatible = "microchip,usb5744"; - reset-gpios = <&gpio 44 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 44 GPIO_ACTIVE_LOW>; }; }; diff --git a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts index 28a3f3c219..b81c2e6b75 100644 --- a/arch/arm/dts/zynqmp-sck-kv-g-revB.dts +++ b/arch/arm/dts/zynqmp-sck-kv-g-revB.dts @@ -115,7 +115,7 @@ status = "okay"; compatible = "microchip,usb5744"; i2c-bus = <&i2c1>; - reset-gpios = <&gpio 44 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 44 GPIO_ACTIVE_LOW>; }; }; From de5358a82cc3bbfe9a93b9d1e4c7405b19a42a3c Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:36:02 +0100 Subject: [PATCH 21/39] firmware: zynqmp: Add and update firmware enums Update enum pm_ioctl_id with more IOCTLs. Add enum pm_sd_config_type to support dynamic sd configuration. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/9aba090ec11d2591dbe6978e73e64384873c99fc.1645626962.git.michal.simek@xilinx.com --- include/zynqmp_firmware.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/zynqmp_firmware.h b/include/zynqmp_firmware.h index 50bf4ef395..9033cc0e73 100644 --- a/include/zynqmp_firmware.h +++ b/include/zynqmp_firmware.h @@ -340,6 +340,29 @@ enum pm_ioctl_id { IOCTL_GET_LAST_RESET_REASON = 23, /* AIE ISR Clear */ IOCTL_AIE_ISR_CLEAR = 24, + /* Register SGI to ATF */ + IOCTL_REGISTER_SGI = 25, + /* Runtime feature configuration */ + IOCTL_SET_FEATURE_CONFIG = 26, + IOCTL_GET_FEATURE_CONFIG = 27, + /* IOCTL for Secure Read/Write Interface */ + IOCTL_READ_REG = 28, + IOCTL_MASK_WRITE_REG = 29, + /* Dynamic SD/GEM/USB configuration */ + IOCTL_SET_SD_CONFIG = 30, + IOCTL_SET_GEM_CONFIG = 31, + IOCTL_SET_USB_CONFIG = 32, + /* AIE/AIEML Operations */ + IOCTL_AIE_OPS = 33, + /* IOCTL to get default/current QoS */ + IOCTL_GET_QOS = 34, +}; + +enum pm_sd_config_type { + SD_CONFIG_EMMC_SEL = 1, /* To set SD_EMMC_SEL in CTRL_REG_SD */ + SD_CONFIG_BASECLK = 2, /* To set SD_BASECLK in SD_CONFIG_REG1 */ + SD_CONFIG_8BIT = 3, /* To set SD_8BIT in SD_CONFIG_REG2 */ + SD_CONFIG_FIXED = 4, /* To set fixed config registers */ }; #define PM_SIP_SVC 0xc2000000 From 7d9ee4667240070407bf2612c36d82bfab8840ba Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:36:03 +0100 Subject: [PATCH 22/39] firmware: zynqmp: Add support for set sd config and is function supported Add firmware API's to set SD configuration and to check if a purticular function is supported. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/f64fa2f73e4775e9ad2f4d91339d6c74b43116a3.1645626962.git.michal.simek@xilinx.com --- drivers/firmware/firmware-zynqmp.c | 51 ++++++++++++++++++++++++++++++ include/zynqmp_firmware.h | 6 ++++ 2 files changed, 57 insertions(+) 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/include/zynqmp_firmware.h b/include/zynqmp_firmware.h index 9033cc0e73..60c3df3da4 100644 --- a/include/zynqmp_firmware.h +++ b/include/zynqmp_firmware.h @@ -395,6 +395,8 @@ int zynqmp_pmufw_config_close(void); void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size); int xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 *ret_payload); +int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value); +int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id); /* Type of Config Object */ #define PM_CONFIG_OBJECT_TYPE_BASE 0x1U @@ -426,5 +428,9 @@ enum zynqmp_pm_request_ack { #define ZYNQMP_PM_CAPABILITY_UNUSABLE 0x8U #define ZYNQMP_PM_MAX_QOS 100U +/* Firmware feature check version mask */ +#define FIRMWARE_VERSION_MASK GENMASK(15, 0) +/* PM API versions */ +#define PM_API_VERSION_2 2 #endif /* _ZYNQMP_FIRMWARE_H_ */ From 3adc17f60bf804fd8fe25e7c20399243f26d7f49 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:36:04 +0100 Subject: [PATCH 23/39] lib: div64: Add support for round up of div64_u64 Most of the frequencies are not rounded up to a proper number. When we need to devide these frequencies to get a number for example frequency in Mhz, we see it as one less than the actual intended value. Ex: If we want to get Mhz from frequency 199999994hz, we will calculate it using div64_u64(199999994, 1000000) and we will get 199Mhz in place of 200Mhz. Add a macro DIV64_U64_ROUND_UP for rounding up div64_u64. This is taken from linux 'commit 68600f623d69("mm: don't miss the last page because of round-off error")'. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/f9fdcba76cd692ae436b1d7883b490e3dc207231.1645626962.git.michal.simek@xilinx.com --- include/linux/math64.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/math64.h b/include/linux/math64.h index 08584c8f23..eaa9fd5b96 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -48,6 +48,9 @@ static inline u64 div64_u64(u64 dividend, u64 divisor) return dividend / divisor; } +#define DIV64_U64_ROUND_UP(ll, d) \ + ({ u64 _tmp = (d); div64_u64((ll) + _tmp - 1, _tmp); }) + /** * div64_s64 - signed 64bit divide with 64bit divisor */ From 90ab7fafb65f13cff94c8260f96b73fed3ff8a21 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:36:05 +0100 Subject: [PATCH 24/39] mmc: zynq_sdhci: Add support for dynamic configuration Add support for dynamic configuration which will takes care of configuring the SD secure space configuration registers using firmware APIs, performing SD reset assert and deassert. High level sequence: - Check for the PM dynamic configuration support, if no error proceed with SD dynamic configurations(next steps) otherwise skip the dynamic configuration. - Put the SD Controller in reset. - Configure SD Fixed configurations. - Configure the SD Slot Type. - Configure the BASE_CLOCK. - Configure the 8-bit support. - Bring the SD Controller out of reset. In the above steps, apart from the Fixed configurations, remaining all configurations are dynamic and they will be read from devicetree. And also remove hardcoded secure register writes, as dynamic sd config support is added. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/030a3ec041ff3efebd574b4d2b477ad85f12cbce.1645626962.git.michal.simek@xilinx.com --- drivers/mmc/zynq_sdhci.c | 117 +++++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index 33d00b06c7..d96f5d543f 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -12,9 +12,12 @@ #include #include "mmc_private.h" #include +#include #include #include #include +#include +#include #include #include #include @@ -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,17 +707,49 @@ static const struct sdhci_ops arasan_ops = { }; #endif -static int arasan_sdhci_probe(struct udevice *dev) +#if defined(CONFIG_ARCH_ZYNQMP) +static int sdhci_zynqmp_set_dynamic_config(struct arasan_sdhci_priv *priv, + struct udevice *dev) { - struct arasan_sdhci_plat *plat = dev_get_plat(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - struct arasan_sdhci_priv *priv = dev_get_priv(dev); - struct sdhci_host *host; - struct clk clk; - unsigned long clock; int ret; + u32 node_id = priv->deviceid ? NODE_SD_1 : NODE_SD_0; + struct clk clk; + unsigned long clock, mhz; - host = priv->host; + 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) { @@ -727,7 +763,68 @@ static int arasan_sdhci_probe(struct udevice *dev) return clock; } - debug("%s: CLK %ld\n", __func__, 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); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct arasan_sdhci_priv *priv = dev_get_priv(dev); + struct sdhci_host *host; + struct clk clk; + unsigned long clock; + int ret; + + 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"); + return ret; + } + + clock = clk_get_rate(&clk); + if (IS_ERR_VALUE(clock)) { + dev_err(dev, "failed to get rate\n"); + return clock; + } + + dev_dbg(dev, "%s: CLK %ld\n", __func__, clock); ret = clk_enable(&clk); if (ret) { From 98cacab76542dba7fa7d42e32fc848d89d88d55a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 15:52:02 +0100 Subject: [PATCH 25/39] video: Add skeleton driver for ZynqMP Display port driver The reason for this driver is to use call power management driver to enable it in PMUFW. There is missing functionality now but should be added in near future. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/598cb9515bbabc803f72e287464e3d107cd106a3.1645627920.git.michal.simek@xilinx.com --- drivers/video/Kconfig | 8 +++++ drivers/video/Makefile | 1 + drivers/video/zynqmp_dpsub.c | 66 ++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 drivers/video/zynqmp_dpsub.c 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 +#include +#include +#include +#include +#include + +#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), +}; From d926695cc5680edfff9cfef36a14933ee3585fbf Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 15:52:03 +0100 Subject: [PATCH 26/39] dma: xilinx: Add Display Port DMA driver Display Port (DP) has own dma driver that's why add this skeleton driver only for handling power domain setting and send configuration object to PMUFW to enable it. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/fe8bc313bcd430b04e9fa6fb770d5799ef28b350.1645627920.git.michal.simek@xilinx.com --- drivers/dma/Kconfig | 7 +++++++ drivers/dma/Makefile | 1 + drivers/dma/xilinx_dpdma.c | 43 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 drivers/dma/xilinx_dpdma.c 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 +#include +#include +#include +#include +#include +#include + +/** + * 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), +}; From 1bc2a79a4cde5f703b33342f2fcea8af208614ed Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Tue, 1 Mar 2022 09:16:50 +0100 Subject: [PATCH 27/39] i2c: i2c-cdns: Start read transaction after write to transfer_size reg Avoid a race condition where read transaction is started keeping expected bytes as 0. Which sometimes would result in sending STOP signal as no data is expected. Observed on QEMU platform. Signed-off-by: Sai Pavan Boddu Reviewed-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/487c8026791bfd60719403a2df2c54bb0ae99232.1646122610.git.michal.simek@xilinx.com --- drivers/i2c/i2c-cdns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c index a650dd69b8..5736afb451 100644 --- a/drivers/i2c/i2c-cdns.c +++ b/drivers/i2c/i2c-cdns.c @@ -375,7 +375,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, ®s->address); if (recv_count > CDNS_I2C_TRANSFER_SIZE) { writel(CDNS_I2C_TRANSFER_SIZE, ®s->transfer_size); @@ -384,6 +383,7 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data, writel(recv_count, ®s->transfer_size); curr_recv_count = recv_count; } + writel(addr, ®s->address); } } From 94b3f3fc7d5eece585a688061fa7a32a1a1c8495 Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Tue, 1 Mar 2022 09:16:51 +0100 Subject: [PATCH 28/39] i2c: i2c-cdns: Fix write transaction state Start write transfer after loading data to FIFO. Signed-off-by: Sai Pavan Boddu Reviewed-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/f0b3e443daa7758e00dfdcc245cf6b2120b0e907.1646122610.git.michal.simek@xilinx.com --- drivers/i2c/i2c-cdns.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c index 5736afb451..07d53be11f 100644 --- a/drivers/i2c/i2c-cdns.c +++ b/drivers/i2c/i2c-cdns.c @@ -251,6 +251,7 @@ 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(®s->control, CDNS_I2C_CONTROL_CLR_FIFO); @@ -269,6 +270,11 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data, while (len-- && !is_arbitration_lost(regs)) { writel(*(cur_data++), ®s->data); + /* Trigger write only after loading data */ + if (start) { + writel(addr, ®s->address); + start = 0; + } if (len && readl(®s->transfer_size) == CDNS_I2C_FIFO_DEPTH) { ret = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP | CDNS_I2C_INTERRUPT_ARBLOST); From f76f86029d75df2546e734e3ae1a50fa62d72526 Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Tue, 1 Mar 2022 09:16:52 +0100 Subject: [PATCH 29/39] i2c: i2c-cdns: Prevent early termination of write During sequential loading of data, hold the bus to prevent controller from sending stop signal in case no data is available in fifo. Signed-off-by: Sai Pavan Boddu Reviewed-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/2407b39d305999cb42438c5423aebc3b514acabb.1646122610.git.michal.simek@xilinx.com --- drivers/i2c/i2c-cdns.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c index 07d53be11f..0da9f6f35a 100644 --- a/drivers/i2c/i2c-cdns.c +++ b/drivers/i2c/i2c-cdns.c @@ -257,16 +257,18 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data, setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO); clrbits_le32(®s->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(®s->control, CDNS_I2C_CONTROL_HOLD); /* Clear the interrupts in status register */ writel(CDNS_I2C_INTERRUPTS_MASK, ®s->interrupt_status); - writel(addr, ®s->address); + /* In case of Probe (i.e no data), start the transfer */ + if (!len) + writel(addr, ®s->address); while (len-- && !is_arbitration_lost(regs)) { writel(*(cur_data++), ®s->data); From 9fb9ec5e24f95bef8f342812b159de38af4a27c6 Mon Sep 17 00:00:00 2001 From: Neal Frager Date: Wed, 2 Mar 2022 15:12:02 +0100 Subject: [PATCH 30/39] arm64: zynqmp: add support for zcu106 rev1.0 This patch adds psu_init for Xilinx ZCU106 rev1.0. Xilinx ZCU106 rev1.0 has newer x16 DDR4 memories and it is SW compatible with revA. Signed-off-by: Neal Frager Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/997b3e23457e4d24ce0e197d742382aaec36c2b2.1646230318.git.michal.simek@xilinx.com --- arch/arm/dts/Makefile | 1 + arch/arm/dts/zynqmp-zcu106-rev1.0.dts | 16 + .../zynqmp-zcu106-rev1.0/psu_init_gpl.c | 842 ++++++++++++++++++ configs/xilinx_zynqmp_virt_defconfig | 2 +- 4 files changed, 860 insertions(+), 1 deletion(-) create mode 100644 arch/arm/dts/zynqmp-zcu106-rev1.0.dts create mode 100644 board/xilinx/zynqmp/zynqmp-zcu106-rev1.0/psu_init_gpl.c diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 960f1a9fd4..2edcb26d7c 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -349,6 +349,7 @@ dtb-$(CONFIG_ARCH_ZYNQMP) += \ zynqmp-zcu104-revA.dtb \ zynqmp-zcu104-revC.dtb \ zynqmp-zcu106-revA.dtb \ + zynqmp-zcu106-rev1.0.dtb \ zynqmp-zcu111-revA.dtb \ zynqmp-zcu1275-revA.dtb \ zynqmp-zcu1275-revB.dtb \ diff --git a/arch/arm/dts/zynqmp-zcu106-rev1.0.dts b/arch/arm/dts/zynqmp-zcu106-rev1.0.dts new file mode 100644 index 0000000000..f43c477a17 --- /dev/null +++ b/arch/arm/dts/zynqmp-zcu106-rev1.0.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * dts file for Xilinx ZynqMP ZCU106 Rev1.0 + * + * (C) Copyright 2016 - 2022, Xilinx, Inc. + * + * Michal Simek + */ + +#include "zynqmp-zcu106-revA.dts" + +/ { + model = "ZynqMP ZCU106 Rev1.0"; + compatible = "xlnx,zynqmp-zcu106-rev1.0", "xlnx,zynqmp-zcu106-revA", + "xlnx,zynqmp-zcu106", "xlnx,zynqmp"; +}; diff --git a/board/xilinx/zynqmp/zynqmp-zcu106-rev1.0/psu_init_gpl.c b/board/xilinx/zynqmp/zynqmp-zcu106-rev1.0/psu_init_gpl.c new file mode 100644 index 0000000000..2ac4e035d8 --- /dev/null +++ b/board/xilinx/zynqmp/zynqmp-zcu106-rev1.0/psu_init_gpl.c @@ -0,0 +1,842 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (c) Copyright 2015 Xilinx, Inc. All rights reserved. + */ + +#include +#include + +static unsigned long psu_pll_init_data(void) +{ + psu_mask_write(0xFF5E0034, 0xFE7FEDEFU, 0x7E4E2C62U); + psu_mask_write(0xFF5E0030, 0x00717F00U, 0x00014600U); + psu_mask_write(0xFF5E0030, 0x00000008U, 0x00000008U); + psu_mask_write(0xFF5E0030, 0x00000001U, 0x00000001U); + psu_mask_write(0xFF5E0030, 0x00000001U, 0x00000000U); + mask_poll(0xFF5E0040, 0x00000002U); + psu_mask_write(0xFF5E0030, 0x00000008U, 0x00000000U); + psu_mask_write(0xFF5E0048, 0x00003F00U, 0x00000200U); + psu_mask_write(0xFF5E0024, 0xFE7FEDEFU, 0x7E4B0C82U); + psu_mask_write(0xFF5E0020, 0x00717F00U, 0x00015A00U); + psu_mask_write(0xFF5E0020, 0x00000008U, 0x00000008U); + psu_mask_write(0xFF5E0020, 0x00000001U, 0x00000001U); + psu_mask_write(0xFF5E0020, 0x00000001U, 0x00000000U); + mask_poll(0xFF5E0040, 0x00000001U); + psu_mask_write(0xFF5E0020, 0x00000008U, 0x00000000U); + psu_mask_write(0xFF5E0044, 0x00003F00U, 0x00000300U); + psu_mask_write(0xFD1A0024, 0xFE7FEDEFU, 0x7E4B0C62U); + psu_mask_write(0xFD1A0020, 0x00717F00U, 0x00014800U); + psu_mask_write(0xFD1A0020, 0x00000008U, 0x00000008U); + psu_mask_write(0xFD1A0020, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD1A0020, 0x00000001U, 0x00000000U); + mask_poll(0xFD1A0044, 0x00000001U); + psu_mask_write(0xFD1A0020, 0x00000008U, 0x00000000U); + psu_mask_write(0xFD1A0048, 0x00003F00U, 0x00000300U); + psu_mask_write(0xFD1A0030, 0xFE7FEDEFU, 0x7E4B0C62U); + psu_mask_write(0xFD1A002C, 0x00717F00U, 0x00014000U); + psu_mask_write(0xFD1A002C, 0x00000008U, 0x00000008U); + psu_mask_write(0xFD1A002C, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD1A002C, 0x00000001U, 0x00000000U); + mask_poll(0xFD1A0044, 0x00000002U); + psu_mask_write(0xFD1A002C, 0x00000008U, 0x00000000U); + psu_mask_write(0xFD1A004C, 0x00003F00U, 0x00000200U); + psu_mask_write(0xFD1A003C, 0xFE7FEDEFU, 0x7E4B0C82U); + psu_mask_write(0xFD1A0038, 0x00717F00U, 0x00015900U); + psu_mask_write(0xFD1A0038, 0x00000008U, 0x00000008U); + psu_mask_write(0xFD1A0038, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD1A0038, 0x00000001U, 0x00000000U); + mask_poll(0xFD1A0044, 0x00000004U); + psu_mask_write(0xFD1A0038, 0x00000008U, 0x00000000U); + psu_mask_write(0xFD1A0050, 0x00003F00U, 0x00000300U); + psu_mask_write(0xFD1A0040, 0x8000FFFFU, 0x80008E69U); + + return 1; +} + +static unsigned long psu_clock_init_data(void) +{ + psu_mask_write(0xFF5E005C, 0x063F3F07U, 0x06010C00U); + psu_mask_write(0xFF5E0060, 0x023F3F07U, 0x02010600U); + psu_mask_write(0xFF5E004C, 0x023F3F07U, 0x02031900U); + psu_mask_write(0xFF5E0068, 0x013F3F07U, 0x01010500U); + psu_mask_write(0xFF5E0070, 0x013F3F07U, 0x01010502U); + psu_mask_write(0xFF18030C, 0x00020000U, 0x00000000U); + psu_mask_write(0xFF5E0074, 0x013F3F07U, 0x01010F00U); + psu_mask_write(0xFF5E0078, 0x013F3F07U, 0x01010F00U); + psu_mask_write(0xFF5E0120, 0x013F3F07U, 0x01010F00U); + psu_mask_write(0xFF5E0124, 0x013F3F07U, 0x01010F00U); + psu_mask_write(0xFF5E0088, 0x013F3F07U, 0x01010F00U); + psu_mask_write(0xFF5E0090, 0x01003F07U, 0x01000302U); + psu_mask_write(0xFF5E009C, 0x01003F07U, 0x01000602U); + psu_mask_write(0xFF5E00A4, 0x01003F07U, 0x01000602U); + psu_mask_write(0xFF5E00A8, 0x01003F07U, 0x01000302U); + psu_mask_write(0xFF5E00AC, 0x01003F07U, 0x01000F02U); + psu_mask_write(0xFF5E00B0, 0x01003F07U, 0x01000602U); + psu_mask_write(0xFF5E00B8, 0x01003F07U, 0x01000302U); + psu_mask_write(0xFF5E00C0, 0x013F3F07U, 0x01010A02U); + psu_mask_write(0xFF5E00C4, 0x013F3F07U, 0x01010F00U); + psu_mask_write(0xFF5E00C8, 0x013F3F07U, 0x01010A02U); + psu_mask_write(0xFF5E00CC, 0x013F3F07U, 0x01010A02U); + psu_mask_write(0xFF5E0108, 0x013F3F07U, 0x01011D02U); + psu_mask_write(0xFF5E0104, 0x00000007U, 0x00000000U); + psu_mask_write(0xFF5E0128, 0x01003F07U, 0x01000F00U); + psu_mask_write(0xFD1A00A0, 0x01003F07U, 0x01000200U); + psu_mask_write(0xFD1A0070, 0x013F3F07U, 0x01010203U); + psu_mask_write(0xFD1A0074, 0x013F3F07U, 0x01013C00U); + psu_mask_write(0xFD1A007C, 0x013F3F07U, 0x01011303U); + psu_mask_write(0xFD1A0060, 0x03003F07U, 0x03000100U); + psu_mask_write(0xFD1A0068, 0x01003F07U, 0x01000200U); + psu_mask_write(0xFD1A0080, 0x00003F07U, 0x00000200U); + psu_mask_write(0xFD1A0084, 0x07003F07U, 0x07000100U); + psu_mask_write(0xFD1A00B8, 0x01003F07U, 0x01000200U); + psu_mask_write(0xFD1A00BC, 0x01003F07U, 0x01000200U); + psu_mask_write(0xFD1A00C0, 0x01003F07U, 0x01000302U); + psu_mask_write(0xFD1A00C4, 0x01003F07U, 0x01000502U); + psu_mask_write(0xFD1A00F8, 0x00003F07U, 0x00000200U); + psu_mask_write(0xFF180380, 0x000000FFU, 0x00000000U); + psu_mask_write(0xFD610100, 0x00000001U, 0x00000000U); + psu_mask_write(0xFF180300, 0x00000001U, 0x00000000U); + psu_mask_write(0xFF410050, 0x00000001U, 0x00000000U); + + return 1; +} + +static unsigned long psu_ddr_init_data(void) +{ + psu_mask_write(0xFD1A0108, 0x00000008U, 0x00000008U); + psu_mask_write(0xFD070000, 0xE30FBE3DU, 0x81040010U); + psu_mask_write(0xFD070010, 0x8000F03FU, 0x00000030U); + psu_mask_write(0xFD070020, 0x000003F3U, 0x00000200U); + psu_mask_write(0xFD070024, 0xFFFFFFFFU, 0x00800000U); + psu_mask_write(0xFD070030, 0x0000007FU, 0x00000000U); + psu_mask_write(0xFD070034, 0x00FFFF1FU, 0x00408410U); + psu_mask_write(0xFD070050, 0x00F1F1F4U, 0x00210000U); + psu_mask_write(0xFD070054, 0x0FFF0FFFU, 0x00000000U); + psu_mask_write(0xFD070060, 0x00000073U, 0x00000001U); + psu_mask_write(0xFD070064, 0x0FFF83FFU, 0x008180BBU); + psu_mask_write(0xFD070070, 0x00000017U, 0x00000010U); + psu_mask_write(0xFD070074, 0x00000003U, 0x00000000U); + psu_mask_write(0xFD0700C4, 0x3F000391U, 0x10000200U); + psu_mask_write(0xFD0700C8, 0x01FF1F3FU, 0x0040051FU); + psu_mask_write(0xFD0700D0, 0xC3FF0FFFU, 0x00020106U); + psu_mask_write(0xFD0700D4, 0x01FF7F0FU, 0x00020000U); + psu_mask_write(0xFD0700D8, 0x0000FF0FU, 0x00002305U); + psu_mask_write(0xFD0700DC, 0xFFFFFFFFU, 0x07300301U); + psu_mask_write(0xFD0700E0, 0xFFFFFFFFU, 0x00200200U); + psu_mask_write(0xFD0700E4, 0x00FF03FFU, 0x00210004U); + psu_mask_write(0xFD0700E8, 0xFFFFFFFFU, 0x000006C0U); + psu_mask_write(0xFD0700EC, 0xFFFF0000U, 0x08190000U); + psu_mask_write(0xFD0700F0, 0x0000003FU, 0x00000010U); + psu_mask_write(0xFD0700F4, 0x00000FFFU, 0x0000066FU); + psu_mask_write(0xFD070100, 0x7F3F7F3FU, 0x11102412U); + psu_mask_write(0xFD070104, 0x001F1F7FU, 0x0004041AU); + psu_mask_write(0xFD070108, 0x3F3F3F3FU, 0x0708060EU); + psu_mask_write(0xFD07010C, 0x3FF3F3FFU, 0x0050400CU); + psu_mask_write(0xFD070110, 0x1F0F0F1FU, 0x08030409U); + psu_mask_write(0xFD070114, 0x0F0F3F1FU, 0x06060403U); + psu_mask_write(0xFD070118, 0x0F0F000FU, 0x01010004U); + psu_mask_write(0xFD07011C, 0x00000F0FU, 0x00000606U); + psu_mask_write(0xFD070120, 0x7F7F7F7FU, 0x04040D07U); + psu_mask_write(0xFD070124, 0x40070F3FU, 0x0002030BU); + psu_mask_write(0xFD07012C, 0x7F1F031FU, 0x1207010EU); + psu_mask_write(0xFD070130, 0x00030F1FU, 0x00020608U); + psu_mask_write(0xFD070180, 0xF7FF03FFU, 0x81000040U); + psu_mask_write(0xFD070184, 0x3FFFFFFFU, 0x020196DCU); + psu_mask_write(0xFD070190, 0x1FBFBF3FU, 0x048B820BU); + psu_mask_write(0xFD070194, 0xF31F0F0FU, 0x00030304U); + psu_mask_write(0xFD070198, 0x0FF1F1F1U, 0x07000101U); + psu_mask_write(0xFD07019C, 0x000000F1U, 0x00000021U); + psu_mask_write(0xFD0701A0, 0xC3FF03FFU, 0x00400003U); + psu_mask_write(0xFD0701A4, 0x00FF00FFU, 0x00C800FFU); + psu_mask_write(0xFD0701B0, 0x00000007U, 0x00000000U); + psu_mask_write(0xFD0701B4, 0x00003F3FU, 0x00000909U); + psu_mask_write(0xFD0701C0, 0x00000007U, 0x00000001U); + psu_mask_write(0XFD070200, 0x0000001FU, 0x0000001FU); + psu_mask_write(0XFD070204, 0x001F1F1FU, 0x001F0909U); + psu_mask_write(0XFD070208, 0x0F0F0F0FU, 0x01010100U); + psu_mask_write(0XFD07020C, 0x0F0F0F0FU, 0x01010101U); + psu_mask_write(0XFD070210, 0x00000F0FU, 0x00000F0FU); + psu_mask_write(0XFD070214, 0x0F0F0F0FU, 0x070F0707U); + psu_mask_write(0XFD070218, 0x8F0F0F0FU, 0x07070707U); + psu_mask_write(0XFD07021C, 0x00000F0FU, 0x00000F0FU); + psu_mask_write(0XFD070220, 0x00001F1FU, 0x00001F01U); + psu_mask_write(0XFD070224, 0x0F0F0F0FU, 0x07070707U); + psu_mask_write(0XFD070228, 0x0F0F0F0FU, 0x07070707U); + psu_mask_write(0XFD07022C, 0x0000000FU, 0x00000007U); + psu_mask_write(0xFD070240, 0x0F1F0F7CU, 0x06000600U); + psu_mask_write(0xFD070244, 0x00003333U, 0x00000001U); + psu_mask_write(0xFD070250, 0x7FFF3F07U, 0x01002001U); + psu_mask_write(0xFD070264, 0xFF00FFFFU, 0x08000040U); + psu_mask_write(0xFD07026C, 0xFF00FFFFU, 0x08000040U); + psu_mask_write(0xFD070280, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD070284, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD070288, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD07028C, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD070290, 0x0000FFFFU, 0x00000000U); + psu_mask_write(0xFD070294, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD070300, 0x00000011U, 0x00000000U); + psu_mask_write(0xFD07030C, 0x80000033U, 0x00000000U); + psu_mask_write(0xFD070320, 0x00000001U, 0x00000000U); + psu_mask_write(0xFD070400, 0x00000111U, 0x00000001U); + psu_mask_write(0xFD070404, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD070408, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD070490, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD070494, 0x0033000FU, 0x0020000BU); + psu_mask_write(0xFD070498, 0x07FF07FFU, 0x00000000U); + psu_mask_write(0xFD0704B4, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD0704B8, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD070540, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD070544, 0x03330F0FU, 0x02000B03U); + psu_mask_write(0xFD070548, 0x07FF07FFU, 0x00000000U); + psu_mask_write(0xFD070564, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD070568, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD0705F0, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD0705F4, 0x03330F0FU, 0x02000B03U); + psu_mask_write(0xFD0705F8, 0x07FF07FFU, 0x00000000U); + psu_mask_write(0xFD070614, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD070618, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD0706A0, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD0706A4, 0x0033000FU, 0x00100003U); + psu_mask_write(0xFD0706A8, 0x07FF07FFU, 0x0000004FU); + psu_mask_write(0xFD0706AC, 0x0033000FU, 0x00100003U); + psu_mask_write(0xFD0706B0, 0x000007FFU, 0x0000004FU); + psu_mask_write(0xFD0706C4, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD0706C8, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD070750, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD070754, 0x0033000FU, 0x00100003U); + psu_mask_write(0xFD070758, 0x07FF07FFU, 0x0000004FU); + psu_mask_write(0xFD07075C, 0x0033000FU, 0x00100003U); + psu_mask_write(0xFD070760, 0x000007FFU, 0x0000004FU); + psu_mask_write(0xFD070774, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD070778, 0x000073FFU, 0x0000200FU); + psu_mask_write(0xFD070800, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD070804, 0x0033000FU, 0x00100003U); + psu_mask_write(0xFD070808, 0x07FF07FFU, 0x0000004FU); + psu_mask_write(0xFD07080C, 0x0033000FU, 0x00100003U); + psu_mask_write(0xFD070810, 0x000007FFU, 0x0000004FU); + psu_mask_write(0xFD070F04, 0x000001FFU, 0x00000000U); + psu_mask_write(0xFD070F08, 0x000000FFU, 0x00000000U); + psu_mask_write(0xFD070F0C, 0x000001FFU, 0x00000010U); + psu_mask_write(0xFD070F10, 0x000000FFU, 0x0000000FU); + psu_mask_write(0xFD072190, 0x1FBFBF3FU, 0x07828002U); + psu_mask_write(0xFD1A0108, 0x00000008U, 0x00000000U); + psu_mask_write(0xFD080010, 0xFFFFFFFFU, 0x07001E00U); + psu_mask_write(0xFD080018, 0xFFFFFFFFU, 0x00F10010U); + psu_mask_write(0xFD08001C, 0xFFFFFFFFU, 0x55AA5480U); + psu_mask_write(0xFD080024, 0xFFFFFFFFU, 0x010100F4U); + psu_mask_write(0xFD080040, 0xFFFFFFFFU, 0x42C21590U); + psu_mask_write(0xFD080044, 0xFFFFFFFFU, 0xD05112C0U); + psu_mask_write(0xFD080068, 0xFFFFFFFFU, 0x01100000U); + psu_mask_write(0xFD080090, 0xFFFFFFFFU, 0x02A04161U); + psu_mask_write(0XFD0800C0, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD0800C4, 0xFFFFFFFFU, 0x000000E5U); + psu_mask_write(0xFD080100, 0xFFFFFFFFU, 0x0800040CU); + psu_mask_write(0xFD080110, 0xFFFFFFFFU, 0x07240F08U); + psu_mask_write(0xFD080114, 0xFFFFFFFFU, 0x28200008U); + psu_mask_write(0xFD080118, 0xFFFFFFFFU, 0x000F0300U); + psu_mask_write(0xFD08011C, 0xFFFFFFFFU, 0x83000800U); + psu_mask_write(0xFD080120, 0xFFFFFFFFU, 0x01762B07U); + psu_mask_write(0xFD080124, 0xFFFFFFFFU, 0x00330F08U); + psu_mask_write(0xFD080128, 0xFFFFFFFFU, 0x00000E0FU); + psu_mask_write(0xFD080140, 0xFFFFFFFFU, 0x08400020U); + psu_mask_write(0xFD080144, 0xFFFFFFFFU, 0x00000C80U); + psu_mask_write(0xFD080150, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080154, 0xFFFFFFFFU, 0x00000300U); + psu_mask_write(0xFD080180, 0xFFFFFFFFU, 0x00000630U); + psu_mask_write(0xFD080184, 0xFFFFFFFFU, 0x00000301U); + psu_mask_write(0xFD080188, 0xFFFFFFFFU, 0x00000020U); + psu_mask_write(0xFD08018C, 0xFFFFFFFFU, 0x00000200U); + psu_mask_write(0xFD080190, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080194, 0xFFFFFFFFU, 0x000006C0U); + psu_mask_write(0xFD080198, 0xFFFFFFFFU, 0x00000819U); + psu_mask_write(0xFD0801AC, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD0801B0, 0xFFFFFFFFU, 0x0000004DU); + psu_mask_write(0xFD0801B4, 0xFFFFFFFFU, 0x00000008U); + psu_mask_write(0xFD0801B8, 0xFFFFFFFFU, 0x0000004DU); + psu_mask_write(0xFD0801D8, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080200, 0xFFFFFFFFU, 0x800091C7U); + psu_mask_write(0xFD080204, 0xFFFFFFFFU, 0x00010236U); + psu_mask_write(0xFD080240, 0xFFFFFFFFU, 0x00141054U); + psu_mask_write(0xFD080250, 0xFFFFFFFFU, 0x00088000U); + psu_mask_write(0xFD080414, 0xFFFFFFFFU, 0x12341000U); + psu_mask_write(0xFD0804F4, 0xFFFFFFFFU, 0x00000005U); + psu_mask_write(0xFD080500, 0xFFFFFFFFU, 0x30000028U); + psu_mask_write(0xFD080508, 0xFFFFFFFFU, 0x0A000000U); + psu_mask_write(0xFD08050C, 0xFFFFFFFFU, 0x00000009U); + psu_mask_write(0xFD080510, 0xFFFFFFFFU, 0x0A000000U); + psu_mask_write(0xFD080520, 0xFFFFFFFFU, 0x0300B0CEU); + psu_mask_write(0xFD080528, 0xFFFFFFFFU, 0xF9032019U); + psu_mask_write(0xFD08052C, 0xFFFFFFFFU, 0x07F001E3U); + psu_mask_write(0xFD080544, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080548, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080558, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD08055C, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080560, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080564, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080680, 0xFFFFFFFFU, 0x008AAA58U); + psu_mask_write(0xFD080684, 0xFFFFFFFFU, 0x000079DDU); + psu_mask_write(0xFD080694, 0xFFFFFFFFU, 0x01E10210U); + psu_mask_write(0xFD080698, 0xFFFFFFFFU, 0x01E10000U); + psu_mask_write(0xFD0806A4, 0xFFFFFFFFU, 0x00087BDBU); + psu_mask_write(0xFD080700, 0xFFFFFFFFU, 0x40800604U); + psu_mask_write(0xFD080704, 0xFFFFFFFFU, 0x00007FFFU); + psu_mask_write(0xFD08070C, 0xFFFFFFFFU, 0x3F000008U); + psu_mask_write(0xFD080710, 0xFFFFFFFFU, 0x0E00B03CU); + psu_mask_write(0xFD080714, 0xFFFFFFFFU, 0x09095555U); + psu_mask_write(0xFD080718, 0xFFFFFFFFU, 0x09092B2BU); + psu_mask_write(0xFD080800, 0xFFFFFFFFU, 0x40800604U); + psu_mask_write(0xFD080804, 0xFFFFFFFFU, 0x00007FFFU); + psu_mask_write(0xFD08080C, 0xFFFFFFFFU, 0x3F000008U); + psu_mask_write(0xFD080810, 0xFFFFFFFFU, 0x0E00B03CU); + psu_mask_write(0xFD080814, 0xFFFFFFFFU, 0x09095555U); + psu_mask_write(0xFD080818, 0xFFFFFFFFU, 0x09092B2BU); + psu_mask_write(0xFD080900, 0xFFFFFFFFU, 0x40800604U); + psu_mask_write(0xFD080904, 0xFFFFFFFFU, 0x00007FFFU); + psu_mask_write(0xFD08090C, 0xFFFFFFFFU, 0x3F000008U); + psu_mask_write(0xFD080910, 0xFFFFFFFFU, 0x0E00B004U); + psu_mask_write(0xFD080914, 0xFFFFFFFFU, 0x09095555U); + psu_mask_write(0xFD080918, 0xFFFFFFFFU, 0x09092B2BU); + psu_mask_write(0xFD080A00, 0xFFFFFFFFU, 0x40800604U); + psu_mask_write(0xFD080A04, 0xFFFFFFFFU, 0x00007FFFU); + psu_mask_write(0xFD080A0C, 0xFFFFFFFFU, 0x3F000008U); + psu_mask_write(0xFD080A10, 0xFFFFFFFFU, 0x0E00B004U); + psu_mask_write(0xFD080A14, 0xFFFFFFFFU, 0x09095555U); + psu_mask_write(0xFD080A18, 0xFFFFFFFFU, 0x09092B2BU); + psu_mask_write(0xFD080B00, 0xFFFFFFFFU, 0x40800604U); + psu_mask_write(0xFD080B04, 0xFFFFFFFFU, 0x00007FFFU); + psu_mask_write(0XFD080B08, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080B0C, 0xFFFFFFFFU, 0x3F000008U); + psu_mask_write(0xFD080B10, 0xFFFFFFFFU, 0x0E00B004U); + psu_mask_write(0xFD080B14, 0xFFFFFFFFU, 0x09095555U); + psu_mask_write(0xFD080B18, 0xFFFFFFFFU, 0x09092B2BU); + psu_mask_write(0xFD080C00, 0xFFFFFFFFU, 0x40800604U); + psu_mask_write(0xFD080C04, 0xFFFFFFFFU, 0x00007FFFU); + psu_mask_write(0XFD080C08, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080C0C, 0xFFFFFFFFU, 0x3F000008U); + psu_mask_write(0xFD080C10, 0xFFFFFFFFU, 0x0E00B03CU); + psu_mask_write(0xFD080C14, 0xFFFFFFFFU, 0x09095555U); + psu_mask_write(0xFD080C18, 0xFFFFFFFFU, 0x09092B2BU); + psu_mask_write(0xFD080D00, 0xFFFFFFFFU, 0x40800604U); + psu_mask_write(0xFD080D04, 0xFFFFFFFFU, 0x00007FFFU); + psu_mask_write(0XFD080D08, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080D0C, 0xFFFFFFFFU, 0x3F000008U); + psu_mask_write(0xFD080D10, 0xFFFFFFFFU, 0x0E00B004U); + psu_mask_write(0xFD080D14, 0xFFFFFFFFU, 0x09095555U); + psu_mask_write(0xFD080D18, 0xFFFFFFFFU, 0x09092B2BU); + psu_mask_write(0xFD080E00, 0xFFFFFFFFU, 0x40800604U); + psu_mask_write(0xFD080E04, 0xFFFFFFFFU, 0x00007FFFU); + psu_mask_write(0XFD080E08, 0xFFFFFFFFU, 0x00000000U); + psu_mask_write(0xFD080E0C, 0xFFFFFFFFU, 0x3F000008U); + psu_mask_write(0xFD080E10, 0xFFFFFFFFU, 0x0E00B03CU); + psu_mask_write(0xFD080E14, 0xFFFFFFFFU, 0x09095555U); + psu_mask_write(0xFD080E18, 0xFFFFFFFFU, 0x09092B2BU); + psu_mask_write(0xFD080F00, 0xFFFFFFFFU, 0x80803660U); + psu_mask_write(0xFD080F04, 0xFFFFFFFFU, 0x55556000U); + psu_mask_write(0xFD080F08, 0xFFFFFFFFU, 0xAAAAAAAAU); + psu_mask_write(0xFD080F0C, 0xFFFFFFFFU, 0x0029A4A4U); + psu_mask_write(0xFD080F10, 0xFFFFFFFFU, 0x0C00B000U); + psu_mask_write(0xFD080F14, 0xFFFFFFFFU, 0x09095555U); + psu_mask_write(0xFD080F18, 0xFFFFFFFFU, 0x09092B2BU); + psu_mask_write(0xFD081400, 0xFFFFFFFFU, 0x2A019FFEU); + psu_mask_write(0xFD081404, 0xFFFFFFFFU, 0x01100000U); + psu_mask_write(0xFD08141C, 0xFFFFFFFFU, 0x01264300U); + psu_mask_write(0xFD08142C, 0xFFFFFFFFU, 0x00041800U); + psu_mask_write(0xFD081430, 0xFFFFFFFFU, 0x70800000U); + psu_mask_write(0xFD081440, 0xFFFFFFFFU, 0x2A019FFEU); + psu_mask_write(0xFD081444, 0xFFFFFFFFU, 0x01100000U); + psu_mask_write(0xFD08145C, 0xFFFFFFFFU, 0x01264300U); + psu_mask_write(0xFD08146C, 0xFFFFFFFFU, 0x00041800U); + psu_mask_write(0xFD081470, 0xFFFFFFFFU, 0x70800000U); + psu_mask_write(0xFD081480, 0xFFFFFFFFU, 0x2A019FFEU); + psu_mask_write(0xFD081484, 0xFFFFFFFFU, 0x01100000U); + psu_mask_write(0xFD08149C, 0xFFFFFFFFU, 0x01264300U); + psu_mask_write(0xFD0814AC, 0xFFFFFFFFU, 0x00041800U); + psu_mask_write(0xFD0814B0, 0xFFFFFFFFU, 0x70800000U); + psu_mask_write(0xFD0814C0, 0xFFFFFFFFU, 0x2A019FFEU); + psu_mask_write(0xFD0814C4, 0xFFFFFFFFU, 0x01100000U); + psu_mask_write(0xFD0814DC, 0xFFFFFFFFU, 0x01264300U); + psu_mask_write(0xFD0814EC, 0xFFFFFFFFU, 0x00041800U); + psu_mask_write(0xFD0814F0, 0xFFFFFFFFU, 0x70800000U); + psu_mask_write(0xFD081500, 0xFFFFFFFFU, 0x15019FFEU); + psu_mask_write(0xFD081504, 0xFFFFFFFFU, 0x21100000U); + psu_mask_write(0xFD08151C, 0xFFFFFFFFU, 0x01266300U); + psu_mask_write(0xFD08152C, 0xFFFFFFFFU, 0x00041800U); + psu_mask_write(0xFD081530, 0xFFFFFFFFU, 0x70400000U); + psu_mask_write(0xFD0817C4, 0xFFFFFFFFU, 0x01100000U); + psu_mask_write(0xFD0817DC, 0xFFFFFFFFU, 0x012643C4U); + + return 1; +} + +static unsigned long psu_mio_init_data(void) +{ + psu_mask_write(0xFF180000, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180004, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180008, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF18000C, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180010, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180014, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180018, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF18001C, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180020, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180024, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180028, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF18002C, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180030, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180034, 0x000000FEU, 0x00000000U); + psu_mask_write(0xFF180038, 0x000000FEU, 0x00000040U); + psu_mask_write(0xFF18003C, 0x000000FEU, 0x00000040U); + psu_mask_write(0xFF180040, 0x000000FEU, 0x00000040U); + psu_mask_write(0xFF180044, 0x000000FEU, 0x00000040U); + psu_mask_write(0xFF180048, 0x000000FEU, 0x000000C0U); + psu_mask_write(0xFF18004C, 0x000000FEU, 0x000000C0U); + psu_mask_write(0xFF180050, 0x000000FEU, 0x000000C0U); + psu_mask_write(0xFF180054, 0x000000FEU, 0x000000C0U); + psu_mask_write(0xFF180058, 0x000000FEU, 0x00000000U); + psu_mask_write(0xFF18005C, 0x000000FEU, 0x00000000U); + psu_mask_write(0xFF180060, 0x000000FEU, 0x00000020U); + psu_mask_write(0xFF180064, 0x000000FEU, 0x00000020U); + psu_mask_write(0xFF180068, 0x000000FEU, 0x00000000U); + psu_mask_write(0xFF18006C, 0x000000FEU, 0x00000018U); + psu_mask_write(0xFF180070, 0x000000FEU, 0x00000018U); + psu_mask_write(0xFF180074, 0x000000FEU, 0x00000018U); + psu_mask_write(0xFF180078, 0x000000FEU, 0x00000018U); + psu_mask_write(0xFF18007C, 0x000000FEU, 0x00000000U); + psu_mask_write(0xFF180080, 0x000000FEU, 0x00000008U); + psu_mask_write(0xFF180084, 0x000000FEU, 0x00000008U); + psu_mask_write(0xFF180088, 0x000000FEU, 0x00000008U); + psu_mask_write(0xFF18008C, 0x000000FEU, 0x00000008U); + psu_mask_write(0xFF180090, 0x000000FEU, 0x00000008U); + psu_mask_write(0xFF180094, 0x000000FEU, 0x00000008U); + psu_mask_write(0xFF180098, 0x000000FEU, 0x00000000U); + psu_mask_write(0xFF18009C, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800A0, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800A4, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800A8, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800AC, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800B0, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800B4, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800B8, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800BC, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800C0, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800C4, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800C8, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800CC, 0x000000FEU, 0x00000010U); + psu_mask_write(0xFF1800D0, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800D4, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800D8, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800DC, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800E0, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800E4, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800E8, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800EC, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800F0, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800F4, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800F8, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF1800FC, 0x000000FEU, 0x00000004U); + psu_mask_write(0xFF180100, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180104, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180108, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF18010C, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180110, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180114, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180118, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF18011C, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180120, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180124, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180128, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF18012C, 0x000000FEU, 0x00000002U); + psu_mask_write(0xFF180130, 0x000000FEU, 0x000000C0U); + psu_mask_write(0xFF180134, 0x000000FEU, 0x000000C0U); + psu_mask_write(0xFF180204, 0xFFFFFFFFU, 0x52240000U); + psu_mask_write(0xFF180208, 0xFFFFFFFFU, 0x00B03000U); + psu_mask_write(0xFF18020C, 0x00003FFFU, 0x00000FC0U); + psu_mask_write(0xFF180138, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF18013C, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF180140, 0x03FFFFFFU, 0x00000000U); + psu_mask_write(0xFF180144, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF180148, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF18014C, 0x03FFFFFFU, 0x00000000U); + psu_mask_write(0xFF180154, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF180158, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF18015C, 0x03FFFFFFU, 0x00000000U); + psu_mask_write(0xFF180160, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF180164, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF180168, 0x03FFFFFFU, 0x00000000U); + psu_mask_write(0xFF180170, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF180174, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF180178, 0x03FFFFFFU, 0x00000000U); + psu_mask_write(0xFF18017C, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF180180, 0x03FFFFFFU, 0x03FFFFFFU); + psu_mask_write(0xFF180184, 0x03FFFFFFU, 0x00000000U); + psu_mask_write(0xFF180200, 0x0000000FU, 0x00000000U); + + return 1; +} + +static unsigned long psu_peripherals_init_data(void) +{ + psu_mask_write(0xFF5E0238, 0x00100000U, 0x00000000U); + psu_mask_write(0xFF5E0230, 0x00000008U, 0x00000000U); + psu_mask_write(0xFF5E0238, 0x00000001U, 0x00000000U); + psu_mask_write(0xFF180390, 0x00000004U, 0x00000000U); + psu_mask_write(0xFF5E023C, 0x00000540U, 0x00000000U); + psu_mask_write(0xFD1A0100, 0x0001807EU, 0x00000000U); + psu_mask_write(0xFF5E0238, 0x00000040U, 0x00000000U); + psu_mask_write(0xFF180310, 0x00008000U, 0x00000000U); + psu_mask_write(0xFF180320, 0x33800000U, 0x02800000U); + psu_mask_write(0xFF18031C, 0x7F800000U, 0x63800000U); + psu_mask_write(0xFF180358, 0x00000008U, 0x00000008U); + psu_mask_write(0xFF180324, 0x03C00000U, 0x00000000U); + psu_mask_write(0xFF5E0238, 0x00000100U, 0x00000000U); + psu_mask_write(0xFF5E0238, 0x00000600U, 0x00000000U); + psu_mask_write(0xFF5E0238, 0x00008000U, 0x00000000U); + psu_mask_write(0xFF5E0238, 0x00007800U, 0x00000000U); + psu_mask_write(0xFF5E0238, 0x00000006U, 0x00000000U); + psu_mask_write(0xFF4B0024, 0x000000FFU, 0x000000FFU); + psu_mask_write(0xFFCA5000, 0x00001FFFU, 0x00000000U); + psu_mask_write(0xFD5C0060, 0x000F000FU, 0x00000000U); + psu_mask_write(0xFFA60040, 0x80000000U, 0x80000000U); + psu_mask_write(0xFF260020, 0xFFFFFFFFU, 0x05F5E100U); + psu_mask_write(0xFF260000, 0x00000001U, 0x00000001U); + + return 1; +} + +static unsigned long psu_serdes_init_data(void) +{ + psu_mask_write(0xFD410000, 0x0000001FU, 0x00000009U); + psu_mask_write(0xFD410004, 0x0000001FU, 0x00000009U); + psu_mask_write(0xFD410008, 0x0000001FU, 0x00000008U); + psu_mask_write(0xFD41000C, 0x0000001FU, 0x0000000FU); + psu_mask_write(0xFD402860, 0x00000088U, 0x00000008U); + psu_mask_write(0xFD402864, 0x00000088U, 0x00000008U); + psu_mask_write(0xFD402868, 0x00000080U, 0x00000080U); + psu_mask_write(0xFD40286C, 0x00000082U, 0x00000002U); + psu_mask_write(0xFD40A094, 0x00000010U, 0x00000010U); + psu_mask_write(0xFD40A368, 0x000000FFU, 0x00000038U); + psu_mask_write(0xFD40A36C, 0x00000007U, 0x00000003U); + psu_mask_write(0xFD40E368, 0x000000FFU, 0x000000E0U); + psu_mask_write(0xFD40E36C, 0x00000007U, 0x00000003U); + psu_mask_write(0xFD402368, 0x000000FFU, 0x00000058U); + psu_mask_write(0xFD40236C, 0x00000007U, 0x00000003U); + psu_mask_write(0xFD406368, 0x000000FFU, 0x00000058U); + psu_mask_write(0xFD40636C, 0x00000007U, 0x00000003U); + psu_mask_write(0xFD402370, 0x000000FFU, 0x0000007CU); + psu_mask_write(0xFD402374, 0x000000FFU, 0x00000033U); + psu_mask_write(0xFD402378, 0x000000FFU, 0x00000002U); + psu_mask_write(0xFD40237C, 0x00000033U, 0x00000030U); + psu_mask_write(0xFD406370, 0x000000FFU, 0x0000007CU); + psu_mask_write(0xFD406374, 0x000000FFU, 0x00000033U); + psu_mask_write(0xFD406378, 0x000000FFU, 0x00000002U); + psu_mask_write(0xFD40637C, 0x00000033U, 0x00000030U); + psu_mask_write(0xFD40A370, 0x000000FFU, 0x000000F4U); + psu_mask_write(0xFD40A374, 0x000000FFU, 0x00000031U); + psu_mask_write(0xFD40A378, 0x000000FFU, 0x00000002U); + psu_mask_write(0xFD40A37C, 0x00000033U, 0x00000030U); + psu_mask_write(0xFD40E370, 0x000000FFU, 0x000000C9U); + psu_mask_write(0xFD40E374, 0x000000FFU, 0x000000D2U); + psu_mask_write(0xFD40E378, 0x000000FFU, 0x00000001U); + psu_mask_write(0xFD40E37C, 0x000000B3U, 0x000000B0U); + psu_mask_write(0xFD40906C, 0x00000003U, 0x00000003U); + psu_mask_write(0xFD4080F4, 0x00000003U, 0x00000003U); + psu_mask_write(0xFD40E360, 0x00000040U, 0x00000040U); + psu_mask_write(0xFD40D06C, 0x0000000FU, 0x0000000FU); + psu_mask_write(0xFD40C0F4, 0x0000000BU, 0x0000000BU); + psu_mask_write(0xFD40CB00, 0x000000F0U, 0x000000F0U); + psu_mask_write(0xFD4090CC, 0x00000020U, 0x00000020U); + psu_mask_write(0xFD401074, 0x00000010U, 0x00000010U); + psu_mask_write(0xFD405074, 0x00000010U, 0x00000010U); + psu_mask_write(0xFD409074, 0x00000010U, 0x00000010U); + psu_mask_write(0xFD40D074, 0x00000010U, 0x00000010U); + psu_mask_write(0xFD401994, 0x00000007U, 0x00000007U); + psu_mask_write(0xFD405994, 0x00000007U, 0x00000007U); + psu_mask_write(0xFD40989C, 0x00000080U, 0x00000080U); + psu_mask_write(0xFD4098F8, 0x000000FFU, 0x0000001AU); + psu_mask_write(0xFD4098FC, 0x000000FFU, 0x0000001AU); + psu_mask_write(0xFD409990, 0x000000FFU, 0x00000010U); + psu_mask_write(0xFD409924, 0x000000FFU, 0x000000FEU); + psu_mask_write(0xFD409928, 0x000000FFU, 0x00000000U); + psu_mask_write(0xFD409900, 0x000000FFU, 0x0000001AU); + psu_mask_write(0xFD40992C, 0x000000FFU, 0x00000000U); + psu_mask_write(0xFD409980, 0x000000FFU, 0x000000FFU); + psu_mask_write(0xFD409914, 0x000000FFU, 0x000000F7U); + psu_mask_write(0xFD409918, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD409940, 0x000000FFU, 0x000000F7U); + psu_mask_write(0xFD409944, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD409994, 0x00000007U, 0x00000007U); + psu_mask_write(0xFD40D89C, 0x00000080U, 0x00000080U); + psu_mask_write(0xFD40D8F8, 0x000000FFU, 0x0000007DU); + psu_mask_write(0xFD40D8FC, 0x000000FFU, 0x0000007DU); + psu_mask_write(0xFD40D990, 0x000000FFU, 0x00000001U); + psu_mask_write(0xFD40D924, 0x000000FFU, 0x0000009CU); + psu_mask_write(0xFD40D928, 0x000000FFU, 0x00000039U); + psu_mask_write(0xFD40D98C, 0x000000F0U, 0x00000020U); + psu_mask_write(0xFD40D900, 0x000000FFU, 0x0000007DU); + psu_mask_write(0xFD40D92C, 0x000000FFU, 0x00000064U); + psu_mask_write(0xFD40D980, 0x000000FFU, 0x000000FFU); + psu_mask_write(0xFD40D914, 0x000000FFU, 0x000000F7U); + psu_mask_write(0xFD40D918, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD40D940, 0x000000FFU, 0x000000F7U); + psu_mask_write(0xFD40D944, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD40D994, 0x00000007U, 0x00000007U); + psu_mask_write(0xFD40107C, 0x0000000FU, 0x00000001U); + psu_mask_write(0xFD40507C, 0x0000000FU, 0x00000001U); + psu_mask_write(0xFD40907C, 0x0000000FU, 0x00000001U); + psu_mask_write(0xFD40D07C, 0x0000000FU, 0x00000001U); + psu_mask_write(0xFD4019A4, 0x000000FFU, 0x000000FFU); + psu_mask_write(0xFD401038, 0x00000040U, 0x00000040U); + psu_mask_write(0xFD40102C, 0x00000040U, 0x00000040U); + psu_mask_write(0xFD4059A4, 0x000000FFU, 0x000000FFU); + psu_mask_write(0xFD405038, 0x00000040U, 0x00000040U); + psu_mask_write(0xFD40502C, 0x00000040U, 0x00000040U); + psu_mask_write(0xFD4099A4, 0x000000FFU, 0x000000FFU); + psu_mask_write(0xFD409038, 0x00000040U, 0x00000040U); + psu_mask_write(0xFD40902C, 0x00000040U, 0x00000040U); + psu_mask_write(0xFD40D9A4, 0x000000FFU, 0x000000FFU); + psu_mask_write(0xFD40D038, 0x00000040U, 0x00000040U); + psu_mask_write(0xFD40D02C, 0x00000040U, 0x00000040U); + psu_mask_write(0xFD4019AC, 0x00000003U, 0x00000000U); + psu_mask_write(0xFD4059AC, 0x00000003U, 0x00000000U); + psu_mask_write(0xFD4099AC, 0x00000003U, 0x00000000U); + psu_mask_write(0xFD40D9AC, 0x00000003U, 0x00000000U); + psu_mask_write(0xFD401978, 0x00000080U, 0x00000080U); + psu_mask_write(0xFD405978, 0x00000080U, 0x00000080U); + psu_mask_write(0xFD409978, 0x00000080U, 0x00000080U); + psu_mask_write(0xFD40D978, 0x00000080U, 0x00000080U); + psu_mask_write(0xFD410010, 0x00000077U, 0x00000044U); + psu_mask_write(0xFD410014, 0x00000077U, 0x00000023U); + psu_mask_write(0xFD400CB4, 0x00000037U, 0x00000037U); + psu_mask_write(0xFD404CB4, 0x00000037U, 0x00000037U); + psu_mask_write(0xFD4001D8, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD4041D8, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD40C1D8, 0x00000001U, 0x00000001U); + psu_mask_write(0xFD40DC14, 0x000000FFU, 0x000000E6U); + psu_mask_write(0xFD40DC40, 0x0000001FU, 0x0000000CU); + psu_mask_write(0xFD40D94C, 0x00000020U, 0x00000020U); + psu_mask_write(0xFD40D950, 0x00000007U, 0x00000006U); + psu_mask_write(0xFD404CC0, 0x0000001FU, 0x00000000U); + psu_mask_write(0xFD400CC0, 0x0000001FU, 0x00000000U); + psu_mask_write(0xFD404048, 0x000000FFU, 0x00000000U); + psu_mask_write(0xFD400048, 0x000000FFU, 0x00000000U); + psu_mask_write(0xFD40C048, 0x000000FFU, 0x00000001U); + + return 1; +} + +static unsigned long psu_resetout_init_data(void) +{ + psu_mask_write(0xFF5E023C, 0x00000400U, 0x00000000U); + psu_mask_write(0xFF9D0080, 0x00000001U, 0x00000001U); + psu_mask_write(0xFF9D007C, 0x00000001U, 0x00000000U); + psu_mask_write(0xFF5E023C, 0x00000140U, 0x00000000U); + psu_mask_write(0xFF5E0230, 0x00000008U, 0x00000000U); + psu_mask_write(0xFD3D0100, 0x00000003U, 0x00000003U); + psu_mask_write(0xFD1A0100, 0x00000002U, 0x00000000U); + psu_mask_write(0xFD1A0100, 0x00010000U, 0x00000000U); + psu_mask_write(0xFD4A0200, 0x00000002U, 0x00000000U); + psu_mask_write(0xFD4A0238, 0x0000000FU, 0x00000000U); + psu_mask_write(0xFE20C200, 0x00003FFFU, 0x00002457U); + psu_mask_write(0xFE20C630, 0x003FFF00U, 0x00000000U); + psu_mask_write(0xFE20C11C, 0x00000400U, 0x00000400U); + psu_mask_write(0xFD480064, 0x00000200U, 0x00000200U); + mask_poll(0xFD4063E4, 0x00000010U); + mask_poll(0xFD40A3E4, 0x00000010U); + mask_poll(0xFD40E3E4, 0x00000010U); + psu_mask_write(0xFD0C00AC, 0xFFFFFFFFU, 0x28184018U); + psu_mask_write(0xFD0C00B0, 0xFFFFFFFFU, 0x0E081406U); + psu_mask_write(0xFD0C00B4, 0xFFFFFFFFU, 0x064A0813U); + psu_mask_write(0xFD0C00B8, 0xFFFFFFFFU, 0x3FFC96A4U); + + return 1; +} + +static unsigned long psu_resetin_init_data(void) +{ + psu_mask_write(0xFF5E023C, 0x00000540U, 0x00000540U); + psu_mask_write(0xFF5E0230, 0x00000008U, 0x00000008U); + psu_mask_write(0xFD1A0100, 0x00000002U, 0x00000002U); + psu_mask_write(0xFD4A0238, 0x0000000FU, 0x0000000AU); + psu_mask_write(0xFD4A0200, 0x00000002U, 0x00000002U); + psu_mask_write(0xFD1A0100, 0x00010000U, 0x00010000U); + + return 1; +} + +static unsigned long psu_ddr_phybringup_data(void) +{ + unsigned int regval = 0; + unsigned int pll_retry = 10; + unsigned int pll_locked = 0; + + while ((pll_retry > 0) && (!pll_locked)) { + Xil_Out32(0xFD080004, 0x00040010); + Xil_Out32(0xFD080004, 0x00040011); + + while ((Xil_In32(0xFD080030) & 0x1) != 1) + ; + + pll_locked = (Xil_In32(0xFD080030) & 0x80000000) >> 31; + pll_locked &= (Xil_In32(0xFD0807E0) & 0x10000) >> 16; + pll_locked &= (Xil_In32(0xFD0809E0) & 0x10000) >> 16; + pll_locked &= (Xil_In32(0xFD080BE0) & 0x10000) >> 16; + pll_locked &= (Xil_In32(0xFD080DE0) & 0x10000) >> 16; + pll_retry--; + } + Xil_Out32(0xFD0800C4, Xil_In32(0xFD0800C4) | (pll_retry << 16)); + Xil_Out32(0xFD080004U, 0x00040063U); + + while ((Xil_In32(0xFD080030U) & 0x0000000FU) != 0x0000000FU) + ; + prog_reg(0xFD080004U, 0x00000001U, 0x00000000U, 0x00000001U); + + while ((Xil_In32(0xFD080030U) & 0x000000FFU) != 0x0000001FU) + ; + + Xil_Out32(0xFD0701B0U, 0x00000001U); + Xil_Out32(0xFD070320U, 0x00000001U); + while ((Xil_In32(0xFD070004U) & 0x0000000FU) != 0x00000001U) + ; + prog_reg(0xFD080014U, 0x00000040U, 0x00000006U, 0x00000001U); + Xil_Out32(0xFD080004, 0x0004FE01); + regval = Xil_In32(0xFD080030); + while (regval != 0x80000FFF) + regval = Xil_In32(0xFD080030); + + Xil_Out32(0xFD080200U, 0x100091C7U); + Xil_Out32(0xFD080018U, 0x00F01EF2U); + prog_reg(0xFD08001CU, 0x00000018U, 0x00000003U, 0x00000003U); + prog_reg(0xFD08142CU, 0x00000030U, 0x00000004U, 0x00000003U); + prog_reg(0xFD08146CU, 0x00000030U, 0x00000004U, 0x00000003U); + prog_reg(0xFD0814ACU, 0x00000030U, 0x00000004U, 0x00000003U); + prog_reg(0xFD0814ECU, 0x00000030U, 0x00000004U, 0x00000003U); + prog_reg(0xFD08152CU, 0x00000030U, 0x00000004U, 0x00000003U); + + Xil_Out32(0xFD080004, 0x00060001); + regval = Xil_In32(0xFD080030); + while ((regval & 0x80004001) != 0x80004001) + regval = Xil_In32(0xFD080030); + + prog_reg(0xFD08001CU, 0x00000018U, 0x00000003U, 0x00000000U); + prog_reg(0xFD08142CU, 0x00000030U, 0x00000004U, 0x00000000U); + prog_reg(0xFD08146CU, 0x00000030U, 0x00000004U, 0x00000000U); + prog_reg(0xFD0814ACU, 0x00000030U, 0x00000004U, 0x00000000U); + prog_reg(0xFD0814ECU, 0x00000030U, 0x00000004U, 0x00000000U); + prog_reg(0xFD08152CU, 0x00000030U, 0x00000004U, 0x00000000U); + + Xil_Out32(0xFD080200U, 0x800091C7U); + Xil_Out32(0xFD080018U, 0x00F12302U); + + Xil_Out32(0xFD080004, 0x0000C001); + regval = Xil_In32(0xFD080030); + while ((regval & 0x80000C01) != 0x80000C01) + regval = Xil_In32(0xFD080030); + + Xil_Out32(0xFD070180U, 0x01000040U); + Xil_Out32(0xFD070060U, 0x00000000U); + prog_reg(0xFD080014U, 0x00000040U, 0x00000006U, 0x00000000U); + + return 1; +} + +static int serdes_fixcal_code(void) +{ + int maskstatus = 1; + unsigned int tmp_0_1, tmp_0_2, tmp_0_3, tmp_0_2_mod; + + Xil_Out32(0xFD40EC4C, 0x00000020); + + Xil_Out32(0xFD410010, 0x00000001); + + maskstatus = mask_poll(0xFD40EF14, 0x2); + + if (maskstatus == 0) { + xil_printf("SERDES initialization timed out\n\r"); + return maskstatus; + } + + tmp_0_1 = mask_read(0xFD400B0C, 0x3F); + + tmp_0_2 = tmp_0_1 & (0x7); + tmp_0_3 = tmp_0_1 & (0x38); + + Xil_Out32(0xFD410010, 0x00000000); + Xil_Out32(0xFD410014, 0x00000000); + + tmp_0_2_mod = (tmp_0_2 << 1) | (0x1); + tmp_0_2_mod = (tmp_0_2_mod << 4); + + tmp_0_3 = tmp_0_3 >> 3; + Xil_Out32(0xFD40EC4C, tmp_0_3); + + Xil_Out32(0xFD40EC48, tmp_0_2_mod); + return maskstatus; +} + +static int serdes_enb_coarse_saturation(void) +{ + Xil_Out32(0xFD402094, 0x00000010); + Xil_Out32(0xFD406094, 0x00000010); + Xil_Out32(0xFD40A094, 0x00000010); + Xil_Out32(0xFD40E094, 0x00000010); + return 1; +} + +static int init_serdes(void) +{ + int status = 1; + + status &= psu_resetin_init_data(); + + status &= serdes_fixcal_code(); + status &= serdes_enb_coarse_saturation(); + + status &= psu_serdes_init_data(); + status &= psu_resetout_init_data(); + + return status; +} + +static void init_peripheral(void) +{ + unsigned int regvalue; + unsigned int tmp_regval; + + Xil_Out32(((0xFF5E0000U) + 0x00000230U), 0x00000000); + Xil_Out32(((0xFF5E0000U) + 0x00000234U), 0x00000000); + Xil_Out32(((0xFF5E0000U) + 0x00000238U), 0x00000000); + + regvalue = Xil_In32(((0xFF5E0000U) + 0x0000023CU)); + regvalue &= 0x7; + Xil_Out32(((0xFF5E0000U) + 0x0000023CU), regvalue); + + Xil_Out32(((0xFD1A0000U) + 0x00000100U), 0x00000000); + + tmp_regval = Xil_In32(0xFD690040); + tmp_regval &= ~0x00000001; + Xil_Out32(0xFD690040, tmp_regval); + + tmp_regval = Xil_In32(0xFD690030); + tmp_regval &= ~0x00000001; + Xil_Out32(0xFD690030, tmp_regval); +} + +int psu_init(void) +{ + int status = 1; + + status &= psu_mio_init_data(); + status &= psu_pll_init_data(); + status &= psu_clock_init_data(); + + status &= psu_ddr_init_data(); + status &= psu_ddr_phybringup_data(); + status &= psu_peripherals_init_data(); + + status &= init_serdes(); + init_peripheral(); + + if (status == 0) + return 1; + return 0; +} diff --git a/configs/xilinx_zynqmp_virt_defconfig b/configs/xilinx_zynqmp_virt_defconfig index 976cb02c0f..0fa03e7c23 100644 --- a/configs/xilinx_zynqmp_virt_defconfig +++ b/configs/xilinx_zynqmp_virt_defconfig @@ -82,7 +82,7 @@ CONFIG_CMD_UBI=y CONFIG_PARTITION_TYPE_GUID=y CONFIG_SPL_OF_CONTROL=y CONFIG_OF_BOARD=y -CONFIG_OF_LIST="avnet-ultra96-rev1 zynqmp-a2197-revA zynqmp-e-a2197-00-revA zynqmp-g-a2197-00-revA zynqmp-m-a2197-01-revA zynqmp-m-a2197-02-revA zynqmp-m-a2197-03-revA zynqmp-p-a2197-00-revA zynqmp-zc1232-revA zynqmp-zc1254-revA zynqmp-zc1751-xm015-dc1 zynqmp-zc1751-xm016-dc2 zynqmp-zc1751-xm017-dc3 zynqmp-zc1751-xm018-dc4 zynqmp-zc1751-xm019-dc5 zynqmp-zcu100-revC zynqmp-zcu102-rev1.1 zynqmp-zcu102-rev1.0 zynqmp-zcu102-revA zynqmp-zcu102-revB zynqmp-zcu104-revA zynqmp-zcu104-revC zynqmp-zcu106-revA zynqmp-zcu111-revA zynqmp-zcu1275-revA zynqmp-zcu1275-revB zynqmp-zcu1285-revA zynqmp-zcu208-revA zynqmp-zcu216-revA zynqmp-topic-miamimp-xilinx-xdp-v1r1 zynqmp-sm-k26-revA zynqmp-smk-k26-revA zynqmp-dlc21-revA" +CONFIG_OF_LIST="avnet-ultra96-rev1 zynqmp-a2197-revA zynqmp-e-a2197-00-revA zynqmp-g-a2197-00-revA zynqmp-m-a2197-01-revA zynqmp-m-a2197-02-revA zynqmp-m-a2197-03-revA zynqmp-p-a2197-00-revA zynqmp-zc1232-revA zynqmp-zc1254-revA zynqmp-zc1751-xm015-dc1 zynqmp-zc1751-xm016-dc2 zynqmp-zc1751-xm017-dc3 zynqmp-zc1751-xm018-dc4 zynqmp-zc1751-xm019-dc5 zynqmp-zcu100-revC zynqmp-zcu102-rev1.1 zynqmp-zcu102-rev1.0 zynqmp-zcu102-revA zynqmp-zcu102-revB zynqmp-zcu104-revA zynqmp-zcu104-revC zynqmp-zcu106-revA zynqmp-zcu106-rev1.0 zynqmp-zcu111-revA zynqmp-zcu1275-revA zynqmp-zcu1275-revB zynqmp-zcu1285-revA zynqmp-zcu208-revA zynqmp-zcu216-revA zynqmp-topic-miamimp-xilinx-xdp-v1r1 zynqmp-sm-k26-revA zynqmp-smk-k26-revA zynqmp-dlc21-revA" CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names interrupt-parent interrupts iommus power-domains" CONFIG_ENV_IS_NOWHERE=y CONFIG_ENV_IS_IN_FAT=y From d24b3e32ab7f89c0b411747203d7f103f475cfbf Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 7 Mar 2022 08:43:49 +0100 Subject: [PATCH 31/39] MAINTAINERS: Remove duplicated entry for ehci-zynq.c ehci-zynq.c is assigned to Zynq and ZynqMP that's why remove one. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/d97779178fa56f1c6af40f5604b0bf349002cd36.1646639027.git.michal.simek@xilinx.com --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index bb6ea06933..c58947fb2f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -656,7 +656,6 @@ F: drivers/soc/soc_xilinx_zynqmp.c F: drivers/spi/zynq_qspi.c F: drivers/spi/zynq_spi.c F: drivers/timer/cadence-ttc.c -F: drivers/usb/host/ehci-zynq.c F: drivers/video/seps525.c F: drivers/watchdog/cdns_wdt.c F: include/zynqmppl.h From db681d4929cac3f5d0a8ce638e7e5306fe6038d2 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 15:45:40 +0100 Subject: [PATCH 32/39] net: phy: Add new read ethernet phy id function Add new function to get ethernet phy id from compatible property of the mdio phy node. Signed-off-by: Michal Simek Signed-off-by: T Karthik Reddy Link: https://lore.kernel.org/r/16019efb3820a50330935fdaae191cec1f101b5c.1645627539.git.michal.simek@xilinx.com --- drivers/core/ofnode.c | 36 ++++++++++++++++++++++++++++++++++++ include/dm/ofnode.h | 13 +++++++++++++ 2 files changed, 49 insertions(+) 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/include/dm/ofnode.h b/include/dm/ofnode.h index 0cb324c8b0..744dffe0a2 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -894,6 +894,19 @@ int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type, */ int ofnode_read_pci_vendev(ofnode node, u16 *vendor, u16 *device); +/** + * ofnode_read_eth_phy_id() - look up eth phy vendor and device id + * + * Look at the compatible property of a device node that represents a eth phy + * device and extract phy vendor id and device id from it. + * + * @param node node to examine + * @param vendor vendor id of the eth phy device + * @param device device id of the eth phy device + * @return 0 if ok, negative on error + */ +int ofnode_read_eth_phy_id(ofnode node, u16 *vendor, u16 *device); + /** * ofnode_read_addr_cells() - Get the number of address cells for a node * From 3249116d8381a1a8f0d17c95acecd7c78b40e569 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 15:45:41 +0100 Subject: [PATCH 33/39] net: phy: Remove static return type for phy_device_create() Remove static return type for phy_device_create() to avoid file scope for this function. Also add required prototype in phy.h. Signed-off-by: Michal Simek Signed-off-by: T Karthik Reddy Link: https://lore.kernel.org/r/1517f4053403fbd53e899d500e7485d068a4f0b6.1645627539.git.michal.simek@xilinx.com --- drivers/net/phy/phy.c | 6 +++--- include/phy.h | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index c9fc20855b..f63705e1b9 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; diff --git a/include/phy.h b/include/phy.h index c66fd43ea8..832d7a1695 100644 --- a/include/phy.h +++ b/include/phy.h @@ -454,6 +454,19 @@ void phy_connect_dev(struct phy_device *phydev, struct udevice *dev); struct phy_device *phy_connect(struct mii_dev *bus, int addr, struct udevice *dev, phy_interface_t interface); +/** + * phy_device_create() - Create a PHY device + * + * @bus: MII/MDIO bus that hosts the PHY + * @addr: PHY address on MDIO bus + * @phy_id: where to store the ID retrieved + * @is_c45: Device Identifiers if is_c45 + * @interface: interface between the MAC and PHY + * @return: pointer to phy_device if a PHY is found, or NULL otherwise + */ +struct phy_device *phy_device_create(struct mii_dev *bus, int addr, + u32 phy_id, bool is_c45, + phy_interface_t interface); static inline ofnode phy_get_ofnode(struct phy_device *phydev) { From a744a284e35420077b0c838598eb24c98710412a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Feb 2022 15:45:42 +0100 Subject: [PATCH 34/39] net: phy: Add support for ethernet-phy-id with gpio reset Ethernet phy like dp83867 is using strapping resistors to setup PHY address. On Xilinx boards strapping is setup on wires which are connected to SOC where internal pull ups/downs influnce phy address. That's why there is a need to setup pins properly (via pinctrl driver for example) and then perform phy reset. I can be workarounded by reset gpio done for mdio bus but this is not working properly when multiply phys sitting on the same bus. That's why it needs to be done via ethernet-phy-id driver where dt binding has gpio reset per phy. DT binding is available here: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/net/ethernet-phy.yaml The driver is are reading the vendor and device id from valid phy node using ofnode_read_eth_phy_id() and creating a phy device. Kconfig PHY_ETHERNET_ID symbol is used because not every platform has gpio support. Signed-off-by: Michal Simek Signed-off-by: T Karthik Reddy Link: https://lore.kernel.org/r/70ab7d71c812b2c972d48c129e416c921af0d7f5.1645627539.git.michal.simek@xilinx.com --- MAINTAINERS | 1 + drivers/net/phy/Kconfig | 8 ++++ drivers/net/phy/Makefile | 1 + drivers/net/phy/ethernet_id.c | 69 +++++++++++++++++++++++++++++++++++ drivers/net/phy/phy.c | 5 +++ include/phy.h | 13 +++++++ 6 files changed, 97 insertions(+) create mode 100644 drivers/net/phy/ethernet_id.c diff --git a/MAINTAINERS b/MAINTAINERS index c58947fb2f..4e740fb5d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -617,6 +617,7 @@ F: drivers/i2c/muxes/pca954x.c F: drivers/i2c/zynq_i2c.c F: drivers/mmc/zynq_sdhci.c F: drivers/mtd/nand/raw/zynq_nand.c +F: drivers/net/phy/ethernet_id.c F: drivers/net/phy/xilinx_phy.c F: drivers/net/zynq_gem.c F: drivers/serial/serial_zynq.c 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 +#include +#include +#include +#include + +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 f63705e1b9..fffa10f68b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -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/include/phy.h b/include/phy.h index 832d7a1695..9ea4bd42db 100644 --- a/include/phy.h +++ b/include/phy.h @@ -468,6 +468,19 @@ struct phy_device *phy_device_create(struct mii_dev *bus, int addr, u32 phy_id, bool is_c45, phy_interface_t interface); +/** + * phy_connect_phy_id() - Connect to phy device by reading PHY id + * from phy node. + * + * @bus: MII/MDIO bus that hosts the PHY + * @dev: Ethernet device to associate to the PHY + * @interface: Interface between the MAC and PHY + * @return: pointer to phy_device if a PHY is found, + * or NULL otherwise + */ +struct phy_device *phy_connect_phy_id(struct mii_dev *bus, struct udevice *dev, + phy_interface_t interface); + static inline ofnode phy_get_ofnode(struct phy_device *phydev) { if (ofnode_valid(phydev->node)) From 57d820da97a587a5301fbfe9536efe54bda64a63 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 7 Mar 2022 16:48:17 +0100 Subject: [PATCH 35/39] cmd: test: pinmux: Do not check all empty spaces There is really no value to check empty spaces. That's why use ut_assert_nextlinen() instead of ut_assert_nextline(). This change ensures that PINMUX_SIZE can be increased without changing tests. Signed-off-by: Michal Simek Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/03aecf4c67ec8d72bf2a90baf1516fc5bd300fe0.1646668094.git.michal.simek@xilinx.com --- test/cmd/pinmux.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/cmd/pinmux.c b/test/cmd/pinmux.c index 8ae807b537..ba338b8dce 100644 --- a/test/cmd/pinmux.c +++ b/test/cmd/pinmux.c @@ -16,18 +16,18 @@ static int dm_test_cmd_pinmux_status_pinname(struct unit_test_state *uts) /* Test that 'pinmux status ' displays the selected pin. */ console_record_reset(); run_command("pinmux status a5", 0); - ut_assert_nextline("a5 : gpio input . "); + ut_assert_nextlinen("a5 : gpio input ."); ut_assert_console_end(); console_record_reset(); run_command("pinmux status P7", 0); - ut_assert_nextline("P7 : GPIO2 bias-pull-down input-enable. "); + ut_assert_nextlinen("P7 : GPIO2 bias-pull-down input-enable."); ut_assert_console_end(); console_record_reset(); run_command("pinmux status P9", 0); - ut_assert_nextline("single-pinctrl pinctrl-single-no-width: missing register width"); - ut_assert_nextline("P9 not found"); + ut_assert_nextlinen("single-pinctrl pinctrl-single-no-width: missing register width"); + ut_assert_nextlinen("P9 not found"); ut_assert_console_end(); return 0; From e19b8dda92772d9ba86db4099a85a7b3cb82fd7a Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:23:04 +0100 Subject: [PATCH 36/39] pinctrl: Increase length of pinmux status buffer Xilinx ZynqMP SOC can set 6 parameters for its pins. pinmux status command will print the status of these parameters for each pin. But current print buffer length is only 40 characters long, increase it to 80 to print all the parameters. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/3a6be84c8354f38754a9838670cc0319e84f29e8.1645626183.git.michal.simek@xilinx.com --- include/dm/pinctrl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dm/pinctrl.h b/include/dm/pinctrl.h index 0c461e56bb..a09b242fd9 100644 --- a/include/dm/pinctrl.h +++ b/include/dm/pinctrl.h @@ -7,7 +7,7 @@ #define __PINCTRL_H #define PINNAME_SIZE 10 -#define PINMUX_SIZE 40 +#define PINMUX_SIZE 80 /** * struct pinconf_param - pin config parameters From dbd673f14da34148f5a6105a39cd4458a92cac67 Mon Sep 17 00:00:00 2001 From: Ashok Reddy Soma Date: Wed, 23 Feb 2022 15:23:05 +0100 Subject: [PATCH 37/39] pinctrl: zynqmp: Add pinctrl driver Add pinctrl driver for Xilinx ZynqMP SOC. This driver is compatible with linux device tree parameters for configuring pinmux and pinconf. Signed-off-by: Ashok Reddy Soma Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/2d7eefa83c8c0129f7243a25de56a289e948f6c6.1645626183.git.michal.simek@xilinx.com --- MAINTAINERS | 1 + drivers/pinctrl/Kconfig | 10 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-zynqmp.c | 644 +++++++++++++++++++++++++++++++ include/zynqmp_firmware.h | 43 +++ 5 files changed, 699 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c diff --git a/MAINTAINERS b/MAINTAINERS index 4e740fb5d2..915316a86e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -620,6 +620,7 @@ F: drivers/mtd/nand/raw/zynq_nand.c F: drivers/net/phy/ethernet_id.c F: drivers/net/phy/xilinx_phy.c F: drivers/net/zynq_gem.c +F: drivers/pinctrl/pinctrl-zynqmp.c F: drivers/serial/serial_zynq.c F: drivers/spi/zynq_qspi.c F: drivers/spi/zynq_spi.c 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 + * Michal Simek + * + * Copyright (C) 2021 Xilinx, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/include/zynqmp_firmware.h b/include/zynqmp_firmware.h index 60c3df3da4..f577008736 100644 --- a/include/zynqmp_firmware.h +++ b/include/zynqmp_firmware.h @@ -177,6 +177,49 @@ enum pm_query_id { PM_QID_CLOCK_GET_MAX_DIVISOR = 13, }; +enum pm_pinctrl_config_param { + PM_PINCTRL_CONFIG_SLEW_RATE = 0, + PM_PINCTRL_CONFIG_BIAS_STATUS = 1, + PM_PINCTRL_CONFIG_PULL_CTRL = 2, + PM_PINCTRL_CONFIG_SCHMITT_CMOS = 3, + PM_PINCTRL_CONFIG_DRIVE_STRENGTH = 4, + PM_PINCTRL_CONFIG_VOLTAGE_STATUS = 5, + PM_PINCTRL_CONFIG_TRI_STATE = 6, + PM_PINCTRL_CONFIG_MAX = 7, +}; + +enum pm_pinctrl_slew_rate { + PM_PINCTRL_SLEW_RATE_FAST = 0, + PM_PINCTRL_SLEW_RATE_SLOW = 1, +}; + +enum pm_pinctrl_bias_status { + PM_PINCTRL_BIAS_DISABLE = 0, + PM_PINCTRL_BIAS_ENABLE = 1, +}; + +enum pm_pinctrl_pull_ctrl { + PM_PINCTRL_BIAS_PULL_DOWN = 0, + PM_PINCTRL_BIAS_PULL_UP = 1, +}; + +enum pm_pinctrl_schmitt_cmos { + PM_PINCTRL_INPUT_TYPE_CMOS = 0, + PM_PINCTRL_INPUT_TYPE_SCHMITT = 1, +}; + +enum pm_pinctrl_drive_strength { + PM_PINCTRL_DRIVE_STRENGTH_2MA = 0, + PM_PINCTRL_DRIVE_STRENGTH_4MA = 1, + PM_PINCTRL_DRIVE_STRENGTH_8MA = 2, + PM_PINCTRL_DRIVE_STRENGTH_12MA = 3, +}; + +enum pm_pinctrl_tri_state { + PM_PINCTRL_TRI_STATE_DISABLE = 0, + PM_PINCTRL_TRI_STATE_ENABLE = 1, +}; + enum zynqmp_pm_reset_action { PM_RESET_ACTION_RELEASE = 0, PM_RESET_ACTION_ASSERT = 1, From e5d8d08981aff1d191f46b3f4432b9f356c80256 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 7 Mar 2022 08:53:38 +0100 Subject: [PATCH 38/39] arm64: zynqmp: Fix i2c addresses for zynqmp-p-a2197 After double checking some i2c addresses are not correct. It is visible from i2c dump ZynqMP> i2c bus Bus 3: i2c@ff020000 74: i2c-mux@74, offset len 1, flags 0 Bus 5: i2c@ff020000->i2c-mux@74->i2c@0 Bus 6: i2c@ff020000->i2c-mux@74->i2c@2 Bus 7: i2c@ff020000->i2c-mux@74->i2c@1 Bus 8: i2c@ff020000->i2c-mux@74->i2c@3 Bus 4: i2c@ff030000 (active 4) 74: i2c-mux@74, offset len 1, flags 0 Bus 9: i2c@ff030000->i2c-mux@74->i2c@0 Bus 10: i2c@ff030000->i2c-mux@74->i2c@3 Bus 11: i2c@ff030000->i2c-mux@74->i2c@4 Bus 12: i2c@ff030000->i2c-mux@74->i2c@5 (active 12) 51: generic_51, offset len 1, flags 0 60: generic_60, offset len 1, flags 0 74: generic_74, offset len 1, flags 0 Bus 13: i2c@ff030000->i2c-mux@74->i2c@6 (active 13) 51: generic_51, offset len 1, flags 0 5d: generic_5d, offset len 1, flags 0 74: generic_74, offset len 1, flags 0 ZynqMP> i2c dev 4 Setting bus to 4 ZynqMP> i2c mw 74 0 18 ZynqMP> i2c probe Valid chip addresses: 18 36 37 50 51 60 74 ZynqMP> i2c mw 74 0 20 ZynqMP> i2c probe Valid chip addresses: 51 60 74 where it is clear that si570 (u5) is at 0x60 address and 8t49n240 (u39) is also at address 0x60 based on log above. i2c address 0x74 is i2c mux and 0x51 is eeprom. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/0a198e9d993411e41473d130d5a5c20b6dc83458.1646639616.git.michal.simek@xilinx.com --- arch/arm/dts/zynqmp-p-a2197-00-revA.dts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/dts/zynqmp-p-a2197-00-revA.dts b/arch/arm/dts/zynqmp-p-a2197-00-revA.dts index 5d21795de9..b3fe42faee 100644 --- a/arch/arm/dts/zynqmp-p-a2197-00-revA.dts +++ b/arch/arm/dts/zynqmp-p-a2197-00-revA.dts @@ -511,10 +511,10 @@ #address-cells = <1>; #size-cells = <0>; reg = <6>; - si570_hsdp: clock-generator@5d { /* u5 */ + si570_hsdp: clock-generator@60 { /* u5 */ #clock-cells = <0>; compatible = "silabs,si570"; - reg = <0x5d>; /* 570JAC000900DG */ + reg = <0x60>; /* 570JAC000900DG */ temperature-stability = <50>; factory-fout = <156250000>; clock-frequency = <156250000>; @@ -528,10 +528,10 @@ /* u36 0xd8 or 0xde - pcie clk buf - 9ZML1241EKILF PCIe GEN 4 CLOCK BUFFER FIXME - no driver */ /* u37 0xd0 DNP - pcie clocking 1 - 9FGV1006BQ505LTGI - PCIe GEN 4 CLOCK GENERATOR FIXME - no linux driver */ /* u38 0xca - pcie clocking 2 - 9ZML1241EKILF PCIe GEN 4 CLOCK BUFFER FIXME - no driver */ - clock_8t49n287: clock-generator@d8 { /* u39 8T49N240 - pcie clocking 3 */ + clock_8t49n287: clock-generator@60 { /* u39 8T49N240 - pcie clocking 3 */ #clock-cells = <1>; /* author David Cater */ compatible = "idt,8t49n240", "idt,8t49n241"; /* FIXME no driver for 240 */ - reg = <0xd8>; + reg = <0x60>; /* Documentation/devicetree/bindings/clock/idt,idt8t49n24x.txt */ /* FIXME there input via J241 Samtec CLK1 and CLK0 from U38 - selection PIN */ From 0ac03fbab51c72fa978569a831c001c4ddad8e2a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 14 Mar 2022 15:26:11 +0100 Subject: [PATCH 39/39] arm64: zynqmp: Add pinctrl emmc description to SM-K26 Production SOM has emmc on it and make sense to describe pin description to be able use EMMC if it is not configured via psu_init. (Still some regs are not handled but this is one step in that direction) Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/3545a0f08d342de98efc82b78f5725eda091555a.1647267969.git.michal.simek@xilinx.com --- arch/arm/dts/zynqmp-sm-k26-revA.dts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/dts/zynqmp-sm-k26-revA.dts b/arch/arm/dts/zynqmp-sm-k26-revA.dts index d242f8712b..14ab31685d 100644 --- a/arch/arm/dts/zynqmp-sm-k26-revA.dts +++ b/arch/arm/dts/zynqmp-sm-k26-revA.dts @@ -14,6 +14,7 @@ #include #include #include +#include / { model = "ZynqMP SM-K26 Rev1/B/A"; @@ -92,6 +93,23 @@ status = "okay"; }; +&pinctrl0 { + status = "okay"; + pinctrl_sdhci0_default: sdhci0-default { + conf { + groups = "sdio0_0_grp"; + slew-rate = ; + power-source = ; + bias-disable; + }; + + mux { + groups = "sdio0_0_grp"; + function = "sdio0"; + }; + }; +}; + &qspi { /* MIO 0-5 - U143 */ status = "okay"; flash@0 { /* MT25QU512A */ @@ -185,6 +203,8 @@ &sdhci0 { /* MIO13-23 - 16GB emmc MTFC16GAPALBH-IT - U133A */ status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdhci0_default>; non-removable; disable-wp; bus-width = <8>;