From 39e020ef163d7ca6e73294a3ae4ded2867c9bc75 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Wed, 27 Jul 2016 12:25:07 +0100 Subject: net: emaclite: Use ioremap_nocache Virtual to physical mapping isn't necessarily 1:1 for all architectures Using ioremap_nocache allows for the arch code to translate the physical address to a virtual address. Signed-off-by: Zubair Lutfullah Kakakhel Reviewed-by: Paul Burton Reviewed-by: Daniel Schwierzeck Acked-by: Joe Hershberger --- drivers/net/xilinx_emaclite.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index 7b85aa0..6df222b 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -19,6 +19,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -595,7 +596,8 @@ static int emaclite_ofdata_to_platdata(struct udevice *dev) int offset = 0; pdata->iobase = (phys_addr_t)dev_get_addr(dev); - emaclite->regs = (struct emaclite_regs *)pdata->iobase; + emaclite->regs = (struct emaclite_regs *)ioremap_nocache(pdata->iobase, + 0x10000); emaclite->phyaddr = -1; -- cgit v1.1 From 611fe0bddb071e439d65f833e55361aecba87d82 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Wed, 27 Jul 2016 12:25:08 +0100 Subject: net: emaclite: use __raw_readl/writel instead of weird define out_be32 and in_be32 are actually #defined to little endian writel/readl in arch/microblaze. Just use __raw_writel/readl instead. That is also what is used in the Linux kernel driver for this IP block Tested on MIPSfpga. Can tftp a kernel. Signed-off-by: Zubair Lutfullah Kakakhel Reviewed-by: Paul Burton Acked-by: Joe Hershberger --- drivers/net/xilinx_emaclite.c | 86 ++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index 6df222b..d86e7a3 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -155,7 +155,7 @@ static int wait_for_bit(const char *func, u32 *reg, const u32 mask, unsigned long start = get_timer(0); while (1) { - val = readl(reg); + val = __raw_readl(reg); if (!set) val = ~val; @@ -194,16 +194,17 @@ static u32 phyread(struct xemaclite *emaclite, u32 phyaddress, u32 registernum, if (mdio_wait(regs)) return 1; - u32 ctrl_reg = in_be32(®s->mdioctrl); - out_be32(®s->mdioaddr, XEL_MDIOADDR_OP_MASK | - ((phyaddress << XEL_MDIOADDR_PHYADR_SHIFT) | registernum)); - out_be32(®s->mdioctrl, ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + u32 ctrl_reg = __raw_readl(®s->mdioctrl); + __raw_writel(XEL_MDIOADDR_OP_MASK + | ((phyaddress << XEL_MDIOADDR_PHYADR_SHIFT) + | registernum), ®s->mdioaddr); + __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, ®s->mdioctrl); if (mdio_wait(regs)) return 1; /* Read data */ - *data = in_be32(®s->mdiord); + *data = __raw_readl(®s->mdiord); return 0; } @@ -221,11 +222,12 @@ static u32 phywrite(struct xemaclite *emaclite, u32 phyaddress, u32 registernum, * Data register. Finally, set the Status bit in the MDIO Control * register to start a MDIO write transaction. */ - u32 ctrl_reg = in_be32(®s->mdioctrl); - out_be32(®s->mdioaddr, ~XEL_MDIOADDR_OP_MASK & - ((phyaddress << XEL_MDIOADDR_PHYADR_SHIFT) | registernum)); - out_be32(®s->mdiowr, data); - out_be32(®s->mdioctrl, ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + u32 ctrl_reg = __raw_readl(®s->mdioctrl); + __raw_writel(~XEL_MDIOADDR_OP_MASK + & ((phyaddress << XEL_MDIOADDR_PHYADR_SHIFT) + | registernum), ®s->mdioaddr); + __raw_writel(data, ®s->mdiowr); + __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, ®s->mdioctrl); if (mdio_wait(regs)) return 1; @@ -328,27 +330,27 @@ static int emaclite_start(struct udevice *dev) * TX - TX_PING & TX_PONG initialization */ /* Restart PING TX */ - out_be32(®s->tx_ping_tsr, 0); + __raw_writel(0, ®s->tx_ping_tsr); /* Copy MAC address */ xemaclite_alignedwrite(pdata->enetaddr, ®s->tx_ping, ENET_ADDR_LENGTH); /* Set the length */ - out_be32(®s->tx_ping_tplr, ENET_ADDR_LENGTH); + __raw_writel(ENET_ADDR_LENGTH, ®s->tx_ping_tplr); /* Update the MAC address in the EMAC Lite */ - out_be32(®s->tx_ping_tsr, XEL_TSR_PROG_MAC_ADDR); + __raw_writel(XEL_TSR_PROG_MAC_ADDR, ®s->tx_ping_tsr); /* Wait for EMAC Lite to finish with the MAC address update */ - while ((in_be32 (®s->tx_ping_tsr) & + while ((__raw_readl(®s->tx_ping_tsr) & XEL_TSR_PROG_MAC_ADDR) != 0) ; if (emaclite->txpp) { /* The same operation with PONG TX */ - out_be32(®s->tx_pong_tsr, 0); + __raw_writel(0, ®s->tx_pong_tsr); xemaclite_alignedwrite(pdata->enetaddr, ®s->tx_pong, ENET_ADDR_LENGTH); - out_be32(®s->tx_pong_tplr, ENET_ADDR_LENGTH); - out_be32(®s->tx_pong_tsr, XEL_TSR_PROG_MAC_ADDR); - while ((in_be32(®s->tx_pong_tsr) & + __raw_writel(ENET_ADDR_LENGTH, ®s->tx_pong_tplr); + __raw_writel(XEL_TSR_PROG_MAC_ADDR, ®s->tx_pong_tsr); + while ((__raw_readl(®s->tx_pong_tsr) & XEL_TSR_PROG_MAC_ADDR) != 0) ; } @@ -357,13 +359,13 @@ static int emaclite_start(struct udevice *dev) * RX - RX_PING & RX_PONG initialization */ /* Write out the value to flush the RX buffer */ - out_be32(®s->rx_ping_rsr, XEL_RSR_RECV_IE_MASK); + __raw_writel(XEL_RSR_RECV_IE_MASK, ®s->rx_ping_rsr); if (emaclite->rxpp) - out_be32(®s->rx_pong_rsr, XEL_RSR_RECV_IE_MASK); + __raw_writel(XEL_RSR_RECV_IE_MASK, ®s->rx_pong_rsr); - out_be32(®s->mdioctrl, XEL_MDIOCTRL_MDIOEN_MASK); - if (in_be32(®s->mdioctrl) & XEL_MDIOCTRL_MDIOEN_MASK) + __raw_writel(XEL_MDIOCTRL_MDIOEN_MASK, ®s->mdioctrl); + if (__raw_readl(®s->mdioctrl) & XEL_MDIOCTRL_MDIOEN_MASK) if (!setup_phy(dev)) return -1; @@ -380,9 +382,9 @@ static int xemaclite_txbufferavailable(struct xemaclite *emaclite) * Read the other buffer register * and determine if the other buffer is available */ - tmp = ~in_be32(®s->tx_ping_tsr); + tmp = ~__raw_readl(®s->tx_ping_tsr); if (emaclite->txpp) - tmp |= ~in_be32(®s->tx_pong_tsr); + tmp |= ~__raw_readl(®s->tx_pong_tsr); return !(tmp & XEL_TSR_XMIT_BUSY_MASK); } @@ -406,40 +408,42 @@ static int emaclite_send(struct udevice *dev, void *ptr, int len) if (!maxtry) { printf("Error: Timeout waiting for ethernet TX buffer\n"); /* Restart PING TX */ - out_be32(®s->tx_ping_tsr, 0); + __raw_writel(0, ®s->tx_ping_tsr); if (emaclite->txpp) { - out_be32(®s->tx_pong_tsr, 0); + __raw_writel(0, ®s->tx_pong_tsr); } return -1; } /* Determine if the expected buffer address is empty */ - reg = in_be32(®s->tx_ping_tsr); + reg = __raw_readl(®s->tx_ping_tsr); if ((reg & XEL_TSR_XMIT_BUSY_MASK) == 0) { debug("Send packet from tx_ping buffer\n"); /* Write the frame to the buffer */ xemaclite_alignedwrite(ptr, ®s->tx_ping, len); - out_be32(®s->tx_ping_tplr, len & - (XEL_TPLR_LENGTH_MASK_HI | XEL_TPLR_LENGTH_MASK_LO)); - reg = in_be32(®s->tx_ping_tsr); + __raw_writel(len + & (XEL_TPLR_LENGTH_MASK_HI | XEL_TPLR_LENGTH_MASK_LO), + ®s->tx_ping_tplr); + reg = __raw_readl(®s->tx_ping_tsr); reg |= XEL_TSR_XMIT_BUSY_MASK; - out_be32(®s->tx_ping_tsr, reg); + __raw_writel(reg, ®s->tx_ping_tsr); return 0; } if (emaclite->txpp) { /* Determine if the expected buffer address is empty */ - reg = in_be32(®s->tx_pong_tsr); + reg = __raw_readl(®s->tx_pong_tsr); if ((reg & XEL_TSR_XMIT_BUSY_MASK) == 0) { debug("Send packet from tx_pong buffer\n"); /* Write the frame to the buffer */ xemaclite_alignedwrite(ptr, ®s->tx_pong, len); - out_be32(®s->tx_pong_tplr, len & + __raw_writel(len & (XEL_TPLR_LENGTH_MASK_HI | - XEL_TPLR_LENGTH_MASK_LO)); - reg = in_be32(®s->tx_pong_tsr); + XEL_TPLR_LENGTH_MASK_LO), + ®s->tx_pong_tplr); + reg = __raw_readl(®s->tx_pong_tsr); reg |= XEL_TSR_XMIT_BUSY_MASK; - out_be32(®s->tx_pong_tsr, reg); + __raw_writel(reg, ®s->tx_pong_tsr); return 0; } } @@ -459,7 +463,7 @@ static int emaclite_recv(struct udevice *dev, int flags, uchar **packetp) try_again: if (!emaclite->use_rx_pong_buffer_next) { - reg = in_be32(®s->rx_ping_rsr); + reg = __raw_readl(®s->rx_ping_rsr); debug("Testing data at rx_ping\n"); if ((reg & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { debug("Data found in rx_ping buffer\n"); @@ -479,7 +483,7 @@ try_again: goto try_again; } } else { - reg = in_be32(®s->rx_pong_rsr); + reg = __raw_readl(®s->rx_pong_rsr); debug("Testing data at rx_pong\n"); if ((reg & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { debug("Data found in rx_pong buffer\n"); @@ -526,9 +530,9 @@ try_again: length - first_read); /* Acknowledge the frame */ - reg = in_be32(ack); + reg = __raw_readl(ack); reg &= ~XEL_RSR_RECV_DONE_MASK; - out_be32(ack, reg); + __raw_writel(reg, ack); debug("Packet receive from 0x%p, length %dB\n", addr, length); *packetp = etherrxbuff; -- cgit v1.1 From 2f1f05f432a21de849bc3d8a8213509960ef568e Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Wed, 27 Jul 2016 12:25:09 +0100 Subject: net: emaclite: Enable driver for MIPS Signed-off-by: Zubair Lutfullah Kakakhel Reviewed-by: Paul Burton Acked-by: Joe Hershberger --- drivers/net/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index be3ed73..302c005 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -175,7 +175,7 @@ config XILINX_AXIEMAC This MAC is present in Xilinx Microblaze, Zynq and ZynqMP SoCs. config XILINX_EMACLITE - depends on DM_ETH && (MICROBLAZE || ARCH_ZYNQ || ARCH_ZYNQMP) + depends on DM_ETH && (MICROBLAZE || ARCH_ZYNQ || ARCH_ZYNQMP || MIPS) select PHYLIB select MII bool "Xilinx Ethernetlite" -- cgit v1.1 From d4e85377e79f52172c1df7469bf7d56ab0a85322 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Fri, 29 Jul 2016 15:11:19 +0100 Subject: mips: xilfpga: Add device tree files Mostly the same as the Kernel upstream device tree file except for - alias for the serial console node - ethernet node as the ethernet stuff isn't upstream on kernel.org yet - uart clock-frequency passed directly in the node Signed-off-by: Zubair Lutfullah Kakakhel Reviewed-by: Paul Burton Reviewed-by: Daniel Schwierzeck --- arch/mips/dts/Makefile | 1 + arch/mips/dts/microAptiv.dtsi | 21 +++++++++++++++ arch/mips/dts/nexys4ddr.dts | 62 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 arch/mips/dts/microAptiv.dtsi create mode 100644 arch/mips/dts/nexys4ddr.dts diff --git a/arch/mips/dts/Makefile b/arch/mips/dts/Makefile index 2f04d73..5f311d4 100644 --- a/arch/mips/dts/Makefile +++ b/arch/mips/dts/Makefile @@ -6,6 +6,7 @@ dtb-$(CONFIG_TARGET_AP121) += ap121.dtb dtb-$(CONFIG_TARGET_AP143) += ap143.dtb dtb-$(CONFIG_TARGET_MALTA) += mti,malta.dtb dtb-$(CONFIG_TARGET_PIC32MZDASK) += pic32mzda_sk.dtb +dtb-$(CONFIG_TARGET_XILFPGA) += nexys4ddr.dtb dtb-$(CONFIG_BOARD_TPLINK_WDR4300) += tplink_wdr4300.dtb targets += $(dtb-y) diff --git a/arch/mips/dts/microAptiv.dtsi b/arch/mips/dts/microAptiv.dtsi new file mode 100644 index 0000000..81d518e --- /dev/null +++ b/arch/mips/dts/microAptiv.dtsi @@ -0,0 +1,21 @@ +/ { + #address-cells = <1>; + #size-cells = <1>; + compatible = "img,xilfpga"; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + device_type = "cpu"; + compatible = "mips,m14Kc"; + clocks = <&ext>; + reg = <0>; + }; + }; + + ext: ext { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; +}; diff --git a/arch/mips/dts/nexys4ddr.dts b/arch/mips/dts/nexys4ddr.dts new file mode 100644 index 0000000..e254ab1 --- /dev/null +++ b/arch/mips/dts/nexys4ddr.dts @@ -0,0 +1,62 @@ +/dts-v1/; + +#include "microAptiv.dtsi" + +/ { + compatible = "digilent,nexys4ddr"; + + memory { + device_type = "memory"; + reg = <0x0 0x08000000>; + }; + + cpuintc: interrupt-controller@0 { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + compatible = "mti,cpu-interrupt-controller"; + }; + + aliases { + console = &axi_uart16550; + }; + + axi_ethernetlite: ethernet@10e00000 { + compatible = "xlnx,xps-ethernetlite-1.00.a"; + device_type = "network"; + local-mac-address = [08 86 4C 0D F7 09]; + phy-handle = <&phy0>; + reg = <0x10e00000 0x10000>; + xlnx,duplex = <0x1>; + xlnx,include-global-buffers = <0x1>; + xlnx,include-internal-loopback = <0x0>; + xlnx,include-mdio = <0x1>; + xlnx,instance = "axi_ethernetlite_inst"; + xlnx,rx-ping-pong = <0x1>; + xlnx,s-axi-id-width = <0x1>; + xlnx,tx-ping-pong = <0x1>; + xlnx,use-internal = <0x0>; + mdio { + #address-cells = <1>; + #size-cells = <0>; + phy0: phy@1 { + compatible = <0x0007c0f0 0xfffffff0>; + device_type = "ethernet-phy"; + reg = <1>; + } ; + } ; + } ; + + + axi_uart16550: serial@10400000 { + compatible = "ns16550a"; + reg = <0x10400000 0x10000>; + + reg-shift = <2>; + reg-offset = <0x1000>; + + clock-frequency = <50000000>; + + }; +}; + -- cgit v1.1 From ebf2b9e3dff089a9c99e5dc8d7e10b06365e4e46 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Fri, 29 Jul 2016 15:11:20 +0100 Subject: mips: Add MIPSfpga platform support MIPSfpga is an FPGA based dev platform. In a nutshell, its a microAptiv cpu core with lots of Xilinx IP blocks The FPGA dev board used is the Nexys4DDR board by Digilent. For more information, check the Readme file in board/imgtec/xilfpga Signed-off-by: Zubair Lutfullah Kakakhel Reviewed-by: Paul Burton Reviewed-by: Daniel Schwierzeck --- arch/mips/Kconfig | 15 +++++++++ board/imgtec/xilfpga/Kconfig | 15 +++++++++ board/imgtec/xilfpga/MAINTAINERS | 6 ++++ board/imgtec/xilfpga/Makefile | 7 +++++ board/imgtec/xilfpga/README | 55 ++++++++++++++++++++++++++++++++ board/imgtec/xilfpga/xilfpga.c | 20 ++++++++++++ configs/imgtec_xilfpga_defconfig | 25 +++++++++++++++ include/configs/imgtec_xilfpga.h | 68 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 211 insertions(+) create mode 100644 board/imgtec/xilfpga/Kconfig create mode 100644 board/imgtec/xilfpga/MAINTAINERS create mode 100644 board/imgtec/xilfpga/Makefile create mode 100644 board/imgtec/xilfpga/README create mode 100644 board/imgtec/xilfpga/xilfpga.c create mode 100644 configs/imgtec_xilfpga_defconfig create mode 100644 include/configs/imgtec_xilfpga.h diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 21066f0..fa344a7 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -73,10 +73,25 @@ config MACH_PIC32 select OF_CONTROL select DM +config TARGET_XILFPGA + bool "Support Imagination Xilfpga" + select OF_CONTROL + select DM + select DM_SERIAL + select DM_GPIO + select DM_ETH + select SUPPORTS_LITTLE_ENDIAN + select SUPPORTS_CPU_MIPS32_R1 + select SUPPORTS_CPU_MIPS32_R2 + select MIPS_L1_CACHE_SHIFT_4 + help + This supports IMGTEC MIPSfpga platform + endchoice source "board/dbau1x00/Kconfig" source "board/imgtec/malta/Kconfig" +source "board/imgtec/xilfpga/Kconfig" source "board/micronas/vct/Kconfig" source "board/pb1x00/Kconfig" source "board/qemu-mips/Kconfig" diff --git a/board/imgtec/xilfpga/Kconfig b/board/imgtec/xilfpga/Kconfig new file mode 100644 index 0000000..b078278 --- /dev/null +++ b/board/imgtec/xilfpga/Kconfig @@ -0,0 +1,15 @@ +if TARGET_XILFPGA + +config SYS_BOARD + default "xilfpga" + +config SYS_VENDOR + default "imgtec" + +config SYS_CONFIG_NAME + default "imgtec_xilfpga" + +config SYS_TEXT_BASE + default 0x80C00000 + +endif diff --git a/board/imgtec/xilfpga/MAINTAINERS b/board/imgtec/xilfpga/MAINTAINERS new file mode 100644 index 0000000..aa04532 --- /dev/null +++ b/board/imgtec/xilfpga/MAINTAINERS @@ -0,0 +1,6 @@ +XILFPGA BOARD +M: Zubair Lutfullah Kakakhel +S: Maintained +F: board/imgtec/xilfpga +F: include/configs/xilfpga.h +F: configs/imgtec_xilfpga_defconfig diff --git a/board/imgtec/xilfpga/Makefile b/board/imgtec/xilfpga/Makefile new file mode 100644 index 0000000..9aaf9ce --- /dev/null +++ b/board/imgtec/xilfpga/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (C) 2016, Imagination Technologies Ltd. +# Zubair Lutfullah Kakakhel +# +# SPDX-License-Identifier: GPL-2.0+ +# +obj-y := xilfpga.o diff --git a/board/imgtec/xilfpga/README b/board/imgtec/xilfpga/README new file mode 100644 index 0000000..ac19d48 --- /dev/null +++ b/board/imgtec/xilfpga/README @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016, Imagination Technologies Ltd. + * + * Zubair Lutfullah Kakakhel, Zubair.Kakakhel@imgtec.com + */ + +MIPSfpga +======================================= + +MIPSfpga is an FPGA based development platform by Imagination Technologies +As we are dealing with a MIPS core instantiated on an FPGA, specifications +are fluid and can be varied in RTL. + +The example project provided by IMGTEC runs on the Nexys4DDR board by +Digilent powered by the ARTIX-7 FPGA by Xilinx. Relevant details about +the example project and the Nexys4DDR board: + +- microAptiv UP core m14Kc +- 50MHz clock speed +- 128Mbyte DDR RAM at 0x0000_0000 +- 8Kbyte RAM at 0x1000_0000 +- axi_intc at 0x1020_0000 +- axi_uart16550 at 0x1040_0000 +- axi_gpio at 0x1060_0000 +- axi_i2c at 0x10A0_0000 +- custom_gpio at 0x10C0_0000 +- axi_ethernetlite at 0x10E0_0000 +- 8Kbyte BootRAM at 0x1FC0_0000 +- 16Mbyte QPI at 0x1D00_0000 + +Boot protocol: +-------------- + +The BootRAM is a writeable "RAM" in FPGA at 0x1FC0_0000. +This is for easy reprogrammibility via JTAG. + +DDR initialization is already handled by a HW IP block. + +When the example project bitstream is loaded, the cpu_reset button +needs to be pressed. + +The bootram initializes the cache and axi_uart +Then checks if there is anything non 0xffff_ffff at location 0x1D40_0000 + +If there is, then that is considered as u-boot. u-boot is copied from +0x1D40_0000 to memory and the bootram jumps into u-boot code. + +At this point, the board is ready to load the Linux kernel + buildroot initramfs + +This can be done in multiple ways: + +1- JTAG load the binary and jump into it. +2- Load kernel stored in the QSPI flash at 0x1D80_0000 +3- Load uImage via tftp. Ethernet works in u-boot. + e.g. env set server ip 192.168.154.45; dhcp uImage; bootm diff --git a/board/imgtec/xilfpga/xilfpga.c b/board/imgtec/xilfpga/xilfpga.c new file mode 100644 index 0000000..77a1952 --- /dev/null +++ b/board/imgtec/xilfpga/xilfpga.c @@ -0,0 +1,20 @@ +/* + * Imagination Technologies MIPSfpga platform code + * + * Copyright (C) 2016, Imagination Technologies Ltd. + * + * Zubair Lutfullah Kakakhel + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#include + +/* initialize the DDR Controller and PHY */ +phys_size_t initdram(int board_type) +{ + /* MIG IP block is smart and doesn't need SW + * to do any init */ + return CONFIG_SYS_SDRAM_SIZE; /* in bytes */ +} diff --git a/configs/imgtec_xilfpga_defconfig b/configs/imgtec_xilfpga_defconfig new file mode 100644 index 0000000..63f03cd --- /dev/null +++ b/configs/imgtec_xilfpga_defconfig @@ -0,0 +1,25 @@ +CONFIG_MIPS=y +CONFIG_SYS_MALLOC_F_LEN=0x600 +CONFIG_TARGET_XILFPGA=y +# CONFIG_MIPS_BOOT_ENV_LEGACY is not set +CONFIG_MIPS_BOOT_FDT=y +CONFIG_DEFAULT_DEVICE_TREE="nexys4ddr" +CONFIG_BOOTDELAY=5 +CONFIG_HUSH_PARSER=y +CONFIG_SYS_PROMPT="MIPSfpga # " +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_SAVEENV is not set +CONFIG_CMD_MEMINFO=y +# CONFIG_CMD_FLASH is not set +# CONFIG_CMD_FPGA is not set +CONFIG_CMD_DHCP=y +CONFIG_CMD_MII=y +CONFIG_CMD_PING=y +CONFIG_CMD_TIME=y +CONFIG_OF_EMBED=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_NETCONSOLE=y +CONFIG_CLK=y +CONFIG_XILINX_EMACLITE=y +CONFIG_SYS_NS16550=y +CONFIG_CMD_DHRYSTONE=y diff --git a/include/configs/imgtec_xilfpga.h b/include/configs/imgtec_xilfpga.h new file mode 100644 index 0000000..0a7fe60 --- /dev/null +++ b/include/configs/imgtec_xilfpga.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016, Imagination Technologies Ltd. + * + * Zubair Lutfullah Kakakhel + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Imagination Technologies Ltd. MIPSfpga + */ + +#ifndef __XILFPGA_CONFIG_H +#define __XILFPGA_CONFIG_H + +/* BootROM + MIG is pretty smart. DDR and Cache initialized */ +#define CONFIG_SKIP_LOWLEVEL_INIT + +/*-------------------------------------------- + * CPU configuration + */ +/* CPU Timer rate */ +#define CONFIG_SYS_MIPS_TIMER_FREQ 50000000 + +/* Cache Configuration */ +#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT + +/*---------------------------------------------------------------------- + * Memory Layout + */ + +/* SDRAM Configuration (for final code, data, stack, heap) */ +#define CONFIG_SYS_SDRAM_BASE 0x80000000 +#define CONFIG_SYS_SDRAM_SIZE 0x08000000 /* 128 Mbytes */ +#define CONFIG_SYS_INIT_SP_ADDR \ + (CONFIG_SYS_SDRAM_BASE + CONFIG_SYS_SDRAM_SIZE - 0x1000) + +#define CONFIG_SYS_MALLOC_LEN (256 << 10) +#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE +#define CONFIG_SYS_LOAD_ADDR 0x80500000 /* default load address */ + +/*---------------------------------------------------------------------- + * Commands + */ +#define CONFIG_SYS_LONGHELP /* undef to save memory */ + +/*------------------------------------------------- + * FLASH configuration + */ +#define CONFIG_SYS_NO_FLASH + +/*------------------------------------------------------------ + * Console Configuration + */ +#define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ +#define CONFIG_SYS_MAXARGS 16 /* max number of command args*/ +#define CONFIG_BAUDRATE 115200 + +/* ------------------------------------------------- + * Environment + */ +#define CONFIG_ENV_IS_NOWHERE 1 +#define CONFIG_ENV_SIZE 0x4000 + +/* --------------------------------------------------------------------- + * Board boot configuration + */ +#define CONFIG_TIMESTAMP /* Print image info with timestamp */ + +#endif /* __XILFPGA_CONFIG_H */ -- cgit v1.1 From 8ebf50692ee9694e1b4baa61228376e83c0e106e Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:46 +0100 Subject: board_f: Add a mach_cpu_init callback Currently we have a mismash of architectures which use arch_cpu_init from architecture-wide code (arc, avr32, blackfin, mips, nios2, xtensa) and architectures which use arch_cpu_init from machine/SoC level code (arm, x86). In order to clean this mess up & allow for both use cases, introduce a new mach_cpu_init callback which is run immediately after arch_cpu_init. This will allow for architectures to have arch-wide code without needing individual machines to all implement their own arch_cpu_init with a call to some common function. Signed-off-by: Paul Burton Reviewed-by: Simon Glass --- common/board_f.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/board_f.c b/common/board_f.c index da381db..9ef998f 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -290,6 +290,11 @@ __weak int arch_cpu_init(void) return 0; } +__weak int mach_cpu_init(void) +{ + return 0; +} + #ifdef CONFIG_SANDBOX static int setup_ram_buf(void) { @@ -860,6 +865,7 @@ static init_fnc_t init_sequence_f[] = { x86_fsp_init, #endif arch_cpu_init, /* basic arch cpu dependent setup */ + mach_cpu_init, /* SoC/machine dependent CPU setup */ initf_dm, arch_cpu_init_dm, mark_bootstage, /* need timer, go after init dm */ -- cgit v1.1 From 0dfe04d6c8a52624c5404392168e52fe70fe0dc9 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:47 +0100 Subject: MIPS: ath79: Use mach_cpu_init instead of arch_cpu_init In order to prepare for MIPS arch code making use of arch_cpu_init in a later patch, stop using it from ath79 SoC code & instead use the new mach_cpu_init which is provided for this purpose. Signed-off-by: Paul Burton --- arch/mips/mach-ath79/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/mach-ath79/cpu.c b/arch/mips/mach-ath79/cpu.c index 5756a06..4ef5092 100644 --- a/arch/mips/mach-ath79/cpu.c +++ b/arch/mips/mach-ath79/cpu.c @@ -46,7 +46,7 @@ static const struct ath79_soc_desc desc[] = { {ATH79_SOC_QCA9561, "9561", REV_ID_MAJOR_QCA9561, 0}, }; -int arch_cpu_init(void) +int mach_cpu_init(void) { void __iomem *base; enum ath79_soc_type soc = ATH79_SOC_UNKNOWN; -- cgit v1.1 From 8cb4817d0f076605c5126f0925f6c7fdb0461bf6 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:48 +0100 Subject: MIPS: Probe cache line sizes once during boot Rather than probing the cache line sizes on every call of any cache maintenance function, probe them once during boot & store the values in the global data structure for later use. This will reduce the overhead of the cache maintenance functions, which isn't a big deal yet but becomes more important once L2 caches which may expose their properties via coprocessor 2 or the CM are supported. Signed-off-by: Paul Burton --- arch/mips/cpu/cpu.c | 7 ++++++ arch/mips/include/asm/cache.h | 9 ++++++++ arch/mips/include/asm/global_data.h | 4 ++++ arch/mips/lib/cache.c | 43 +++++++++++++++++++++---------------- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/arch/mips/cpu/cpu.c b/arch/mips/cpu/cpu.c index 391feb3..1b919ed 100644 --- a/arch/mips/cpu/cpu.c +++ b/arch/mips/cpu/cpu.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -35,3 +36,9 @@ void write_one_tlb(int index, u32 pagemask, u32 hi, u32 low0, u32 low1) write_c0_index(index); tlb_write_indexed(); } + +int arch_cpu_init(void) +{ + mips_cache_probe(); + return 0; +} diff --git a/arch/mips/include/asm/cache.h b/arch/mips/include/asm/cache.h index 0cea581..669c362 100644 --- a/arch/mips/include/asm/cache.h +++ b/arch/mips/include/asm/cache.h @@ -19,4 +19,13 @@ */ #define CONFIG_SYS_CACHELINE_SIZE ARCH_DMA_MINALIGN +/** + * mips_cache_probe() - Probe the properties of the caches + * + * Call this to probe the properties such as line sizes of the caches + * present in the system, if any. This must be done before cache maintenance + * functions such as flush_cache may be called. + */ +void mips_cache_probe(void); + #endif /* __MIPS_CACHE_H__ */ diff --git a/arch/mips/include/asm/global_data.h b/arch/mips/include/asm/global_data.h index 37f8ed5..8533b69 100644 --- a/arch/mips/include/asm/global_data.h +++ b/arch/mips/include/asm/global_data.h @@ -21,6 +21,10 @@ struct arch_global_data { unsigned long rev; unsigned long ver; #endif +#ifdef CONFIG_SYS_CACHE_SIZE_AUTO + unsigned short l1i_line_size; + unsigned short l1d_line_size; +#endif }; #include diff --git a/arch/mips/lib/cache.c b/arch/mips/lib/cache.c index db81953..d8baf08 100644 --- a/arch/mips/lib/cache.c +++ b/arch/mips/lib/cache.c @@ -9,32 +9,39 @@ #include #include -static inline unsigned long icache_line_size(void) -{ - unsigned long conf1, il; +DECLARE_GLOBAL_DATA_PTR; - if (!config_enabled(CONFIG_SYS_CACHE_SIZE_AUTO)) - return CONFIG_SYS_ICACHE_LINE_SIZE; +void mips_cache_probe(void) +{ +#ifdef CONFIG_SYS_CACHE_SIZE_AUTO + unsigned long conf1, il, dl; conf1 = read_c0_config1(); + il = (conf1 & MIPS_CONF1_IL) >> MIPS_CONF1_IL_SHF; - if (!il) - return 0; - return 2 << il; + dl = (conf1 & MIPS_CONF1_DL) >> MIPS_CONF1_DL_SHF; + + gd->arch.l1i_line_size = il ? (2 << il) : 0; + gd->arch.l1d_line_size = dl ? (2 << dl) : 0; +#endif } -static inline unsigned long dcache_line_size(void) +static inline unsigned long icache_line_size(void) { - unsigned long conf1, dl; - - if (!config_enabled(CONFIG_SYS_CACHE_SIZE_AUTO)) - return CONFIG_SYS_DCACHE_LINE_SIZE; +#ifdef CONFIG_SYS_CACHE_SIZE_AUTO + return gd->arch.l1i_line_size; +#else + return CONFIG_SYS_ICACHE_LINE_SIZE; +#endif +} - conf1 = read_c0_config1(); - dl = (conf1 & MIPS_CONF1_DL) >> MIPS_CONF1_DL_SHF; - if (!dl) - return 0; - return 2 << dl; +static inline unsigned long dcache_line_size(void) +{ +#ifdef CONFIG_SYS_CACHE_SIZE_AUTO + return gd->arch.l1d_line_size; +#else + return CONFIG_SYS_DCACHE_LINE_SIZE; +#endif } #define cache_loop(start, end, lsize, ops...) do { \ -- cgit v1.1 From 33b5c9b2092e10fa3b8b325823c846368f25bba9 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:49 +0100 Subject: MIPS: Enable use of the instruction cache earlier Enable use of the instruction cache immediately after it has been initialised. This will only take effect if U-Boot was linked to run from kseg0 rather than kseg1, but when this is the case the data cache initialisation code will run cached & thus significantly faster. Signed-off-by: Paul Burton --- arch/mips/cpu/start.S | 8 -------- arch/mips/lib/cache_init.S | 13 +++++++++++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S index fc6dd66..827a544 100644 --- a/arch/mips/cpu/start.S +++ b/arch/mips/cpu/start.S @@ -12,10 +12,6 @@ #include #include -#ifndef CONFIG_SYS_MIPS_CACHE_MODE -#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT -#endif - #ifndef CONFIG_SYS_INIT_SP_ADDR #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + \ CONFIG_SYS_INIT_SP_OFFSET) @@ -154,10 +150,6 @@ reset: PTR_LA t9, mips_cache_reset jalr t9 nop - - /* ... and enable them */ - li t0, CONFIG_SYS_MIPS_CACHE_MODE - mtc0 t0, CP0_CONFIG #endif /* Set up temporary stack */ diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index bc8ab27..974dfe9 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -172,6 +172,19 @@ LEAF(mips_cache_reset) cache_loop t0, t1, t8, INDEX_STORE_TAG_I #endif + /* Enable use of the I-cache by setting Config.K0 */ + sync + mfc0 t0, CP0_CONFIG + li t1, CONFIG_SYS_MIPS_CACHE_MODE +#if __mips_isa_rev >= 2 + ins t0, t1, 0, 3 +#else + ori t0, t0, CONF_CM_CMASK + xori t0, t0, CONF_CM_CMASK + or t0, t0, t1 +#endif + mtc0 t0, CP0_CONFIG + /* * then initialize D-cache. */ -- cgit v1.1 From 4f9226b40379847339af8a7777be26f2db72e79b Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:50 +0100 Subject: MIPS: Preserve Config implementation-defined bits The coprocessor 0 Config register includes 9 implementation defined bits, which in some processors do things like enable write combining or other functionality. We ought not to wipe them to 0 during boot. Rather than doing so, preserve their value & only clear the bits standardised by the MIPS architecture. Signed-off-by: Paul Burton --- arch/mips/cpu/start.S | 5 +++-- arch/mips/include/asm/mipsregs.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S index 827a544..6aec430 100644 --- a/arch/mips/cpu/start.S +++ b/arch/mips/cpu/start.S @@ -123,8 +123,9 @@ reset: mtc0 zero, CP0_COMPARE #ifndef CONFIG_SKIP_LOWLEVEL_INIT - /* CONFIG0 register */ - li t0, CONF_CM_UNCACHED + mfc0 t0, CP0_CONFIG + and t0, t0, MIPS_CONF_IMPL + or t0, t0, CONF_CM_UNCACHED mtc0 t0, CP0_CONFIG #endif diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 3185dc7..cd4f952 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -450,6 +450,7 @@ #define MIPS_CONF_MT_FTLB (_ULCAST_(4) << 7) #define MIPS_CONF_AR (_ULCAST_(7) << 10) #define MIPS_CONF_AT (_ULCAST_(3) << 13) +#define MIPS_CONF_IMPL (_ULCAST_(0x1ff) << 16) #define MIPS_CONF_M (_ULCAST_(1) << 31) /* -- cgit v1.1 From f8981277f581564bf701d310fe0f68903cf3f542 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:51 +0100 Subject: MIPS: If we don't need DDR for cache init, init cache first On systems where cache initialisation doesn't require zeroed memory (ie. systems where CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD is not defined) perform cache initialisation prior to lowlevel_init & DDR initialisation. This allows for DDR initialisation code to run cached & thus significantly faster. Signed-off-by: Paul Burton --- arch/mips/cpu/start.S | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S index 6aec430..6f1d219 100644 --- a/arch/mips/cpu/start.S +++ b/arch/mips/cpu/start.S @@ -142,15 +142,24 @@ reset: PTR_L gp, 0(ra) #ifndef CONFIG_SKIP_LOWLEVEL_INIT +# ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD /* Initialize any external memory */ PTR_LA t9, lowlevel_init jalr t9 nop +# endif /* Initialize caches... */ PTR_LA t9, mips_cache_reset jalr t9 nop + +# ifndef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD + /* Initialize any external memory */ + PTR_LA t9, lowlevel_init + jalr t9 + nop +# endif #endif /* Set up temporary stack */ -- cgit v1.1 From 5c72e5a62e998ce374a27b0589328bb284a27c8c Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:52 +0100 Subject: MIPS: Define register names for cache init Define names for registers holding cache sizes throughout mips_cache_reset, in order to make the code easier to read & allow for changing register assignments more easily. Signed-off-by: Paul Burton --- arch/mips/lib/cache_init.S | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index 974dfe9..9be3a07 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -98,19 +98,23 @@ * RETURNS: N/A * */ +#define R_IC_SIZE t2 +#define R_IC_LINE t8 +#define R_DC_SIZE t3 +#define R_DC_LINE t9 LEAF(mips_cache_reset) #ifndef CONFIG_SYS_CACHE_SIZE_AUTO - li t2, CONFIG_SYS_ICACHE_SIZE - li t8, CONFIG_SYS_ICACHE_LINE_SIZE + li R_IC_SIZE, CONFIG_SYS_ICACHE_SIZE + li R_IC_LINE, CONFIG_SYS_ICACHE_LINE_SIZE #else - l1_info t2, t8, MIPS_CONF1_IA_SHF + l1_info R_IC_SIZE, R_IC_LINE, MIPS_CONF1_IA_SHF #endif #ifndef CONFIG_SYS_CACHE_SIZE_AUTO - li t3, CONFIG_SYS_DCACHE_SIZE - li t9, CONFIG_SYS_DCACHE_LINE_SIZE + li R_DC_SIZE, CONFIG_SYS_DCACHE_SIZE + li R_DC_LINE, CONFIG_SYS_DCACHE_LINE_SIZE #else - l1_info t3, t9, MIPS_CONF1_DA_SHF + l1_info R_DC_SIZE, R_DC_LINE, MIPS_CONF1_DA_SHF #endif #ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD @@ -123,9 +127,9 @@ LEAF(mips_cache_reset) li v0, CONFIG_SYS_DCACHE_SIZE #endif #else - move v0, t2 - sltu t1, t2, t3 - movn v0, t3, t1 + move v0, R_IC_SIZE + sltu t1, R_IC_SIZE, R_DC_SIZE + movn v0, R_DC_SIZE, t1 #endif /* * Now clear that much memory starting from zero. @@ -158,18 +162,18 @@ LEAF(mips_cache_reset) /* * Initialize the I-cache first, */ - blez t2, 1f + blez R_IC_SIZE, 1f PTR_LI t0, INDEX_BASE - PTR_ADDU t1, t0, t2 + PTR_ADDU t1, t0, R_IC_SIZE /* clear tag to invalidate */ - cache_loop t0, t1, t8, INDEX_STORE_TAG_I + cache_loop t0, t1, R_IC_LINE, INDEX_STORE_TAG_I #ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD /* fill once, so data field parity is correct */ PTR_LI t0, INDEX_BASE - cache_loop t0, t1, t8, FILL + cache_loop t0, t1, R_IC_LINE, FILL /* invalidate again - prudent but not strictly neccessary */ PTR_LI t0, INDEX_BASE - cache_loop t0, t1, t8, INDEX_STORE_TAG_I + cache_loop t0, t1, R_IC_LINE, INDEX_STORE_TAG_I #endif /* Enable use of the I-cache by setting Config.K0 */ @@ -188,20 +192,20 @@ LEAF(mips_cache_reset) /* * then initialize D-cache. */ -1: blez t3, 3f +1: blez R_DC_SIZE, 3f PTR_LI t0, INDEX_BASE - PTR_ADDU t1, t0, t3 + PTR_ADDU t1, t0, R_DC_SIZE /* clear all tags */ - cache_loop t0, t1, t9, INDEX_STORE_TAG_D + cache_loop t0, t1, R_DC_LINE, INDEX_STORE_TAG_D #ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD /* load from each line (in cached space) */ PTR_LI t0, INDEX_BASE 2: LONG_L zero, 0(t0) - PTR_ADDU t0, t9 + PTR_ADDU t0, R_DC_LINE bne t0, t1, 2b /* clear all tags */ PTR_LI t0, INDEX_BASE - cache_loop t0, t1, t9, INDEX_STORE_TAG_D + cache_loop t0, t1, R_DC_LINE, INDEX_STORE_TAG_D #endif 3: jr ra -- cgit v1.1 From b2b135d980a7171c9b0eefbb38861ccbe9a47f97 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:53 +0100 Subject: MIPS: Map CM Global Control Registers Map the Global Control Registers (GCRs) provided by the MIPS Coherence Manager (CM) in preparation for using some of them in later patches. Signed-off-by: Paul Burton --- arch/mips/Kconfig | 16 ++++++++++++++++ arch/mips/cpu/Makefile | 2 ++ arch/mips/cpu/cm_init.S | 45 +++++++++++++++++++++++++++++++++++++++++++++ arch/mips/cpu/start.S | 6 ++++++ arch/mips/include/asm/cm.h | 19 +++++++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 arch/mips/cpu/cm_init.S create mode 100644 arch/mips/include/asm/cm.h diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index fa344a7..47056b4 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -318,6 +318,22 @@ config MIPS_L1_CACHE_SHIFT config DYNAMIC_IO_PORT_BASE bool +config MIPS_CM + bool + help + Select this if your system contains a MIPS Coherence Manager and you + wish U-Boot to configure it or make use of it to retrieve system + information such as cache configuration. + +config MIPS_CM_BASE + hex + default 0x1fbf8000 + help + The physical base address at which to map the MIPS Coherence Manager + Global Configuration Registers (GCRs). This should be set such that + the GCRs occupy a region of the physical address space which is + otherwise unused, or at minimum that software doesn't need to access. + endif endmenu diff --git a/arch/mips/cpu/Makefile b/arch/mips/cpu/Makefile index fc6b455..429fd3a 100644 --- a/arch/mips/cpu/Makefile +++ b/arch/mips/cpu/Makefile @@ -7,3 +7,5 @@ extra-y = start.o obj-y += time.o obj-y += interrupts.o obj-y += cpu.o + +obj-$(CONFIG_MIPS_CM) += cm_init.o diff --git a/arch/mips/cpu/cm_init.S b/arch/mips/cpu/cm_init.S new file mode 100644 index 0000000..ddcaa49 --- /dev/null +++ b/arch/mips/cpu/cm_init.S @@ -0,0 +1,45 @@ +/* + * MIPS Coherence Manager (CM) Initialisation + * + * Copyright (c) 2016 Imagination Technologies Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +LEAF(mips_cm_map) + /* Config3 must exist for a CM to be present */ + mfc0 t0, CP0_CONFIG, 1 + bgez t0, 2f + mfc0 t0, CP0_CONFIG, 2 + bgez t0, 2f + + /* Check Config3.CMGCR to determine CM presence */ + mfc0 t0, CP0_CONFIG, 3 + and t0, t0, MIPS_CONF3_CMGCR + beqz t0, 2f + + /* Find the current physical GCR base address */ +1: MFC0 t0, CP0_CMGCRBASE + PTR_SLL t0, t0, 4 + + /* If the GCRs are where we want, we're done */ + PTR_LI t1, CONFIG_MIPS_CM_BASE + beq t0, t1, 2f + + /* Move the GCRs to our configured base address */ + PTR_LI t2, CKSEG1 + PTR_ADDU t0, t0, t2 + sw zero, GCR_BASE_UPPER(t0) + sw t1, GCR_BASE(t0) + + /* Re-check the GCR base */ + b 1b + +2: jr ra + END(mips_cm_map) diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S index 6f1d219..c157d03 100644 --- a/arch/mips/cpu/start.S +++ b/arch/mips/cpu/start.S @@ -141,6 +141,12 @@ reset: 1: PTR_L gp, 0(ra) +#ifdef CONFIG_MIPS_CM + PTR_LA t9, mips_cm_map + jalr t9 + nop +#endif + #ifndef CONFIG_SKIP_LOWLEVEL_INIT # ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD /* Initialize any external memory */ diff --git a/arch/mips/include/asm/cm.h b/arch/mips/include/asm/cm.h new file mode 100644 index 0000000..0261733 --- /dev/null +++ b/arch/mips/include/asm/cm.h @@ -0,0 +1,19 @@ +/* + * MIPS Coherence Manager (CM) Register Definitions + * + * Copyright (c) 2016 Imagination Technologies Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef __MIPS_ASM_CM_H__ +#define __MIPS_ASM_CM_H__ + +/* Global Control Register (GCR) offsets */ +#define GCR_BASE 0x0008 +#define GCR_BASE_UPPER 0x000c +#define GCR_REV 0x0030 + +/* GCR_REV CM versions */ +#define GCR_REV_CM3 0x0800 + +#endif /* __MIPS_ASM_CM_H__ */ -- cgit v1.1 From 4baa0ab67d504f3b4318f999631e3f83d0c52c4a Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:54 +0100 Subject: MIPS: L2 cache support This patch adds support for initialising & maintaining L2 caches on MIPS systems. The L2 cache configuration may be advertised through either coprocessor 0 or the MIPS Coherence Manager depending upon the system, and support for both is included. If the L2 can be bypassed then we bypass it early in boot & initialise the L1 caches first, such that we can start making use of the L1 instruction cache as early as possible. Otherwise we initialise the L2 first such that the L1s have no opportunity to generate access to the uninitialised L2. Signed-off-by: Paul Burton --- arch/mips/Kconfig | 6 ++ arch/mips/include/asm/cm.h | 38 ++++++++ arch/mips/include/asm/global_data.h | 3 + arch/mips/include/asm/mipsregs.h | 5 + arch/mips/lib/cache.c | 62 +++++++++++- arch/mips/lib/cache_init.S | 183 +++++++++++++++++++++++++++++++++++- 6 files changed, 291 insertions(+), 6 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 47056b4..7671437 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -315,6 +315,12 @@ config MIPS_L1_CACHE_SHIFT default "4" if MIPS_L1_CACHE_SHIFT_4 default "5" +config MIPS_L2_CACHE + bool + help + Select this if your system includes an L2 cache and you want U-Boot + to initialise & maintain it. + config DYNAMIC_IO_PORT_BASE bool diff --git a/arch/mips/include/asm/cm.h b/arch/mips/include/asm/cm.h index 0261733..62ecef2 100644 --- a/arch/mips/include/asm/cm.h +++ b/arch/mips/include/asm/cm.h @@ -12,8 +12,46 @@ #define GCR_BASE 0x0008 #define GCR_BASE_UPPER 0x000c #define GCR_REV 0x0030 +#define GCR_L2_CONFIG 0x0130 +#define GCR_L2_TAG_ADDR 0x0600 +#define GCR_L2_TAG_ADDR_UPPER 0x0604 +#define GCR_L2_TAG_STATE 0x0608 +#define GCR_L2_TAG_STATE_UPPER 0x060c +#define GCR_L2_DATA 0x0610 +#define GCR_L2_DATA_UPPER 0x0614 /* GCR_REV CM versions */ #define GCR_REV_CM3 0x0800 +/* GCR_L2_CONFIG fields */ +#define GCR_L2_CONFIG_ASSOC_SHIFT 0 +#define GCR_L2_CONFIG_ASSOC_BITS 8 +#define GCR_L2_CONFIG_LINESZ_SHIFT 8 +#define GCR_L2_CONFIG_LINESZ_BITS 4 +#define GCR_L2_CONFIG_SETSZ_SHIFT 12 +#define GCR_L2_CONFIG_SETSZ_BITS 4 +#define GCR_L2_CONFIG_BYPASS (1 << 20) + +#ifndef __ASSEMBLY__ + +#include + +static inline void *mips_cm_base(void) +{ + return (void *)CKSEG1ADDR(CONFIG_MIPS_CM_BASE); +} + +static inline unsigned long mips_cm_l2_line_size(void) +{ + unsigned long l2conf, line_sz; + + l2conf = __raw_readl(mips_cm_base() + GCR_L2_CONFIG); + + line_sz = l2conf >> GCR_L2_CONFIG_LINESZ_SHIFT; + line_sz &= GENMASK(GCR_L2_CONFIG_LINESZ_BITS - 1, 0); + return line_sz ? (2 << line_sz) : 0; +} + +#endif /* !__ASSEMBLY__ */ + #endif /* __MIPS_ASM_CM_H__ */ diff --git a/arch/mips/include/asm/global_data.h b/arch/mips/include/asm/global_data.h index 8533b69..0078bbe 100644 --- a/arch/mips/include/asm/global_data.h +++ b/arch/mips/include/asm/global_data.h @@ -25,6 +25,9 @@ struct arch_global_data { unsigned short l1i_line_size; unsigned short l1d_line_size; #endif +#ifdef CONFIG_MIPS_L2_CACHE + unsigned short l2_line_size; +#endif }; #include diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index cd4f952..b4c2dff 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -485,9 +485,13 @@ #define MIPS_CONF1_TLBS_SIZE (6) #define MIPS_CONF1_TLBS (_ULCAST_(63) << MIPS_CONF1_TLBS_SHIFT) +#define MIPS_CONF2_SA_SHF 0 #define MIPS_CONF2_SA (_ULCAST_(15) << 0) +#define MIPS_CONF2_SL_SHF 4 #define MIPS_CONF2_SL (_ULCAST_(15) << 4) +#define MIPS_CONF2_SS_SHF 8 #define MIPS_CONF2_SS (_ULCAST_(15) << 8) +#define MIPS_CONF2_L2B (_ULCAST_(1) << 12) #define MIPS_CONF2_SU (_ULCAST_(15) << 12) #define MIPS_CONF2_TA (_ULCAST_(15) << 16) #define MIPS_CONF2_TL (_ULCAST_(15) << 20) @@ -551,6 +555,7 @@ #define MIPS_CONF5_MVH (_ULCAST_(1) << 5) #define MIPS_CONF5_FRE (_ULCAST_(1) << 8) #define MIPS_CONF5_UFE (_ULCAST_(1) << 9) +#define MIPS_CONF5_L2C (_ULCAST_(1) << 10) #define MIPS_CONF5_MSAEN (_ULCAST_(1) << 27) #define MIPS_CONF5_EVA (_ULCAST_(1) << 28) #define MIPS_CONF5_CV (_ULCAST_(1) << 29) diff --git a/arch/mips/lib/cache.c b/arch/mips/lib/cache.c index d8baf08..bd14ba6 100644 --- a/arch/mips/lib/cache.c +++ b/arch/mips/lib/cache.c @@ -7,10 +7,44 @@ #include #include +#include #include DECLARE_GLOBAL_DATA_PTR; +static void probe_l2(void) +{ +#ifdef CONFIG_MIPS_L2_CACHE + unsigned long conf2, sl; + bool l2c = false; + + if (!(read_c0_config1() & MIPS_CONF_M)) + return; + + conf2 = read_c0_config2(); + + if (__mips_isa_rev >= 6) { + l2c = conf2 & MIPS_CONF_M; + if (l2c) + l2c = read_c0_config3() & MIPS_CONF_M; + if (l2c) + l2c = read_c0_config4() & MIPS_CONF_M; + if (l2c) + l2c = read_c0_config5() & MIPS_CONF5_L2C; + } + + if (l2c && config_enabled(CONFIG_MIPS_CM)) { + gd->arch.l2_line_size = mips_cm_l2_line_size(); + } else if (l2c) { + /* We don't know how to retrieve L2 config on this system */ + BUG(); + } else { + sl = (conf2 & MIPS_CONF2_SL) >> MIPS_CONF2_SL_SHF; + gd->arch.l2_line_size = sl ? (2 << sl) : 0; + } +#endif +} + void mips_cache_probe(void) { #ifdef CONFIG_SYS_CACHE_SIZE_AUTO @@ -24,6 +58,7 @@ void mips_cache_probe(void) gd->arch.l1i_line_size = il ? (2 << il) : 0; gd->arch.l1d_line_size = dl ? (2 << dl) : 0; #endif + probe_l2(); } static inline unsigned long icache_line_size(void) @@ -44,6 +79,15 @@ static inline unsigned long dcache_line_size(void) #endif } +static inline unsigned long scache_line_size(void) +{ +#ifdef CONFIG_MIPS_L2_CACHE + return gd->arch.l2_line_size; +#else + return 0; +#endif +} + #define cache_loop(start, end, lsize, ops...) do { \ const void *addr = (const void *)(start & ~(lsize - 1)); \ const void *aend = (const void *)((end - 1) & ~(lsize - 1)); \ @@ -60,12 +104,13 @@ void flush_cache(ulong start_addr, ulong size) { unsigned long ilsize = icache_line_size(); unsigned long dlsize = dcache_line_size(); + unsigned long slsize = scache_line_size(); /* aend will be miscalculated when size is zero, so we return here */ if (size == 0) return; - if (ilsize == dlsize) { + if ((ilsize == dlsize) && !slsize) { /* flush I-cache & D-cache simultaneously */ cache_loop(start_addr, start_addr + size, ilsize, HIT_WRITEBACK_INV_D, HIT_INVALIDATE_I); @@ -75,6 +120,11 @@ void flush_cache(ulong start_addr, ulong size) /* flush D-cache */ cache_loop(start_addr, start_addr + size, dlsize, HIT_WRITEBACK_INV_D); + /* flush L2 cache */ + if (slsize) + cache_loop(start_addr, start_addr + size, slsize, + HIT_WRITEBACK_INV_SD); + /* flush I-cache */ cache_loop(start_addr, start_addr + size, ilsize, HIT_INVALIDATE_I); } @@ -82,21 +132,31 @@ void flush_cache(ulong start_addr, ulong size) void flush_dcache_range(ulong start_addr, ulong stop) { unsigned long lsize = dcache_line_size(); + unsigned long slsize = scache_line_size(); /* aend will be miscalculated when size is zero, so we return here */ if (start_addr == stop) return; cache_loop(start_addr, stop, lsize, HIT_WRITEBACK_INV_D); + + /* flush L2 cache */ + if (slsize) + cache_loop(start_addr, stop, slsize, HIT_WRITEBACK_INV_SD); } void invalidate_dcache_range(ulong start_addr, ulong stop) { unsigned long lsize = dcache_line_size(); + unsigned long slsize = scache_line_size(); /* aend will be miscalculated when size is zero, so we return here */ if (start_addr == stop) return; + /* invalidate L2 cache */ + if (slsize) + cache_loop(start_addr, stop, slsize, HIT_INVALIDATE_SD); + cache_loop(start_addr, stop, lsize, HIT_INVALIDATE_D); } diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index 9be3a07..ecb88e5 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -13,6 +13,7 @@ #include #include #include +#include #ifndef CONFIG_SYS_MIPS_CACHE_MODE #define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT @@ -95,14 +96,135 @@ * with good parity is available. This routine will initialise an area of * memory starting at location zero to be used as a source of parity. * + * Note that this function does not follow the standard calling convention & + * may clobber typically callee-saved registers. + * * RETURNS: N/A * */ -#define R_IC_SIZE t2 -#define R_IC_LINE t8 -#define R_DC_SIZE t3 -#define R_DC_LINE t9 +#define R_RETURN s0 +#define R_IC_SIZE s1 +#define R_IC_LINE s2 +#define R_DC_SIZE s3 +#define R_DC_LINE s4 +#define R_L2_SIZE s5 +#define R_L2_LINE s6 +#define R_L2_BYPASSED s7 +#define R_L2_L2C t8 LEAF(mips_cache_reset) + move R_RETURN, ra + +#ifdef CONFIG_MIPS_L2_CACHE + /* + * For there to be an L2 present, Config2 must be present. If it isn't + * then we proceed knowing there's no L2 cache. + */ + move R_L2_SIZE, zero + move R_L2_LINE, zero + move R_L2_BYPASSED, zero + move R_L2_L2C, zero + mfc0 t0, CP0_CONFIG, 1 + bgez t0, l2_probe_done + + /* + * From MIPSr6 onwards the L2 cache configuration might not be reported + * by Config2. The Config5.L2C bit indicates whether this is the case, + * and if it is then we need knowledge of where else to look. For cores + * from Imagination Technologies this is a CM GCR. + */ +# if __mips_isa_rev >= 6 + /* Check that Config5 exists */ + mfc0 t0, CP0_CONFIG, 2 + bgez t0, l2_probe_cop0 + mfc0 t0, CP0_CONFIG, 3 + bgez t0, l2_probe_cop0 + mfc0 t0, CP0_CONFIG, 4 + bgez t0, l2_probe_cop0 + + /* Check Config5.L2C is set */ + mfc0 t0, CP0_CONFIG, 5 + and R_L2_L2C, t0, MIPS_CONF5_L2C + beqz R_L2_L2C, l2_probe_cop0 + + /* Config5.L2C is set */ +# ifdef CONFIG_MIPS_CM + /* The CM will provide L2 configuration */ + PTR_LI t0, CKSEG1ADDR(CONFIG_MIPS_CM_BASE) + lw t1, GCR_L2_CONFIG(t0) + bgez t1, l2_probe_done + + ext R_L2_LINE, t1, \ + GCR_L2_CONFIG_LINESZ_SHIFT, GCR_L2_CONFIG_LINESZ_BITS + beqz R_L2_LINE, l2_probe_done + li t2, 2 + sllv R_L2_LINE, t2, R_L2_LINE + + ext t2, t1, GCR_L2_CONFIG_ASSOC_SHIFT, GCR_L2_CONFIG_ASSOC_BITS + addiu t2, t2, 1 + mul R_L2_SIZE, R_L2_LINE, t2 + + ext t2, t1, GCR_L2_CONFIG_SETSZ_SHIFT, GCR_L2_CONFIG_SETSZ_BITS + sllv R_L2_SIZE, R_L2_SIZE, t2 + li t2, 64 + mul R_L2_SIZE, R_L2_SIZE, t2 + + /* Bypass the L2 cache so that we can init the L1s early */ + or t1, t1, GCR_L2_CONFIG_BYPASS + sw t1, GCR_L2_CONFIG(t0) + sync + li R_L2_BYPASSED, 1 + + /* Zero the L2 tag registers */ + sw zero, GCR_L2_TAG_ADDR(t0) + sw zero, GCR_L2_TAG_ADDR_UPPER(t0) + sw zero, GCR_L2_TAG_STATE(t0) + sw zero, GCR_L2_TAG_STATE_UPPER(t0) + sw zero, GCR_L2_DATA(t0) + sw zero, GCR_L2_DATA_UPPER(t0) + sync +# else + /* We don't know how to retrieve L2 configuration on this system */ +# endif + b l2_probe_done +# endif + + /* + * For pre-r6 systems, or r6 systems with Config5.L2C==0, probe the L2 + * cache configuration from the cop0 Config2 register. + */ +l2_probe_cop0: + mfc0 t0, CP0_CONFIG, 2 + + srl R_L2_LINE, t0, MIPS_CONF2_SL_SHF + andi R_L2_LINE, R_L2_LINE, MIPS_CONF2_SL >> MIPS_CONF2_SL_SHF + beqz R_L2_LINE, l2_probe_done + li t1, 2 + sllv R_L2_LINE, t1, R_L2_LINE + + srl t1, t0, MIPS_CONF2_SA_SHF + andi t1, t1, MIPS_CONF2_SA >> MIPS_CONF2_SA_SHF + addiu t1, t1, 1 + mul R_L2_SIZE, R_L2_LINE, t1 + + srl t1, t0, MIPS_CONF2_SS_SHF + andi t1, t1, MIPS_CONF2_SS >> MIPS_CONF2_SS_SHF + sllv R_L2_SIZE, R_L2_SIZE, t1 + li t1, 64 + mul R_L2_SIZE, R_L2_SIZE, t1 + + /* Attempt to bypass the L2 so that we can init the L1s early */ + or t0, t0, MIPS_CONF2_L2B + mtc0 t0, CP0_CONFIG, 2 + ehb + mfc0 t0, CP0_CONFIG, 2 + and R_L2_BYPASSED, t0, MIPS_CONF2_L2B + + /* Zero the L2 tag registers */ + mtc0 zero, CP0_TAGLO, 4 + ehb +l2_probe_done: +#endif + #ifndef CONFIG_SYS_CACHE_SIZE_AUTO li R_IC_SIZE, CONFIG_SYS_ICACHE_SIZE li R_IC_LINE, CONFIG_SYS_ICACHE_LINE_SIZE @@ -142,11 +264,33 @@ LEAF(mips_cache_reset) #endif /* CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD */ +#ifdef CONFIG_MIPS_L2_CACHE + /* + * If the L2 is bypassed, init the L1 first so that we can execute the + * rest of the cache initialisation using the L1 instruction cache. + */ + bnez R_L2_BYPASSED, l1_init + +l2_init: + PTR_LI t0, INDEX_BASE + PTR_ADDU t1, t0, R_L2_SIZE +1: cache INDEX_STORE_TAG_SD, 0(t0) + PTR_ADDU t0, t0, R_L2_LINE + bne t0, t1, 1b + + /* + * If the L2 was bypassed then we already initialised the L1s before + * the L2, so we are now done. + */ + bnez R_L2_BYPASSED, l2_unbypass +#endif + /* * The TagLo registers used depend upon the CPU implementation, but the * architecture requires that it is safe for software to write to both * TagLo selects 0 & 2 covering supported cases. */ +l1_init: mtc0 zero, CP0_TAGLO mtc0 zero, CP0_TAGLO, 2 @@ -207,8 +351,37 @@ LEAF(mips_cache_reset) PTR_LI t0, INDEX_BASE cache_loop t0, t1, R_DC_LINE, INDEX_STORE_TAG_D #endif +3: + +#ifdef CONFIG_MIPS_L2_CACHE + /* If the L2 isn't bypassed then we're done */ + beqz R_L2_BYPASSED, return + + /* The L2 is bypassed - go initialise it */ + b l2_init + +l2_unbypass: +# if __mips_isa_rev >= 6 + beqz R_L2_L2C, 1f + + li t0, CKSEG1ADDR(CONFIG_MIPS_CM_BASE) + lw t1, GCR_L2_CONFIG(t0) + xor t1, t1, GCR_L2_CONFIG_BYPASS + sw t1, GCR_L2_CONFIG(t0) + sync + ehb + b 2f +# endif +1: mfc0 t0, CP0_CONFIG, 2 + xor t0, t0, MIPS_CONF2_L2B + mtc0 t0, CP0_CONFIG, 2 + ehb + +2: +#endif -3: jr ra +return: + jr ra END(mips_cache_reset) /* -- cgit v1.1 From 7953354b07bba8fa9599bf5d212308e6cdf9cbe2 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:55 +0100 Subject: MIPS: Join the coherent domain when a CM is present MIPS Linux expects the bootloader to leave the boot CPU a member of the coherent domain when running on a system with a CM, and we will need to do so if we wish to make use of IOCUs to have cache-coherent DMA in U-Boot (and on some systems there is no choice in that matter). When a CM is present, join the coherent domain after completing cache initialisation. Signed-off-by: Paul Burton --- arch/mips/include/asm/cm.h | 5 +++++ arch/mips/lib/cache_init.S | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/arch/mips/include/asm/cm.h b/arch/mips/include/asm/cm.h index 62ecef2..b9ab0c6 100644 --- a/arch/mips/include/asm/cm.h +++ b/arch/mips/include/asm/cm.h @@ -19,6 +19,7 @@ #define GCR_L2_TAG_STATE_UPPER 0x060c #define GCR_L2_DATA 0x0610 #define GCR_L2_DATA_UPPER 0x0614 +#define GCR_Cx_COHERENCE 0x2008 /* GCR_REV CM versions */ #define GCR_REV_CM3 0x0800 @@ -32,6 +33,10 @@ #define GCR_L2_CONFIG_SETSZ_BITS 4 #define GCR_L2_CONFIG_BYPASS (1 << 20) +/* GCR_Cx_COHERENCE */ +#define GCR_Cx_COHERENCE_DOM_EN (0xff << 0) +#define GCR_Cx_COHERENCE_EN (0x1 << 0) + #ifndef __ASSEMBLY__ #include diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index ecb88e5..e61432e 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -378,6 +378,44 @@ l2_unbypass: ehb 2: +# ifdef CONFIG_MIPS_CM + /* Config3 must exist for a CM to be present */ + mfc0 t0, CP0_CONFIG, 1 + bgez t0, 2f + mfc0 t0, CP0_CONFIG, 2 + bgez t0, 2f + + /* Check Config3.CMGCR to determine CM presence */ + mfc0 t0, CP0_CONFIG, 3 + and t0, t0, MIPS_CONF3_CMGCR + beqz t0, 2f + + /* Change Config.K0 to a coherent CCA */ + mfc0 t0, CP0_CONFIG + li t1, CONF_CM_CACHABLE_COW +#if __mips_isa_rev >= 2 + ins t0, t1, 0, 3 +#else + ori t0, t0, CONF_CM_CMASK + xori t0, t0, CONF_CM_CMASK + or t0, t0, t1 +#endif + mtc0 t0, CP0_CONFIG + + /* + * Join the coherent domain such that the caches of this core are kept + * coherent with those of other cores. + */ + PTR_LI t0, CKSEG1ADDR(CONFIG_MIPS_CM_BASE) + lw t1, GCR_REV(t0) + li t2, GCR_REV_CM3 + li t3, GCR_Cx_COHERENCE_EN + bge t1, t2, 1f + li t3, GCR_Cx_COHERENCE_DOM_EN +1: sw t3, GCR_Cx_COHERENCE(t0) + ehb +2: +# endif #endif return: -- cgit v1.1 From 566ce04de4a2b4c66be8e13751dbb0bfe80117b3 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:56 +0100 Subject: MIPS: Malta: Enable CM & L2 support Enable support for the MIPS Coherence Manager & L2 caches on the MIPS Malta board, removing the need for us to attempt to bypass the L2 during boot (which would fail with recent CPUs that expose L2 config via the CM anyway). Signed-off-by: Paul Burton --- arch/mips/Kconfig | 2 ++ board/imgtec/malta/lowlevel_init.S | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 7671437..f113b91 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -26,6 +26,8 @@ config TARGET_MALTA select DM select DM_SERIAL select DYNAMIC_IO_PORT_BASE + select MIPS_CM + select MIPS_L2_CACHE select OF_CONTROL select OF_ISA_BUS select SUPPORTS_BIG_ENDIAN diff --git a/board/imgtec/malta/lowlevel_init.S b/board/imgtec/malta/lowlevel_init.S index 3d48cdc..6df4d9f 100644 --- a/board/imgtec/malta/lowlevel_init.S +++ b/board/imgtec/malta/lowlevel_init.S @@ -28,12 +28,6 @@ .globl lowlevel_init lowlevel_init: - /* disable any L2 cache for now */ - sync - mfc0 t0, CP0_CONFIG, 2 - ori t0, t0, 0x1 << 12 - mtc0 t0, CP0_CONFIG, 2 - /* detect the core card */ PTR_LI t0, CKSEG1ADDR(MALTA_REVISION) lw t0, 0(t0) -- cgit v1.1 From c5b8412d60e22b49348a63848cbf7b6ab5ccb16e Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:57 +0100 Subject: MIPS: Ensure Config.K0=2 applies before any memory accesses During boot we set Config.K0=2 (uncached) such that any accesses to the kseg0 memory region are performed uncached before the caches are initialised. This write to the Config register introduces an execution hazard between it & any following memory accesses (such as the load of _gp), which we need to clear in order to ensure those memory accesses are actually performed uncached. Clear this execution hazard with the insertion of an ehb execution hazard barrier instruction. Signed-off-by: Paul Burton --- arch/mips/cpu/start.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S index c157d03..8f85ede 100644 --- a/arch/mips/cpu/start.S +++ b/arch/mips/cpu/start.S @@ -127,6 +127,7 @@ reset: and t0, t0, MIPS_CONF_IMPL or t0, t0, CONF_CM_UNCACHED mtc0 t0, CP0_CONFIG + ehb #endif /* -- cgit v1.1 From d608254b0aa23607df1dcb5a7ca07de9a8ec9bb0 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:58 +0100 Subject: MIPS: Clear hazard between TagLo writes & cache ops Writing to the coprocessor 0 TagLo registers introduces an execution hazard in that we need that write to complete before any cache instructions execute. Ensure that hazard is cleared by inserting an ehb instruction between the TagLo writes & cache op loop. Signed-off-by: Paul Burton --- arch/mips/lib/cache_init.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index e61432e..53e903a 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -293,6 +293,7 @@ l2_init: l1_init: mtc0 zero, CP0_TAGLO mtc0 zero, CP0_TAGLO, 2 + ehb /* * The caches are probably in an indeterminate state, so we force good -- cgit v1.1 From 639200f6a0dcfe67e4c923b6108703e192946388 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:18:59 +0100 Subject: MIPS: Ensure cache ops complete in mips_cache_reset Ensure that cache operations complete before returning from mips_cache_reset by placing a completion barrier (sync instruction) before the return. Without this there is no guarantee that the cache ops will complete before any subsequent memory accesses, since they are indexed cache ops & thus not implicitly ordered with memory accesses. Signed-off-by: Paul Burton --- arch/mips/lib/cache_init.S | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index 53e903a..698a5af 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -420,6 +420,8 @@ l2_unbypass: #endif return: + /* Ensure all cache operations complete before returning */ + sync jr ra END(mips_cache_reset) -- cgit v1.1 From 3f96f87520c22efe4a8b43f45f1b2011da50dc06 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:28 +0100 Subject: clk: Use dummy clk_get_by_* functions when CONFIG_CLK is disabled The implementations of clk_get_by_index & clk_get_by_name are only available when CONFIG_CLK is enabled. Provide the dummies when this is not the case in order to avoid build failures. Signed-off-by: Paul Burton Reviewed-by: Simon Glass --- include/clk.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clk.h b/include/clk.h index dc18b03..9b24522 100644 --- a/include/clk.h +++ b/include/clk.h @@ -59,7 +59,7 @@ struct clk { unsigned long id; }; -#if CONFIG_IS_ENABLED(OF_CONTROL) +#if CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(CLK) struct phandle_2_cell; int clk_get_by_index_platdata(struct udevice *dev, int index, struct phandle_2_cell *cells, struct clk *clk); -- cgit v1.1 From 50fce1d5d874070b058f09e6814c1914e168582d Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:29 +0100 Subject: serial: ns16550: Support clocks via phandle Previously ns16550 compatible UARTs probed via device tree have needed their device tree nodes to contain a clock-frequency property. An alternative to this commonly used with Linux is to reference a clock via a phandle. This patch allows U-Boot to support that, retrieving the clock frequency by probing the appropriate clock device. For example, a system might choose to provide the UART base clock as a reference to a clock common to multiple devices: sys_clk: clock { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <10000000>; }; uart0: uart@10000000 { compatible = "ns16550a"; reg = <0x10000000 0x1000>; clocks = <&sys_clk>; }; uart1: uart@10000000 { compatible = "ns16550a"; reg = <0x10001000 0x1000>; clocks = <&sys_clk>; }; This removes the need for the frequency information to be duplicated in multiple nodes and allows the device tree to be more descriptive of the system. Signed-off-by: Paul Burton Reviewed-by: Simon Glass --- drivers/serial/ns16550.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 88fca15..3f6ea4d 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -352,6 +353,8 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev) { struct ns16550_platdata *plat = dev->platdata; fdt_addr_t addr; + struct clk clk; + int err; /* try Processor Local Bus device first */ addr = dev_get_addr(dev); @@ -397,9 +400,21 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev) "reg-offset", 0); plat->reg_shift = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg-shift", 0); - plat->clock = fdtdec_get_int(gd->fdt_blob, dev->of_offset, - "clock-frequency", - CONFIG_SYS_NS16550_CLK); + + err = clk_get_by_index(dev, 0, &clk); + if (!err) { + err = clk_get_rate(&clk); + if (!IS_ERR_VALUE(err)) + plat->clock = err; + } else if (err != -ENODEV && err != -ENOSYS) { + debug("ns16550 failed to get clock\n"); + return err; + } + + if (!plat->clock) + plat->clock = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "clock-frequency", + CONFIG_SYS_NS16550_CLK); if (!plat->clock) { debug("ns16550 clock not defined\n"); return -EINVAL; -- cgit v1.1 From b419e87287ddb26ed991a64b2b14db7841b5f8c6 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:30 +0100 Subject: dt-bindings: Add interrupt-controller/mips-gic.h header Import a copy of the dt-bindings/interrupt-controller/mips-gic.h header from Linux, such that we can use device trees which include it without modification. Signed-off-by: Paul Burton --- include/dt-bindings/interrupt-controller/mips-gic.h | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 include/dt-bindings/interrupt-controller/mips-gic.h diff --git a/include/dt-bindings/interrupt-controller/mips-gic.h b/include/dt-bindings/interrupt-controller/mips-gic.h new file mode 100644 index 0000000..cf35a57 --- /dev/null +++ b/include/dt-bindings/interrupt-controller/mips-gic.h @@ -0,0 +1,9 @@ +#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_MIPS_GIC_H +#define _DT_BINDINGS_INTERRUPT_CONTROLLER_MIPS_GIC_H + +#include + +#define GIC_SHARED 0 +#define GIC_LOCAL 1 + +#endif -- cgit v1.1 From a29e45a9c468ba254bbf18886454c53ff16bec49 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:31 +0100 Subject: pci: xilinx: Add a driver for Xilinx AXI to PCIe bridge This patch adds a driver for the Xilinx AXI bridge for PCI express, an IP block which can be used on some generations of Xilinx FPGAs. This is mostly a case of implementing PCIe ECAM specification, but with some quirks about what devices are valid to access. Signed-off-by: Paul Burton Reviewed-by: Simon Glass --- drivers/pci/Kconfig | 7 ++ drivers/pci/Makefile | 1 + drivers/pci/pcie_xilinx.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 drivers/pci/pcie_xilinx.c diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 669e37b..9a7c187 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -39,4 +39,11 @@ config PCI_TEGRA with a total of 5 lanes. Some boards require this for Ethernet support to work (e.g. beaver, jetson-tk1). +config PCI_XILINX + bool "Xilinx AXI Bridge for PCI Express" + depends on DM_PCI + help + Enable support for the Xilinx AXI bridge for PCI express, an IP block + which can be used on some generations of Xilinx FPGAs. + endmenu diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index f8be9bf..9583e91 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o obj-$(CONFIG_TSI108_PCI) += tsi108_pci.o obj-$(CONFIG_WINBOND_83C553) += w83c553f.o obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o +obj-$(CONFIG_PCI_XILINX) += pcie_xilinx.o diff --git a/drivers/pci/pcie_xilinx.c b/drivers/pci/pcie_xilinx.c new file mode 100644 index 0000000..5216001 --- /dev/null +++ b/drivers/pci/pcie_xilinx.c @@ -0,0 +1,220 @@ +/* + * Xilinx AXI Bridge for PCI Express Driver + * + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include + +#include + +/** + * struct xilinx_pcie - Xilinx PCIe controller state + * @hose: The parent classes PCI controller state + * @cfg_base: The base address of memory mapped configuration space + */ +struct xilinx_pcie { + struct pci_controller hose; + void *cfg_base; +}; + +/* Register definitions */ +#define XILINX_PCIE_REG_PSCR 0x144 +#define XILINX_PCIE_REG_PSCR_LNKUP BIT(11) + +/** + * pcie_xilinx_link_up() - Check whether the PCIe link is up + * @pcie: Pointer to the PCI controller state + * + * Checks whether the PCIe link for the given device is up or down. + * + * Return: true if the link is up, else false + */ +static bool pcie_xilinx_link_up(struct xilinx_pcie *pcie) +{ + uint32_t pscr = __raw_readl(pcie->cfg_base + XILINX_PCIE_REG_PSCR); + + return pscr & XILINX_PCIE_REG_PSCR_LNKUP; +} + +/** + * pcie_xilinx_config_address() - Calculate the address of a config access + * @pcie: Pointer to the PCI controller state + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @paddress: Pointer to the pointer to write the calculates address to + * + * Calculates the address that should be accessed to perform a PCIe + * configuration space access for a given device identified by the PCIe + * controller device @pcie and the bus, device & function numbers in @bdf. If + * access to the device is not valid then the function will return an error + * code. Otherwise the address to access will be written to the pointer pointed + * to by @paddress. + * + * Return: 0 on success, else -ENODEV + */ +static int pcie_xilinx_config_address(struct xilinx_pcie *pcie, pci_dev_t bdf, + uint offset, void **paddress) +{ + unsigned int bus = PCI_BUS(bdf); + unsigned int dev = PCI_DEV(bdf); + unsigned int func = PCI_FUNC(bdf); + void *addr; + + if ((bus > 0) && !pcie_xilinx_link_up(pcie)) + return -ENODEV; + + /* + * Busses 0 (host-PCIe bridge) & 1 (its immediate child) are + * limited to a single device each. + */ + if ((bus < 2) && (dev > 0)) + return -ENODEV; + + addr = pcie->cfg_base; + addr += bus << 20; + addr += dev << 15; + addr += func << 12; + addr += offset; + *paddress = addr; + + return 0; +} + +/** + * pcie_xilinx_read_config() - Read from configuration space + * @pcie: Pointer to the PCI controller state + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @valuep: A pointer at which to store the read value + * @size: Indicates the size of access to perform + * + * Read a value of size @size from offset @offset within the configuration + * space of the device identified by the bus, device & function numbers in @bdf + * on the PCI bus @bus. + * + * Return: 0 on success, else -ENODEV or -EINVAL + */ +static int pcie_xilinx_read_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + struct xilinx_pcie *pcie = dev_get_priv(bus); + void *address; + int err; + + err = pcie_xilinx_config_address(pcie, bdf, offset, &address); + if (err < 0) { + *valuep = pci_get_ff(size); + return 0; + } + + switch (size) { + case PCI_SIZE_8: + *valuep = __raw_readb(address); + return 0; + case PCI_SIZE_16: + *valuep = __raw_readw(address); + return 0; + case PCI_SIZE_32: + *valuep = __raw_readl(address); + return 0; + default: + return -EINVAL; + } +} + +/** + * pcie_xilinx_write_config() - Write to configuration space + * @pcie: Pointer to the PCI controller state + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @value: The value to write + * @size: Indicates the size of access to perform + * + * Write the value @value of size @size from offset @offset within the + * configuration space of the device identified by the bus, device & function + * numbers in @bdf on the PCI bus @bus. + * + * Return: 0 on success, else -ENODEV or -EINVAL + */ +static int pcie_xilinx_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + struct xilinx_pcie *pcie = dev_get_priv(bus); + void *address; + int err; + + err = pcie_xilinx_config_address(pcie, bdf, offset, &address); + if (err < 0) + return 0; + + switch (size) { + case PCI_SIZE_8: + __raw_writeb(value, address); + return 0; + case PCI_SIZE_16: + __raw_writew(value, address); + return 0; + case PCI_SIZE_32: + __raw_writel(value, address); + return 0; + default: + return -EINVAL; + } +} + +/** + * pcie_xilinx_ofdata_to_platdata() - Translate from DT to device state + * @dev: A pointer to the device being operated on + * + * Translate relevant data from the device tree pertaining to device @dev into + * state that the driver will later make use of. This state is stored in the + * device's private data structure. + * + * Return: 0 on success, else -EINVAL + */ +static int pcie_xilinx_ofdata_to_platdata(struct udevice *dev) +{ + struct xilinx_pcie *pcie = dev_get_priv(dev); + struct fdt_resource reg_res; + DECLARE_GLOBAL_DATA_PTR; + int err; + + err = fdt_get_resource(gd->fdt_blob, dev->of_offset, "reg", + 0, ®_res); + if (err < 0) { + error("\"reg\" resource not found\n"); + return err; + } + + pcie->cfg_base = map_physmem(reg_res.start, + fdt_resource_size(®_res), + MAP_NOCACHE); + + return 0; +} + +static const struct dm_pci_ops pcie_xilinx_ops = { + .read_config = pcie_xilinx_read_config, + .write_config = pcie_xilinx_write_config, +}; + +static const struct udevice_id pcie_xilinx_ids[] = { + { .compatible = "xlnx,axi-pcie-host-1.00.a" }, + { } +}; + +U_BOOT_DRIVER(pcie_xilinx) = { + .name = "pcie_xilinx", + .id = UCLASS_PCI, + .of_match = pcie_xilinx_ids, + .ops = &pcie_xilinx_ops, + .ofdata_to_platdata = pcie_xilinx_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct xilinx_pcie), +}; -- cgit v1.1 From 65f62b1ca12050c4e3b3e5ed641447918f4c4b7b Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:32 +0100 Subject: pci: Flip condition for detecting non-PCI parent devices In pci_uclass_pre_probe an attempt is made to detect whether the parent of a device is a PCI device and that the device is thus a bridge. This was being done by checking whether the parent of the device is of the UCLASS_ROOT class. This causes problems if the PCI controller is a child of some other non-PCI node, for example a simple-bus node. For example, if the device tree contains something like the following then pci_uclass_pre_probe would incorrectly believe that the PCI controller is a bridge, with a PCI parent: / { some_child { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; ranges = <>; pci_controller: pci@10000000 { compatible = "my-pci-controller"; device_type = "pci"; reg = <0x10000000 0x2000000>; }; }; }; Avoid this incorrect detection of bridges by instead checking whether the parent devices class is UCLASS_PCI and treating a device as a bridge when this is true, making use of device_is_on_pci_bus to perform this test. Signed-off-by: Paul Burton Reviewed-by: Simon Glass --- drivers/pci/pci-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 342b78c..3b00e6a 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -837,7 +837,7 @@ static int pci_uclass_pre_probe(struct udevice *bus) hose = bus->uclass_priv; /* For bridges, use the top-level PCI controller */ - if (device_get_uclass_id(bus->parent) == UCLASS_ROOT) { + if (!device_is_on_pci_bus(bus)) { hose->ctlr = bus; ret = decode_regions(hose, gd->fdt_blob, bus->parent->of_offset, bus->of_offset); -- cgit v1.1 From 154bf12f78c012895e7b42f844b7b92c4f015509 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:33 +0100 Subject: net: pch_gbe: Use dm_pci_map_bar to discover MMIO base Reading the PCI BAR & converting the result to a physical address is not safe across all architectures. For example on MIPS the virtual:physical mapping is not 1:1, so we cannot directly make use of the physical address. Use the more generic BAR-mapping function dm_pci_map_bar to discover the MMIO base address, which should work across architectures. Signed-off-by: Paul Burton Acked-by: Joe Hershberger --- drivers/net/pch_gbe.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/pch_gbe.c b/drivers/net/pch_gbe.c index 137818b..2d0a700 100644 --- a/drivers/net/pch_gbe.c +++ b/drivers/net/pch_gbe.c @@ -421,7 +421,7 @@ int pch_gbe_probe(struct udevice *dev) { struct pch_gbe_priv *priv; struct eth_pdata *plat = dev_get_platdata(dev); - u32 iobase; + void *iobase; /* * The priv structure contains the descriptors and frame buffers which @@ -432,11 +432,9 @@ int pch_gbe_probe(struct udevice *dev) priv->dev = dev; - dm_pci_read_config32(dev, PCI_BASE_ADDRESS_1, &iobase); - iobase &= PCI_BASE_ADDRESS_MEM_MASK; - iobase = dm_pci_mem_to_phys(dev, iobase); + iobase = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_1, PCI_REGION_MEM); - plat->iobase = iobase; + plat->iobase = (ulong)iobase; priv->mac_regs = (struct pch_gbe_regs *)iobase; /* Read MAC address from SROM and initialize dev->enetaddr with it */ -- cgit v1.1 From 96cb57c5ea1905c05435b42ac378938971b83766 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:34 +0100 Subject: net: pch_gbe: Make 64 bit safe The pch_gbe driver previously casted pointers to & from unsigned 32 bit integers in many locations. This breaks the driver on 64 bit systems, producing streams of compiler warnings about mismatched pointer & integer sizes and then failing to keep track of addresses correctly at runtime. Fix the driver for 64 bit systems by using unsigned longs in place of the previously used 32 bit integers. Signed-off-by: Paul Burton Acked-by: Joe Hershberger --- drivers/net/pch_gbe.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/pch_gbe.c b/drivers/net/pch_gbe.c index 2d0a700..d40fff0 100644 --- a/drivers/net/pch_gbe.c +++ b/drivers/net/pch_gbe.c @@ -118,14 +118,14 @@ static void pch_gbe_rx_descs_init(struct udevice *dev) memset(rx_desc, 0, sizeof(struct pch_gbe_rx_desc) * PCH_GBE_DESC_NUM); for (i = 0; i < PCH_GBE_DESC_NUM; i++) rx_desc->buffer_addr = dm_pci_phys_to_mem(priv->dev, - (u32)(priv->rx_buff[i])); + (ulong)(priv->rx_buff[i])); - writel(dm_pci_phys_to_mem(priv->dev, (u32)rx_desc), + writel(dm_pci_phys_to_mem(priv->dev, (ulong)rx_desc), &mac_regs->rx_dsc_base); writel(sizeof(struct pch_gbe_rx_desc) * (PCH_GBE_DESC_NUM - 1), &mac_regs->rx_dsc_size); - writel(dm_pci_phys_to_mem(priv->dev, (u32)(rx_desc + 1)), + writel(dm_pci_phys_to_mem(priv->dev, (ulong)(rx_desc + 1)), &mac_regs->rx_dsc_sw_p); } @@ -137,11 +137,11 @@ static void pch_gbe_tx_descs_init(struct udevice *dev) memset(tx_desc, 0, sizeof(struct pch_gbe_tx_desc) * PCH_GBE_DESC_NUM); - writel(dm_pci_phys_to_mem(priv->dev, (u32)tx_desc), + writel(dm_pci_phys_to_mem(priv->dev, (ulong)tx_desc), &mac_regs->tx_dsc_base); writel(sizeof(struct pch_gbe_tx_desc) * (PCH_GBE_DESC_NUM - 1), &mac_regs->tx_dsc_size); - writel(dm_pci_phys_to_mem(priv->dev, (u32)(tx_desc + 1)), + writel(dm_pci_phys_to_mem(priv->dev, (ulong)(tx_desc + 1)), &mac_regs->tx_dsc_sw_p); } @@ -251,7 +251,7 @@ static int pch_gbe_send(struct udevice *dev, void *packet, int length) if (length < 64) frame_ctrl |= PCH_GBE_TXD_CTRL_APAD; - tx_desc->buffer_addr = dm_pci_phys_to_mem(priv->dev, (u32)packet); + tx_desc->buffer_addr = dm_pci_phys_to_mem(priv->dev, (ulong)packet); tx_desc->length = length; tx_desc->tx_words_eob = length + 3; tx_desc->tx_frame_ctrl = frame_ctrl; @@ -262,7 +262,7 @@ static int pch_gbe_send(struct udevice *dev, void *packet, int length) if (++priv->tx_idx >= PCH_GBE_DESC_NUM) priv->tx_idx = 0; - writel(dm_pci_phys_to_mem(priv->dev, (u32)(tx_head + priv->tx_idx)), + writel(dm_pci_phys_to_mem(priv->dev, (ulong)(tx_head + priv->tx_idx)), &mac_regs->tx_dsc_sw_p); start = get_timer(0); @@ -283,7 +283,7 @@ static int pch_gbe_recv(struct udevice *dev, int flags, uchar **packetp) struct pch_gbe_priv *priv = dev_get_priv(dev); struct pch_gbe_regs *mac_regs = priv->mac_regs; struct pch_gbe_rx_desc *rx_desc; - u32 hw_desc, buffer_addr, length; + ulong hw_desc, buffer_addr, length; rx_desc = &priv->rx_desc[priv->rx_idx]; @@ -291,7 +291,7 @@ static int pch_gbe_recv(struct udevice *dev, int flags, uchar **packetp) hw_desc = readl(&mac_regs->rx_dsc_hw_p_hld); /* Just return if not receiving any packet */ - if ((u32)rx_desc == hw_desc) + if ((ulong)rx_desc == hw_desc) return -EAGAIN; buffer_addr = dm_pci_mem_to_phys(priv->dev, rx_desc->buffer_addr); @@ -315,7 +315,7 @@ static int pch_gbe_free_pkt(struct udevice *dev, uchar *packet, int length) if (++rx_swp >= PCH_GBE_DESC_NUM) rx_swp = 0; - writel(dm_pci_phys_to_mem(priv->dev, (u32)(rx_head + rx_swp)), + writel(dm_pci_phys_to_mem(priv->dev, (ulong)(rx_head + rx_swp)), &mac_regs->rx_dsc_sw_p); return 0; -- cgit v1.1 From 3bfb8cb43b16aeac138dbdbc1311cd718bbc4066 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:35 +0100 Subject: dm: regmap: Implement simple regmap_read & regmap_write The regmap_read & regmap_write functions were previously declared in regmap.h but not implemented anywhere. The regmap implementation & commit message of 6f98b7504f70 ("dm: Add support for register maps (regmap)") indicate that only memory mapped accesses are supported for now, so providing simple implementations of regmap_read & regmap_write is trivial. The access size is presumed to be 4 bytes & endianness is presumed native, which are the defaults for the regmap code in Linux. Signed-off-by: Paul Burton Reviewed-by: Simon Glass --- drivers/core/regmap.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 0299ff0..c68bcba 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -13,6 +13,8 @@ #include #include +#include + DECLARE_GLOBAL_DATA_PTR; static struct regmap *regmap_alloc_count(int count) @@ -117,3 +119,21 @@ int regmap_uninit(struct regmap *map) return 0; } + +int regmap_read(struct regmap *map, uint offset, uint *valp) +{ + uint32_t *ptr = map_physmem(map->base + offset, 4, MAP_NOCACHE); + + *valp = le32_to_cpu(readl(ptr)); + + return 0; +} + +int regmap_write(struct regmap *map, uint offset, uint val) +{ + uint32_t *ptr = map_physmem(map->base + offset, 4, MAP_NOCACHE); + + writel(cpu_to_le32(val), ptr); + + return 0; +} -- cgit v1.1 From ce701721596618afc0affc50f1708ece156891f9 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:36 +0100 Subject: dm: core: Match compatible strings in order of priority Device model drivers have previously been matched to FDT nodes by virtue of being the first driver in the driver list to be compatible with the node. This ignores the fact that compatible strings in the device tree are listed in order of priority - that is, if we have a node with 2 compatible strings & a driver that matches each then we should always probe the driver that matches the first compatible string. Fix this by looping through the compatible strings for a node when attempting to bind it in lists_bind_fdt and checking each driver for a match of the first string, then each driver for a match of the second string etc. Effectively this inverts the loops over compatible strings & drivers. Signed-off-by: Paul Burton --- drivers/core/lists.c | 78 +++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 6a634e6..23b6ba7 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -101,36 +101,24 @@ int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) /** - * driver_check_compatible() - Check if a driver is compatible with this node + * driver_check_compatible() - Check if a driver matches a compatible string * - * @param blob: Device tree pointer - * @param offset: Offset of node in device tree * @param of_match: List of compatible strings to match * @param of_idp: Returns the match that was found - * @return 0 if there is a match, -ENOENT if no match, -ENODEV if the node - * does not have a compatible string, other error <0 if there is a device - * tree error + * @param compat: The compatible string to search for + * @return 0 if there is a match, -ENOENT if no match */ -static int driver_check_compatible(const void *blob, int offset, - const struct udevice_id *of_match, - const struct udevice_id **of_idp) +static int driver_check_compatible(const struct udevice_id *of_match, + const struct udevice_id **of_idp, + const char *compat) { - int ret; - - *of_idp = NULL; if (!of_match) return -ENOENT; while (of_match->compatible) { - ret = fdt_node_check_compatible(blob, offset, - of_match->compatible); - if (!ret) { + if (!strcmp(of_match->compatible, compat)) { *of_idp = of_match; return 0; - } else if (ret == -FDT_ERR_NOTFOUND) { - return -ENODEV; - } else if (ret < 0) { - return -EINVAL; } of_match++; } @@ -147,28 +135,46 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, struct driver *entry; struct udevice *dev; bool found = false; - const char *name; + const char *name, *compat_list, *compat; + int compat_length, i; int result = 0; int ret = 0; - dm_dbg("bind node %s\n", fdt_get_name(blob, offset, NULL)); + name = fdt_get_name(blob, offset, NULL); + dm_dbg("bind node %s\n", name); if (devp) *devp = NULL; - for (entry = driver; entry != driver + n_ents; entry++) { - ret = driver_check_compatible(blob, offset, entry->of_match, - &id); - name = fdt_get_name(blob, offset, NULL); - if (ret == -ENOENT) { - continue; - } else if (ret == -ENODEV) { + + compat_list = fdt_getprop(blob, offset, "compatible", &compat_length); + if (!compat_list) { + if (compat_length == -FDT_ERR_NOTFOUND) { dm_dbg("Device '%s' has no compatible string\n", name); - break; - } else if (ret) { - dm_warn("Device tree error at offset %d\n", offset); - result = ret; - break; + return 0; } + dm_warn("Device tree error at offset %d\n", offset); + return compat_length; + } + + /* + * Walk through the compatible string list, attempting to match each + * compatible string in order such that we match in order of priority + * from the first string to the last. + */ + for (i = 0; i < compat_length; i += strlen(compat) + 1) { + compat = compat_list + i; + dm_dbg(" - attempt to match compatible string '%s'\n", + compat); + + for (entry = driver; entry != driver + n_ents; entry++) { + ret = driver_check_compatible(entry->of_match, &id, + compat); + if (!ret) + break; + } + if (entry == driver + n_ents) + continue; + dm_dbg(" - found match at '%s'\n", entry->name); ret = device_bind_with_driver_data(parent, entry, name, id->data, offset, &dev); @@ -188,10 +194,8 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, break; } - if (!found && !result && ret != -ENODEV) { - dm_dbg("No match for node '%s'\n", - fdt_get_name(blob, offset, NULL)); - } + if (!found && !result && ret != -ENODEV) + dm_dbg("No match for node '%s'\n", name); return result; } -- cgit v1.1 From 8291bc87474ae2eb8564447a5c474f5d2a45836c Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:37 +0100 Subject: dm: syscon: Provide a generic syscon driver Provide a trivial syscon driver matching the generic "syscon" compatible string, allowing for simple system controllers to be used without a custom driver just as in Linux. Signed-off-by: Paul Burton --- drivers/core/syscon-uclass.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c index 01bd968..2148469 100644 --- a/drivers/core/syscon-uclass.c +++ b/drivers/core/syscon-uclass.c @@ -95,3 +95,14 @@ UCLASS_DRIVER(syscon) = { .per_device_auto_alloc_size = sizeof(struct syscon_uc_info), .pre_probe = syscon_pre_probe, }; + +static const struct udevice_id generic_syscon_ids[] = { + { .compatible = "syscon" }, + { } +}; + +U_BOOT_DRIVER(generic_syscon) = { + .name = "syscon", + .id = UCLASS_SYSCON, + .of_match = generic_syscon_ids, +}; -- cgit v1.1 From dd7c749474e0976cfc7e78dc9032d7fa8b9b9632 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:38 +0100 Subject: clk: boston: Providea simple driver for Boston board clocks Add a simple driver for the clocks provided by the MIPS Boston development board. The system provides information about 2 clocks whose rates are fixed by the bitfile flashed in the boards FPGA, and this driver simply reads the rates of these 2 clocks. Signed-off-by: Paul Burton Reviewed-by: Simon Glass --- drivers/clk/Kconfig | 8 +++ drivers/clk/Makefile | 1 + drivers/clk/clk_boston.c | 97 ++++++++++++++++++++++++++++++++ include/dt-bindings/clock/boston-clock.h | 13 +++++ 4 files changed, 119 insertions(+) create mode 100644 drivers/clk/clk_boston.c create mode 100644 include/dt-bindings/clock/boston-clock.h diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 8f3b96a..c05ce2a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -20,6 +20,14 @@ config SPL_CLK setting up clocks within SPL, and allows the same drivers to be used as U-Boot proper. +config CLK_BOSTON + def_bool y if TARGET_BOSTON + depends on CLK + select REGMAP + select SYSCON + help + Enable this to support the clocks + source "drivers/clk/tegra/Kconfig" source "drivers/clk/uniphier/Kconfig" source "drivers/clk/exynos/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 778d748..40a5e8c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -15,3 +15,4 @@ obj-y += tegra/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_CLK_EXYNOS) += exynos/ obj-$(CONFIG_CLK_AT91) += at91/ +obj-$(CONFIG_CLK_BOSTON) += clk_boston.o diff --git a/drivers/clk/clk_boston.c b/drivers/clk/clk_boston.c new file mode 100644 index 0000000..78f1b75 --- /dev/null +++ b/drivers/clk/clk_boston.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include + +struct clk_boston { + struct regmap *regmap; +}; + +#define BOSTON_PLAT_MMCMDIV 0x30 +# define BOSTON_PLAT_MMCMDIV_CLK0DIV (0xff << 0) +# define BOSTON_PLAT_MMCMDIV_INPUT (0xff << 8) +# define BOSTON_PLAT_MMCMDIV_MUL (0xff << 16) +# define BOSTON_PLAT_MMCMDIV_CLK1DIV (0xff << 24) + +static uint32_t ext_field(uint32_t val, uint32_t mask) +{ + return (val & mask) >> (ffs(mask) - 1); +} + +static ulong clk_boston_get_rate(struct clk *clk) +{ + struct clk_boston *state = dev_get_platdata(clk->dev); + uint32_t in_rate, mul, div; + uint mmcmdiv; + int err; + + err = regmap_read(state->regmap, BOSTON_PLAT_MMCMDIV, &mmcmdiv); + if (err) + return 0; + + in_rate = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_INPUT); + mul = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_MUL); + + switch (clk->id) { + case BOSTON_CLK_SYS: + div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK0DIV); + break; + case BOSTON_CLK_CPU: + div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK1DIV); + break; + default: + return 0; + } + + return (in_rate * mul * 1000000) / div; +} + +const struct clk_ops clk_boston_ops = { + .get_rate = clk_boston_get_rate, +}; + +static int clk_boston_ofdata_to_platdata(struct udevice *dev) +{ + struct clk_boston *state = dev_get_platdata(dev); + struct udevice *syscon; + int err; + + err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + "regmap", &syscon); + if (err) { + error("unable to find syscon device\n"); + return err; + } + + state->regmap = syscon_get_regmap(syscon); + if (!state->regmap) { + error("unable to find regmap\n"); + return -ENODEV; + } + + return 0; +} + +static const struct udevice_id clk_boston_match[] = { + { + .compatible = "img,boston-clock", + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(clk_boston) = { + .name = "boston_clock", + .id = UCLASS_CLK, + .of_match = clk_boston_match, + .ofdata_to_platdata = clk_boston_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct clk_boston), + .ops = &clk_boston_ops, +}; diff --git a/include/dt-bindings/clock/boston-clock.h b/include/dt-bindings/clock/boston-clock.h new file mode 100644 index 0000000..25f9cd2 --- /dev/null +++ b/include/dt-bindings/clock/boston-clock.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __DT_BINDINGS_CLOCK_BOSTON_CLOCK_H__ +#define __DT_BINDINGS_CLOCK_BOSTON_CLOCK_H__ + +#define BOSTON_CLK_SYS 0 +#define BOSTON_CLK_CPU 1 + +#endif /* __DT_BINDINGS_CLOCK_BOSTON_CLOCK_H__ */ -- cgit v1.1 From ad8783cb1cb258b71d81800f72cd64eb44081653 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 8 Sep 2016 07:47:39 +0100 Subject: boston: Introduce support for the MIPS Boston development board This patch introduces support for building U-Boot to run on the MIPS Boston development board. This is a board built around an FPGA & an Intel EG20T Platform Controller Hub, used largely as part of the development of new CPUs and their software support. It is essentially the successor to the older MIPS Malta board. Signed-off-by: Paul Burton --- arch/mips/Kconfig | 18 +++ arch/mips/dts/Makefile | 1 + arch/mips/dts/img,boston.dts | 222 ++++++++++++++++++++++++++++++++++++ board/imgtec/boston/Kconfig | 16 +++ board/imgtec/boston/MAINTAINERS | 6 + board/imgtec/boston/Makefile | 9 ++ board/imgtec/boston/boston-lcd.h | 21 ++++ board/imgtec/boston/boston-regs.h | 26 +++++ board/imgtec/boston/checkboard.c | 30 +++++ board/imgtec/boston/ddr.c | 30 +++++ board/imgtec/boston/lowlevel_init.S | 56 +++++++++ configs/boston32r2_defconfig | 41 +++++++ configs/boston32r2el_defconfig | 42 +++++++ configs/boston64r2_defconfig | 41 +++++++ configs/boston64r2el_defconfig | 42 +++++++ doc/README.boston | 58 ++++++++++ include/configs/boston.h | 81 +++++++++++++ 17 files changed, 740 insertions(+) create mode 100644 arch/mips/dts/img,boston.dts create mode 100644 board/imgtec/boston/Kconfig create mode 100644 board/imgtec/boston/MAINTAINERS create mode 100644 board/imgtec/boston/Makefile create mode 100644 board/imgtec/boston/boston-lcd.h create mode 100644 board/imgtec/boston/boston-regs.h create mode 100644 board/imgtec/boston/checkboard.c create mode 100644 board/imgtec/boston/ddr.c create mode 100644 board/imgtec/boston/lowlevel_init.S create mode 100644 configs/boston32r2_defconfig create mode 100644 configs/boston32r2el_defconfig create mode 100644 configs/boston64r2_defconfig create mode 100644 configs/boston64r2el_defconfig create mode 100644 doc/README.boston create mode 100644 include/configs/boston.h diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index f113b91..097ad58 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -75,6 +75,23 @@ config MACH_PIC32 select OF_CONTROL select DM +config TARGET_BOSTON + bool "Support Boston" + select DM + select DM_SERIAL + select OF_CONTROL + select MIPS_CM + select MIPS_L1_CACHE_SHIFT_6 + select MIPS_L2_CACHE + select SUPPORTS_BIG_ENDIAN + select SUPPORTS_LITTLE_ENDIAN + select SUPPORTS_CPU_MIPS32_R1 + select SUPPORTS_CPU_MIPS32_R2 + select SUPPORTS_CPU_MIPS32_R6 + select SUPPORTS_CPU_MIPS64_R1 + select SUPPORTS_CPU_MIPS64_R2 + select SUPPORTS_CPU_MIPS64_R6 + config TARGET_XILFPGA bool "Support Imagination Xilfpga" select OF_CONTROL @@ -92,6 +109,7 @@ config TARGET_XILFPGA endchoice source "board/dbau1x00/Kconfig" +source "board/imgtec/boston/Kconfig" source "board/imgtec/malta/Kconfig" source "board/imgtec/xilfpga/Kconfig" source "board/micronas/vct/Kconfig" diff --git a/arch/mips/dts/Makefile b/arch/mips/dts/Makefile index 5f311d4..30fcc2b 100644 --- a/arch/mips/dts/Makefile +++ b/arch/mips/dts/Makefile @@ -4,6 +4,7 @@ dtb-$(CONFIG_TARGET_AP121) += ap121.dtb dtb-$(CONFIG_TARGET_AP143) += ap143.dtb +dtb-$(CONFIG_TARGET_BOSTON) += img,boston.dtb dtb-$(CONFIG_TARGET_MALTA) += mti,malta.dtb dtb-$(CONFIG_TARGET_PIC32MZDASK) += pic32mzda_sk.dtb dtb-$(CONFIG_TARGET_XILFPGA) += nexys4ddr.dtb diff --git a/arch/mips/dts/img,boston.dts b/arch/mips/dts/img,boston.dts new file mode 100644 index 0000000..1d4eeda --- /dev/null +++ b/arch/mips/dts/img,boston.dts @@ -0,0 +1,222 @@ +/dts-v1/; + +#include +#include +#include +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + compatible = "img,boston"; + + chosen { + stdout-path = &uart0; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "img,mips"; + reg = <0>; + clocks = <&clk_boston BOSTON_CLK_CPU>; + }; + }; + + memory@0 { + device_type = "memory"; + reg = <0x00000000 0x10000000>; + }; + + gic: interrupt-controller { + compatible = "mti,gic"; + + interrupt-controller; + #interrupt-cells = <3>; + + timer { + compatible = "mti,gic-timer"; + interrupts = ; + clocks = <&clk_boston BOSTON_CLK_CPU>; + }; + }; + + pci0: pci@10000000 { + status = "disabled"; + compatible = "xlnx,axi-pcie-host-1.00.a"; + device_type = "pci"; + reg = <0x10000000 0x2000000>; + + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + + interrupt-parent = <&gic>; + interrupts = ; + + ranges = <0x02000000 0 0x40000000 + 0x40000000 0 0x40000000>; + + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pci0_intc 0>, + <0 0 0 2 &pci0_intc 1>, + <0 0 0 3 &pci0_intc 2>, + <0 0 0 4 &pci0_intc 3>; + + pci0_intc: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + + pci1: pci@12000000 { + status = "disabled"; + compatible = "xlnx,axi-pcie-host-1.00.a"; + device_type = "pci"; + reg = <0x12000000 0x2000000>; + + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + + interrupt-parent = <&gic>; + interrupts = ; + + ranges = <0x02000000 0 0x20000000 + 0x20000000 0 0x20000000>; + + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pci1_intc 0>, + <0 0 0 2 &pci1_intc 1>, + <0 0 0 3 &pci1_intc 2>, + <0 0 0 4 &pci1_intc 3>; + + pci1_intc: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + + pci2: pci@14000000 { + compatible = "xlnx,axi-pcie-host-1.00.a"; + device_type = "pci"; + reg = <0x14000000 0x2000000>; + + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + + interrupt-parent = <&gic>; + interrupts = ; + + ranges = <0x02000000 0 0x16000000 + 0x16000000 0 0x100000>; + + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pci2_intc 0>, + <0 0 0 2 &pci2_intc 1>, + <0 0 0 3 &pci2_intc 2>, + <0 0 0 4 &pci2_intc 3>; + + pci2_intc: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + + pci2_root@0,0,0 { + compatible = "pci10ee,7021"; + reg = <0x00000000 0 0 0 0>; + + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + + eg20t_bridge@1,0,0 { + compatible = "pci8086,8800"; + reg = <0x00010000 0 0 0 0>; + + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + + eg20t_mac@2,0,1 { + compatible = "pci8086,8802"; + reg = <0x00020100 0 0 0 0>; + phy-reset-gpios = <&eg20t_gpio 6 GPIO_ACTIVE_LOW>; + }; + + eg20t_gpio: eg20t_gpio@2,0,2 { + compatible = "pci8086,8803"; + reg = <0x00020200 0 0 0 0>; + + gpio-controller; + #gpio-cells = <2>; + }; + + eg20t_i2c@2,12,2 { + compatible = "pci8086,8817"; + reg = <0x00026200 0 0 0 0>; + + #address-cells = <1>; + #size-cells = <0>; + + rtc@0x68 { + compatible = "st,m41t81s"; + reg = <0x68>; + }; + }; + }; + }; + }; + + plat_regs: system-controller@17ffd000 { + compatible = "img,boston-platform-regs", "syscon"; + reg = <0x17ffd000 0x1000>; + u-boot,dm-pre-reloc; + }; + + clk_boston: clock { + compatible = "img,boston-clock"; + #clock-cells = <1>; + regmap = <&plat_regs>; + u-boot,dm-pre-reloc; + }; + + reboot: syscon-reboot { + compatible = "syscon-reboot"; + regmap = <&plat_regs>; + offset = <0x10>; + mask = <0x10>; + }; + + uart0: uart@17ffe000 { + compatible = "ns16550a"; + reg = <0x17ffe000 0x1000>; + reg-shift = <2>; + reg-io-width = <4>; + + interrupt-parent = <&gic>; + interrupts = ; + + clocks = <&clk_boston BOSTON_CLK_SYS>; + + u-boot,dm-pre-reloc; + }; + + lcd: lcd@17fff000 { + compatible = "img,boston-lcd"; + reg = <0x17fff000 0x8>; + }; + + flash@18000000 { + compatible = "cfi-flash"; + reg = <0x18000000 0x8000000>; + bank-width = <2>; + }; +}; diff --git a/board/imgtec/boston/Kconfig b/board/imgtec/boston/Kconfig new file mode 100644 index 0000000..ab76a3c --- /dev/null +++ b/board/imgtec/boston/Kconfig @@ -0,0 +1,16 @@ +if TARGET_BOSTON + +config SYS_BOARD + default "boston" + +config SYS_VENDOR + default "imgtec" + +config SYS_CONFIG_NAME + default "boston" + +config SYS_TEXT_BASE + default 0x9fc00000 if 32BIT + default 0xffffffff9fc00000 if 64BIT + +endif diff --git a/board/imgtec/boston/MAINTAINERS b/board/imgtec/boston/MAINTAINERS new file mode 100644 index 0000000..30dd481 --- /dev/null +++ b/board/imgtec/boston/MAINTAINERS @@ -0,0 +1,6 @@ +BOSTON BOARD +M: Paul Burton +S: Maintained +F: board/imgtec/boston/ +F: include/configs/boston.h +F: configs/boston_defconfig diff --git a/board/imgtec/boston/Makefile b/board/imgtec/boston/Makefile new file mode 100644 index 0000000..deda457 --- /dev/null +++ b/board/imgtec/boston/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2016 Imagination Technologies +# +# SPDX-License-Identifier: GPL-2.0 +# + +obj-y += checkboard.o +obj-y += ddr.o +obj-y += lowlevel_init.o diff --git a/board/imgtec/boston/boston-lcd.h b/board/imgtec/boston/boston-lcd.h new file mode 100644 index 0000000..9f5c1b9 --- /dev/null +++ b/board/imgtec/boston/boston-lcd.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __BOARD_BOSTON_LCD_H__ +#define __BOARD_BOSTON_LCD_H__ + +/** + * lowlevel_display() - Display a message on Boston's LCD + * @msg: The string to display + * + * Display the string @msg on the 7 character LCD display of the Boston board. + * This is typically used for debug or to present some form of status + * indication to the user, allowing faults to be identified when things go + * wrong early enough that the UART isn't up. + */ +void lowlevel_display(const char msg[static 8]); + +#endif /* __BOARD_BOSTON_LCD_H__ */ diff --git a/board/imgtec/boston/boston-regs.h b/board/imgtec/boston/boston-regs.h new file mode 100644 index 0000000..b9dfbb4 --- /dev/null +++ b/board/imgtec/boston/boston-regs.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __BOARD_BOSTON_REGS_H__ +#define __BOARD_BOSTON_REGS_H__ + +#include + +#define BOSTON_PLAT_BASE CKSEG1ADDR(0x17ffd000) +#define BOSTON_LCD_BASE CKSEG1ADDR(0x17fff000) + +/* + * Platform Register Definitions + */ +#define BOSTON_PLAT_CORE_CL (BOSTON_PLAT_BASE + 0x04) + +#define BOSTON_PLAT_DDR3STAT (BOSTON_PLAT_BASE + 0x14) +# define BOSTON_PLAT_DDR3STAT_CALIB (1 << 2) + +#define BOSTON_PLAT_DDRCONF0 (BOSTON_PLAT_BASE + 0x38) +# define BOSTON_PLAT_DDRCONF0_SIZE (0xf << 0) + +#endif /* __BOARD_BOSTON_REGS_H__ */ diff --git a/board/imgtec/boston/checkboard.c b/board/imgtec/boston/checkboard.c new file mode 100644 index 0000000..93eae7f --- /dev/null +++ b/board/imgtec/boston/checkboard.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include + +#include +#include + +#include "boston-lcd.h" +#include "boston-regs.h" + +int checkboard(void) +{ + u32 changelist; + + lowlevel_display("U-boot "); + + printf("Board: MIPS Boston\n"); + + printf("CPU: 0x%08x", read_c0_prid()); + changelist = __raw_readl((uint32_t *)BOSTON_PLAT_CORE_CL); + if (changelist > 1) + printf(" cl%x", changelist); + putc('\n'); + + return 0; +} diff --git a/board/imgtec/boston/ddr.c b/board/imgtec/boston/ddr.c new file mode 100644 index 0000000..ceffef6 --- /dev/null +++ b/board/imgtec/boston/ddr.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include + +#include + +#include "boston-regs.h" + +phys_size_t initdram(int board_type) +{ + u32 ddrconf0 = __raw_readl((uint32_t *)BOSTON_PLAT_DDRCONF0); + + return (phys_size_t)(ddrconf0 & BOSTON_PLAT_DDRCONF0_SIZE) << 30; +} + +ulong board_get_usable_ram_top(ulong total_size) +{ + DECLARE_GLOBAL_DATA_PTR; + + if (gd->ram_top < CONFIG_SYS_SDRAM_BASE) { + /* 2GB wrapped around to 0 */ + return CKSEG0ADDR(256 << 20); + } + + return min_t(unsigned long, gd->ram_top, CKSEG0ADDR(256 << 20)); +} diff --git a/board/imgtec/boston/lowlevel_init.S b/board/imgtec/boston/lowlevel_init.S new file mode 100644 index 0000000..0c01aa9 --- /dev/null +++ b/board/imgtec/boston/lowlevel_init.S @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include + +#include +#include +#include +#include + +#include "boston-regs.h" + +.data + +msg_ddr_cal: .ascii "DDR Cal " +msg_ddr_ok: .ascii "DDR OK " + +.text + +LEAF(lowlevel_init) + move s0, ra + + PTR_LA a0, msg_ddr_cal + bal lowlevel_display + + PTR_LI t0, BOSTON_PLAT_DDR3STAT +1: lw t1, 0(t0) + andi t1, t1, BOSTON_PLAT_DDR3STAT_CALIB + beqz t1, 1b + + PTR_LA a0, msg_ddr_ok + bal lowlevel_display + + move v0, zero + jr s0 + END(lowlevel_init) + +LEAF(lowlevel_display) + .set push + .set noat + PTR_LI AT, BOSTON_LCD_BASE +#ifdef CONFIG_64BIT + ld k1, 0(a0) + sd k1, 0(AT) +#else + lw k1, 0(a0) + sw k1, 0(AT) + lw k1, 4(a0) + sw k1, 4(AT) +#endif + .set pop +1: jr ra + END(lowlevel_display) diff --git a/configs/boston32r2_defconfig b/configs/boston32r2_defconfig new file mode 100644 index 0000000..ca66248 --- /dev/null +++ b/configs/boston32r2_defconfig @@ -0,0 +1,41 @@ +CONFIG_MIPS=y +CONFIG_TARGET_BOSTON=y +CONFIG_SYS_TEXT_BASE=0x9fc00000 +# CONFIG_MIPS_BOOT_CMDLINE_LEGACY is not set +# CONFIG_MIPS_BOOT_ENV_LEGACY is not set +CONFIG_MIPS_BOOT_FDT=y +CONFIG_DEFAULT_DEVICE_TREE="img,boston" +CONFIG_FIT=y +CONFIG_FIT_VERBOSE=y +CONFIG_FIT_BEST_MATCH=y +CONFIG_OF_STDOUT_VIA_ALIAS=y +CONFIG_HUSH_PARSER=y +CONFIG_SYS_PROMPT="boston # " +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_IMLS is not set +CONFIG_CMD_GREPENV=y +CONFIG_CMD_MEMTEST=y +# CONFIG_CMD_LOADB is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_FPGA is not set +CONFIG_CMD_DHCP=y +CONFIG_CMD_PING=y +CONFIG_CMD_SNTP=y +CONFIG_CMD_DNS=y +CONFIG_CMD_LINK_LOCAL=y +CONFIG_CMD_TIME=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_CMD_FAT=y +CONFIG_CMD_FS_GENERIC=y +CONFIG_OF_EMBED=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_CLK=y +CONFIG_MTD=y +CONFIG_CFI_FLASH=y +CONFIG_DM_ETH=y +CONFIG_PCH_GBE=y +CONFIG_DM_PCI=y +CONFIG_PCI_XILINX=y +CONFIG_SYS_NS16550=y +CONFIG_LZ4=y diff --git a/configs/boston32r2el_defconfig b/configs/boston32r2el_defconfig new file mode 100644 index 0000000..67f54bf --- /dev/null +++ b/configs/boston32r2el_defconfig @@ -0,0 +1,42 @@ +CONFIG_MIPS=y +CONFIG_TARGET_BOSTON=y +CONFIG_SYS_TEXT_BASE=0x9fc00000 +CONFIG_SYS_LITTLE_ENDIAN=y +# CONFIG_MIPS_BOOT_CMDLINE_LEGACY is not set +# CONFIG_MIPS_BOOT_ENV_LEGACY is not set +CONFIG_MIPS_BOOT_FDT=y +CONFIG_DEFAULT_DEVICE_TREE="img,boston" +CONFIG_FIT=y +CONFIG_FIT_VERBOSE=y +CONFIG_FIT_BEST_MATCH=y +CONFIG_OF_STDOUT_VIA_ALIAS=y +CONFIG_HUSH_PARSER=y +CONFIG_SYS_PROMPT="boston # " +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_IMLS is not set +CONFIG_CMD_GREPENV=y +CONFIG_CMD_MEMTEST=y +# CONFIG_CMD_LOADB is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_FPGA is not set +CONFIG_CMD_DHCP=y +CONFIG_CMD_PING=y +CONFIG_CMD_SNTP=y +CONFIG_CMD_DNS=y +CONFIG_CMD_LINK_LOCAL=y +CONFIG_CMD_TIME=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_CMD_FAT=y +CONFIG_CMD_FS_GENERIC=y +CONFIG_OF_EMBED=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_CLK=y +CONFIG_MTD=y +CONFIG_CFI_FLASH=y +CONFIG_DM_ETH=y +CONFIG_PCH_GBE=y +CONFIG_DM_PCI=y +CONFIG_PCI_XILINX=y +CONFIG_SYS_NS16550=y +CONFIG_LZ4=y diff --git a/configs/boston64r2_defconfig b/configs/boston64r2_defconfig new file mode 100644 index 0000000..1245d1b --- /dev/null +++ b/configs/boston64r2_defconfig @@ -0,0 +1,41 @@ +CONFIG_MIPS=y +CONFIG_TARGET_BOSTON=y +CONFIG_CPU_MIPS64_R2=y +# CONFIG_MIPS_BOOT_CMDLINE_LEGACY is not set +# CONFIG_MIPS_BOOT_ENV_LEGACY is not set +CONFIG_MIPS_BOOT_FDT=y +CONFIG_DEFAULT_DEVICE_TREE="img,boston" +CONFIG_FIT=y +CONFIG_FIT_VERBOSE=y +CONFIG_FIT_BEST_MATCH=y +CONFIG_OF_STDOUT_VIA_ALIAS=y +CONFIG_HUSH_PARSER=y +CONFIG_SYS_PROMPT="boston # " +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_IMLS is not set +CONFIG_CMD_GREPENV=y +CONFIG_CMD_MEMTEST=y +# CONFIG_CMD_LOADB is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_FPGA is not set +CONFIG_CMD_DHCP=y +CONFIG_CMD_PING=y +CONFIG_CMD_SNTP=y +CONFIG_CMD_DNS=y +CONFIG_CMD_LINK_LOCAL=y +CONFIG_CMD_TIME=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_CMD_FAT=y +CONFIG_CMD_FS_GENERIC=y +CONFIG_OF_EMBED=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_CLK=y +CONFIG_MTD=y +CONFIG_CFI_FLASH=y +CONFIG_DM_ETH=y +CONFIG_PCH_GBE=y +CONFIG_DM_PCI=y +CONFIG_PCI_XILINX=y +CONFIG_SYS_NS16550=y +CONFIG_LZ4=y diff --git a/configs/boston64r2el_defconfig b/configs/boston64r2el_defconfig new file mode 100644 index 0000000..9b5fa5a --- /dev/null +++ b/configs/boston64r2el_defconfig @@ -0,0 +1,42 @@ +CONFIG_MIPS=y +CONFIG_TARGET_BOSTON=y +CONFIG_SYS_LITTLE_ENDIAN=y +CONFIG_CPU_MIPS64_R2=y +# CONFIG_MIPS_BOOT_CMDLINE_LEGACY is not set +# CONFIG_MIPS_BOOT_ENV_LEGACY is not set +CONFIG_MIPS_BOOT_FDT=y +CONFIG_DEFAULT_DEVICE_TREE="img,boston" +CONFIG_FIT=y +CONFIG_FIT_VERBOSE=y +CONFIG_FIT_BEST_MATCH=y +CONFIG_OF_STDOUT_VIA_ALIAS=y +CONFIG_HUSH_PARSER=y +CONFIG_SYS_PROMPT="boston # " +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_IMLS is not set +CONFIG_CMD_GREPENV=y +CONFIG_CMD_MEMTEST=y +# CONFIG_CMD_LOADB is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_FPGA is not set +CONFIG_CMD_DHCP=y +CONFIG_CMD_PING=y +CONFIG_CMD_SNTP=y +CONFIG_CMD_DNS=y +CONFIG_CMD_LINK_LOCAL=y +CONFIG_CMD_TIME=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_CMD_FAT=y +CONFIG_CMD_FS_GENERIC=y +CONFIG_OF_EMBED=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_CLK=y +CONFIG_MTD=y +CONFIG_CFI_FLASH=y +CONFIG_DM_ETH=y +CONFIG_PCH_GBE=y +CONFIG_DM_PCI=y +CONFIG_PCI_XILINX=y +CONFIG_SYS_NS16550=y +CONFIG_LZ4=y diff --git a/doc/README.boston b/doc/README.boston new file mode 100644 index 0000000..38f6710 --- /dev/null +++ b/doc/README.boston @@ -0,0 +1,58 @@ +MIPS Boston Development Board + +--------- + About +--------- + +The MIPS Boston development board is built around an FPGA & 3 PCIe controllers, +one of which is connected to an Intel EG20T Platform Controller Hub which +provides most connectivity to the board. It is used during the development & +testing of both new CPUs and the software support for them. It is essentially +the successor of the older MIPS Malta board. + +-------- + QEMU +-------- + +U-Boot can be run on a currently out-of-tree branch of QEMU with support for +the Boston board added. This QEMU code can currently be found in the "boston" +branch of git://git.linux-mips.org/pub/scm/paul/qemu.git and used like so: + + $ git clone git://git.linux-mips.org/pub/scm/paul/qemu.git -b boston + $ cd qemu + $ ./configure --target-list=mips64el-softmmu + $ make + $ ./mips64el-softmmu/qemu-system-mips64el -M boston -m 2G \ + -bios u-boot.bin -serial stdio + +Please note that QEMU will default to emulating the I6400 CPU which implements +the MIPS64r6 ISA, and at the time of writing doesn't implement any earlier CPUs +with support for the CPS features the Boston board relies upon. You will +therefore need to configure U-Boot to build for MIPSr6 in order to obtain a +binary that will work in QEMU. + +------------- + Toolchain +------------- + +If building for MIPSr6 then you will need a toolchain including GCC 5.x or +newer, or the Codescape toolchain available for download from Imagination +Technologies: + + http://codescape-mips-sdk.imgtec.com/components/toolchain/2015.06-05/ + +The "IMG GNU Linux Toolchain" is capable of building for all current MIPS ISAs, +architecture revisions & both endiannesses. + +-------- + TODO +-------- + + - AHCI support + - CPU driver + - Exception handling (+UHI?) + - Flash support + - IOCU support + - L2 cache support + - More general LCD display driver + - Multi-arch-variant multi-endian fat binary diff --git a/include/configs/boston.h b/include/configs/boston.h new file mode 100644 index 0000000..e958054 --- /dev/null +++ b/include/configs/boston.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __CONFIGS_BOSTON_H__ +#define __CONFIGS_BOSTON_H__ + +/* + * General board configuration + */ +#define CONFIG_DISPLAY_BOARDINFO + +/* + * CPU + */ +#define CONFIG_SYS_MIPS_TIMER_FREQ 30000000 + +/* + * PCI + */ +#define CONFIG_PCI +#define CONFIG_PCI_PNP +#define CONFIG_CMD_PCI + +/* + * Memory map + */ +#ifdef CONFIG_64BIT +# define CONFIG_SYS_SDRAM_BASE 0xffffffff80000000 +#else +# define CONFIG_SYS_SDRAM_BASE 0x80000000 +#endif + +#define CONFIG_SYS_INIT_SP_OFFSET 0x400000 + +#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE + +#define CONFIG_SYS_LOAD_ADDR (CONFIG_SYS_SDRAM_BASE + 0x100000) + +#define CONFIG_SYS_MEMTEST_START (CONFIG_SYS_SDRAM_BASE + 0) +#define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_SDRAM_BASE + 0x10000000) + +#define CONFIG_SYS_MALLOC_LEN (256 * 1024) + +/* + * Console + */ +#define CONFIG_SYS_MAXARGS 16 +#define CONFIG_SYS_CBSIZE 256 +#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ + sizeof(CONFIG_SYS_PROMPT) + 16) +#define CONFIG_SYS_LONGHELP +#define CONFIG_BAUDRATE 115200 + +/* + * Flash + */ +#define CONFIG_FLASH_CFI_DRIVER +#define CONFIG_SYS_FLASH_CFI +#define CONFIG_SYS_FLASH_PROTECTION +#define CONFIG_SYS_FLASH_USE_BUFFER_WRITE +#define CONFIG_SYS_MAX_FLASH_BANKS_DETECT 1 +#define CONFIG_SYS_MAX_FLASH_SECT 1024 + +/* + * Environment + */ +#define CONFIG_ENV_IS_IN_FLASH +#define CONFIG_ENV_SECT_SIZE 0x20000 +#define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE +#ifdef CONFIG_64BIT +# define CONFIG_ENV_ADDR \ + (0xffffffffb8000000 + (128 << 20) - CONFIG_ENV_SIZE) +#else +# define CONFIG_ENV_ADDR \ + (0xb8000000 + (128 << 20) - CONFIG_ENV_SIZE) +#endif + +#endif /* __CONFIGS_BOSTON_H__ */ -- cgit v1.1 From d263cda5ae41279208d9576eab1c5215a57252a6 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 11:11:06 +0100 Subject: MIPS: Fix cache maintenance in relocate_code & simplify The relocate_code function was handling cache maintenance incorrectly. It copied U-Boot to its new location, flushed the caches & then proceeded to apply relocations & jump to the new code without flushing the caches again. This is problematic as the instruction cache could potentially have already fetched instructions that hadn't had relocs applied. Rework this to perform the flush_cache call using the code in the original copy of U-Boot, after having applied relocations to the new copy of U-Boot. The new U-Boot can then be jumped to safely once that cache flush has been performed. As part of this, since the old U-Boot is used up until after that cache flush, complexity around loading values from the GOT using a jump & link instruction & loads from a table is removed. Instead we can simply load the needed values with PTR_LA fromt the original GOT. Signed-off-by: Paul Burton --- arch/mips/cpu/start.S | 45 +++++++++++++++++++-------------------------- arch/mips/cpu/u-boot.lds | 2 ++ 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S index 8f85ede..cbc02fa 100644 --- a/arch/mips/cpu/start.S +++ b/arch/mips/cpu/start.S @@ -223,12 +223,9 @@ ENTRY(relocate_code) PTR_LI t0, CONFIG_SYS_MONITOR_BASE PTR_SUB s1, s2, t0 # s1 <-- relocation offset - PTR_LA t3, in_ram - PTR_L t2, -(3 * PTRSIZE)(t3) # t2 <-- __image_copy_end + PTR_LA t2, __image_copy_end move t1, a2 - PTR_ADD gp, s1 # adjust gp - /* * t0 = source address * t1 = target address @@ -241,32 +238,14 @@ ENTRY(relocate_code) blt t0, t2, 1b PTR_ADDU t1, PTRSIZE - /* If caches were enabled, we would have to flush them here. */ - PTR_SUB a1, t1, s2 # a1 <-- size - PTR_LA t9, flush_cache - jalr t9 - move a0, s2 # a0 <-- destination address - - /* Jump to where we've relocated ourselves */ - PTR_ADDIU t0, s2, in_ram - _start - jr t0 - nop - - PTR __rel_dyn_end - PTR __rel_dyn_start - PTR __image_copy_end - PTR _GLOBAL_OFFSET_TABLE_ - PTR num_got_entries - -in_ram: /* * Now we want to update GOT. * * GOT[0] is reserved. GOT[1] is also reserved for the dynamic object * generated by GNU ld. Skip these reserved entries from relocation. */ - PTR_L t3, -(1 * PTRSIZE)(t0) # t3 <-- num_got_entries - PTR_L t8, -(2 * PTRSIZE)(t0) # t8 <-- _GLOBAL_OFFSET_TABLE_ + PTR_LA t3, num_got_entries + PTR_LA t8, _GLOBAL_OFFSET_TABLE_ PTR_ADD t8, s1 # t8 now holds relocated _G_O_T_ PTR_ADDIU t8, t8, 2 * PTRSIZE # skipping first two entries PTR_LI t2, 2 @@ -281,8 +260,8 @@ in_ram: PTR_ADDIU t8, PTRSIZE /* Update dynamic relocations */ - PTR_L t1, -(4 * PTRSIZE)(t0) # t1 <-- __rel_dyn_start - PTR_L t2, -(5 * PTRSIZE)(t0) # t2 <-- __rel_dyn_end + PTR_LA t1, __rel_dyn_start + PTR_LA t2, __rel_dyn_end b 2f # skip first reserved entry PTR_ADDIU t1, 2 * PTRSIZE @@ -307,6 +286,20 @@ in_ram: PTR_ADDIU t1, 2 * PTRSIZE # each rel.dyn entry is 2*PTRSIZE bytes /* + * Flush caches to ensure our newly modified instructions are visible + * to the instruction cache. We're still running with the old GOT, so + * apply the reloc offset to the start address. + */ + PTR_LA a0, __text_start + PTR_LA a1, __text_end + PTR_SUB a1, a1, a0 + PTR_LA t9, flush_cache + jalr t9 + PTR_ADD a0, s1 + + PTR_ADD gp, s1 # adjust gp + + /* * Clear BSS * * GOT is now relocated. Thus __bss_start and __bss_end can be diff --git a/arch/mips/cpu/u-boot.lds b/arch/mips/cpu/u-boot.lds index 7d71c11..0129c99 100644 --- a/arch/mips/cpu/u-boot.lds +++ b/arch/mips/cpu/u-boot.lds @@ -19,7 +19,9 @@ SECTIONS . = ALIGN(4); .text : { + __text_start = .; *(.text*) + __text_end = .; } . = ALIGN(4); -- cgit v1.1 From 31d36f748c52b22833aa946f6c406cc8fb2f1908 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 21 Sep 2016 14:59:54 +0100 Subject: MIPS: Hang if run on a secondary CPU Some systems are configured such that multiple CPUs begin running from their reset vector following a system reset. If this occurs then U-Boot will be run on multiple CPUs simultaneously, which causes all sorts of issues as the multiple instances of U-Boot clobber each other. Prevent this from happening by simply hanging with an infinite loop if we run on a CPU whose ID, as determined by GlobalNumber or EBase.CPUNum as appropriate, is non-zero. Signed-off-by: Paul Burton --- arch/mips/cpu/start.S | 21 ++++++++++++++++++++- arch/mips/include/asm/mipsregs.h | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S index cbc02fa..3f0fc12 100644 --- a/arch/mips/cpu/start.S +++ b/arch/mips/cpu/start.S @@ -108,9 +108,28 @@ ENTRY(_start) .align 4 reset: +#if __mips_isa_rev >= 6 + mfc0 t0, CP0_CONFIG, 5 + and t0, t0, MIPS_CONF5_VP + beqz t0, 1f + nop + + b 2f + mfc0 t0, CP0_GLOBALNUMBER +#endif + +1: mfc0 t0, CP0_EBASE + and t0, t0, EBASE_CPUNUM + + /* Hang if this isn't the first CPU in the system */ +2: beqz t0, 4f + nop +3: wait + b 3b + nop /* Clear watch registers */ - MTC0 zero, CP0_WATCHLO +4: MTC0 zero, CP0_WATCHLO mtc0 zero, CP0_WATCHHI /* WP(Watch Pending), SW0/1 should be cleared */ diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index b4c2dff..9ab5063 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -39,6 +39,7 @@ #define CP0_ENTRYLO0 $2 #define CP0_ENTRYLO1 $3 #define CP0_CONF $3 +#define CP0_GLOBALNUMBER $3, 1 #define CP0_CONTEXT $4 #define CP0_PAGEMASK $5 #define CP0_WIRED $6 @@ -361,6 +362,11 @@ #define CAUSEF_BD (_ULCAST_(1) << 31) /* + * Bits in the coprocessor 0 EBase register. + */ +#define EBASE_CPUNUM 0x3ff + +/* * Bits in the coprocessor 0 config register. */ /* Generic bits. */ @@ -553,6 +559,7 @@ #define MIPS_CONF5_MRP (_ULCAST_(1) << 3) #define MIPS_CONF5_LLB (_ULCAST_(1) << 4) #define MIPS_CONF5_MVH (_ULCAST_(1) << 5) +#define MIPS_CONF5_VP (_ULCAST_(1) << 7) #define MIPS_CONF5_FRE (_ULCAST_(1) << 8) #define MIPS_CONF5_UFE (_ULCAST_(1) << 9) #define MIPS_CONF5_L2C (_ULCAST_(1) << 10) -- cgit v1.1