diff --git a/cores/axis_rp_adc_v1_0/core_config.tcl b/cores/axis_rp_adc_v1_0/core_config.tcl index c61fb615ceb8d23657ddcf52b43bf52e5fa3b70b..7b59ee3efed5d773344bb5fe2866051db7f7de42 100644 --- a/cores/axis_rp_adc_v1_0/core_config.tcl +++ b/cores/axis_rp_adc_v1_0/core_config.tcl @@ -1,4 +1,4 @@ -set display_name {AXI4-Stream Red Pitaya ADC} +set display_name {AXI4-Stream Red Pitaya ADC v1.0} set core [ipx::current_core] diff --git a/cores/axis_rp_adc_v3_0/core_config.tcl b/cores/axis_rp_adc_v3_0/core_config.tcl index c61fb615ceb8d23657ddcf52b43bf52e5fa3b70b..e01ad25776f5d08985e89ed780d049c99981bb10 100644 --- a/cores/axis_rp_adc_v3_0/core_config.tcl +++ b/cores/axis_rp_adc_v3_0/core_config.tcl @@ -1,4 +1,4 @@ -set display_name {AXI4-Stream Red Pitaya ADC} +set display_name {AXI4-Stream Red Pitaya ADC v3.0} set core [ipx::current_core] diff --git a/patches/cma.c b/patches/cma.c new file mode 100644 index 0000000000000000000000000000000000000000..621210743df0156c7261ffba1f64744bc7a0f0e4 --- /dev/null +++ b/patches/cma.c @@ -0,0 +1,96 @@ +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/dma-mapping.h> +#include <linux/dma-map-ops.h> + +#define CMA_ALLOC _IOWR('Z', 0, u32) + +static struct device *dma_device = NULL; +static size_t dma_size = 0; +static void *cpu_addr = NULL; +static dma_addr_t dma_addr; + +static void cma_free(void) +{ + if(!cpu_addr) return; + dma_free_coherent(dma_device, dma_size, cpu_addr, dma_addr); + cpu_addr = NULL; +} + +static long cma_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long rc; + u32 buffer; + + if(cmd != CMA_ALLOC) return -ENOTTY; + + rc = copy_from_user(&buffer, (void __user *)arg, sizeof(buffer)); + if(rc) return rc; + + cma_free(); + + dma_size = buffer; + cpu_addr = dma_alloc_coherent(dma_device, dma_size, &dma_addr, GFP_KERNEL); + + if(IS_ERR_OR_NULL(cpu_addr)) + { + rc = PTR_ERR(cpu_addr); + if(rc == 0) rc = -ENOMEM; + cpu_addr = NULL; + return rc; + } + + buffer = dma_addr; + return copy_to_user((void __user *)arg, &buffer, sizeof(buffer)); +} + +static int cma_mmap(struct file *file, struct vm_area_struct *vma) +{ + return dma_mmap_coherent(dma_device, vma, cpu_addr, dma_addr, dma_size); +} + +static int cma_release(struct inode *inode, struct file *file) +{ + cma_free(); + return 0; +} + +static struct file_operations cma_fops = +{ + .unlocked_ioctl = cma_ioctl, + .mmap = cma_mmap, + .release = cma_release +}; + +struct miscdevice cma_device = +{ + .minor = MISC_DYNAMIC_MINOR, + .name = "cma", + .fops = &cma_fops +}; + +static int __init cma_init(void) +{ + int rc; + + rc = misc_register(&cma_device); + if(rc) return rc; + + dma_device = cma_device.this_device; + + dma_device->dma_ops = &arm_coherent_dma_ops; + dma_device->coherent_dma_mask = DMA_BIT_MASK(32); + + return 0; +} + +static void __exit cma_exit(void) +{ + cma_free(); + misc_deregister(&cma_device); +} + +module_init(cma_init); +module_exit(cma_exit); +MODULE_LICENSE("MIT"); diff --git a/patches/devicetree.patch b/patches/devicetree.patch new file mode 100644 index 0000000000000000000000000000000000000000..fc16466a0c4bfdde90d537e80901fbe747e8173e --- /dev/null +++ b/patches/devicetree.patch @@ -0,0 +1,147 @@ +diff -rupN pl.dtsi.old pl.dtsi +--- pl.dtsi.old ++++ pl.dtsi +@@ -15,7 +15,7 @@ + #interrupt-cells = <2>; + clock-names = "s_axi_aclk"; + clocks = <&misc_clk_0>; +- compatible = "xlnx,axi-intc-4.1", "xlnx,xps-intc-1.00.a"; ++ compatible = "generic-uio"; + interrupt-controller ; + interrupt-names = "irq"; + interrupt-parent = <&intc>; +@@ -32,19 +32,19 @@ + cfg_0: axi_cfg_register@40001000 { + clock-names = "aclk"; + clocks = <&misc_clk_0>; +- compatible = "xlnx,axi-cfg-register-1.0"; ++ compatible = "generic-uio"; + reg = <0x40001000 0x1000>; + }; + sts_0: axi_sts_register@40002000 { + clock-names = "aclk"; + clocks = <&misc_clk_0>; +- compatible = "xlnx,axi-sts-register-1.0"; ++ compatible = "generic-uio"; + reg = <0x40002000 0x1000>; + }; + xadc_wiz_0: xadc_wiz@40003000 { + clock-names = "s_axi_aclk"; + clocks = <&misc_clk_0>; +- compatible = "xlnx,xadc-wiz-3.3", "xlnx,axi-xadc-1.00.a"; ++ compatible = "generic-uio"; + reg = <0x40003000 0x1000>; + xlnx,alarm-limit-r0 = <0xb5ed>; + xlnx,alarm-limit-r1 = <0x57e4>; +@@ -126,5 +126,17 @@ + xlnx,vaux8 = <0x1>; + xlnx,vaux9 = <0x1>; + }; +- }; ++ hst0_reader_0: axi_bram_reader@40010000 { ++ clock-names = "aclk"; ++ clocks = <&misc_clk_0>; ++ compatible = "generic-uio"; ++ reg = <0x40010000 0x10000>; ++ }; ++ hst1_reader_1: axi_bram_reader@40020000 { ++ clock-names = "aclk"; ++ clocks = <&misc_clk_0>; ++ compatible = "generic-uio"; ++ reg = <0x40020000 0x10000>; ++ }; ++ }; + }; +diff -rupN pcw.dtsi.old pcw.dtsi +--- pcw.dtsi.old ++++ pcw.dtsi +@@ -26,6 +26,19 @@ + clock-frequency = <400000>; + status = "okay"; + }; ++&i2c0 { ++ eep@50 { ++ compatible = "24c64"; ++ size = <8192>; ++ pagesize = <32>; ++ reg = <0x50>; ++ }; ++ bmp180@77{ ++ compatible = "bosch,bmp280"; ++ reg = <0x77>; ++ status = "okay"; ++ }; ++}; + &intc { + num_cpus = <2>; + num_interrupts = <96>; +@@ -45,6 +51,11 @@ + is-decoded-cs = <0>; + num-cs = <1>; + status = "okay"; ++ spidev@0 { ++ compatible = "spidev"; ++ reg = <0x0>; ++ spi-max-frequency = <10000000>; ++ }; + }; + &uart0 { + device_type = "serial"; +@@ -57,11 +68,33 @@ + status = "okay"; + }; + &usb0 { ++ dr_mode = "host"; +- phy_type = "ulpi"; ++ usb-phy = <&usb_phy0>; + status = "okay"; +- usb-reset = <&gpio0 48 0>; ++ xlnx,phy-reset-gpio = <&gpio0 48 0>; + }; + &clkc { + fclk-enable = <0x0>; + ps-clk-frequency = <33333333>; + }; ++&adc { ++ xlnx,channels { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ channel@0 { ++ reg = <0>; ++ }; ++ channel@1 { ++ reg = <1>; ++ }; ++ channel@2 { ++ reg = <2>; ++ }; ++ channel@9 { ++ reg = <9>; ++ }; ++ channel@10 { ++ reg = <10>; ++ }; ++ }; ++}; +diff -rupN system-top.dts.old system-top.dts +--- system-top.dts.old ++++ system-top.dts +@@ -14,6 +14,18 @@ + bootargs = "console=ttyPS0,115200 earlyprintk earlycon"; + stdout-path = "serial0:115200n8"; + }; ++ usb_phy0: phy0 { ++ #phy-cells = <0>; ++ compatible = "ulpi-phy"; ++ reg = <0xe0002000 0x1000>; ++ view-port = <0x0170>; ++ drv-vbus; ++ }; ++ pps { ++ compatible = "pps-gpio"; ++ gpios = <&gpio0 54 0>; ++ capture-clear; ++ }; + aliases { + ethernet0 = &gem0; + i2c0 = &i2c0; diff --git a/patches/linux-5.10.patch b/patches/linux-5.10.patch new file mode 100644 index 0000000000000000000000000000000000000000..f37550adb0d9daa8bbb5ba74dd6b79a316365496 --- /dev/null +++ b/patches/linux-5.10.patch @@ -0,0 +1,494 @@ +diff -rupN old/linux-5.10/arch/arm/mach-zynq/common.c linux-5.10/arch/arm/mach-zynq/common.c +--- old/linux-5.10/arch/arm/mach-zynq/common.c ++++ linux-5.10/arch/arm/mach-zynq/common.c +@@ -95,6 +95,7 @@ static void __init zynq_init_late(void) + { + zynq_core_pm_init(); + zynq_pm_late_init(); ++ zynq_prefetch_init(); + } + + /** +@@ -186,8 +187,13 @@ static const char * const zynq_dt_match[ + + DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform") + /* 64KB way size, 8-way associativity, parity disabled */ +- .l2c_aux_val = 0x00400000, ++#ifdef CONFIG_XILINX_PREFETCH ++ .l2c_aux_val = 0x30400000, ++ .l2c_aux_mask = 0xcfbfffff, ++#else ++ .l2c_aux_val = 0x00400000, + .l2c_aux_mask = 0xffbfffff, ++#endif + .smp = smp_ops(zynq_smp_ops), + .map_io = zynq_map_io, + .init_irq = zynq_irq_init, +diff -rupN old/linux-5.10/arch/arm/mach-zynq/common.h linux-5.10/arch/arm/mach-zynq/common.h +--- old/linux-5.10/arch/arm/mach-zynq/common.h ++++ linux-5.10/arch/arm/mach-zynq/common.h +@@ -15,6 +15,7 @@ extern void zynq_slcr_cpu_stop(int cpu); + extern void zynq_slcr_cpu_start(int cpu); + extern bool zynq_slcr_cpu_state_read(int cpu); + extern void zynq_slcr_cpu_state_write(int cpu, bool die); ++extern u32 zynq_slcr_get_ocm_config(void); + extern u32 zynq_slcr_get_device_id(void); + + #ifdef CONFIG_SMP +@@ -29,6 +30,22 @@ extern void __iomem *zynq_scu_base; + + void zynq_pm_late_init(void); + ++static inline void zynq_prefetch_init(void) ++{ ++ /* ++ * Enable prefetching in aux control register. L2 prefetch must ++ * only be enabled if the slave supports it (PL310 does) ++ */ ++ asm volatile ("mrc p15, 0, r1, c1, c0, 1\n" ++#ifdef CONFIG_XILINX_PREFETCH ++ "orr r1, r1, #6\n" ++#else ++ "bic r1, r1, #6\n" ++#endif ++ "mcr p15, 0, r1, c1, c0, 1\n" ++ : : : "r1"); ++} ++ + static inline void zynq_core_pm_init(void) + { + /* A9 clock gating */ +diff -rupN old/linux-5.10/arch/arm/mach-zynq/Kconfig linux-5.10/arch/arm/mach-zynq/Kconfig +--- old/linux-5.10/arch/arm/mach-zynq/Kconfig ++++ linux-5.10/arch/arm/mach-zynq/Kconfig +@@ -16,3 +16,19 @@ config ARCH_ZYNQ + select SOC_BUS + help + Support for Xilinx Zynq ARM Cortex A9 Platform ++ ++if ARCH_ZYNQ ++ ++menu "Xilinx Specific Options" ++ ++config XILINX_PREFETCH ++ bool "Cache Prefetch" ++ default y ++ help ++ This option turns on L1 & L2 cache prefetching to get the best performance ++ in many cases. This may not always be the best performance depending on ++ the usage. ++ ++endmenu ++ ++endif +diff -rupN old/linux-5.10/arch/arm/mach-zynq/Makefile linux-5.10/arch/arm/mach-zynq/Makefile +--- old/linux-5.10/arch/arm/mach-zynq/Makefile ++++ linux-5.10/arch/arm/mach-zynq/Makefile +@@ -4,5 +4,5 @@ + # + + # Common support +-obj-y := common.o slcr.o pm.o ++obj-y := common.o slcr.o zynq_ocm.o pm.o + obj-$(CONFIG_SMP) += headsmp.o platsmp.o +diff -rupN old/linux-5.10/arch/arm/mach-zynq/platsmp.c linux-5.10/arch/arm/mach-zynq/platsmp.c +--- old/linux-5.10/arch/arm/mach-zynq/platsmp.c ++++ linux-5.10/arch/arm/mach-zynq/platsmp.c +@@ -115,6 +115,7 @@ static void __init zynq_smp_prepare_cpus + static void zynq_secondary_init(unsigned int cpu) + { + zynq_core_pm_init(); ++ zynq_prefetch_init(); + } + + #ifdef CONFIG_HOTPLUG_CPU +diff -rupN old/linux-5.10/arch/arm/mach-zynq/slcr.c linux-5.10/arch/arm/mach-zynq/slcr.c +--- old/linux-5.10/arch/arm/mach-zynq/slcr.c ++++ linux-5.10/arch/arm/mach-zynq/slcr.c +@@ -20,6 +20,7 @@ + #define SLCR_REBOOT_STATUS_OFFSET 0x258 /* PS Reboot Status */ + #define SLCR_PSS_IDCODE 0x530 /* PS IDCODE */ + #define SLCR_L2C_RAM 0xA1C /* L2C_RAM in AR#54190 */ ++#define SLCR_OCM_CFG_OFFSET 0x910 /* OCM Address Mapping */ + + #define SLCR_UNLOCK_MAGIC 0xDF0D + #define SLCR_A9_CPU_CLKSTOP 0x10 +@@ -116,6 +117,19 @@ static struct notifier_block zynq_slcr_r + }; + + /** ++ * zynq_slcr_get_ocm_config - Get SLCR OCM config ++ * ++ * return: OCM config bits ++ */ ++u32 zynq_slcr_get_ocm_config(void) ++{ ++ u32 ret; ++ ++ zynq_slcr_read(&ret, SLCR_OCM_CFG_OFFSET); ++ return ret; ++} ++ ++/** + * zynq_slcr_cpu_start - Start cpu + * @cpu: cpu number + */ +diff -rupN old/linux-5.10/drivers/char/Kconfig linux-5.10/drivers/char/Kconfig +--- old/linux-5.10/drivers/char/Kconfig ++++ linux-5.10/drivers/char/Kconfig +@@ -471,6 +471,14 @@ config ADI + and SSM (Silicon Secured Memory). Intended consumers of this + driver include crash and makedumpfile. + ++config DEVCMA ++ bool "/dev/cma virtual device support" ++ default y ++ ++config XILINX_DEVCFG ++ tristate "Xilinx Device Configuration" ++ depends on ARCH_ZYNQ ++ + endmenu + + config RANDOM_TRUST_CPU +diff -rupN old/linux-5.10/drivers/char/Makefile linux-5.10/drivers/char/Makefile +--- old/linux-5.10/drivers/char/Makefile ++++ linux-5.10/drivers/char/Makefile +@@ -47,3 +47,5 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o + obj-$(CONFIG_XILLYBUS) += xillybus/ + obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o + obj-$(CONFIG_ADI) += adi.o ++obj-$(CONFIG_DEVCMA) += cma.o ++obj-$(CONFIG_XILINX_DEVCFG) += xilinx_devcfg.o +diff -rupN old/linux-5.10/drivers/net/phy/intel-xway.c linux-5.10/drivers/net/phy/intel-xway.c +--- old/linux-5.10/drivers/net/phy/intel-xway.c ++++ linux-5.10/drivers/net/phy/intel-xway.c +@@ -180,6 +180,12 @@ static int xway_gphy_config_init(struct + if (err) + return err; + ++ /* Set SGMII RX & TX timing skew to 2 ns & 2.5 ns respectively. */ ++ /* Set MII power supply to 2V5. */ ++ err = phy_write(phydev, 0x17, 0x4D00); ++ if (err) ++ return err; ++ + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH, + XWAY_MMD_LEDCH_NACS_NONE | + XWAY_MMD_LEDCH_SBF_F02HZ | +@@ -189,20 +195,16 @@ static int xway_gphy_config_init(struct + XWAY_MMD_LEDCH_SCAN_NONE); + + /** +- * In most cases only one LED is connected to this phy, so +- * configure them all to constant on and pulse mode. LED3 is +- * only available in some packages, leave it in its reset +- * configuration. ++ * Set LED0 blinking on RX/TX. ++ * Set LED1 blinking on link speed: slow=10M, fast=100M, on=1G. + */ +- ledxh = XWAY_MMD_LEDxH_BLINKF_NONE | XWAY_MMD_LEDxH_CON_LINK10XX; +- ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT | +- XWAY_MMD_LEDxL_BLINKS_NONE; +- phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, ledxh); ++ ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT; ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, 0); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0L, ledxl); ++ ledxh = XWAY_MMD_LEDxH_CON_LINK1000 | XWAY_MMD_LEDxH_BLINKF_LINK100; ++ ledxl = XWAY_MMD_LEDxH_CON_LINK10; + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1H, ledxh); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1L, ledxl); +- phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh); +- phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl); + + return 0; + } +diff -rupN old/linux-5.10/drivers/net/wireless/realtek/Kconfig linux-5.10/drivers/net/wireless/realtek/Kconfig +--- old/linux-5.10/drivers/net/wireless/realtek/Kconfig ++++ linux-5.10/drivers/net/wireless/realtek/Kconfig +@@ -13,8 +13,9 @@ config WLAN_VENDOR_REALTEK + if WLAN_VENDOR_REALTEK + + source "drivers/net/wireless/realtek/rtl818x/Kconfig" +-source "drivers/net/wireless/realtek/rtlwifi/Kconfig" + source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig" + source "drivers/net/wireless/realtek/rtw88/Kconfig" ++source "drivers/net/wireless/realtek/rtl8188eu/Kconfig" ++source "drivers/net/wireless/realtek/rtl8192cu/Kconfig" + + endif # WLAN_VENDOR_REALTEK +diff -rupN old/linux-5.10/drivers/net/wireless/realtek/Makefile linux-5.10/drivers/net/wireless/realtek/Makefile +--- old/linux-5.10/drivers/net/wireless/realtek/Makefile ++++ linux-5.10/drivers/net/wireless/realtek/Makefile +@@ -5,7 +5,8 @@ + + obj-$(CONFIG_RTL8180) += rtl818x/ + obj-$(CONFIG_RTL8187) += rtl818x/ +-obj-$(CONFIG_RTLWIFI) += rtlwifi/ + obj-$(CONFIG_RTL8XXXU) += rtl8xxxu/ + obj-$(CONFIG_RTW88) += rtw88/ ++obj-$(CONFIG_RTL8188EU) += rtl8188eu/ ++obj-$(CONFIG_RTL8192CU) += rtl8192cu/ + +diff -rupN old/linux-5.10/drivers/net/wireless/realtek/rtl8188eu/Kconfig linux-5.10/drivers/net/wireless/realtek/rtl8188eu/Kconfig +--- old/linux-5.10/drivers/net/wireless/realtek/rtl8188eu/Kconfig ++++ linux-5.10/drivers/net/wireless/realtek/rtl8188eu/Kconfig +@@ -1,6 +1,6 @@ + config RTL8188EU + tristate "Realtek 8188E USB WiFi" + depends on USB +- ---help--- ++ help + Help message of RTL8188EU + +diff -rupN old/linux-5.10/drivers/net/wireless/realtek/rtl8192cu/Kconfig linux-5.10/drivers/net/wireless/realtek/rtl8192cu/Kconfig +--- old/linux-5.10/drivers/net/wireless/realtek/rtl8192cu/Kconfig 1970-01-01 00:00:00.000000000 +0000 ++++ linux-5.10/drivers/net/wireless/realtek/rtl8192cu/Kconfig +@@ -0,0 +1,9 @@ ++config RTL8192CU ++ tristate "Realtek 8192CU USB WiFi" ++ depends on MAC80211 && USB ++ select CFG80211_WEXT ++ select WIRELESS_EXT ++ select WEXT_PRIV ++ help ++ This option adds the Realtek RTL8192CU USB device. ++ +diff -rupN old/linux-5.10/drivers/pps/clients/pps-gpio.c linux-5.10/drivers/pps/clients/pps-gpio.c +--- old/linux-5.10/drivers/pps/clients/pps-gpio.c ++++ linux-5.10/drivers/pps/clients/pps-gpio.c +@@ -145,6 +145,8 @@ static int pps_gpio_setup(struct platfor + + if (of_property_read_bool(np, "assert-falling-edge")) + data->assert_falling_edge = true; ++ if (of_get_property(np, "capture-clear", NULL)) ++ data->capture_clear = true; + return 0; + } + +diff -rupN old/linux-5.10/drivers/usb/chipidea/ci_hdrc_usb2.c linux-5.10/drivers/usb/chipidea/ci_hdrc_usb2.c +--- old/linux-5.10/drivers/usb/chipidea/ci_hdrc_usb2.c ++++ linux-5.10/drivers/usb/chipidea/ci_hdrc_usb2.c +@@ -30,6 +30,7 @@ static const struct ci_hdrc_platform_dat + + static const struct ci_hdrc_platform_data ci_zynq_pdata = { + .capoffset = DEF_CAPOFFSET, ++ .flags = CI_HDRC_PHY_VBUS_CONTROL, + }; + + static const struct ci_hdrc_platform_data ci_zevio_pdata = { +@@ -64,6 +65,9 @@ static int ci_hdrc_usb2_probe(struct pla + if (match && match->data) { + /* struct copy */ + *ci_pdata = *(struct ci_hdrc_platform_data *)match->data; ++ ci_pdata->usb_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); ++ if (IS_ERR(ci_pdata->usb_phy)) ++ return PTR_ERR(ci_pdata->usb_phy); + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +diff -rupN old/linux-5.10/drivers/usb/chipidea/host.c linux-5.10/drivers/usb/chipidea/host.c +--- old/linux-5.10/drivers/usb/chipidea/host.c ++++ linux-5.10/drivers/usb/chipidea/host.c +@@ -57,6 +57,14 @@ static int ehci_ci_portpower(struct usb_ + priv->enabled = enable; + } + ++ if (ci->platdata->flags & CI_HDRC_PHY_VBUS_CONTROL && ++ ci->usb_phy && ci->usb_phy->set_vbus) { ++ if (enable) ++ ci->usb_phy->set_vbus(ci->usb_phy, 1); ++ else ++ ci->usb_phy->set_vbus(ci->usb_phy, 0); ++ } ++ + if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) { + /* + * Marvell 28nm HSIC PHY requires forcing the port to HS mode. +diff -rupN old/linux-5.10/drivers/usb/chipidea/otg_fsm.c linux-5.10/drivers/usb/chipidea/otg_fsm.c +--- old/linux-5.10/drivers/usb/chipidea/otg_fsm.c ++++ linux-5.10/drivers/usb/chipidea/otg_fsm.c +@@ -471,6 +471,11 @@ static void ci_otg_drv_vbus(struct otg_f + return; + } + } ++ ++ if (ci->platdata->flags & CI_HDRC_PHY_VBUS_CONTROL && ++ ci->usb_phy && ci->usb_phy->set_vbus) ++ ci->usb_phy->set_vbus(ci->usb_phy, 1); ++ + /* Disable data pulse irq */ + hw_write_otgsc(ci, OTGSC_DPIE, 0); + +@@ -480,6 +485,10 @@ static void ci_otg_drv_vbus(struct otg_f + if (ci->platdata->reg_vbus) + regulator_disable(ci->platdata->reg_vbus); + ++ if (ci->platdata->flags & CI_HDRC_PHY_VBUS_CONTROL && ++ ci->usb_phy && ci->usb_phy->set_vbus) ++ ci->usb_phy->set_vbus(ci->usb_phy, 0); ++ + fsm->a_bus_drop = 1; + fsm->a_bus_req = 0; + } +diff -rupN old/linux-5.10/drivers/usb/phy/phy-ulpi.c linux-5.10/drivers/usb/phy/phy-ulpi.c +--- old/linux-5.10/drivers/usb/phy/phy-ulpi.c ++++ linux-5.10/drivers/usb/phy/phy-ulpi.c +@@ -13,9 +13,16 @@ + #include <linux/kernel.h> + #include <linux/slab.h> + #include <linux/export.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/io.h> ++#include <linux/of_address.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> + #include <linux/usb.h> + #include <linux/usb/otg.h> + #include <linux/usb/ulpi.h> ++#include <linux/usb/phy.h> + + + struct ulpi_info { +@@ -39,6 +46,13 @@ static struct ulpi_info ulpi_ids[] = { + ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"), + }; + ++struct ulpi_phy { ++ struct usb_phy *usb_phy; ++ void __iomem *regs; ++ unsigned int vp_offset; ++ unsigned int flags; ++}; ++ + static int ulpi_set_otg_flags(struct usb_phy *phy) + { + unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | +@@ -240,6 +254,23 @@ static int ulpi_set_vbus(struct usb_otg + return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); + } + ++static int usbphy_set_vbus(struct usb_phy *phy, int on) ++{ ++ unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL); ++ ++ flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); ++ ++ if (on) { ++ if (phy->flags & ULPI_OTG_DRVVBUS) ++ flags |= ULPI_OTG_CTRL_DRVVBUS; ++ ++ if (phy->flags & ULPI_OTG_DRVVBUS_EXT) ++ flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; ++ } ++ ++ return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); ++} ++ + static void otg_ulpi_init(struct usb_phy *phy, struct usb_otg *otg, + struct usb_phy_io_ops *ops, + unsigned int flags) +@@ -249,6 +280,7 @@ static void otg_ulpi_init(struct usb_phy + phy->io_ops = ops; + phy->otg = otg; + phy->init = ulpi_init; ++ phy->set_vbus = usbphy_set_vbus; + + otg->usb_phy = phy; + otg->set_host = ulpi_set_host; +@@ -301,3 +333,83 @@ devm_otg_ulpi_create(struct device *dev, + return phy; + } + EXPORT_SYMBOL_GPL(devm_otg_ulpi_create); ++ ++static int ulpi_phy_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *res; ++ struct ulpi_phy *uphy; ++ bool flag; ++ int ret; ++ ++ uphy = devm_kzalloc(&pdev->dev, sizeof(*uphy), GFP_KERNEL); ++ if (!uphy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); ++ return -ENODEV; ++ } ++ ++ uphy->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); ++ if (!uphy->regs) { ++ dev_err(&pdev->dev, "failed to map phy I/O memory\n"); ++ return -EFAULT; ++ } ++ ++ if (IS_ERR(uphy->regs)) ++ return PTR_ERR(uphy->regs); ++ ++ if (of_property_read_u32(np, "view-port", &uphy->vp_offset)) ++ dev_dbg(&pdev->dev, "Missing view-port property\n"); ++ ++ if (IS_ERR(uphy->regs)) { ++ dev_err(&pdev->dev, "view-port register not specified\n"); ++ return PTR_ERR(uphy->regs); ++ } ++ ++ flag = of_property_read_bool(np, "drv-vbus"); ++ if (flag) ++ uphy->flags |= ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT; ++ ++ uphy->usb_phy = otg_ulpi_create(&ulpi_viewport_access_ops, uphy->flags); ++ ++ uphy->usb_phy->dev = &pdev->dev; ++ ++ uphy->usb_phy->io_priv = uphy->regs + uphy->vp_offset; ++ ++ ret = usb_add_phy_dev(uphy->usb_phy); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int ulpi_phy_remove(struct platform_device *pdev) ++{ ++ struct ulpi_phy *uphy = platform_get_drvdata(pdev); ++ ++ usb_remove_phy(uphy->usb_phy); ++ ++ return 0; ++} ++ ++static const struct of_device_id ulpi_phy_table[] = { ++ { .compatible = "ulpi-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, ulpi_phy_table); ++ ++static struct platform_driver ulpi_phy_driver = { ++ .probe = ulpi_phy_probe, ++ .remove = ulpi_phy_remove, ++ .driver = { ++ .name = "ulpi-phy", ++ .of_match_table = ulpi_phy_table, ++ }, ++}; ++module_platform_driver(ulpi_phy_driver); ++ ++MODULE_DESCRIPTION("ULPI PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff -rupN old/linux-5.10/include/linux/usb/chipidea.h linux-5.10/include/linux/usb/chipidea.h +--- old/linux-5.10/include/linux/usb/chipidea.h ++++ linux-5.10/include/linux/usb/chipidea.h +@@ -62,6 +62,7 @@ struct ci_hdrc_platform_data { + #define CI_HDRC_REQUIRES_ALIGNED_DMA BIT(13) + #define CI_HDRC_IMX_IS_HSIC BIT(14) + #define CI_HDRC_PMQOS BIT(15) ++#define CI_HDRC_PHY_VBUS_CONTROL BIT(16) + enum usb_dr_mode dr_mode; + #define CI_HDRC_CONTROLLER_RESET_EVENT 0 + #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 diff --git a/patches/u-boot-2021.04.patch b/patches/u-boot-2021.04.patch new file mode 100644 index 0000000000000000000000000000000000000000..c9223599e86eb6b4f48407080ca17a51b89424d3 --- /dev/null +++ b/patches/u-boot-2021.04.patch @@ -0,0 +1,43 @@ +diff -rupN old/u-boot-2021.04/arch/arm/dts/Makefile u-boot-2021.04/arch/arm/dts/Makefile +--- old/u-boot-2021.04/arch/arm/dts/Makefile ++++ u-boot-2021.04/arch/arm/dts/Makefile +@@ -286,7 +286,8 @@ dtb-$(CONFIG_ARCH_ZYNQ) += \ + zynq-zturn.dtb \ + zynq-zturn-v5.dtb \ + zynq-zybo.dtb \ +- zynq-zybo-z7.dtb ++ zynq-zybo-z7.dtb \ ++ zynq-red-pitaya.dtb + dtb-$(CONFIG_ARCH_ZYNQMP) += \ + avnet-ultra96-rev1.dtb \ + avnet-ultrazedev-cc-v1.0-ultrazedev-som-v1.0.dtb \ +diff -rupN old/u-boot-2021.04/board/xilinx/zynq/board.c u-boot-2021.04/board/xilinx/zynq/board.c +--- old/u-boot-2021.04/board/xilinx/zynq/board.c ++++ u-boot-2021.04/board/xilinx/zynq/board.c +@@ -37,12 +37,12 @@ int board_late_init(void) + const char *mode; + char *new_targets; + char *env_targets; +- ++/* + if (!(gd->flags & GD_FLG_ENV_DEFAULT)) { + debug("Saved variables - Skipping\n"); + return 0; + } +- ++*/ + if (!CONFIG_IS_ENABLED(ENV_VARS_UBOOT_RUNTIME_CONFIG)) + return 0; + +diff -rupN old/u-boot-2021.04/common/main.c u-boot-2021.04/common/main.c +--- old/u-boot-2021.04/common/main.c ++++ u-boot-2021.04/common/main.c +@@ -61,6 +61,8 @@ void main_loop(void) + if (cli_process_fdt(&s)) + cli_secure_boot_cmd(s); + ++ env_set("sdboot", "echo Importing environment from SD... && mmcinfo && load mmc 0 0x2000000 uEnv.txt && env import -t 0x2000000 ${filesize} && boot"); ++ + autoboot_command(s); + + cli_loop(); diff --git a/patches/xilinx_devcfg.c b/patches/xilinx_devcfg.c new file mode 100644 index 0000000000000000000000000000000000000000..ea36195621022c59a3f49ba02d4ea3e672489d56 --- /dev/null +++ b/patches/xilinx_devcfg.c @@ -0,0 +1,2160 @@ +/* + * Xilinx Zynq Device Config driver + * + * Copyright (c) 2011 - 2013 Xilinx Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/cdev.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/sysctl.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#define DRIVER_NAME "xdevcfg" +#define XDEVCFG_DEVICES 1 + +/* An array, which is set to true when the device is registered. */ +static DEFINE_MUTEX(xdevcfg_mutex); + +/* Constant Definitions */ +#define XDCFG_CTRL_OFFSET 0x00 /* Control Register */ +#define XDCFG_LOCK_OFFSET 0x04 /* Lock Register */ +#define XDCFG_INT_STS_OFFSET 0x0C /* Interrupt Status Register */ +#define XDCFG_INT_MASK_OFFSET 0x10 /* Interrupt Mask Register */ +#define XDCFG_STATUS_OFFSET 0x14 /* Status Register */ +#define XDCFG_DMA_SRC_ADDR_OFFSET 0x18 /* DMA Source Address Register */ +#define XDCFG_DMA_DEST_ADDR_OFFSET 0x1C /* DMA Destination Address Reg */ +#define XDCFG_DMA_SRC_LEN_OFFSET 0x20 /* DMA Source Transfer Length */ +#define XDCFG_DMA_DEST_LEN_OFFSET 0x24 /* DMA Destination Transfer */ +#define XDCFG_UNLOCK_OFFSET 0x34 /* Unlock Register */ +#define XDCFG_MCTRL_OFFSET 0x80 /* Misc. Control Register */ + +/* Control Register Bit definitions */ +#define XDCFG_CTRL_PCFG_PROG_B_MASK 0x40000000 /* Program signal to + * Reset FPGA + */ +#define XDCFG_CTRL_PCAP_PR_MASK 0x08000000 /* Enable PCAP for PR */ +#define XDCFG_CTRL_PCAP_MODE_MASK 0x04000000 /* Enable PCAP */ +#define XDCFG_CTRL_PCAP_RATE_EN_MASK 0x02000000 /* Enable PCAP Quad Rate */ +#define XDCFG_CTRL_PCFG_AES_EN_MASK 0x00000E00 /* AES Enable Mask */ +#define XDCFG_CTRL_SEU_EN_MASK 0x00000100 /* SEU Enable Mask */ +#define XDCFG_CTRL_SPNIDEN_MASK 0x00000040 /* Secure Non Invasive + * Debug Enable + */ +#define XDCFG_CTRL_SPIDEN_MASK 0x00000020 /* Secure Invasive + * Debug Enable + */ +#define XDCFG_CTRL_NIDEN_MASK 0x00000010 /* Non-Invasive Debug + * Enable + */ +#define XDCFG_CTRL_DBGEN_MASK 0x00000008 /* Invasive Debug + * Enable + */ +#define XDCFG_CTRL_DAP_EN_MASK 0x00000007 /* DAP Enable Mask */ + +/* Lock register bit definitions */ + +#define XDCFG_LOCK_AES_EN_MASK 0x00000008 /* Lock AES_EN update */ +#define XDCFG_LOCK_SEU_MASK 0x00000004 /* Lock SEU_En update */ +#define XDCFG_LOCK_DBG_MASK 0x00000001 /* This bit locks + * security config + * including: DAP_En, + * DBGEN,NIDEN, SPNIEN + */ + +/* Miscellaneous Control Register bit definitions */ +#define XDCFG_MCTRL_PCAP_LPBK_MASK 0x00000010 /* Internal PCAP loopback */ + +/* Status register bit definitions */ +#define XDCFG_STATUS_PCFG_INIT_MASK 0x00000010 /* FPGA init status */ + +/* Interrupt Status/Mask Register Bit definitions */ +#define XDCFG_IXR_DMA_DONE_MASK 0x00002000 /* DMA Command Done */ +#define XDCFG_IXR_D_P_DONE_MASK 0x00001000 /* DMA and PCAP Cmd Done */ +#define XDCFG_IXR_PCFG_DONE_MASK 0x00000004 /* FPGA programmed */ +#define XDCFG_IXR_ERROR_FLAGS_MASK 0x00F0F860 +#define XDCFG_IXR_ALL_MASK 0xF8F7F87F +/* Miscellaneous constant values */ +#define XDCFG_DMA_INVALID_ADDRESS 0xFFFFFFFF /* Invalid DMA address */ + +static const char * const fclk_name[] = { + "fclk0", + "fclk1", + "fclk2", + "fclk3" +}; +#define NUMFCLKS ARRAY_SIZE(fclk_name) + +/** + * struct xdevcfg_drvdata - Device Configuration driver structure + * + * @dev: Pointer to the device structure + * @cdev: Instance of the cdev structure + * @devt: Pointer to the dev_t structure + * @class: Pointer to device class + * @fclk_class: Pointer to fclk device class + * @dma_done: The dma_done status bit for the DMA command completion + * @error_status: The error status captured during the DMA transfer + * @irq: Interrupt number + * @clk: Peripheral clock for devcfg + * @fclk: Array holding references to the FPGA clocks + * @fclk_exported: Flag inidcating whether an FPGA clock is exported + * @is_open: The status bit to indicate whether the device is opened + * @sem: Instance for the mutex + * @lock: Instance of spinlock + * @base_address: The virtual device base address of the device registers + * @ep107: Flags is used to identify the platform + * @endian_swap: Flags is used to identify the endianness format + * @residue_buf: Array holding stragglers from last time (0 to 3 bytes) + * @residue_len: stragglers length in bytes + * @is_partial_bitstream: Status bit to indicate partial/full bitstream + */ +struct xdevcfg_drvdata { + struct device *dev; + struct cdev cdev; + dev_t devt; + struct class *class; + struct class *fclk_class; + int irq; + struct clk *clk; + struct clk *fclk[NUMFCLKS]; + u8 fclk_exported[NUMFCLKS]; + bool dma_done; + int error_status; + bool is_open; + struct mutex sem; + spinlock_t lock; + void __iomem *base_address; + int ep107; + bool is_partial_bitstream; + bool endian_swap; + char residue_buf[3]; + int residue_len; +}; + +/** + * struct fclk_data - FPGA clock data + * @clk: Pointer to clock + * @enabled: Flag indicating enable status of the clock + * @rate_rnd: Rate to be rounded for round rate operation + */ +struct fclk_data { + struct clk *clk; + int enabled; + unsigned long rate_rnd; +}; + +/* Register read/write access routines */ +#define xdevcfg_writereg(offset, val) __raw_writel(val, offset) +#define xdevcfg_readreg(offset) __raw_readl(offset) + +#define SLCR_FPGA_RST_CTRL_OFFSET 0x240 /* FPGA Software Reset Control */ +#define SLCR_LVL_SHFTR_EN_OFFSET 0x900 /* Level Shifters Enable */ + +static struct regmap *zynq_slcr_regmap; + +/** + * zynq_slcr_write - Write to a register in SLCR block + * + * @val: Value to write to the register + * @offset: Register offset in SLCR block + * + * Return: a negative value on error, 0 on success + */ +static int zynq_slcr_write(u32 val, u32 offset) +{ + return regmap_write(zynq_slcr_regmap, offset, val); +} + +/** + * zynq_slcr_init_preload_fpga - Disable communication from the PL to PS. + */ +static void zynq_slcr_init_preload_fpga(void) +{ + /* Assert FPGA top level output resets */ + zynq_slcr_write(0xF, SLCR_FPGA_RST_CTRL_OFFSET); + + /* Disable level shifters */ + zynq_slcr_write(0, SLCR_LVL_SHFTR_EN_OFFSET); + + /* Enable output level shifters */ + zynq_slcr_write(0xA, SLCR_LVL_SHFTR_EN_OFFSET); +} + +/** + * zynq_slcr_init_postload_fpga - Re-enable communication from the PL to PS. + */ +static void zynq_slcr_init_postload_fpga(void) +{ + /* Enable level shifters */ + zynq_slcr_write(0xf, SLCR_LVL_SHFTR_EN_OFFSET); + + /* Deassert AXI interface resets */ + zynq_slcr_write(0, SLCR_FPGA_RST_CTRL_OFFSET); +} + +/** + * xdevcfg_reset_pl - Reset the programmable logic. + * @base_address: The base address of the device. + * + * Must be called with PCAP clock enabled + */ +static void xdevcfg_reset_pl(void __iomem *base_address) +{ + /* + * Create a rising edge on PCFG_INIT. PCFG_INIT follows PCFG_PROG_B, + * so we need to * poll it after setting PCFG_PROG_B to make sure that + * the rising edge happens. + */ + xdevcfg_writereg(base_address + XDCFG_CTRL_OFFSET, + (xdevcfg_readreg(base_address + XDCFG_CTRL_OFFSET) & + ~XDCFG_CTRL_PCFG_PROG_B_MASK)); + while (xdevcfg_readreg(base_address + XDCFG_STATUS_OFFSET) & + XDCFG_STATUS_PCFG_INIT_MASK) + ; + + usleep_range(5000, 5100); + xdevcfg_writereg(base_address + XDCFG_CTRL_OFFSET, + (xdevcfg_readreg(base_address + XDCFG_CTRL_OFFSET) | + XDCFG_CTRL_PCFG_PROG_B_MASK)); + while (!(xdevcfg_readreg(base_address + XDCFG_STATUS_OFFSET) & + XDCFG_STATUS_PCFG_INIT_MASK)) + ; +} + +/** + * xdevcfg_irq - The main interrupt handler. + * @irq: The interrupt number. + * @data: Pointer to the driver data structure. + * returns: IRQ_HANDLED after the interrupt is handled. + **/ +static irqreturn_t xdevcfg_irq(int irq, void *data) +{ + u32 intr_status; + struct xdevcfg_drvdata *drvdata = data; + + spin_lock(&drvdata->lock); + + intr_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_INT_STS_OFFSET); + + /* Clear the interrupts */ + xdevcfg_writereg(drvdata->base_address + XDCFG_INT_STS_OFFSET, + intr_status); + + if ((intr_status & XDCFG_IXR_D_P_DONE_MASK) == + XDCFG_IXR_D_P_DONE_MASK) + drvdata->dma_done = 1; + + if ((intr_status & XDCFG_IXR_ERROR_FLAGS_MASK) == + XDCFG_IXR_ERROR_FLAGS_MASK) + drvdata->error_status = 1; + + spin_unlock(&drvdata->lock); + + return IRQ_HANDLED; +} + +/** + * xdevcfg_write - The is the driver write function. + * + * @file: Pointer to the file structure. + * @buf: Pointer to the bitstream location. + * @count: The number of bytes to be written. + * @ppos: Pointer to the offset value + * returns: Success or error status. + **/ +static ssize_t +xdevcfg_write(struct file *file, const char __user *buf, size_t count, + loff_t *ppos) +{ + char *kbuf; + int status; + unsigned long timeout; + u32 intr_reg, dma_len; + dma_addr_t dma_addr; + u32 transfer_length = 0; + struct xdevcfg_drvdata *drvdata = file->private_data; + size_t user_count = count; + int i; + + status = clk_enable(drvdata->clk); + if (status) + return status; + + status = mutex_lock_interruptible(&drvdata->sem); + + if (status) + goto err_clk; + + dma_len = count + drvdata->residue_len; + kbuf = dma_alloc_coherent(drvdata->dev, dma_len, &dma_addr, GFP_KERNEL); + if (!kbuf) { + status = -ENOMEM; + goto err_unlock; + } + + /* Collect stragglers from last time (0 to 3 bytes) */ + memcpy(kbuf, drvdata->residue_buf, drvdata->residue_len); + + /* Fetch user data, appending to stragglers */ + if (copy_from_user(kbuf + drvdata->residue_len, buf, count)) { + status = -EFAULT; + goto error; + } + + /* Include stragglers in total bytes to be handled */ + count += drvdata->residue_len; + + /* First block contains a header */ + if (*ppos == 0 && count > 4) { + /* Look for sync word */ + for (i = 0; i < count - 4; i++) { + if (memcmp(kbuf + i, "\x66\x55\x99\xAA", 4) == 0) { + pr_debug("Found normal sync word\n"); + drvdata->endian_swap = 0; + break; + } + if (memcmp(kbuf + i, "\xAA\x99\x55\x66", 4) == 0) { + pr_debug("Found swapped sync word\n"); + drvdata->endian_swap = 1; + break; + } + } + /* Remove the header, aligning the data on word boundary */ + if (i != count - 4) { + count -= i; + memmove(kbuf, kbuf + i, count); + } + } + + /* Save stragglers for next time */ + drvdata->residue_len = count % 4; + count -= drvdata->residue_len; + memcpy(drvdata->residue_buf, kbuf + count, drvdata->residue_len); + + /* Fixup endianess of the data */ + if (drvdata->endian_swap) { + for (i = 0; i < count; i += 4) { + u32 *p = (u32 *)&kbuf[i]; + *p = swab32(*p); + } + } + + /* Enable DMA and error interrupts */ + xdevcfg_writereg(drvdata->base_address + XDCFG_INT_STS_OFFSET, + XDCFG_IXR_ALL_MASK); + + + xdevcfg_writereg(drvdata->base_address + XDCFG_INT_MASK_OFFSET, + (u32) (~(XDCFG_IXR_D_P_DONE_MASK | + XDCFG_IXR_ERROR_FLAGS_MASK))); + + drvdata->dma_done = 0; + drvdata->error_status = 0; + + /* Initiate DMA write command */ + if (count < 0x1000) + xdevcfg_writereg(drvdata->base_address + + XDCFG_DMA_SRC_ADDR_OFFSET, (u32)(dma_addr + 1)); + else + xdevcfg_writereg(drvdata->base_address + + XDCFG_DMA_SRC_ADDR_OFFSET, (u32) dma_addr); + + xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_DEST_ADDR_OFFSET, + (u32)XDCFG_DMA_INVALID_ADDRESS); + /* Convert number of bytes to number of words. */ + if (count % 4) + transfer_length = (count / 4 + 1); + else + transfer_length = count / 4; + xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_SRC_LEN_OFFSET, + transfer_length); + xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_DEST_LEN_OFFSET, 0); + + timeout = jiffies + msecs_to_jiffies(1000); + + while (!READ_ONCE(drvdata->dma_done)) { + if (time_after(jiffies, timeout)) { + status = -ETIMEDOUT; + goto error; + } + } + + if (READ_ONCE(drvdata->error_status)) + status = drvdata->error_status; + + /* Disable the DMA and error interrupts */ + intr_reg = xdevcfg_readreg(drvdata->base_address + + XDCFG_INT_MASK_OFFSET); + xdevcfg_writereg(drvdata->base_address + XDCFG_INT_MASK_OFFSET, + intr_reg | (XDCFG_IXR_D_P_DONE_MASK | + XDCFG_IXR_ERROR_FLAGS_MASK)); + + /* If we didn't write correctly, then bail out. */ + if (status) { + status = -EFAULT; + goto error; + } + + *ppos += user_count; + status = user_count; + +error: + dma_free_coherent(drvdata->dev, dma_len, kbuf, dma_addr); +err_unlock: + mutex_unlock(&drvdata->sem); +err_clk: + clk_disable(drvdata->clk); + return status; +} + + +/** + * xdevcfg_read - The is the driver read function. + * @file: Pointer to the file structure. + * @buf: Pointer to the bitstream location. + * @count: The number of bytes read. + * @ppos: Pointer to the offsetvalue + * returns: Success or error status. + */ +static ssize_t +xdevcfg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + u32 *kbuf; + int status; + unsigned long timeout; + dma_addr_t dma_addr; + struct xdevcfg_drvdata *drvdata = file->private_data; + u32 intr_reg; + + status = clk_enable(drvdata->clk); + if (status) + return status; + + status = mutex_lock_interruptible(&drvdata->sem); + if (status) + goto err_clk; + + /* Get new data from the ICAP, and return was requested. */ + kbuf = dma_alloc_coherent(drvdata->dev, count, &dma_addr, GFP_KERNEL); + if (!kbuf) { + status = -ENOMEM; + goto err_unlock; + } + + drvdata->dma_done = 0; + drvdata->error_status = 0; + + /* Enable DMA and error interrupts */ + xdevcfg_writereg(drvdata->base_address + XDCFG_INT_STS_OFFSET, + XDCFG_IXR_ALL_MASK); + + xdevcfg_writereg(drvdata->base_address + XDCFG_INT_MASK_OFFSET, + (u32) (~(XDCFG_IXR_D_P_DONE_MASK | + XDCFG_IXR_ERROR_FLAGS_MASK))); + /* Initiate DMA read command */ + xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_SRC_ADDR_OFFSET, + (u32)XDCFG_DMA_INVALID_ADDRESS); + xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_DEST_ADDR_OFFSET, + (u32)dma_addr); + xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_SRC_LEN_OFFSET, 0); + xdevcfg_writereg(drvdata->base_address + XDCFG_DMA_DEST_LEN_OFFSET, + count / 4); + + timeout = jiffies + msecs_to_jiffies(1000); + + while (!drvdata->dma_done) { + if (time_after(jiffies, timeout)) { + status = -ETIMEDOUT; + goto error; + } + } + + if (drvdata->error_status) + status = drvdata->error_status; + + /* Disable and clear DMA and error interrupts */ + intr_reg = xdevcfg_readreg(drvdata->base_address + + XDCFG_INT_MASK_OFFSET); + xdevcfg_writereg(drvdata->base_address + XDCFG_INT_MASK_OFFSET, + intr_reg | (XDCFG_IXR_D_P_DONE_MASK | + XDCFG_IXR_ERROR_FLAGS_MASK)); + + + /* If we didn't read correctly, then bail out. */ + if (status) { + status = -EFAULT; + goto error; + } + + /* If we fail to return the data to the user, then bail out. */ + if (copy_to_user(buf, kbuf, count)) { + status = -EFAULT; + goto error; + } + + status = count; +error: + dma_free_coherent(drvdata->dev, count, kbuf, dma_addr); +err_unlock: + mutex_unlock(&drvdata->sem); +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +static void xdevcfg_enable_partial(struct xdevcfg_drvdata *drvdata) +{ + u32 reg = xdevcfg_readreg(drvdata->base_address + XDCFG_CTRL_OFFSET); + + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + reg | XDCFG_CTRL_PCAP_PR_MASK); +} + +static void xdevcfg_disable_partial(struct xdevcfg_drvdata *drvdata) +{ + u32 reg = xdevcfg_readreg(drvdata->base_address + XDCFG_CTRL_OFFSET); + + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + reg & ~XDCFG_CTRL_PCAP_PR_MASK); +} + +/** + * xdevcfg_open - The is the driver open function. + * @inode: Pointer to the inode structure of this device. + * @file: Pointer to the file structure. + * returns: Success or error status. + */ +static int xdevcfg_open(struct inode *inode, struct file *file) +{ + struct xdevcfg_drvdata *drvdata; + int status; + + drvdata = container_of(inode->i_cdev, struct xdevcfg_drvdata, cdev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + status = mutex_lock_interruptible(&drvdata->sem); + if (status) + goto err_clk; + + if (drvdata->is_open) { + status = -EBUSY; + goto error; + } + + file->private_data = drvdata; + drvdata->is_open = 1; + drvdata->endian_swap = 0; + drvdata->residue_len = 0; + + /* + * If is_partial_bitstream is set, then PROG_B is not asserted + * (xdevcfg_reset_pl function) and also zynq_slcr_init_preload_fpga and + * zynq_slcr_init_postload_fpga functions are not invoked. + */ + if (drvdata->is_partial_bitstream) + xdevcfg_enable_partial(drvdata); + else + zynq_slcr_init_preload_fpga(); + + /* + * Only do the reset of the PL for Zynq as it causes problems on the + * EP107 and the issue is not understood, but not worth investigating + * as the emulation platform is very different than silicon and not a + * complete implementation. Also, do not reset if it is a partial + * bitstream. + */ + if ((!drvdata->ep107) && (!drvdata->is_partial_bitstream)) + xdevcfg_reset_pl(drvdata->base_address); + + xdevcfg_writereg(drvdata->base_address + XDCFG_INT_STS_OFFSET, + XDCFG_IXR_PCFG_DONE_MASK); + +error: + mutex_unlock(&drvdata->sem); +err_clk: + clk_disable(drvdata->clk); + return status; +} + +/** + * xdevcfg_release - The is the driver release function. + * @inode: Pointer to the inode structure of this device. + * @file: Pointer to the file structure. + * returns: Success. + */ +static int xdevcfg_release(struct inode *inode, struct file *file) +{ + struct xdevcfg_drvdata *drvdata = file->private_data; + + if (drvdata->is_partial_bitstream) + xdevcfg_disable_partial(drvdata); + else + zynq_slcr_init_postload_fpga(); + + if (drvdata->residue_len) + dev_info(drvdata->dev, "Did not transfer last %d bytes\n", + drvdata->residue_len); + + drvdata->is_open = 0; + + return 0; +} + +static const struct file_operations xdevcfg_fops = { + .owner = THIS_MODULE, + .write = xdevcfg_write, + .read = xdevcfg_read, + .open = xdevcfg_open, + .release = xdevcfg_release, +}; + +/* + * The following functions are the routines provided to the user to + * set/get the status bit value in the control/lock registers. + */ + +/** + * xdevcfg_set_dap_en - This function sets the DAP bits in the + * control register with the given value. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: negative error if the string could not be converted + * or the size of the buffer. + */ +static ssize_t xdevcfg_set_dap_en(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 ctrl_reg_status; + unsigned long flags; + unsigned long mask_bit; + int status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + ctrl_reg_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET); + spin_lock_irqsave(&drvdata->lock, flags); + + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + goto err_unlock; + + if (mask_bit > 7) { + status = -EINVAL; + goto err_unlock; + } + + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status | + (((u32)mask_bit) & XDCFG_CTRL_DAP_EN_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_unlock: + spin_unlock_irqrestore(&drvdata->lock, flags); + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_dap_en_status - The function returns the DAP_EN bits status in + * the control register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: Size of the buffer. + */ +static ssize_t xdevcfg_show_dap_en_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 dap_en_status; + int status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + dap_en_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET) & XDCFG_CTRL_DAP_EN_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", dap_en_status); + + return status; +} + +static DEVICE_ATTR(enable_dap, 0644, xdevcfg_show_dap_en_status, + xdevcfg_set_dap_en); + +/** + * xdevcfg_set_dbgen - This function sets the DBGEN bit in the + * control register with the given value. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or size + */ +static ssize_t xdevcfg_set_dbgen(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 ctrl_reg_status; + unsigned long flags; + unsigned long mask_bit; + int status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + ctrl_reg_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET); + + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + goto err_clk; + + if (mask_bit > 1) { + status = -EINVAL; + goto err_clk; + } + + spin_lock_irqsave(&drvdata->lock, flags); + + if (mask_bit) + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status | XDCFG_CTRL_DBGEN_MASK)); + else + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status & (~XDCFG_CTRL_DBGEN_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_dbgen_status - The function returns the DBGEN bit status in + * the control register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: Size of the buffer. + */ +static ssize_t xdevcfg_show_dbgen_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 dbgen_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + dbgen_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET) & XDCFG_CTRL_DBGEN_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", (dbgen_status >> 3)); + + return status; +} + +static DEVICE_ATTR(enable_dbg_in, 0644, xdevcfg_show_dbgen_status, + xdevcfg_set_dbgen); + +/** + * xdevcfg_set_niden - This function sets the NIDEN bit in the + * control register with the given value. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or size + */ +static ssize_t xdevcfg_set_niden(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 ctrl_reg_status; + unsigned long flags; + unsigned long mask_bit; + int status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + ctrl_reg_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET); + + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + goto err_clk; + + if (mask_bit > 1) { + status = -EINVAL; + goto err_clk; + } + + spin_lock_irqsave(&drvdata->lock, flags); + + if (mask_bit) + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status | XDCFG_CTRL_NIDEN_MASK)); + else + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status & (~XDCFG_CTRL_NIDEN_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_niden_status - The function returns the NIDEN bit status in + * the control register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: Size of the buffer. + */ +static ssize_t xdevcfg_show_niden_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 niden_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + niden_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET) & XDCFG_CTRL_NIDEN_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", (niden_status >> 4)); + + return status; +} + +static DEVICE_ATTR(enable_dbg_nonin, 0644, xdevcfg_show_niden_status, + xdevcfg_set_niden); + +/** + * xdevcfg_set_spiden - This function sets the SPIDEN bit in the + * control register with the given value. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or size + */ +static ssize_t xdevcfg_set_spiden(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 ctrl_reg_status; + unsigned long flags; + unsigned long mask_bit; + int status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + ctrl_reg_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET); + + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + goto err_clk; + + if (mask_bit > 1) { + status = -EINVAL; + goto err_clk; + } + + spin_lock_irqsave(&drvdata->lock, flags); + + if (mask_bit) + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status | XDCFG_CTRL_SPIDEN_MASK)); + else + + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status & (~XDCFG_CTRL_SPIDEN_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_spiden_status - The function returns the SPIDEN bit status in + * the control register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: Size of the buffer. + */ +static ssize_t xdevcfg_show_spiden_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 spiden_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + spiden_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET) & XDCFG_CTRL_SPIDEN_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", (spiden_status >> 5)); + + return status; +} + +static DEVICE_ATTR(enable_sec_dbg_in, 0644, xdevcfg_show_spiden_status, + xdevcfg_set_spiden); + +/** + * xdevcfg_set_spniden - This function sets the SPNIDEN bit in the + * control register with the given value. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or the size of buffer + */ +static ssize_t xdevcfg_set_spniden(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 ctrl_reg_status; + unsigned long flags; + unsigned long mask_bit; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + ctrl_reg_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET); + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + goto err_clk; + + if (mask_bit > 1) { + status = -EINVAL; + goto err_clk; + } + + spin_lock_irqsave(&drvdata->lock, flags); + + if (mask_bit) + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status | XDCFG_CTRL_SPNIDEN_MASK)); + else + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status & (~XDCFG_CTRL_SPNIDEN_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_spniden_status - The function returns the SPNIDEN bit status + * in the control register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: Size of the buffer. + */ +static ssize_t xdevcfg_show_spniden_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 spniden_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + spniden_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET) & XDCFG_CTRL_SPNIDEN_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", (spniden_status >> 6)); + + return status; +} + +static DEVICE_ATTR(enable_sec_dbg_nonin, 0644, xdevcfg_show_spniden_status, + xdevcfg_set_spniden); + +/** + * xdevcfg_set_seu - This function sets the SEU_EN bit in the + * control register with the given value + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or size + */ +static ssize_t xdevcfg_set_seu(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 ctrl_reg_status; + unsigned long flags; + unsigned long mask_bit; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + ctrl_reg_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET); + + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + goto err_clk; + + if (mask_bit > 1) { + status = -EINVAL; + goto err_clk; + } + + spin_lock_irqsave(&drvdata->lock, flags); + + if (mask_bit) + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status | XDCFG_CTRL_SEU_EN_MASK)); + else + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status & (~XDCFG_CTRL_SEU_EN_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_seu_status - The function returns the SEU_EN bit status + * in the control register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: size of the buffer. + */ +static ssize_t xdevcfg_show_seu_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 seu_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + seu_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET) & XDCFG_CTRL_SEU_EN_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", (seu_status > 8)); + + return status; +} + +static DEVICE_ATTR(enable_seu, 0644, xdevcfg_show_seu_status, xdevcfg_set_seu); + +/** + * xdevcfg_set_aes - This function sets the AES_EN bits in the + * control register with either all 1s or all 0s. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or size + * + * The user must send only one bit in the buffer to notify whether he wants to + * either set or reset these bits. + */ +static ssize_t xdevcfg_set_aes(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 ctrl_reg_status; + unsigned long flags; + unsigned long mask_bit; + int status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + ctrl_reg_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET); + + status = kstrtoul(buf, 10, &mask_bit); + + if (status < 0) + goto err_clk; + + if (mask_bit > 1) { + status = -EINVAL; + goto err_clk; + } + + + spin_lock_irqsave(&drvdata->lock, flags); + + if (mask_bit) + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status | + XDCFG_CTRL_PCFG_AES_EN_MASK | + XDCFG_CTRL_PCAP_RATE_EN_MASK)); + else + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, + (ctrl_reg_status & + ~(XDCFG_CTRL_PCFG_AES_EN_MASK | + XDCFG_CTRL_PCAP_RATE_EN_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_aes_status - The function returns the AES_EN bit status + * in the control register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: size of the buffer. + */ +static ssize_t xdevcfg_show_aes_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 aes_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + aes_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_CTRL_OFFSET) & XDCFG_CTRL_PCFG_AES_EN_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", (aes_status >> 9)); + + return status; +} + +static DEVICE_ATTR(enable_aes, 0644, xdevcfg_show_aes_status, xdevcfg_set_aes); + +/** + * xdevcfg_set_aes_en_lock - This function sets the LOCK_AES_EN bit in the + * lock register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or size + */ +static ssize_t xdevcfg_set_aes_en_lock(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 aes_en_lock_status; + unsigned long flags; + unsigned long mask_bit; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + aes_en_lock_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_LOCK_OFFSET); + + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + goto err_clk; + + if (mask_bit > 1) { + status = -EINVAL; + goto err_clk; + } + + spin_lock_irqsave(&drvdata->lock, flags); + + if (mask_bit) + xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET, + (aes_en_lock_status | XDCFG_LOCK_AES_EN_MASK)); + else + xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET, + (aes_en_lock_status & + (~XDCFG_LOCK_AES_EN_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_aes_en_lock_status - The function returns the LOCK_AES_EN bit + * status in the lock register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: size of the buffer. + */ +static ssize_t xdevcfg_show_aes_en_lock_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 aes_en_lock_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + aes_en_lock_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_LOCK_OFFSET) & XDCFG_LOCK_AES_EN_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", (aes_en_lock_status >> 3)); + + return status; +} + +static DEVICE_ATTR(aes_en_lock, 0644, xdevcfg_show_aes_en_lock_status, + xdevcfg_set_aes_en_lock); + +/** + * xdevcfg_set_seu_lock - This function sets the LOCK_SEU bit in the + * lock register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or size + */ +static ssize_t xdevcfg_set_seu_lock(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 seu_lock_status; + unsigned long flags; + unsigned long mask_bit; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + seu_lock_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_LOCK_OFFSET); + + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + goto err_clk; + + if (mask_bit > 1) { + status = -EINVAL; + goto err_clk; + } + + spin_lock_irqsave(&drvdata->lock, flags); + + if (mask_bit) + xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET, + (seu_lock_status | XDCFG_LOCK_SEU_MASK)); + else + xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET, + (seu_lock_status & (~XDCFG_LOCK_SEU_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_seu_lock_status - The function returns the LOCK_SEU bit + * status in the lock register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: size of the buffer. + */ +static ssize_t xdevcfg_show_seu_lock_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 seu_lock_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + seu_lock_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_LOCK_OFFSET) & XDCFG_LOCK_SEU_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", (seu_lock_status >> 2)); + + return status; +} + +static DEVICE_ATTR(seu_lock, 0644, xdevcfg_show_seu_lock_status, + xdevcfg_set_seu_lock); + +/** + * xdevcfg_set_dbg_lock - This function sets the LOCK_DBG bit in the + * lock register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or size + */ +static ssize_t xdevcfg_set_dbg_lock(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u32 lock_reg_status; + unsigned long flags; + unsigned long mask_bit; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + lock_reg_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_LOCK_OFFSET); + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + goto err_clk; + + if (mask_bit > 1) { + status = -EINVAL; + goto err_clk; + } + + spin_lock_irqsave(&drvdata->lock, flags); + + if (mask_bit) + xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET, + (lock_reg_status | XDCFG_LOCK_DBG_MASK)); + else + xdevcfg_writereg(drvdata->base_address + XDCFG_LOCK_OFFSET, + (lock_reg_status & (~XDCFG_LOCK_DBG_MASK))); + + spin_unlock_irqrestore(&drvdata->lock, flags); + + clk_disable(drvdata->clk); + + return size; + +err_clk: + clk_disable(drvdata->clk); + + return status; +} + +/** + * xdevcfg_show_dbg_lock_status - The function returns the LOCK_DBG bit + * status in the lock register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: size of the buffer. + */ +static ssize_t xdevcfg_show_dbg_lock_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 dbg_lock_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + dbg_lock_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_LOCK_OFFSET) & XDCFG_LOCK_DBG_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", dbg_lock_status); + + return status; +} + +static DEVICE_ATTR(dbg_lock, 0644, xdevcfg_show_dbg_lock_status, + xdevcfg_set_dbg_lock); + +/** + * xdevcfg_show_prog_done_status - The function returns the PROG_DONE bit + * status in the interrupt status register. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: size of the buffer. + */ +static ssize_t xdevcfg_show_prog_done_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 prog_done_status; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = clk_enable(drvdata->clk); + if (status) + return status; + + prog_done_status = xdevcfg_readreg(drvdata->base_address + + XDCFG_INT_STS_OFFSET) & XDCFG_IXR_PCFG_DONE_MASK; + + clk_disable(drvdata->clk); + + status = sprintf(buf, "%d\n", (prog_done_status >> 2)); + + return status; +} + +static DEVICE_ATTR(prog_done, 0644, xdevcfg_show_prog_done_status, + NULL); + +/** + * xdevcfg_set_is_partial_bitstream - This function sets the + * is_partial_bitstream variable. If is_partial_bitstream is set, + * then PROG_B is not asserted (xdevcfg_reset_pl) and also + * zynq_slcr_init_preload_fpga and zynq_slcr_init_postload_fpga functions + * are not invoked. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * @size: The number of bytes used from the buffer + * returns: -EINVAL if invalid parameter is sent or size + */ +static ssize_t xdevcfg_set_is_partial_bitstream(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned long mask_bit; + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = kstrtoul(buf, 10, &mask_bit); + + if (status) + return status; + + if (mask_bit > 1) + return -EINVAL; + + if (mask_bit) + drvdata->is_partial_bitstream = 1; + else + drvdata->is_partial_bitstream = 0; + + return size; +} + +/** + * xdevcfg_show_is_partial_bitstream_status - The function returns the + * value of is_partial_bitstream variable. + * @dev: Pointer to the device structure. + * @attr: Pointer to the device attribute structure. + * @buf: Pointer to the buffer location for the configuration + * data. + * returns: size of the buffer. + */ +static ssize_t xdevcfg_show_is_partial_bitstream_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t status; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + status = sprintf(buf, "%d\n", drvdata->is_partial_bitstream); + + return status; +} + +static DEVICE_ATTR(is_partial_bitstream, 0644, + xdevcfg_show_is_partial_bitstream_status, + xdevcfg_set_is_partial_bitstream); + +static const struct attribute *xdevcfg_attrs[] = { + &dev_attr_prog_done.attr, /* PCFG_DONE bit in Intr Status register */ + &dev_attr_dbg_lock.attr, /* Debug lock bit in Lock register */ + &dev_attr_seu_lock.attr, /* SEU lock bit in Lock register */ + &dev_attr_aes_en_lock.attr, /* AES EN lock bit in Lock register */ + &dev_attr_enable_aes.attr, /* AES EN bit in Control register */ + &dev_attr_enable_seu.attr, /* SEU EN bit in Control register */ + &dev_attr_enable_sec_dbg_nonin.attr, /*SPNIDEN bit in Control register*/ + &dev_attr_enable_sec_dbg_in.attr, /*SPIDEN bit in Control register */ + &dev_attr_enable_dbg_nonin.attr, /* NIDEN bit in Control register */ + &dev_attr_enable_dbg_in.attr, /* DBGEN bit in Control register */ + &dev_attr_enable_dap.attr, /* DAP_EN bits in Control register */ + &dev_attr_is_partial_bitstream.attr, /* Flag for partial bitstream */ + NULL, +}; + + +static const struct attribute_group xdevcfg_attr_group = { + .attrs = (struct attribute **) xdevcfg_attrs, +}; + +static ssize_t fclk_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fclk_data *pdata = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->enabled); +} + +static ssize_t fclk_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long enable; + int ret; + struct fclk_data *pdata = dev_get_drvdata(dev); + + ret = kstrtoul(buf, 0, &enable); + if (ret) + return -EINVAL; + + enable = !!enable; + if (enable == pdata->enabled) + return count; + + if (enable) + ret = clk_enable(pdata->clk); + else + clk_disable(pdata->clk); + + if (ret) + return ret; + + pdata->enabled = enable; + return count; +} + +static DEVICE_ATTR(enable, 0644, fclk_enable_show, fclk_enable_store); + +static ssize_t fclk_set_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fclk_data *pdata = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%lu\n", clk_get_rate(pdata->clk)); +} + +static ssize_t fclk_set_rate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + unsigned long rate; + struct fclk_data *pdata = dev_get_drvdata(dev); + + ret = kstrtoul(buf, 0, &rate); + if (ret) + return -EINVAL; + + rate = clk_round_rate(pdata->clk, rate); + ret = clk_set_rate(pdata->clk, rate); + + return ret ? ret : count; +} + +static DEVICE_ATTR(set_rate, 0644, fclk_set_rate_show, fclk_set_rate_store); + +static ssize_t fclk_round_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fclk_data *pdata = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%lu => %lu\n", pdata->rate_rnd, + clk_round_rate(pdata->clk, pdata->rate_rnd)); +} + +static ssize_t fclk_round_rate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + unsigned long rate; + struct fclk_data *pdata = dev_get_drvdata(dev); + + ret = kstrtoul(buf, 0, &rate); + if (ret) + return -EINVAL; + + pdata->rate_rnd = rate; + + return count; +} + +static DEVICE_ATTR(round_rate, 0644, fclk_round_rate_show, + fclk_round_rate_store); + +static const struct attribute *fclk_ctrl_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_set_rate.attr, + &dev_attr_round_rate.attr, + NULL, +}; + +static const struct attribute_group fclk_ctrl_attr_grp = { + .attrs = (struct attribute **)fclk_ctrl_attrs, +}; + +static ssize_t xdevcfg_fclk_export_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int i, ret; + struct device *subdev; + struct fclk_data *fdata; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + for (i = 0; i < NUMFCLKS; i++) { + if (!strncmp(buf, fclk_name[i], strlen(fclk_name[i]))) + break; + } + + if (i < NUMFCLKS && !drvdata->fclk_exported[i]) { + drvdata->fclk_exported[i] = 1; + subdev = device_create(drvdata->fclk_class, dev, MKDEV(0, 0), + NULL, fclk_name[i]); + if (IS_ERR(subdev)) + return PTR_ERR(subdev); + ret = clk_prepare(drvdata->fclk[i]); + if (ret) + return ret; + fdata = kzalloc(sizeof(*fdata), GFP_KERNEL); + if (!fdata) { + ret = -ENOMEM; + goto err_unprepare; + } + fdata->clk = drvdata->fclk[i]; + dev_set_drvdata(subdev, fdata); + ret = sysfs_create_group(&subdev->kobj, &fclk_ctrl_attr_grp); + if (ret) + goto err_free; + } else { + return -EINVAL; + } + + return size; + +err_free: + kfree(fdata); +err_unprepare: + clk_unprepare(drvdata->fclk[i]); + + return ret; +} + +static ssize_t xdevcfg_fclk_export_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + ssize_t count = 0; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + for (i = 0; i < NUMFCLKS; i++) { + if (!drvdata->fclk_exported[i]) + count += scnprintf(buf + count, PAGE_SIZE - count, + "%s\n", fclk_name[i]); + } + return count; +} + +static DEVICE_ATTR(fclk_export, 0644, xdevcfg_fclk_export_show, + xdevcfg_fclk_export_store); + +static int match_fclk(struct device *dev, const void *data) +{ + struct fclk_data *fdata = dev_get_drvdata(dev); + + return fdata->clk == data; +} + +static ssize_t xdevcfg_fclk_unexport_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int i; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + for (i = 0; i < NUMFCLKS; i++) { + if (!strncmp(buf, fclk_name[i], strlen(fclk_name[i]))) + break; + } + + if (i < NUMFCLKS && drvdata->fclk_exported[i]) { + struct fclk_data *fdata; + struct device *subdev; + + drvdata->fclk_exported[i] = 0; + subdev = class_find_device(drvdata->fclk_class, NULL, + drvdata->fclk[i], match_fclk); + fdata = dev_get_drvdata(subdev); + if (fdata->enabled) + clk_disable(fdata->clk); + clk_unprepare(fdata->clk); + kfree(fdata); + device_unregister(subdev); + put_device(subdev); + } else { + return -EINVAL; + } + + return size; +} + +static ssize_t xdevcfg_fclk_unexport_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + ssize_t count = 0; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + for (i = 0; i < NUMFCLKS; i++) { + if (drvdata->fclk_exported[i]) + count += scnprintf(buf + count, PAGE_SIZE - count, + "%s\n", fclk_name[i]); + } + return count; +} + +static DEVICE_ATTR(fclk_unexport, 0644, xdevcfg_fclk_unexport_show, + xdevcfg_fclk_unexport_store); + +static const struct attribute *fclk_exp_attrs[] = { + &dev_attr_fclk_export.attr, + &dev_attr_fclk_unexport.attr, + NULL, +}; + +static const struct attribute_group fclk_exp_attr_grp = { + .attrs = (struct attribute **)fclk_exp_attrs, +}; + +static void xdevcfg_fclk_init(struct device *dev) +{ + int i; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + for (i = 0; i < NUMFCLKS; i++) { + drvdata->fclk[i] = clk_get(dev, fclk_name[i]); + if (IS_ERR(drvdata->fclk[i])) { + dev_warn(dev, "fclk not found\n"); + return; + } + } + + drvdata->fclk_class = class_create(THIS_MODULE, "fclk"); + if (IS_ERR(drvdata->fclk_class)) { + dev_warn(dev, "failed to create fclk class\n"); + return; + } + + if (sysfs_create_group(&dev->kobj, &fclk_exp_attr_grp)) + dev_warn(dev, "failed to create sysfs entries\n"); +} + +static void xdevcfg_fclk_remove(struct device *dev) +{ + int i; + struct xdevcfg_drvdata *drvdata = dev_get_drvdata(dev); + + for (i = 0; i < NUMFCLKS; i++) { + if (drvdata->fclk_exported[i]) { + struct fclk_data *fdata; + struct device *subdev; + + drvdata->fclk_exported[i] = 0; + subdev = class_find_device(drvdata->fclk_class, NULL, + drvdata->fclk[i], match_fclk); + fdata = dev_get_drvdata(subdev); + if (fdata->enabled) + clk_disable(fdata->clk); + clk_unprepare(fdata->clk); + kfree(fdata); + device_unregister(subdev); + put_device(subdev); + + } + } + + class_destroy(drvdata->fclk_class); + sysfs_remove_group(&dev->kobj, &fclk_exp_attr_grp); + +} + +/** + * xdevcfg_drv_probe - Probe call for the device. + * + * @pdev: handle to the platform device structure. + * + * Returns: 0 on success, negative error otherwise. + * + * It does all the memory allocation and registration for the device. + */ +static int xdevcfg_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + struct xdevcfg_drvdata *drvdata; + dev_t devt; + int retval; + u32 ctrlreg; + struct device_node *np; + const void *prop; + int size; + struct device *dev; + + zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr"); + if (IS_ERR(zynq_slcr_regmap)) { + pr_err("%s: failed to find zynq-slcr\n", __func__); + return -ENODEV; + } + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drvdata->base_address = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(drvdata->base_address)) + return PTR_ERR(drvdata->base_address); + + drvdata->irq = platform_get_irq(pdev, 0); + retval = devm_request_irq(&pdev->dev, drvdata->irq, &xdevcfg_irq, + 0, dev_name(&pdev->dev), drvdata); + if (retval) { + dev_err(&pdev->dev, "No IRQ available"); + return retval; + } + + platform_set_drvdata(pdev, drvdata); + spin_lock_init(&drvdata->lock); + mutex_init(&drvdata->sem); + drvdata->is_open = 0; + drvdata->is_partial_bitstream = 0; + drvdata->dma_done = 0; + drvdata->error_status = 0; + dev_info(&pdev->dev, "ioremap %pa to %p\n", + &res->start, drvdata->base_address); + + drvdata->clk = devm_clk_get(&pdev->dev, "ref_clk"); + if (IS_ERR(drvdata->clk)) { + dev_err(&pdev->dev, "input clock not found\n"); + return PTR_ERR(drvdata->clk); + } + + retval = clk_prepare_enable(drvdata->clk); + if (retval) { + dev_err(&pdev->dev, "unable to enable clock\n"); + return retval; + } + + /* + * Figure out from the device tree if this is running on the EP107 + * emulation platform as it doesn't match the silicon exactly and the + * driver needs to work accordingly. + */ + np = of_get_next_parent(pdev->dev.of_node); + np = of_get_next_parent(np); + prop = of_get_property(np, "compatible", &size); + + if (prop != NULL) { + if ((strcmp((const char *)prop, "xlnx,zynq-ep107")) == 0) + drvdata->ep107 = 1; + else + drvdata->ep107 = 0; + } + + /* Unlock the device */ + xdevcfg_writereg(drvdata->base_address + XDCFG_UNLOCK_OFFSET, + 0x757BDF0D); + + /* + * Set the configuration register with the following options + * - Reset FPGA + * - Enable the PCAP interface + * - Set the throughput rate for maximum speed + * - Set the CPU in user mode + */ + ctrlreg = xdevcfg_readreg(drvdata->base_address + XDCFG_CTRL_OFFSET); + ctrlreg &= ~XDCFG_CTRL_PCAP_PR_MASK; + ctrlreg |= XDCFG_CTRL_PCFG_PROG_B_MASK | XDCFG_CTRL_PCAP_MODE_MASK; + xdevcfg_writereg(drvdata->base_address + XDCFG_CTRL_OFFSET, ctrlreg); + + /* Ensure internal PCAP loopback is disabled */ + ctrlreg = xdevcfg_readreg(drvdata->base_address + XDCFG_MCTRL_OFFSET); + xdevcfg_writereg(drvdata->base_address + XDCFG_MCTRL_OFFSET, + (~XDCFG_MCTRL_PCAP_LPBK_MASK & + ctrlreg)); + + + retval = alloc_chrdev_region(&devt, 0, XDEVCFG_DEVICES, DRIVER_NAME); + if (retval < 0) + goto failed5; + + drvdata->devt = devt; + + cdev_init(&drvdata->cdev, &xdevcfg_fops); + drvdata->cdev.owner = THIS_MODULE; + retval = cdev_add(&drvdata->cdev, devt, 1); + if (retval) { + dev_err(&pdev->dev, "cdev_add() failed\n"); + goto failed6; + } + + drvdata->class = class_create(THIS_MODULE, DRIVER_NAME); + if (IS_ERR(drvdata->class)) { + dev_err(&pdev->dev, "failed to create class\n"); + goto failed6; + } + + dev = device_create(drvdata->class, &pdev->dev, devt, drvdata, + DRIVER_NAME); + if (IS_ERR(dev)) { + dev_err(&pdev->dev, "unable to create device\n"); + goto failed7; + } + + drvdata->dev = &pdev->dev; + + /* create sysfs files for the device */ + retval = sysfs_create_group(&(pdev->dev.kobj), &xdevcfg_attr_group); + if (retval) { + dev_err(&pdev->dev, "Failed to create sysfs attr group\n"); + cdev_del(&drvdata->cdev); + goto failed8; + } + + xdevcfg_fclk_init(&pdev->dev); + + clk_disable(drvdata->clk); + + return 0; /* Success */ + +failed8: + device_destroy(drvdata->class, drvdata->devt); +failed7: + class_destroy(drvdata->class); +failed6: + /* Unregister char driver */ + unregister_chrdev_region(devt, XDEVCFG_DEVICES); +failed5: + clk_disable_unprepare(drvdata->clk); + + return retval; +} + +/** + * xdevcfg_drv_remove - Remove call for the device. + * + * @pdev: handle to the platform device structure. + * + * Returns: 0 or error status. + * + * Unregister the device after releasing the resources. + */ +static int xdevcfg_drv_remove(struct platform_device *pdev) +{ + struct xdevcfg_drvdata *drvdata; + + drvdata = platform_get_drvdata(pdev); + + if (!drvdata) + return -ENODEV; + + unregister_chrdev_region(drvdata->devt, XDEVCFG_DEVICES); + + sysfs_remove_group(&pdev->dev.kobj, &xdevcfg_attr_group); + + xdevcfg_fclk_remove(&pdev->dev); + device_destroy(drvdata->class, drvdata->devt); + class_destroy(drvdata->class); + cdev_del(&drvdata->cdev); + clk_unprepare(drvdata->clk); + + return 0; /* Success */ +} + +static const struct of_device_id xdevcfg_of_match[] = { + { .compatible = "xlnx,zynq-devcfg-1.0", }, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, xdevcfg_of_match); + +/* Driver Structure */ +static struct platform_driver xdevcfg_platform_driver = { + .probe = xdevcfg_drv_probe, + .remove = xdevcfg_drv_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = xdevcfg_of_match, + }, +}; + +module_platform_driver(xdevcfg_platform_driver); + +MODULE_AUTHOR("Xilinx, Inc"); +MODULE_DESCRIPTION("Xilinx Device Config Driver"); +MODULE_LICENSE("GPL"); diff --git a/patches/xilinx_zynq_defconfig b/patches/xilinx_zynq_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..72063935e48964cb82df90dff70144db9743ac8f --- /dev/null +++ b/patches/xilinx_zynq_defconfig @@ -0,0 +1,396 @@ +CONFIG_LOCALVERSION="-xilinx" +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_CGROUPS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_BPF_SYSCALL=y +# CONFIG_BUG is not set +CONFIG_EMBEDDED=y +CONFIG_PERF_EVENTS=y +CONFIG_SLAB=y +CONFIG_ARCH_VEXPRESS=y +CONFIG_ARCH_ZYNQ=y +CONFIG_PL310_ERRATA_588369=y +CONFIG_PL310_ERRATA_727915=y +CONFIG_PL310_ERRATA_769419=y +CONFIG_ARM_ERRATA_754322=y +CONFIG_ARM_ERRATA_754327=y +CONFIG_ARM_ERRATA_764369=y +CONFIG_ARM_ERRATA_775420=y +CONFIG_SMP=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_BIG_LITTLE=y +CONFIG_BL_SWITCHER=y +CONFIG_HIGHMEM=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_ARM_ZYNQ_CPUIDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_COMPACTION is not set +CONFIG_CMA=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=m +CONFIG_SYN_COOKIES=y +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +CONFIG_CAN=y +CONFIG_CAN_XILINXCAN=y +CONFIG_UEVENT_HELPER=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_PCI=y +CONFIG_PCI_MSI=y +CONFIG_PCIE_XILINX=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_PL353=y +CONFIG_MTD_SPI_NOR=y +CONFIG_OF_OVERLAY=y +CONFIG_OF_CONFIGFS=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_XILINX_TRAFGEN=y +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_NETDEVICES=y +CONFIG_MACB=y +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_E1000E=y +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +CONFIG_R8169=y +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_XILINX_EMACLITE=y +CONFIG_XILINX_AXI_EMAC=y +CONFIG_MARVELL_PHY=y +CONFIG_VITESSE_PHY=y +CONFIG_INTEL_XWAY_PHY=y +CONFIG_MDIO_BITBANG=y +CONFIG_INPUT_SPARSEKMAP=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_GPIO_POLLED=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_XILINX_PS_UART=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +# CONFIG_HW_RANDOM is not set +CONFIG_XILINX_DEVCFG=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_CADENCE=y +CONFIG_SPI=y +CONFIG_SPI_CADENCE=y +CONFIG_SPI_XILINX=y +CONFIG_SPI_ZYNQ_QSPI=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_XILINX=y +CONFIG_GPIO_ZYNQ=y +CONFIG_PMBUS=y +CONFIG_SENSORS_UCD9000=y +CONFIG_SENSORS_UCD9200=y +CONFIG_BMP280=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_XILINX_WATCHDOG=y +CONFIG_CADENCE_WATCHDOG=y +CONFIG_REGULATOR=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_XILINX=y +CONFIG_VIDEO_XILINX_CFA=y +CONFIG_VIDEO_XILINX_CRESAMPLE=y +CONFIG_VIDEO_XILINX_REMAPPER=y +CONFIG_VIDEO_XILINX_RGB2YUV=y +CONFIG_VIDEO_XILINX_SCALER=y +CONFIG_VIDEO_XILINX_SWITCH=y +CONFIG_VIDEO_XILINX_TPG=y +CONFIG_VIDEO_ADV7604=y +CONFIG_DRM=y +CONFIG_DRM_XLNX=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_ADI=y +CONFIG_SND_SOC_ADI_AXI_I2S=y +CONFIG_SND_SOC_ADI_AXI_SPDIF=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_STORAGE=y +CONFIG_USB_UAS=m +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_ULPI=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_XILINX=y +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_ZERO=m +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_TRANSIENT=y +CONFIG_LEDS_TRIGGER_CAMERA=y +CONFIG_EDAC=y +CONFIG_EDAC_SYNOPSYS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PCF8563=y +CONFIG_DMADEVICES=y +CONFIG_PL330_DMA=y +CONFIG_XILINX_DMA=y +CONFIG_UIO=y +CONFIG_UIO_PDRV_GENIRQ=y +CONFIG_UIO_XILINX_APM=y +CONFIG_COMMON_CLK_SI570=y +CONFIG_REMOTEPROC=y +CONFIG_ZYNQ_REMOTEPROC=m +CONFIG_MEMORY=y +CONFIG_IIO=y +CONFIG_XILINX_XADC=y +CONFIG_XILINX_INTC=y +CONFIG_RAS=y +CONFIG_EXT3_FS=y +# CONFIG_DNOTIFY is not set +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_SUMMARY=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_DMA_CMA=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_FTRACE is not set +CONFIG_OVERLAY_FS=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SYN_COOKIES=y +CONFIG_WIRELESS=y +CONFIG_CFG80211=m +CONFIG_MAC80211=m +CONFIG_RTL8188EU=m +CONFIG_RTL8192CU=m +CONFIG_RT2X00=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT3573=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +CONFIG_RT2800USB_UNKNOWN=y +CONFIG_MT7601U=m +CONFIG_ATH9K_HTC=m +CONFIG_B43=m +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_USB=y +CONFIG_PPP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_SYNC_TTY=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_MBIM=m +CONFIG_USB_NET_HUAWEI_CDC_NCM=m +CONFIG_USB_NET_QMI_WWAN=m +CONFIG_NAMESPACES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_NAT_IPV4=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_MANGLE=m +CONFIG_MACVLAN=m +CONFIG_TUN=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +CONFIG_FB_DEFERRED_IO=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_UDL=m +CONFIG_FUSE_FS=m +CONFIG_SPI_SPIDEV=m +CONFIG_PPS_CLIENT_GPIO=m +CONFIG_PPS_CLIENT_LDISC=m +CONFIG_USB_GPIO_VBUS=y +CONFIG_USB_ULPI_BUS=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_SND_USB_AUDIO=m +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_CIFS=m +CONFIG_CIFS_STATS2=y +CONFIG_CIFS_UPCALL=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_ACL=y +CONFIG_CIFS_DFS_UPCALL=y +CONFIG_STAGING=y +CONFIG_FB_TFT=m +CONFIG_FB_TFT_AGM1264K_FL=m +CONFIG_FB_TFT_BD663474=m +CONFIG_FB_TFT_HX8340BN=m +CONFIG_FB_TFT_HX8347D=m +CONFIG_FB_TFT_HX8353D=m +CONFIG_FB_TFT_HX8357D=m +CONFIG_FB_TFT_ILI9163=m +CONFIG_FB_TFT_ILI9320=m +CONFIG_FB_TFT_ILI9325=m +CONFIG_FB_TFT_ILI9340=m +CONFIG_FB_TFT_ILI9341=m +CONFIG_FB_TFT_ILI9481=m +CONFIG_FB_TFT_ILI9486=m +CONFIG_FB_TFT_PCD8544=m +CONFIG_FB_TFT_RA8875=m +CONFIG_FB_TFT_S6D02A1=m +CONFIG_FB_TFT_S6D1121=m +CONFIG_FB_TFT_SSD1289=m +CONFIG_FB_TFT_SSD1306=m +CONFIG_FB_TFT_SSD1331=m +CONFIG_FB_TFT_SSD1351=m +CONFIG_FB_TFT_ST7735R=m +CONFIG_FB_TFT_ST7789V=m +CONFIG_FB_TFT_TINYLCD=m +CONFIG_FB_TFT_TLS8204=m +CONFIG_FB_TFT_UC1611=m +CONFIG_FB_TFT_UC1701=m +CONFIG_FB_TFT_UPD161704=m +CONFIG_FB_TFT_WATTEROTT=m +CONFIG_FB_FLEX=m +CONFIG_FB_TFT_FBTFT_DEVICE=m +CONFIG_USB_ACM=m +CONFIG_USB_TMC=m +CONFIG_VIDEOBUF2_VMALLOC=m +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=8 +CONFIG_U_SERIAL_CONSOLE=y +CONFIG_USB_F_ACM=m +CONFIG_USB_U_SERIAL=m +CONFIG_USB_U_ETHER=m +CONFIG_USB_F_SERIAL=m +CONFIG_USB_F_OBEX=m +CONFIG_USB_F_NCM=m +CONFIG_USB_F_ECM=m +CONFIG_USB_F_EEM=m +CONFIG_USB_F_SUBSET=m +CONFIG_USB_F_RNDIS=m +CONFIG_USB_F_FS=m +CONFIG_USB_F_UAC1=m +CONFIG_USB_F_UAC2=m +CONFIG_USB_F_UVC=m +CONFIG_USB_F_MIDI=m +CONFIG_USB_F_HID=m +CONFIG_USB_F_PRINTER=m +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_F_LB_SS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_PRINTER=y +CONFIG_USB_AUDIO=m +CONFIG_GADGET_UAC1=y +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +CONFIG_USB_ETH_EEM=y +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_MULTI_RNDIS=y +CONFIG_USB_G_MULTI_CDC=y +CONFIG_USB_G_HID=m +CONFIG_USB_G_WEBCAM=m diff --git a/patches/zynq-red-pitaya.dts b/patches/zynq-red-pitaya.dts new file mode 100644 index 0000000000000000000000000000000000000000..1c6ceb9dfd064193c954d9241b118a3c4f97370b --- /dev/null +++ b/patches/zynq-red-pitaya.dts @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 - 2015 Xilinx + * Copyright (C) 2012 National Instruments Corp. + */ +/dts-v1/; +#include "zynq-7000.dtsi" + +/ { + model = "Red Pitaya Board"; + compatible = "xlnx,zynq-red-pitaya", "xlnx,zynq-7000"; + + aliases { + ethernet0 = &gem0; + serial0 = &uart0; + mmc0 = &sdhci0; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x20000000>; + }; + + chosen { + bootargs = ""; + stdout-path = "serial0:115200n8"; + }; + + usb_phy0: phy0 { + #phy-cells = <0>; + compatible = "usb-nop-xceiv"; + }; +}; + +&clkc { + ps-clk-frequency = <33333333>; +}; + +&gem0 { + status = "okay"; + phy-mode = "rgmii-id"; + phy-handle = <ðernet_phy>; + + ethernet_phy: ethernet-phy@1 { + reg = <1>; + device_type = "ethernet-phy"; + }; +}; + +&i2c0 { + status = "okay"; + clock-frequency = <400000>; + eeprom@50 { + compatible = "24c64"; + reg = <0x50>; + }; + bmp180@77{ + compatible = "bosch,bmp280"; + reg = <0x77>; + }; +}; + +&sdhci0 { + u-boot,dm-pre-reloc; + status = "okay"; +}; + +&uart0 { + u-boot,dm-pre-reloc; + status = "okay"; +}; + +&usb0 { + status = "okay"; + dr_mode = "host"; + usb-phy = <&usb_phy0>; +}; diff --git a/patches/zynq_ocm.c b/patches/zynq_ocm.c new file mode 100644 index 0000000000000000000000000000000000000000..324b7c125bf5681ecff2bb5f6f090bcd293ffc44 --- /dev/null +++ b/patches/zynq_ocm.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2013 Xilinx + * + * Based on "Generic on-chip SRAM allocation driver" + * + * Copyright (C) 2012 Philipp Zabel, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/genalloc.h> + +#include "common.h" + +#define ZYNQ_OCM_HIGHADDR 0xfffc0000 +#define ZYNQ_OCM_LOWADDR 0x0 +#define ZYNQ_OCM_BLOCK_SIZE 0x10000 +#define ZYNQ_OCM_BLOCKS 4 +#define ZYNQ_OCM_GRANULARITY 32 + +#define ZYNQ_OCM_PARITY_CTRL 0x0 +#define ZYNQ_OCM_PARITY_ENABLE 0x1e + +#define ZYNQ_OCM_PARITY_ERRADDRESS 0x4 + +#define ZYNQ_OCM_IRQ_STS 0x8 +#define ZYNQ_OCM_IRQ_STS_ERR_MASK 0x7 + +struct zynq_ocm_dev { + void __iomem *base; + int irq; + struct gen_pool *pool; + struct resource res[ZYNQ_OCM_BLOCKS]; +}; + +/** + * zynq_ocm_irq_handler - Interrupt service routine of the OCM controller + * @irq: IRQ number + * @data: Pointer to the zynq_ocm_dev structure + * + * Return: IRQ_HANDLED when handled; IRQ_NONE otherwise. + */ +static irqreturn_t zynq_ocm_irq_handler(int irq, void *data) +{ + u32 sts; + u32 err_addr; + struct zynq_ocm_dev *zynq_ocm = data; + + /* check status */ + sts = readl(zynq_ocm->base + ZYNQ_OCM_IRQ_STS); + if (sts & ZYNQ_OCM_IRQ_STS_ERR_MASK) { + /* check error address */ + err_addr = readl(zynq_ocm->base + ZYNQ_OCM_PARITY_ERRADDRESS); + pr_err("%s: OCM err intr generated at 0x%04x (stat: 0x%08x).", + __func__, err_addr, sts & ZYNQ_OCM_IRQ_STS_ERR_MASK); + return IRQ_HANDLED; + } + pr_warn("%s: Interrupt generated by OCM, but no error is found.", + __func__); + + return IRQ_NONE; +} + +/** + * zynq_ocm_probe - Probe method for the OCM driver + * @pdev: Pointer to the platform_device structure + * + * This function initializes the driver data structures and the hardware. + * + * Return: 0 on success and error value on failure + */ +static int zynq_ocm_probe(struct platform_device *pdev) +{ + int ret; + struct zynq_ocm_dev *zynq_ocm; + u32 i, ocm_config, curr; + struct resource *res; + + ocm_config = zynq_slcr_get_ocm_config(); + + zynq_ocm = devm_kzalloc(&pdev->dev, sizeof(*zynq_ocm), GFP_KERNEL); + if (!zynq_ocm) + return -ENOMEM; + + zynq_ocm->pool = devm_gen_pool_create(&pdev->dev, + ilog2(ZYNQ_OCM_GRANULARITY), + NUMA_NO_NODE, NULL); + if (!zynq_ocm->pool) + return -ENOMEM; + + curr = 0; /* For storing current struct resource for OCM */ + for (i = 0; i < ZYNQ_OCM_BLOCKS; i++) { + u32 base, start, end; + + /* Setup base address for 64kB OCM block */ + if (ocm_config & BIT(i)) + base = ZYNQ_OCM_HIGHADDR; + else + base = ZYNQ_OCM_LOWADDR; + + /* Calculate start and end block addresses */ + start = i * ZYNQ_OCM_BLOCK_SIZE + base; + end = start + (ZYNQ_OCM_BLOCK_SIZE - 1); + + /* Concatenate OCM blocks together to get bigger pool */ + if (i > 0 && start == (zynq_ocm->res[curr - 1].end + 1)) { + zynq_ocm->res[curr - 1].end = end; + } else { +#ifdef CONFIG_SMP + /* + * OCM block if placed at 0x0 has special meaning + * for SMP because jump trampoline is added there. + * Ensure that this address won't be allocated. + */ + if (!base) { + u32 trampoline_code_size = + &zynq_secondary_trampoline_end - + &zynq_secondary_trampoline; + dev_dbg(&pdev->dev, + "Allocate reset vector table %dB\n", + trampoline_code_size); + /* postpone start offset */ + start += trampoline_code_size; + } +#endif + /* First resource is always initialized */ + zynq_ocm->res[curr].start = start; + zynq_ocm->res[curr].end = end; + zynq_ocm->res[curr].flags = IORESOURCE_MEM; + curr++; /* Increment curr value */ + } + dev_dbg(&pdev->dev, "OCM block %d, start %x, end %x\n", + i, start, end); + } + + /* + * Separate pool allocation from OCM block detection to ensure + * the biggest possible pool. + */ + for (i = 0; i < ZYNQ_OCM_BLOCKS; i++) { + unsigned long size; + void __iomem *virt_base; + + /* Skip all zero size resources */ + if (zynq_ocm->res[i].end == 0) + break; + dev_dbg(&pdev->dev, "OCM resources %d, start %x, end %x\n", + i, zynq_ocm->res[i].start, zynq_ocm->res[i].end); + size = resource_size(&zynq_ocm->res[i]); + virt_base = devm_ioremap_resource(&pdev->dev, + &zynq_ocm->res[i]); + if (IS_ERR(virt_base)) + return PTR_ERR(virt_base); + + ret = gen_pool_add_virt(zynq_ocm->pool, + (unsigned long)virt_base, + zynq_ocm->res[i].start, size, -1); + if (ret < 0) { + dev_err(&pdev->dev, "Gen pool failed\n"); + return ret; + } + dev_info(&pdev->dev, "ZYNQ OCM pool: %ld KiB @ 0x%p\n", + size / 1024, virt_base); + } + + /* Get OCM config space */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + zynq_ocm->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(zynq_ocm->base)) + return PTR_ERR(zynq_ocm->base); + + /* Allocate OCM parity IRQ */ + zynq_ocm->irq = platform_get_irq(pdev, 0); + if (zynq_ocm->irq < 0) { + dev_err(&pdev->dev, "irq resource not found\n"); + return zynq_ocm->irq; + } + ret = devm_request_irq(&pdev->dev, zynq_ocm->irq, zynq_ocm_irq_handler, + 0, pdev->name, zynq_ocm); + if (ret != 0) { + dev_err(&pdev->dev, "request_irq failed\n"); + return ret; + } + + /* Enable parity errors */ + writel(ZYNQ_OCM_PARITY_ENABLE, zynq_ocm->base + ZYNQ_OCM_PARITY_CTRL); + + platform_set_drvdata(pdev, zynq_ocm); + + return 0; +} + +/** + * zynq_ocm_remove - Remove method for the OCM driver + * @pdev: Pointer to the platform_device structure + * + * This function is called if a device is physically removed from the system or + * if the driver module is being unloaded. It frees all resources allocated to + * the device. + * + * Return: 0 on success and error value on failure + */ +static int zynq_ocm_remove(struct platform_device *pdev) +{ + struct zynq_ocm_dev *zynq_ocm = platform_get_drvdata(pdev); + + if (gen_pool_avail(zynq_ocm->pool) < gen_pool_size(zynq_ocm->pool)) + dev_dbg(&pdev->dev, "removed while SRAM allocated\n"); + + return 0; +} + +static struct of_device_id zynq_ocm_dt_ids[] = { + { .compatible = "xlnx,zynq-ocmc-1.0" }, + { /* end of table */ } +}; + +static struct platform_driver zynq_ocm_driver = { + .driver = { + .name = "zynq-ocm", + .of_match_table = zynq_ocm_dt_ids, + }, + .probe = zynq_ocm_probe, + .remove = zynq_ocm_remove, +}; + +static int __init zynq_ocm_init(void) +{ + return platform_driver_register(&zynq_ocm_driver); +} + +arch_initcall(zynq_ocm_init); diff --git a/patches/zynq_red_pitaya.h b/patches/zynq_red_pitaya.h new file mode 100644 index 0000000000000000000000000000000000000000..2be3bd17d46527f12d4f8ec1e060f24d4272dac9 --- /dev/null +++ b/patches/zynq_red_pitaya.h @@ -0,0 +1,16 @@ +#ifndef __CONFIG_ZYNQ_RED_PITAYA_H +#define __CONFIG_ZYNQ_RED_PITAYA_H + +#include <configs/zynq-common.h> + +#undef CONFIG_SYS_I2C_EEPROM_ADDR_LEN +#undef CONFIG_SYS_I2C_EEPROM_ADDR +#undef CONFIG_SYS_EEPROM_PAGE_WRITE_BITS +#undef CONFIG_SYS_EEPROM_SIZE + +#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 2 +#define CONFIG_SYS_I2C_EEPROM_ADDR 0x50 +#define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 5 +#define CONFIG_SYS_EEPROM_SIZE 8192 /* Bytes */ + +#endif /* __CONFIG_ZYNQ_RED_PITAYA_H */ diff --git a/patches/zynq_red_pitaya_defconfig b/patches/zynq_red_pitaya_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..6b6197b2750e68e1107a02975ebe6dad68af6955 --- /dev/null +++ b/patches/zynq_red_pitaya_defconfig @@ -0,0 +1,24 @@ +CONFIG_ARM=y +CONFIG_ARCH_ZYNQ=y +CONFIG_SYS_TEXT_BASE=0x4000000 +CONFIG_SYS_CUSTOM_LDSCRIPT=y +CONFIG_SYS_LDSCRIPT="arch/arm/mach-zynq/u-boot.lds" +CONFIG_DEFAULT_DEVICE_TREE="zynq-red-pitaya" +CONFIG_DISTRO_DEFAULTS=y +CONFIG_CMD_MMC=y +CONFIG_ENV_OVERWRITE=y +CONFIG_ENV_IS_IN_EEPROM=y +CONFIG_ENV_OFFSET=0x1800 +CONFIG_ENV_SIZE=0x400 +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_MISC=y +CONFIG_I2C_EEPROM=y +CONFIG_DM_I2C=y +CONFIG_SYS_I2C_CADENCE=y +CONFIG_SYS_I2C_EEPROM_ADDR=0x50 +CONFIG_SYS_I2C_EEPROM_ADDR_LEN=2 +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ZYNQ=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_ZYNQ_GEM=y +CONFIG_ZYNQ_SERIAL=y