From f9b2c40b742cb9b26875407628bbe87cf5d5afc9 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 20 Nov 2022 16:21:08 +0800 Subject: [PATCH 01/41] dt-bindings: serial: add bindings doc for Bouffalolab uart driver Add bindings doc for Bouffalolab UART Driver Signed-off-by: Jisheng Zhang --- .../bindings/serial/bouffalolab,uart.yaml | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/bouffalolab,uart.yaml diff --git a/Documentation/devicetree/bindings/serial/bouffalolab,uart.yaml b/Documentation/devicetree/bindings/serial/bouffalolab,uart.yaml new file mode 100644 index 00000000000000..6cef956d33d25e --- /dev/null +++ b/Documentation/devicetree/bindings/serial/bouffalolab,uart.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2022 Jisheng Zhang +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/serial/bouffalolab,uart.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Bouffalolab UART Controller + +maintainers: + - Jisheng Zhang + +allOf: + - $ref: serial.yaml# + +properties: + compatible: + const: bouffalolab,uart + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + #include + aliases { + serial0 = &uart0; + }; + + uart0: serial@30002000 { + compatible = "bouffalolab,uart"; + reg = <0x30002000 0x1000>; + interrupts = <53 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&xtal>; + }; +... From 2c19a5bf97717be85036e886250502c2e35364ec Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 20 Nov 2022 16:21:11 +0800 Subject: [PATCH 02/41] riscv: add the Bouffalolab SoC family Kconfig option The Bouffalolab bl808 SoC contains three riscv CPUs, namely M0, D0 and LP. The D0 is 64bit RISC-V GC compatible, so can run linux. Signed-off-by: Jisheng Zhang --- arch/riscv/Kconfig.socs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 4b6deb2715f1c4..a68ab2172230e5 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -1,5 +1,11 @@ menu "SoC selection" +config SOC_BOUFFALOLAB + bool "Bouffalolab SoCs" + select SIFIVE_PLIC + help + This enables support for Bouffalolab SoC platforms. + config SOC_MICROCHIP_POLARFIRE bool "Microchip PolarFire SoCs" select MCHP_CLK_MPFS From 77c6c7a056e0e02ccd5b182bf263ea27a71cc959 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 20 Nov 2022 16:21:12 +0800 Subject: [PATCH 03/41] riscv: dts: bouffalolab: add the bl808 SoC base device tree Add a baisc dtsi for the bouffalolab bl808 SoC. Signed-off-by: Jisheng Zhang --- arch/riscv/boot/dts/Makefile | 1 + arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 74 ++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 arch/riscv/boot/dts/bouffalolab/bl808.dtsi diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile index b0ff5fbabb0c9a..2d4376810bcc75 100644 --- a/arch/riscv/boot/dts/Makefile +++ b/arch/riscv/boot/dts/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +subdir-y += bouffalolab subdir-y += sifive subdir-y += starfive subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi new file mode 100644 index 00000000000000..c98ebb14ee10a6 --- /dev/null +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +/* + * Copyright (C) 2022 Jisheng Zhang + */ + +#include + +/ { + compatible = "bouffalolab,bl808"; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + timebase-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "thead,c906", "riscv"; + device_type = "cpu"; + reg = <0>; + d-cache-block-size = <64>; + d-cache-sets = <256>; + d-cache-size = <32768>; + i-cache-block-size = <64>; + i-cache-sets = <128>; + i-cache-size = <32768>; + mmu-type = "riscv,sv39"; + riscv,isa = "rv64imafdc"; + + cpu0_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + }; + + xtal: xtal-clk { + compatible = "fixed-clock"; + clock-frequency = <40000000>; + clock-output-names = "xtal"; + #clock-cells = <0>; + }; + + soc { + compatible = "simple-bus"; + ranges; + interrupt-parent = <&plic>; + dma-noncoherent; + #address-cells = <1>; + #size-cells = <1>; + + uart0: serial@30002000 { + compatible = "bouffalolab,uart"; + reg = <0x30002000 0x1000>; + interrupts = <20 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&xtal>; + status = "disabled"; + }; + + plic: interrupt-controller@e0000000 { + compatible = "thead,c900-plic"; + reg = <0xe0000000 0x4000000>; + interrupts-extended = <&cpu0_intc 0xffffffff>, + <&cpu0_intc 9>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + riscv,ndev = <64>; + }; + }; +}; From cddacea7c8a17fdefacde5abd9974678e6f51058 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 20 Nov 2022 16:21:13 +0800 Subject: [PATCH 04/41] riscv: dts: bouffalolab: add Sipeed M1S dock devicetree Sipeed manufactures a M1S system-on-module and dock board, add basic support for them. Signed-off-by: Jisheng Zhang --- arch/riscv/boot/dts/bouffalolab/Makefile | 2 ++ .../boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 arch/riscv/boot/dts/bouffalolab/Makefile create mode 100644 arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts diff --git a/arch/riscv/boot/dts/bouffalolab/Makefile b/arch/riscv/boot/dts/bouffalolab/Makefile new file mode 100644 index 00000000000000..42e17e1a97bd17 --- /dev/null +++ b/arch/riscv/boot/dts/bouffalolab/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_SOC_BOUFFALOLAB) += bl808-sipeed-m1s.dtb diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts new file mode 100644 index 00000000000000..64421fb2ad67d9 --- /dev/null +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +/* + * Copyright (C) 2022 Jisheng Zhang + */ + +/dts-v1/; + +#include "bl808.dtsi" + +/ { + model = "Sipeed M1S"; + compatible = "sipeed,m1s", "bouffalolab,bl808"; + + aliases { + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:2000000n8"; + }; + + memory@50000000 { + device_type = "memory"; + reg = <0x50000000 0x04000000>; + }; +}; + +&uart0 { + status = "okay"; +}; From 69f07729f4f5bd7aa4a9095976a45e5dd8c9b335 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 20 Nov 2022 16:21:14 +0800 Subject: [PATCH 05/41] MAINTAINERS: add myself as Bouffalolab SoC entry maintainer I want to maintain this Bouffalolab riscv SoC entry from now on. Signed-off-by: Jisheng Zhang --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 135d93368d36ed..a8ee2a529ecb54 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17975,6 +17975,12 @@ F: arch/riscv/ N: riscv K: riscv +RISC-V BOUFFALOLAB SOC SUPPORT +M: Jisheng Zhang +L: linux-riscv at lists.infradead.org +S: Maintained +F: arch/riscv/boot/dts/bouffalolab/ + RISC-V MICROCHIP FPGA SUPPORT M: Conor Dooley M: Daire McNamara From 398fa5f8dd0b26e2ef59c38db908ce4bae033017 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Sun, 8 Jan 2023 01:35:45 -0800 Subject: [PATCH 06/41] riscv: bl808: Add defconfig --- arch/riscv/configs/bl808_defconfig | 143 +++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 arch/riscv/configs/bl808_defconfig diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig new file mode 100644 index 00000000000000..8a441a64be1a85 --- /dev/null +++ b/arch/riscv/configs/bl808_defconfig @@ -0,0 +1,143 @@ +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y +CONFIG_USER_NS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_PERF_EVENTS=y +CONFIG_SOC_BOUFFALOLAB=y +CONFIG_SOC_VIRT=y +CONFIG_ERRATA_THEAD=y +CONFIG_SMP=y +CONFIG_NR_CPUS=8 +CONFIG_RISCV_SBI_V01=y +# CONFIG_COMPAT is not set +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CMDLINE_PARTITION=y +CONFIG_PAGE_REPORTING=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_DNS_RESOLVER=y +CONFIG_NETLINK_DIAG=y +# CONFIG_WIRELESS is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK_RO=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_ROM=y +CONFIG_MTD_ABSENT=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_PHYSMAP_VERSATILE=y +CONFIG_MTD_PHYSMAP_GEMINI=y +CONFIG_MTD_PLATRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_VIRTIO_BLK=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_VIRTIO=y +CONFIG_NETDEVICES=y +CONFIG_VIRTIO_NET=y +# CONFIG_ETHERNET is not set +CONFIG_MDIO_DEVICE=y +# CONFIG_WLAN is not set +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=y +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_EARLYCON_RISCV_SBI=y +CONFIG_SERIAL_BFLB=y +CONFIG_SERIAL_BFLB_CONSOLE=y +CONFIG_SERIAL_SIFIVE=y +CONFIG_SERIAL_SIFIVE_CONSOLE=y +CONFIG_HVC_RISCV_SBI=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_XILINX=y +CONFIG_I2C_SLAVE=y +CONFIG_I2C_SLAVE_EEPROM=y +CONFIG_I2C_DEBUG_CORE=y +CONFIG_I2C_DEBUG_ALGO=y +CONFIG_I2C_DEBUG_BUS=y +CONFIG_FB=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_VGA_CONSOLE is not set +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_ITE is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_REDRAGON is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_RTC_CLASS=y +CONFIG_SYNC_FILE=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VHOST_MENU is not set +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_VIRTIO=y +CONFIG_GENERIC_PHY=y +CONFIG_AUTOFS4_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=y +# CONFIG_EFIVAR_FS is not set +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_KEYS=y +CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity" +CONFIG_CRYPTO=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_DEV_VIRTIO=y +CONFIG_CRC16=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +CONFIG_XZ_DEC=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_VM_PGTABLE=y +CONFIG_DEBUG_TIMEKEEPING=y +CONFIG_FUNCTION_ERROR_INJECTION=y +# CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_MEMTEST=y From 87c20a57492ca7eeff77278c74385a9ba6d39f2a Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Sun, 8 Jan 2023 02:25:29 -0800 Subject: [PATCH 07/41] riscv: dts: bouffalolab: add bootargs/initrd --- arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index 64421fb2ad67d9..84e5aac6cbf851 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -17,6 +17,9 @@ chosen { stdout-path = "serial0:2000000n8"; + bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mtdblock0 ro rootfstype=squashfs"; + linux,initrd-start = <0x0 0x52000000>; + linux,initrd-end = <0x0 0x52941784>; }; memory@50000000 { From c463c23723ed8823f03a7d0417136b800154af9b Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Sun, 8 Jan 2023 02:26:04 -0800 Subject: [PATCH 08/41] riscv: dts: bouffalolab: add xip_flash --- .../boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index 84e5aac6cbf851..bdb502ea5a5489 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -26,6 +26,20 @@ device_type = "memory"; reg = <0x50000000 0x04000000>; }; + + xip_flash@58500000 { + compatible = "mtd-rom"; + reg = <0x58500000 0x400000>; + linux,mtd-name = "xip-flash.0"; + erase-size = <0x10000>; + bank-width = <4>; + + rootfs@0 { + label = "rootfs"; + reg = <0x00000 0x280000>; + read-only; + }; + }; }; &uart0 { From 950fd2ac1cd7529abee5852773db654e617173d1 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Wed, 11 Jan 2023 18:14:53 -0800 Subject: [PATCH 09/41] WIP: add BFLB MBOX interrupt controller driver --- .../boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 4 ++++ arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index bdb502ea5a5489..70259bad7dfd31 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -45,3 +45,7 @@ &uart0 { status = "okay"; }; + +&ipclic { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index c98ebb14ee10a6..c5cda8d74ccd9d 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -4,6 +4,7 @@ */ #include +#include / { compatible = "bouffalolab,bl808"; @@ -60,6 +61,19 @@ status = "disabled"; }; + ipclic: mailbox@30005000 { + compatible = "bouffalolab,bflb-ipc"; + reg = <0x30005000 0x20>, + <0x30005020 0x20>, + <0x2000a800 0x20>, + <0x2000a820 0x20>; + interrupts = <54 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + #interrupt-cells = <3>; + #mbox-cells = <2>; + status = "disabled"; + }; + plic: interrupt-controller@e0000000 { compatible = "thead,c900-plic"; reg = <0xe0000000 0x4000000>; From 661769605519fdb71bd4600663f8e0d5c810af7b Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Wed, 11 Jan 2023 18:16:51 -0800 Subject: [PATCH 10/41] WIP: sdhci: add BFLB sdhci driver --- arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 4 ++++ arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index 70259bad7dfd31..effaeda67c3fb7 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -46,6 +46,10 @@ status = "okay"; }; +&sdhci0 { + status = "okay"; +}; + &ipclic { status = "okay"; }; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index c5cda8d74ccd9d..6f859194f82c36 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -61,6 +61,17 @@ status = "disabled"; }; + sdhci0: sdhci@20060000 { + compatible = "bouffalolab,bflb-sdhci"; + reg = <0x20060000 0x100>; + interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 + BFLB_IPC_DEVICE_SDHCI + IRQ_TYPE_EDGE_RISING>; + mboxes = <&ipclic BFLB_IPC_SOURCE_M0 BFLB_IPC_DEVICE_SDHCI>; + clocks = <&xtal>; + status = "disabled"; + }; + ipclic: mailbox@30005000 { compatible = "bouffalolab,bflb-ipc"; reg = <0x30005000 0x20>, From 4b74d104b878abe090836f86e42475a0b80235c9 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Wed, 11 Jan 2023 15:22:09 -0800 Subject: [PATCH 11/41] bl808_defconfig: enable sdhci driver --- arch/riscv/configs/bl808_defconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index 8a441a64be1a85..049fe1c25d74ec 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -103,6 +103,10 @@ CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y # CONFIG_HID_MICROSOFT is not set # CONFIG_HID_MONTEREY is not set # CONFIG_USB_SUPPORT is not set +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_BFLB=y CONFIG_RTC_CLASS=y CONFIG_SYNC_FILE=y # CONFIG_VIRTIO_MENU is not set From 69e4d28bdf14e9a6c764f14ec7466c603d976094 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Wed, 11 Jan 2023 15:22:29 -0800 Subject: [PATCH 12/41] bl808_defconfig: enable mailbox irqchip driver --- arch/riscv/configs/bl808_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index 049fe1c25d74ec..d2786235830294 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -111,6 +111,8 @@ CONFIG_RTC_CLASS=y CONFIG_SYNC_FILE=y # CONFIG_VIRTIO_MENU is not set # CONFIG_VHOST_MENU is not set +CONFIG_MAILBOX=y +CONFIG_BFLB_IPC=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_VIRTIO=y From 3b946f78adcf9cd06ab60e444ce696aa3bab66d8 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Wed, 11 Jan 2023 15:23:03 -0800 Subject: [PATCH 13/41] bl808_defconfig: enable irq debugfs --- arch/riscv/configs/bl808_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index d2786235830294..0cc8c786a522b2 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -1,5 +1,6 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y +CONFIG_GENERIC_IRQ_DEBUGFS=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y CONFIG_BPF_SYSCALL=y From e4017e32952a79b01a829092298e558bee92681e Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Wed, 11 Jan 2023 18:14:53 -0800 Subject: [PATCH 14/41] WIP: add BFLB MBOX interrupt controller driver --- include/dt-bindings/mailbox/bflb-ipc.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 include/dt-bindings/mailbox/bflb-ipc.h diff --git a/include/dt-bindings/mailbox/bflb-ipc.h b/include/dt-bindings/mailbox/bflb-ipc.h new file mode 100644 index 00000000000000..1d4c4be6292e97 --- /dev/null +++ b/include/dt-bindings/mailbox/bflb-ipc.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* + * Copyright (C) 2023 Allen Martin + */ + +#ifndef __DT_BINDINGS_MAILBOX_BFLB_IPC_H +#define __DT_BINDINGS_MAILBOX_BFLB_IPC_H + +/* Source processor */ +#define BFLB_IPC_SOURCE_M0 0 +#define BFLB_IPC_SOURCE_LP 1 + +/* Peripheral device ID */ +#define BFLB_IPC_DEVICE_SDHCI 0 + +#endif From 885fc70c6a228c9285c23ec4d7f07a98964f643f Mon Sep 17 00:00:00 2001 From: Alexander Horner <33007665+alexhorner@users.noreply.github.com> Date: Sat, 14 Jan 2023 00:05:40 +0000 Subject: [PATCH 15/41] UART2 working under Linux! --- arch/riscv/boot/dts/bouffalolab/Makefile | 1 + .../dts/bouffalolab/bl808-pine64-ox64.dts | 60 +++++++++++++++++++ arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 13 +++- include/dt-bindings/mailbox/bflb-ipc.h | 1 + 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts diff --git a/arch/riscv/boot/dts/bouffalolab/Makefile b/arch/riscv/boot/dts/bouffalolab/Makefile index 42e17e1a97bd17..bc7aad3d560406 100644 --- a/arch/riscv/boot/dts/bouffalolab/Makefile +++ b/arch/riscv/boot/dts/bouffalolab/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_SOC_BOUFFALOLAB) += bl808-sipeed-m1s.dtb +dtb-$(CONFIG_SOC_BOUFFALOLAB) += bl808-pine64-ox64.dtb diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts new file mode 100644 index 00000000000000..a3b1ae9f04780d --- /dev/null +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +/* + * Copyright (C) 2022 Jisheng Zhang + */ + +/dts-v1/; + +#include "bl808.dtsi" + +/ { + model = "Pine64 Ox64"; + compatible = "sipeed,m1s", "bouffalolab,bl808"; + + aliases { + serial0 = &uart0; + serial1 = &uart1; + }; + + chosen { + stdout-path = "serial0:2000000n8"; + bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mtdblock0 ro rootfstype=squashfs"; + linux,initrd-start = <0x0 0x52000000>; + linux,initrd-end = <0x0 0x52941784>; + }; + + memory@50000000 { + device_type = "memory"; + reg = <0x50000000 0x04000000>; + }; + + xip_flash@58500000 { + compatible = "mtd-rom"; + reg = <0x58500000 0x400000>; + linux,mtd-name = "xip-flash.0"; + erase-size = <0x10000>; + bank-width = <4>; + + rootfs@0 { + label = "rootfs"; + reg = <0x00000 0x280000>; + read-only; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&sdhci0 { + status = "okay"; +}; + +&ipclic { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index 6f859194f82c36..755071f80b5904 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -60,7 +60,18 @@ clocks = <&xtal>; status = "disabled"; }; - + + uart1: serial@0x2000AA00 { + compatible = "bouffalolab,uart"; + reg = <0x2000AA00 0x0100>; + interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 + BFLB_IPC_DEVICE_UART2 + IRQ_TYPE_EDGE_RISING>; + mboxes = <&ipclic BFLB_IPC_SOURCE_M0 BFLB_IPC_DEVICE_UART2>; + clocks = <&xtal>; + status = "disabled"; + }; + sdhci0: sdhci@20060000 { compatible = "bouffalolab,bflb-sdhci"; reg = <0x20060000 0x100>; diff --git a/include/dt-bindings/mailbox/bflb-ipc.h b/include/dt-bindings/mailbox/bflb-ipc.h index 1d4c4be6292e97..e96fe62cbeb9a2 100644 --- a/include/dt-bindings/mailbox/bflb-ipc.h +++ b/include/dt-bindings/mailbox/bflb-ipc.h @@ -12,5 +12,6 @@ /* Peripheral device ID */ #define BFLB_IPC_DEVICE_SDHCI 0 +#define BFLB_IPC_DEVICE_UART2 1 #endif From 9a54e5b68abb12792cda5342b581a46356e57825 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Sat, 14 Jan 2023 17:59:13 -0800 Subject: [PATCH 16/41] dts: bl808: add fake sdh clock at 96MHz --- arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index 755071f80b5904..ecb285cd7763ed 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -45,6 +45,13 @@ #clock-cells = <0>; }; + sdh: sdh-clk { + compatible = "fixed-clock"; + clock-frequency = <96000000>; + clock-output-names = "sdh"; + #clock-cells = <0>; + }; + soc { compatible = "simple-bus"; ranges; @@ -79,7 +86,7 @@ BFLB_IPC_DEVICE_SDHCI IRQ_TYPE_EDGE_RISING>; mboxes = <&ipclic BFLB_IPC_SOURCE_M0 BFLB_IPC_DEVICE_SDHCI>; - clocks = <&xtal>; + clocks = <&sdh>; status = "disabled"; }; From ad398a49333c425e52f8f46840d4276bdcbdc457 Mon Sep 17 00:00:00 2001 From: Alexander Horner <33007665+alexhorner@users.noreply.github.com> Date: Sun, 15 Jan 2023 13:38:26 +0000 Subject: [PATCH 17/41] Disable flash rootfs for now, edit bootargs to use SDHCI ext4 partition 1 rootfs --- arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index a3b1ae9f04780d..d3d228c3d8c0ea 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -18,7 +18,7 @@ chosen { stdout-path = "serial0:2000000n8"; - bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mtdblock0 ro rootfstype=squashfs"; + bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mmcblk0p1 rootwait rootfstype=ext4"; linux,initrd-start = <0x0 0x52000000>; linux,initrd-end = <0x0 0x52941784>; }; @@ -35,11 +35,11 @@ erase-size = <0x10000>; bank-width = <4>; - rootfs@0 { + /*rootfs@0 { label = "rootfs"; - reg = <0x00000 0x280000>; + reg = <0x00000 0x400000>; read-only; - }; + };*/ }; }; From b2fe2832f829538921d9c8ac3bbcbd053b776c6b Mon Sep 17 00:00:00 2001 From: Alexander Horner <33007665+alexhorner@users.noreply.github.com> Date: Sun, 15 Jan 2023 14:24:55 +0000 Subject: [PATCH 18/41] Update defconfig with EXT4 support for SD rootfs --- arch/riscv/configs/bl808_defconfig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index 0cc8c786a522b2..2acab58e801b67 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -118,6 +118,7 @@ CONFIG_BFLB_IPC=y CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_VIRTIO=y CONFIG_GENERIC_PHY=y +CONFIG_EXT4_FS=y CONFIG_AUTOFS4_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y @@ -133,11 +134,8 @@ CONFIG_NLS_ISO8859_1=y CONFIG_NLS_UTF8=y CONFIG_KEYS=y CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity" -CONFIG_CRYPTO=y -CONFIG_CRYPTO_CRC32C=y CONFIG_CRYPTO_USER_API_HASH=y CONFIG_CRYPTO_DEV_VIRTIO=y -CONFIG_CRC16=y CONFIG_CRC_ITU_T=y CONFIG_CRC7=y CONFIG_XZ_DEC=y From 4c45c424d0d3e02b1d47b275d37d477f944d6914 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Fri, 13 Jan 2023 02:38:06 -0800 Subject: [PATCH 19/41] usb: add bflb ehci controller --- arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts | 4 ++++ arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 4 ++++ arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 10 ++++++++++ include/dt-bindings/mailbox/bflb-ipc.h | 1 + 4 files changed, 19 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index d3d228c3d8c0ea..66a892f880c71a 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -58,3 +58,7 @@ &ipclic { status = "okay"; }; + +&ehci0 { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index effaeda67c3fb7..19155bcc269373 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -53,3 +53,7 @@ &ipclic { status = "okay"; }; + +&ehci0 { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index ecb285cd7763ed..f3e4f17e69c033 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -90,6 +90,16 @@ status = "disabled"; }; + ehci0: usb@20072000 { + compatible = "generic-ehci"; + reg = <0x20072010 0x1000>; + interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 + BFLB_IPC_DEVICE_USB + IRQ_TYPE_EDGE_RISING>; + clocks = <&xtal>; + status = "disabled"; + }; + ipclic: mailbox@30005000 { compatible = "bouffalolab,bflb-ipc"; reg = <0x30005000 0x20>, diff --git a/include/dt-bindings/mailbox/bflb-ipc.h b/include/dt-bindings/mailbox/bflb-ipc.h index e96fe62cbeb9a2..327e150384b95f 100644 --- a/include/dt-bindings/mailbox/bflb-ipc.h +++ b/include/dt-bindings/mailbox/bflb-ipc.h @@ -13,5 +13,6 @@ /* Peripheral device ID */ #define BFLB_IPC_DEVICE_SDHCI 0 #define BFLB_IPC_DEVICE_UART2 1 +#define BFLB_IPC_DEVICE_USB 2 #endif From 0a7184765d42e951d892e1c7b70c6eb08eb67ec9 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Fri, 13 Jan 2023 02:38:58 -0800 Subject: [PATCH 20/41] bl808_defconfig: enable USB and EHCI --- arch/riscv/configs/bl808_defconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index 2acab58e801b67..c43e21711750c7 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -103,7 +103,9 @@ CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y # CONFIG_HID_REDRAGON is not set # CONFIG_HID_MICROSOFT is not set # CONFIG_HID_MONTEREY is not set -# CONFIG_USB_SUPPORT is not set +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y From aa9ca42882bd51620df7d5b591bb0c368ea830e4 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Sun, 15 Jan 2023 21:06:40 -0800 Subject: [PATCH 21/41] dts: bl808: fix offset of ehci controller ehci controller registers are at address 0x20072000 not 0x20072010 --- arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index f3e4f17e69c033..5e90f3ee04d716 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -92,7 +92,7 @@ ehci0: usb@20072000 { compatible = "generic-ehci"; - reg = <0x20072010 0x1000>; + reg = <0x20072000 0x1000>; interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 BFLB_IPC_DEVICE_USB IRQ_TYPE_EDGE_RISING>; From 3258905a37d4e65b045453a0955a87a03469a6d2 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Sun, 15 Jan 2023 21:11:40 -0800 Subject: [PATCH 22/41] bl808_defconfig: enable USB serial and network devices --- arch/riscv/configs/bl808_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index c43e21711750c7..39ad1cb2b4bc55 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -106,6 +106,8 @@ CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SERIAL=y CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y From dcecd699e76f054916fde5f661fecc410938c5f9 Mon Sep 17 00:00:00 2001 From: Justin Hammond Date: Fri, 20 Jan 2023 13:45:52 +0800 Subject: [PATCH 23/41] Rename DTS Files --- arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts | 8 +------- arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 2 +- arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 10 +++++----- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index 66a892f880c71a..eede26e4f043c0 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -9,7 +9,7 @@ / { model = "Pine64 Ox64"; - compatible = "sipeed,m1s", "bouffalolab,bl808"; + compatible = "sipeed,m1s", "bflb,bl808"; aliases { serial0 = &uart0; @@ -34,12 +34,6 @@ linux,mtd-name = "xip-flash.0"; erase-size = <0x10000>; bank-width = <4>; - - /*rootfs@0 { - label = "rootfs"; - reg = <0x00000 0x400000>; - read-only; - };*/ }; }; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index 19155bcc269373..ed2d18482920c6 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -9,7 +9,7 @@ / { model = "Sipeed M1S"; - compatible = "sipeed,m1s", "bouffalolab,bl808"; + compatible = "sipeed,m1s", "bflb,bl808"; aliases { serial0 = &uart0; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index 5e90f3ee04d716..bdfed1bde043ff 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -7,7 +7,7 @@ #include / { - compatible = "bouffalolab,bl808"; + compatible = "bflb,bl808"; #address-cells = <1>; #size-cells = <1>; @@ -61,7 +61,7 @@ #size-cells = <1>; uart0: serial@30002000 { - compatible = "bouffalolab,uart"; + compatible = "bflb,bl808-uart"; reg = <0x30002000 0x1000>; interrupts = <20 IRQ_TYPE_LEVEL_HIGH>; clocks = <&xtal>; @@ -69,7 +69,7 @@ }; uart1: serial@0x2000AA00 { - compatible = "bouffalolab,uart"; + compatible = "bflb,bl808-uart"; reg = <0x2000AA00 0x0100>; interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 BFLB_IPC_DEVICE_UART2 @@ -80,7 +80,7 @@ }; sdhci0: sdhci@20060000 { - compatible = "bouffalolab,bflb-sdhci"; + compatible = "bflb,bl808-sdhci"; reg = <0x20060000 0x100>; interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 BFLB_IPC_DEVICE_SDHCI @@ -101,7 +101,7 @@ }; ipclic: mailbox@30005000 { - compatible = "bouffalolab,bflb-ipc"; + compatible = "bflb,bl808-ipc"; reg = <0x30005000 0x20>, <0x30005020 0x20>, <0x2000a800 0x20>, From 5de10314906445dd50565284c6fb822e86e3490c Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Fri, 20 Jan 2023 19:01:49 -0800 Subject: [PATCH 24/41] dts: bl808-pine64-ox64: change rootfs to /dev/mmcblk0p2 --- arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index eede26e4f043c0..4e183098f171ec 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -18,7 +18,7 @@ chosen { stdout-path = "serial0:2000000n8"; - bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mmcblk0p1 rootwait rootfstype=ext4"; + bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mmcblk0p2 rootwait rootfstype=ext4"; linux,initrd-start = <0x0 0x52000000>; linux,initrd-end = <0x0 0x52941784>; }; From 37884166bce69b4289af44e9e1422b7b1078c463 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 24 Jan 2023 17:00:32 -0800 Subject: [PATCH 25/41] dts: bl808-pine64-ox64: disable secondary UART Pinmux for this device conflicts with EMAC, so disable it until pinmux is changed to some other pins. --- arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index 4e183098f171ec..96cc039133cc04 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -41,10 +41,6 @@ status = "okay"; }; -&uart1 { - status = "okay"; -}; - &sdhci0 { status = "okay"; }; From 92d66e03a0a0f921142c67472eec01cbbfde13b9 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 24 Jan 2023 17:02:40 -0800 Subject: [PATCH 26/41] dts: bl808: add entry for EMAC device Add device-tree node for EMAC ethernet device and fake clock node to represent 50MHz clock to the device. Add a virtualized interrupt bit for forwareded EMAC interrupts. --- arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 17 +++++++++++++++++ include/dt-bindings/mailbox/bflb-ipc.h | 1 + 2 files changed, 18 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index bdfed1bde043ff..dd795667dea68f 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -52,6 +52,13 @@ #clock-cells = <0>; }; + enet: enet-clk { + compatible = "fixed-clock"; + clock-frequency = <50000000>; + clock-output-names = "enet"; + #clock-cells = <0>; + }; + soc { compatible = "simple-bus"; ranges; @@ -100,6 +107,16 @@ status = "disabled"; }; + enet0: emac@20070000 { + compatible = "opencores,ethoc"; + reg = <0x20070000 0x1000>; + interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 + BFLB_IPC_DEVICE_EMAC + IRQ_TYPE_EDGE_RISING>; + clocks = <&enet>; + status = "disabled"; + }; + ipclic: mailbox@30005000 { compatible = "bflb,bl808-ipc"; reg = <0x30005000 0x20>, diff --git a/include/dt-bindings/mailbox/bflb-ipc.h b/include/dt-bindings/mailbox/bflb-ipc.h index 327e150384b95f..0a3c6745a673c1 100644 --- a/include/dt-bindings/mailbox/bflb-ipc.h +++ b/include/dt-bindings/mailbox/bflb-ipc.h @@ -14,5 +14,6 @@ #define BFLB_IPC_DEVICE_SDHCI 0 #define BFLB_IPC_DEVICE_UART2 1 #define BFLB_IPC_DEVICE_USB 2 +#define BFLB_IPC_DEVICE_EMAC 3 #endif From 2ad217b4095e489b76e3d8f1d9c42a668c5e013b Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 24 Jan 2023 17:13:31 -0800 Subject: [PATCH 27/41] riscv: bl808_defconfig: enable ETHOC driver --- arch/riscv/configs/bl808_defconfig | 45 ++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index 39ad1cb2b4bc55..d51f2d78c139d6 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -63,8 +63,48 @@ CONFIG_BLK_DEV_SD=y CONFIG_SCSI_VIRTIO=y CONFIG_NETDEVICES=y CONFIG_VIRTIO_NET=y -# CONFIG_ETHERNET is not set -CONFIG_MDIO_DEVICE=y +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_ASIX is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CADENCE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_NET_VENDOR_DAVICOM is not set +# CONFIG_NET_VENDOR_ENGLEDER is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_FUNGIBLE is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_WANGXUN is not set +# CONFIG_NET_VENDOR_LITEX is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MICROSEMI is not set +# CONFIG_NET_VENDOR_MICROSOFT is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +CONFIG_ETHOC=y +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +# CONFIG_NET_VENDOR_SOCIONEXT is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +# CONFIG_NET_VENDOR_VERTEXCOM is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_NET_VENDOR_XILINX is not set # CONFIG_WLAN is not set CONFIG_INPUT_MOUSEDEV=y CONFIG_INPUT_EVDEV=y @@ -88,6 +128,7 @@ CONFIG_I2C_SLAVE_EEPROM=y CONFIG_I2C_DEBUG_CORE=y CONFIG_I2C_DEBUG_ALGO=y CONFIG_I2C_DEBUG_BUS=y +# CONFIG_PTP_1588_CLOCK is not set CONFIG_FB=y CONFIG_BACKLIGHT_CLASS_DEVICE=y # CONFIG_VGA_CONSOLE is not set From a7bbe72dd574b66708c0cc060898551f3917b74a Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 24 Jan 2023 17:14:05 -0800 Subject: [PATCH 28/41] dts: bl808: enable enet nodes --- arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts | 4 ++++ arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index 96cc039133cc04..f9ed636291f614 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -52,3 +52,7 @@ &ehci0 { status = "okay"; }; + +&enet0 { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index ed2d18482920c6..031c824f2061ae 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -57,3 +57,7 @@ &ehci0 { status = "okay"; }; + +&enet0 { + status = "okay"; +}; From ee0931def8e7ff676e11ebe147c1128862e2c8f6 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 5 Feb 2023 16:13:42 -0600 Subject: [PATCH 29/41] riscv: dts: bflb: m1s: Fix address/size-cells The number of address cells needed here (one) does not match the implicitly-defined default number of cells. Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index 031c824f2061ae..5307508e7a9462 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -33,6 +33,8 @@ linux,mtd-name = "xip-flash.0"; erase-size = <0x10000>; bank-width = <4>; + #address-cells = <1>; + #size-cells = <1>; rootfs@0 { label = "rootfs"; From be296b48621098e41309ac433fc3ebdd3d64c4bd Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Sun, 5 Feb 2023 20:18:50 -0800 Subject: [PATCH 30/41] riscv: dts: bflb: ox64: Fix address/size-cells The number of address cells needed here (one) does not match the implicitly-defined default number of cells. Signed-off-by: Allen Martin --- arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index f9ed636291f614..f1db126d88e76f 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -34,6 +34,8 @@ linux,mtd-name = "xip-flash.0"; erase-size = <0x10000>; bank-width = <4>; + #address-cells = <1>; + #size-cells = <1>; }; }; From d48d4b1210f8a99578a429fecc282e83d7f8bc92 Mon Sep 17 00:00:00 2001 From: Justin Hammond Date: Mon, 6 Feb 2023 13:38:55 +0800 Subject: [PATCH 31/41] Add timer node for OpenSBI 1.2 compatibility --- arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index dd795667dea68f..34652e17aece8f 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -140,5 +140,12 @@ #interrupt-cells = <2>; riscv,ndev = <64>; }; + + clint: timer@e4000000 { + compatible = "thead,c900-clint"; + reg = <0xe4000000 0xc000>; + interrupts-extended = <&cpu0_intc 3>, + <&cpu0_intc 7>; + }; }; }; From b79eef5b82707b2fe5823dc6e5e30f3fd94561ff Mon Sep 17 00:00:00 2001 From: Justin Hammond Date: Mon, 6 Feb 2023 13:41:53 +0800 Subject: [PATCH 32/41] Tabstops are 8 chars, not 4 --- arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index 34652e17aece8f..7cd033b6a1cf36 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -145,7 +145,7 @@ compatible = "thead,c900-clint"; reg = <0xe4000000 0xc000>; interrupts-extended = <&cpu0_intc 3>, - <&cpu0_intc 7>; + <&cpu0_intc 7>; }; }; }; From 4fb02196485265eeb2c13868932219d3de09a809 Mon Sep 17 00:00:00 2001 From: Alexander Horner <33007665+alexhorner@users.noreply.github.com> Date: Thu, 16 Feb 2023 19:42:01 +0000 Subject: [PATCH 33/41] Update device trees for new GPIO and HWRNG drivers --- .../dts/bouffalolab/bl808-pine64-ox64.dts | 8 +++++ arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index f1db126d88e76f..5050c80b6f1150 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -39,6 +39,14 @@ }; }; +&pinctrl { + status = "okay"; +}; + +&seceng { + status = "okay"; +}; + &uart0 { status = "okay"; }; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index 7cd033b6a1cf36..73a4e055c7a9e6 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -67,6 +67,36 @@ #address-cells = <1>; #size-cells = <1>; + pinctrl: pinctrl@0x200008C4 { + compatible = "bflb,pinctrl"; + //Last register is for gpio_cfg141 at 0x20000af8 + reg = <0x200008C4 0x1000>; + //clocks = <&gpio_clk>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 0 46>; + bflb,npins = <46>; + + status = "disabled"; + + interrupt-controller; + #interrupt-cells = <2>; + interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 + BFLB_IPC_DEVICE_GPIO IRQ_TYPE_EDGE_RISING>; + + sdh_pins: sdh-pins { + pins = "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5"; + function = "sdh"; + }; + }; + + seceng: seceng@0x20004000 { + compatible = "bflb,seceng"; + reg = <0x20004000 0x1000>; + status = "disabled"; + }; + uart0: serial@30002000 { compatible = "bflb,bl808-uart"; reg = <0x30002000 0x1000>; From 56820fe33133f4d37dc277a344c5a54bfeb502c3 Mon Sep 17 00:00:00 2001 From: Alexander Horner <33007665+alexhorner@users.noreply.github.com> Date: Tue, 14 Feb 2023 23:12:03 +0000 Subject: [PATCH 34/41] Update bl808_defconfig for new drivers --- arch/riscv/configs/bl808_defconfig | 40 ++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index d51f2d78c139d6..9e234dcb5ec0d7 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -11,8 +11,10 @@ CONFIG_CGROUP_SCHED=y CONFIG_CFS_BANDWIDTH=y CONFIG_CGROUP_PERF=y CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y CONFIG_USER_NS=y CONFIG_CHECKPOINT_RESTORE=y +CONFIG_EXPERT=y CONFIG_PERF_EVENTS=y CONFIG_SOC_BOUFFALOLAB=y CONFIG_SOC_VIRT=y @@ -129,21 +131,15 @@ CONFIG_I2C_DEBUG_CORE=y CONFIG_I2C_DEBUG_ALGO=y CONFIG_I2C_DEBUG_BUS=y # CONFIG_PTP_1588_CLOCK is not set +CONFIG_PINCTRL=y +CONFIG_PINCTRL_BFLB_GPIO=y +CONFIG_GPIO_SYSFS=y CONFIG_FB=y CONFIG_BACKLIGHT_CLASS_DEVICE=y # CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y -# CONFIG_HID_A4TECH is not set -# CONFIG_HID_BELKIN is not set -# CONFIG_HID_CHERRY is not set -# CONFIG_HID_CYPRESS is not set -# CONFIG_HID_EZKEY is not set -# CONFIG_HID_ITE is not set -# CONFIG_HID_KENSINGTON is not set -# CONFIG_HID_REDRAGON is not set -# CONFIG_HID_MICROSOFT is not set -# CONFIG_HID_MONTEREY is not set +CONFIG_HID_CHICONY=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_HCD_PLATFORM=y @@ -153,6 +149,27 @@ CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_BFLB=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_USER=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_MTD=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_ACTIVITY=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_TRANSIENT=y +CONFIG_LEDS_TRIGGER_CAMERA=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LEDS_TRIGGER_NETDEV=y +CONFIG_LEDS_TRIGGER_PATTERN=y +CONFIG_LEDS_TRIGGER_AUDIO=y +CONFIG_LEDS_TRIGGER_TTY=y CONFIG_RTC_CLASS=y CONFIG_SYNC_FILE=y # CONFIG_VIRTIO_MENU is not set @@ -180,6 +197,7 @@ CONFIG_NLS_UTF8=y CONFIG_KEYS=y CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity" CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_DEV_BFLB_SECENG=y CONFIG_CRYPTO_DEV_VIRTIO=y CONFIG_CRC_ITU_T=y CONFIG_CRC7=y @@ -187,7 +205,9 @@ CONFIG_XZ_DEC=y CONFIG_PRINTK_TIME=y CONFIG_DEBUG_FS=y CONFIG_DEBUG_VM_PGTABLE=y +CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_TIMEKEEPING=y +# CONFIG_FTRACE is not set CONFIG_FUNCTION_ERROR_INJECTION=y # CONFIG_RUNTIME_TESTING_MENU is not set CONFIG_MEMTEST=y From 5bc30359db03519fe18fafbbf0b1f4c38dbebdf7 Mon Sep 17 00:00:00 2001 From: Alexander Horner <33007665+alexhorner@users.noreply.github.com> Date: Thu, 16 Feb 2023 19:43:15 +0000 Subject: [PATCH 35/41] Bring M1s device tree up to date with Ox64's changes, fix compatible string on the Ox64 device tree --- .../dts/bouffalolab/bl808-pine64-ox64.dts | 2 +- .../boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 31 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index 5050c80b6f1150..c5b1e86310d983 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -9,7 +9,7 @@ / { model = "Pine64 Ox64"; - compatible = "sipeed,m1s", "bflb,bl808"; + compatible = "pine64,ox64", "bflb,bl808"; aliases { serial0 = &uart0; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index 5307508e7a9462..ecdce792132336 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -6,9 +6,11 @@ /dts-v1/; #include "bl808.dtsi" +#include +#include / { - model = "Sipeed M1S"; + model = "Sipeed M1s"; compatible = "sipeed,m1s", "bflb,bl808"; aliases { @@ -17,7 +19,7 @@ chosen { stdout-path = "serial0:2000000n8"; - bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mtdblock0 ro rootfstype=squashfs"; + bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mmcblk0p2 rootwait rootfstype=ext4"; linux,initrd-start = <0x0 0x52000000>; linux,initrd-end = <0x0 0x52941784>; }; @@ -35,15 +37,30 @@ bank-width = <4>; #address-cells = <1>; #size-cells = <1>; + }; + + leds { + compatible = "gpio-leds"; + + led { + gpios = <&pinctrl 8 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&pinctrl { + status = "okay"; - rootfs@0 { - label = "rootfs"; - reg = <0x00000 0x280000>; - read-only; - }; + led { + pins = "GPIO8"; + function = "gpio"; }; }; +&seceng { + status = "okay"; +}; + &uart0 { status = "okay"; }; From b9595ae86079168be9389006f090fd046f91111a Mon Sep 17 00:00:00 2001 From: Alexander Horner <33007665+alexhorner@users.noreply.github.com> Date: Tue, 14 Feb 2023 22:44:18 +0000 Subject: [PATCH 36/41] Add GPIO/PINCTRL and HWRNG/Crypto drivers --- include/dt-bindings/mailbox/bflb-ipc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/dt-bindings/mailbox/bflb-ipc.h b/include/dt-bindings/mailbox/bflb-ipc.h index 0a3c6745a673c1..9763460edbcc75 100644 --- a/include/dt-bindings/mailbox/bflb-ipc.h +++ b/include/dt-bindings/mailbox/bflb-ipc.h @@ -13,7 +13,8 @@ /* Peripheral device ID */ #define BFLB_IPC_DEVICE_SDHCI 0 #define BFLB_IPC_DEVICE_UART2 1 -#define BFLB_IPC_DEVICE_USB 2 +#define BFLB_IPC_DEVICE_USB 2 #define BFLB_IPC_DEVICE_EMAC 3 +#define BFLB_IPC_DEVICE_GPIO 4 #endif From dad2ca7a58481c831f3c563c0158c2d5e29ade77 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Mon, 20 Feb 2023 16:21:30 -0500 Subject: [PATCH 37/41] BL808 USB Support patch 1 of 5: Remove mainline fotg210 driver and replace with more updated driver from faraday semiconductor found at https://github.com/FaradayA380Platform/Linux.git . All code here is authored by Faraday Semiconductor and released under their terms. --- drivers/usb/Kconfig | 2 - drivers/usb/Makefile | 2 - drivers/usb/fotg210/Kconfig | 38 - drivers/usb/fotg210/Makefile | 10 - drivers/usb/fotg210/fotg210-core.c | 163 -- drivers/usb/fotg210/fotg210-udc.c | 1307 --------- drivers/usb/fotg210/fotg210-udc.h | 252 -- drivers/usb/fotg210/fotg210.h | 42 - drivers/usb/gadget/udc/Kconfig | 42 + drivers/usb/gadget/udc/Makefile | 1 + drivers/usb/gadget/udc/fotg210-udc.c | 2326 +++++++++++++++++ drivers/usb/gadget/udc/fotg210.h | 385 +++ drivers/usb/host/Kconfig | 11 + drivers/usb/host/Makefile | 1 + drivers/usb/{fotg210 => host}/fotg210-hcd.c | 1647 ++++++++++-- .../{fotg210/fotg210-hcd.h => host/fotg210.h} | 95 +- 16 files changed, 4328 insertions(+), 1996 deletions(-) delete mode 100644 drivers/usb/fotg210/Kconfig delete mode 100644 drivers/usb/fotg210/Makefile delete mode 100644 drivers/usb/fotg210/fotg210-core.c delete mode 100644 drivers/usb/fotg210/fotg210-udc.c delete mode 100644 drivers/usb/fotg210/fotg210-udc.h delete mode 100644 drivers/usb/fotg210/fotg210.h create mode 100644 drivers/usb/gadget/udc/fotg210-udc.c create mode 100644 drivers/usb/gadget/udc/fotg210.h rename drivers/usb/{fotg210 => host}/fotg210-hcd.c (78%) rename drivers/usb/{fotg210/fotg210-hcd.h => host/fotg210.h} (88%) diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index a871a988829dbf..761275140b6d92 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -115,8 +115,6 @@ comment "USB dual-mode controller drivers" source "drivers/usb/cdns3/Kconfig" -source "drivers/usb/fotg210/Kconfig" - source "drivers/usb/mtu3/Kconfig" source "drivers/usb/musb/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index a81e6ef293af27..643edf5fe18c61 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -17,8 +17,6 @@ obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns3/ obj-$(CONFIG_USB_CDNS3) += cdns3/ obj-$(CONFIG_USB_CDNSP_PCI) += cdns3/ -obj-$(CONFIG_USB_FOTG210) += fotg210/ - obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_USB_MTU3) += mtu3/ diff --git a/drivers/usb/fotg210/Kconfig b/drivers/usb/fotg210/Kconfig deleted file mode 100644 index 2b05968735baa7..00000000000000 --- a/drivers/usb/fotg210/Kconfig +++ /dev/null @@ -1,38 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -config USB_FOTG210 - tristate "Faraday FOTG210 USB2 Dual Role controller" - depends on USB || USB_GADGET - depends on HAS_DMA && HAS_IOMEM - depends on ARCH_GEMINI || COMPILE_TEST - default ARCH_GEMINI - select MFD_SYSCON - help - Faraday FOTG210 is a dual-mode USB controller that can act - in both host controller and peripheral controller mode. - -if USB_FOTG210 - -config USB_FOTG210_HCD - bool "Faraday FOTG210 USB Host Controller support" - depends on USB=y || USB=USB_FOTG210 - help - Faraday FOTG210 is an OTG controller which can be configured as - an USB2.0 host. It is designed to meet USB2.0 EHCI specification - with minor modification. - - To compile this driver as a module, choose M here: the - module will be called fotg210-hcd. - -config USB_FOTG210_UDC - depends on USB_GADGET=y || USB_GADGET=USB_FOTG210 - bool "Faraday FOTG210 USB Peripheral Controller support" - help - Faraday USB2.0 OTG controller which can be configured as - high speed or full speed USB device. This driver suppports - Bulk Transfer so far. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "fotg210-udc". - -endif diff --git a/drivers/usb/fotg210/Makefile b/drivers/usb/fotg210/Makefile deleted file mode 100644 index 5aecff21f24b80..00000000000000 --- a/drivers/usb/fotg210/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# This setup links the different object files into one single -# module so we don't have to EXPORT() a lot of internal symbols -# or create unnecessary submodules. -fotg210-objs-y += fotg210-core.o -fotg210-objs-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o -fotg210-objs-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o -fotg210-objs := $(fotg210-objs-y) -obj-$(CONFIG_USB_FOTG210) += fotg210.o diff --git a/drivers/usb/fotg210/fotg210-core.c b/drivers/usb/fotg210/fotg210-core.c deleted file mode 100644 index ee740a6da463fb..00000000000000 --- a/drivers/usb/fotg210/fotg210-core.c +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Central probing code for the FOTG210 dual role driver - * We register one driver for the hardware and then we decide - * whether to proceed with probing the host or the peripheral - * driver. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fotg210.h" - -/* - * Gemini-specific initialization function, only executed on the - * Gemini SoC using the global misc control register. - * - * The gemini USB blocks are connected to either Mini-A (host mode) or - * Mini-B (peripheral mode) plugs. There is no role switch support on the - * Gemini SoC, just either-or. - */ -#define GEMINI_GLOBAL_MISC_CTRL 0x30 -#define GEMINI_MISC_USB0_WAKEUP BIT(14) -#define GEMINI_MISC_USB1_WAKEUP BIT(15) -#define GEMINI_MISC_USB0_VBUS_ON BIT(22) -#define GEMINI_MISC_USB1_VBUS_ON BIT(23) -#define GEMINI_MISC_USB0_MINI_B BIT(29) -#define GEMINI_MISC_USB1_MINI_B BIT(30) - -static int fotg210_gemini_init(struct device *dev, struct resource *res, - enum usb_dr_mode mode) -{ - struct device_node *np = dev->of_node; - struct regmap *map; - bool wakeup; - u32 mask, val; - int ret; - - map = syscon_regmap_lookup_by_phandle(np, "syscon"); - if (IS_ERR(map)) { - dev_err(dev, "no syscon\n"); - return PTR_ERR(map); - } - wakeup = of_property_read_bool(np, "wakeup-source"); - - /* - * Figure out if this is USB0 or USB1 by simply checking the - * physical base address. - */ - mask = 0; - if (res->start == 0x69000000) { - mask = GEMINI_MISC_USB1_VBUS_ON | GEMINI_MISC_USB1_MINI_B | - GEMINI_MISC_USB1_WAKEUP; - if (mode == USB_DR_MODE_HOST) - val = GEMINI_MISC_USB1_VBUS_ON; - else - val = GEMINI_MISC_USB1_MINI_B; - if (wakeup) - val |= GEMINI_MISC_USB1_WAKEUP; - } else { - mask = GEMINI_MISC_USB0_VBUS_ON | GEMINI_MISC_USB0_MINI_B | - GEMINI_MISC_USB0_WAKEUP; - if (mode == USB_DR_MODE_HOST) - val = GEMINI_MISC_USB0_VBUS_ON; - else - val = GEMINI_MISC_USB0_MINI_B; - if (wakeup) - val |= GEMINI_MISC_USB0_WAKEUP; - } - - ret = regmap_update_bits(map, GEMINI_GLOBAL_MISC_CTRL, mask, val); - if (ret) { - dev_err(dev, "failed to initialize Gemini PHY\n"); - return ret; - } - - dev_info(dev, "initialized Gemini PHY in %s mode\n", - (mode == USB_DR_MODE_HOST) ? "host" : "gadget"); - return 0; -} - -static int fotg210_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - enum usb_dr_mode mode; - int ret; - - mode = usb_get_dr_mode(dev); - - if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ret = fotg210_gemini_init(dev, res, mode); - if (ret) - return ret; - } - - if (mode == USB_DR_MODE_PERIPHERAL) - ret = fotg210_udc_probe(pdev); - else - ret = fotg210_hcd_probe(pdev); - - return ret; -} - -static int fotg210_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - enum usb_dr_mode mode; - - mode = usb_get_dr_mode(dev); - - if (mode == USB_DR_MODE_PERIPHERAL) - fotg210_udc_remove(pdev); - else - fotg210_hcd_remove(pdev); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id fotg210_of_match[] = { - { .compatible = "faraday,fotg210" }, - {}, -}; -MODULE_DEVICE_TABLE(of, fotg210_of_match); -#endif - -static struct platform_driver fotg210_driver = { - .driver = { - .name = "fotg210", - .of_match_table = of_match_ptr(fotg210_of_match), - }, - .probe = fotg210_probe, - .remove = fotg210_remove, -}; - -static int __init fotg210_init(void) -{ - if (IS_ENABLED(CONFIG_USB_FOTG210_HCD) && !usb_disabled()) - fotg210_hcd_init(); - return platform_driver_register(&fotg210_driver); -} -module_init(fotg210_init); - -static void __exit fotg210_cleanup(void) -{ - platform_driver_unregister(&fotg210_driver); - if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) - fotg210_hcd_cleanup(); -} -module_exit(fotg210_cleanup); - -MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("FOTG210 Dual Role Controller Driver"); diff --git a/drivers/usb/fotg210/fotg210-udc.c b/drivers/usb/fotg210/fotg210-udc.c deleted file mode 100644 index eb076746f03206..00000000000000 --- a/drivers/usb/fotg210/fotg210-udc.c +++ /dev/null @@ -1,1307 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * FOTG210 UDC Driver supports Bulk transfer so far - * - * Copyright (C) 2013 Faraday Technology Corporation - * - * Author : Yuan-Hsin Chen - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fotg210.h" -#include "fotg210-udc.h" - -#define DRIVER_DESC "FOTG210 USB Device Controller Driver" -#define DRIVER_VERSION "30-April-2013" - -static const char udc_name[] = "fotg210_udc"; -static const char * const fotg210_ep_name[] = { - "ep0", "ep1", "ep2", "ep3", "ep4"}; - -static void fotg210_disable_fifo_int(struct fotg210_ep *ep) -{ - u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); - - if (ep->dir_in) - value |= DMISGR1_MF_IN_INT(ep->epnum - 1); - else - value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); - iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); -} - -static void fotg210_enable_fifo_int(struct fotg210_ep *ep) -{ - u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); - - if (ep->dir_in) - value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1); - else - value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); - iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); -} - -static void fotg210_set_cxdone(struct fotg210_udc *fotg210) -{ - u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); - - value |= DCFESR_CX_DONE; - iowrite32(value, fotg210->reg + FOTG210_DCFESR); -} - -static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, - int status) -{ - list_del_init(&req->queue); - - /* don't modify queue heads during completion callback */ - if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) - req->req.status = -ESHUTDOWN; - else - req->req.status = status; - - spin_unlock(&ep->fotg210->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&ep->fotg210->lock); - - if (ep->epnum) { - if (list_empty(&ep->queue)) - fotg210_disable_fifo_int(ep); - } else { - fotg210_set_cxdone(ep->fotg210); - } -} - -static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum, - u32 dir_in) -{ - struct fotg210_udc *fotg210 = ep->fotg210; - u32 val; - - /* Driver should map an ep to a fifo and then map the fifo - * to the ep. What a brain-damaged design! - */ - - /* map a fifo to an ep */ - val = ioread32(fotg210->reg + FOTG210_EPMAP); - val &= ~EPMAP_FIFONOMSK(epnum, dir_in); - val |= EPMAP_FIFONO(epnum, dir_in); - iowrite32(val, fotg210->reg + FOTG210_EPMAP); - - /* map the ep to the fifo */ - val = ioread32(fotg210->reg + FOTG210_FIFOMAP); - val &= ~FIFOMAP_EPNOMSK(epnum); - val |= FIFOMAP_EPNO(epnum); - iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); - - /* enable fifo */ - val = ioread32(fotg210->reg + FOTG210_FIFOCF); - val |= FIFOCF_FIFO_EN(epnum - 1); - iowrite32(val, fotg210->reg + FOTG210_FIFOCF); -} - -static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in) -{ - struct fotg210_udc *fotg210 = ep->fotg210; - u32 val; - - val = ioread32(fotg210->reg + FOTG210_FIFOMAP); - val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1)); - iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); -} - -static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type) -{ - struct fotg210_udc *fotg210 = ep->fotg210; - u32 val; - - val = ioread32(fotg210->reg + FOTG210_FIFOCF); - val |= FIFOCF_TYPE(type, epnum - 1); - iowrite32(val, fotg210->reg + FOTG210_FIFOCF); -} - -static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps, - u32 dir_in) -{ - struct fotg210_udc *fotg210 = ep->fotg210; - u32 val; - u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) : - FOTG210_OUTEPMPSR(epnum); - - val = ioread32(fotg210->reg + offset); - val |= INOUTEPMPSR_MPS(mps); - iowrite32(val, fotg210->reg + offset); -} - -static int fotg210_config_ep(struct fotg210_ep *ep, - const struct usb_endpoint_descriptor *desc) -{ - struct fotg210_udc *fotg210 = ep->fotg210; - - fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in); - fotg210_set_tfrtype(ep, ep->epnum, ep->type); - fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in); - fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in); - - fotg210->ep[ep->epnum] = ep; - - return 0; -} - -static int fotg210_ep_enable(struct usb_ep *_ep, - const struct usb_endpoint_descriptor *desc) -{ - struct fotg210_ep *ep; - - ep = container_of(_ep, struct fotg210_ep, ep); - - ep->desc = desc; - ep->epnum = usb_endpoint_num(desc); - ep->type = usb_endpoint_type(desc); - ep->dir_in = usb_endpoint_dir_in(desc); - ep->ep.maxpacket = usb_endpoint_maxp(desc); - - return fotg210_config_ep(ep, desc); -} - -static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) -{ - struct fotg210_ep *ep = fotg210->ep[epnum]; - u32 value; - void __iomem *reg; - - reg = (ep->dir_in) ? - fotg210->reg + FOTG210_INEPMPSR(epnum) : - fotg210->reg + FOTG210_OUTEPMPSR(epnum); - - /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ - * bit. Controller wouldn't clear this bit. WTF!!! - */ - - value = ioread32(reg); - value |= INOUTEPMPSR_RESET_TSEQ; - iowrite32(value, reg); - - value = ioread32(reg); - value &= ~INOUTEPMPSR_RESET_TSEQ; - iowrite32(value, reg); -} - -static int fotg210_ep_release(struct fotg210_ep *ep) -{ - if (!ep->epnum) - return 0; - ep->epnum = 0; - ep->stall = 0; - ep->wedged = 0; - - fotg210_reset_tseq(ep->fotg210, ep->epnum); - - return 0; -} - -static int fotg210_ep_disable(struct usb_ep *_ep) -{ - struct fotg210_ep *ep; - struct fotg210_request *req; - unsigned long flags; - - BUG_ON(!_ep); - - ep = container_of(_ep, struct fotg210_ep, ep); - - while (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, - struct fotg210_request, queue); - spin_lock_irqsave(&ep->fotg210->lock, flags); - fotg210_done(ep, req, -ECONNRESET); - spin_unlock_irqrestore(&ep->fotg210->lock, flags); - } - - return fotg210_ep_release(ep); -} - -static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, - gfp_t gfp_flags) -{ - struct fotg210_request *req; - - req = kzalloc(sizeof(struct fotg210_request), gfp_flags); - if (!req) - return NULL; - - INIT_LIST_HEAD(&req->queue); - - return &req->req; -} - -static void fotg210_ep_free_request(struct usb_ep *_ep, - struct usb_request *_req) -{ - struct fotg210_request *req; - - req = container_of(_req, struct fotg210_request, req); - kfree(req); -} - -static void fotg210_enable_dma(struct fotg210_ep *ep, - dma_addr_t d, u32 len) -{ - u32 value; - struct fotg210_udc *fotg210 = ep->fotg210; - - /* set transfer length and direction */ - value = ioread32(fotg210->reg + FOTG210_DMACPSR1); - value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1)); - value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); - iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); - - /* set device DMA target FIFO number */ - value = ioread32(fotg210->reg + FOTG210_DMATFNR); - if (ep->epnum) - value |= DMATFNR_ACC_FN(ep->epnum - 1); - else - value |= DMATFNR_ACC_CXF; - iowrite32(value, fotg210->reg + FOTG210_DMATFNR); - - /* set DMA memory address */ - iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); - - /* enable MDMA_EROR and MDMA_CMPLT interrupt */ - value = ioread32(fotg210->reg + FOTG210_DMISGR2); - value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); - iowrite32(value, fotg210->reg + FOTG210_DMISGR2); - - /* start DMA */ - value = ioread32(fotg210->reg + FOTG210_DMACPSR1); - value |= DMACPSR1_DMA_START; - iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); -} - -static void fotg210_disable_dma(struct fotg210_ep *ep) -{ - iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); -} - -static void fotg210_wait_dma_done(struct fotg210_ep *ep) -{ - u32 value; - - do { - value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); - if ((value & DISGR2_USBRST_INT) || - (value & DISGR2_DMA_ERROR)) - goto dma_reset; - } while (!(value & DISGR2_DMA_CMPLT)); - - value &= ~DISGR2_DMA_CMPLT; - iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); - return; - -dma_reset: - value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); - value |= DMACPSR1_DMA_ABORT; - iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); - - /* reset fifo */ - if (ep->epnum) { - value = ioread32(ep->fotg210->reg + - FOTG210_FIBCR(ep->epnum - 1)); - value |= FIBCR_FFRST; - iowrite32(value, ep->fotg210->reg + - FOTG210_FIBCR(ep->epnum - 1)); - } else { - value = ioread32(ep->fotg210->reg + FOTG210_DCFESR); - value |= DCFESR_CX_CLR; - iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR); - } -} - -static void fotg210_start_dma(struct fotg210_ep *ep, - struct fotg210_request *req) -{ - struct device *dev = &ep->fotg210->gadget.dev; - dma_addr_t d; - u8 *buffer; - u32 length; - - if (ep->epnum) { - if (ep->dir_in) { - buffer = req->req.buf; - length = req->req.length; - } else { - buffer = req->req.buf + req->req.actual; - length = ioread32(ep->fotg210->reg + - FOTG210_FIBCR(ep->epnum - 1)) & FIBCR_BCFX; - if (length > req->req.length - req->req.actual) - length = req->req.length - req->req.actual; - } - } else { - buffer = req->req.buf + req->req.actual; - if (req->req.length - req->req.actual > ep->ep.maxpacket) - length = ep->ep.maxpacket; - else - length = req->req.length - req->req.actual; - } - - d = dma_map_single(dev, buffer, length, - ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - if (dma_mapping_error(dev, d)) { - pr_err("dma_mapping_error\n"); - return; - } - - fotg210_enable_dma(ep, d, length); - - /* check if dma is done */ - fotg210_wait_dma_done(ep); - - fotg210_disable_dma(ep); - - /* update actual transfer length */ - req->req.actual += length; - - dma_unmap_single(dev, d, length, DMA_TO_DEVICE); -} - -static void fotg210_ep0_queue(struct fotg210_ep *ep, - struct fotg210_request *req) -{ - if (!req->req.length) { - fotg210_done(ep, req, 0); - return; - } - if (ep->dir_in) { /* if IN */ - fotg210_start_dma(ep, req); - if (req->req.length == req->req.actual) - fotg210_done(ep, req, 0); - } else { /* OUT */ - u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR0); - - value &= ~DMISGR0_MCX_OUT_INT; - iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0); - } -} - -static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, - gfp_t gfp_flags) -{ - struct fotg210_ep *ep; - struct fotg210_request *req; - unsigned long flags; - int request = 0; - - ep = container_of(_ep, struct fotg210_ep, ep); - req = container_of(_req, struct fotg210_request, req); - - if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - spin_lock_irqsave(&ep->fotg210->lock, flags); - - if (list_empty(&ep->queue)) - request = 1; - - list_add_tail(&req->queue, &ep->queue); - - req->req.actual = 0; - req->req.status = -EINPROGRESS; - - if (!ep->epnum) /* ep0 */ - fotg210_ep0_queue(ep, req); - else if (request && !ep->stall) - fotg210_enable_fifo_int(ep); - - spin_unlock_irqrestore(&ep->fotg210->lock, flags); - - return 0; -} - -static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) -{ - struct fotg210_ep *ep; - struct fotg210_request *req; - unsigned long flags; - - ep = container_of(_ep, struct fotg210_ep, ep); - req = container_of(_req, struct fotg210_request, req); - - spin_lock_irqsave(&ep->fotg210->lock, flags); - if (!list_empty(&ep->queue)) - fotg210_done(ep, req, -ECONNRESET); - spin_unlock_irqrestore(&ep->fotg210->lock, flags); - - return 0; -} - -static void fotg210_set_epnstall(struct fotg210_ep *ep) -{ - struct fotg210_udc *fotg210 = ep->fotg210; - u32 value; - void __iomem *reg; - - /* check if IN FIFO is empty before stall */ - if (ep->dir_in) { - do { - value = ioread32(fotg210->reg + FOTG210_DCFESR); - } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1))); - } - - reg = (ep->dir_in) ? - fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : - fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); - value = ioread32(reg); - value |= INOUTEPMPSR_STL_EP; - iowrite32(value, reg); -} - -static void fotg210_clear_epnstall(struct fotg210_ep *ep) -{ - struct fotg210_udc *fotg210 = ep->fotg210; - u32 value; - void __iomem *reg; - - reg = (ep->dir_in) ? - fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : - fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); - value = ioread32(reg); - value &= ~INOUTEPMPSR_STL_EP; - iowrite32(value, reg); -} - -static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) -{ - struct fotg210_ep *ep; - struct fotg210_udc *fotg210; - unsigned long flags; - - ep = container_of(_ep, struct fotg210_ep, ep); - - fotg210 = ep->fotg210; - - spin_lock_irqsave(&ep->fotg210->lock, flags); - - if (value) { - fotg210_set_epnstall(ep); - ep->stall = 1; - if (wedge) - ep->wedged = 1; - } else { - fotg210_reset_tseq(fotg210, ep->epnum); - fotg210_clear_epnstall(ep); - ep->stall = 0; - ep->wedged = 0; - if (!list_empty(&ep->queue)) - fotg210_enable_fifo_int(ep); - } - - spin_unlock_irqrestore(&ep->fotg210->lock, flags); - return 0; -} - -static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) -{ - return fotg210_set_halt_and_wedge(_ep, value, 0); -} - -static int fotg210_ep_set_wedge(struct usb_ep *_ep) -{ - return fotg210_set_halt_and_wedge(_ep, 1, 1); -} - -static void fotg210_ep_fifo_flush(struct usb_ep *_ep) -{ -} - -static const struct usb_ep_ops fotg210_ep_ops = { - .enable = fotg210_ep_enable, - .disable = fotg210_ep_disable, - - .alloc_request = fotg210_ep_alloc_request, - .free_request = fotg210_ep_free_request, - - .queue = fotg210_ep_queue, - .dequeue = fotg210_ep_dequeue, - - .set_halt = fotg210_ep_set_halt, - .fifo_flush = fotg210_ep_fifo_flush, - .set_wedge = fotg210_ep_set_wedge, -}; - -static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) -{ - u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); - - value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 - | TX0BYTE_EP4); - iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); -} - -static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) -{ - u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); - - value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 - | RX0BYTE_EP4); - iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); -} - -/* read 8-byte setup packet only */ -static void fotg210_rdsetupp(struct fotg210_udc *fotg210, - u8 *buffer) -{ - int i = 0; - u8 *tmp = buffer; - u32 data; - u32 length = 8; - - iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); - - for (i = (length >> 2); i > 0; i--) { - data = ioread32(fotg210->reg + FOTG210_CXPORT); - *tmp = data & 0xFF; - *(tmp + 1) = (data >> 8) & 0xFF; - *(tmp + 2) = (data >> 16) & 0xFF; - *(tmp + 3) = (data >> 24) & 0xFF; - tmp = tmp + 4; - } - - switch (length % 4) { - case 1: - data = ioread32(fotg210->reg + FOTG210_CXPORT); - *tmp = data & 0xFF; - break; - case 2: - data = ioread32(fotg210->reg + FOTG210_CXPORT); - *tmp = data & 0xFF; - *(tmp + 1) = (data >> 8) & 0xFF; - break; - case 3: - data = ioread32(fotg210->reg + FOTG210_CXPORT); - *tmp = data & 0xFF; - *(tmp + 1) = (data >> 8) & 0xFF; - *(tmp + 2) = (data >> 16) & 0xFF; - break; - default: - break; - } - - iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); -} - -static void fotg210_set_configuration(struct fotg210_udc *fotg210) -{ - u32 value = ioread32(fotg210->reg + FOTG210_DAR); - - value |= DAR_AFT_CONF; - iowrite32(value, fotg210->reg + FOTG210_DAR); -} - -static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) -{ - u32 value = ioread32(fotg210->reg + FOTG210_DAR); - - value |= (addr & 0x7F); - iowrite32(value, fotg210->reg + FOTG210_DAR); -} - -static void fotg210_set_cxstall(struct fotg210_udc *fotg210) -{ - u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); - - value |= DCFESR_CX_STL; - iowrite32(value, fotg210->reg + FOTG210_DCFESR); -} - -static void fotg210_request_error(struct fotg210_udc *fotg210) -{ - fotg210_set_cxstall(fotg210); - pr_err("request error!!\n"); -} - -static void fotg210_set_address(struct fotg210_udc *fotg210, - struct usb_ctrlrequest *ctrl) -{ - if (le16_to_cpu(ctrl->wValue) >= 0x0100) { - fotg210_request_error(fotg210); - } else { - fotg210_set_dev_addr(fotg210, le16_to_cpu(ctrl->wValue)); - fotg210_set_cxdone(fotg210); - } -} - -static void fotg210_set_feature(struct fotg210_udc *fotg210, - struct usb_ctrlrequest *ctrl) -{ - switch (ctrl->bRequestType & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - fotg210_set_cxdone(fotg210); - break; - case USB_RECIP_INTERFACE: - fotg210_set_cxdone(fotg210); - break; - case USB_RECIP_ENDPOINT: { - u8 epnum; - epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; - if (epnum) - fotg210_set_epnstall(fotg210->ep[epnum]); - else - fotg210_set_cxstall(fotg210); - fotg210_set_cxdone(fotg210); - } - break; - default: - fotg210_request_error(fotg210); - break; - } -} - -static void fotg210_clear_feature(struct fotg210_udc *fotg210, - struct usb_ctrlrequest *ctrl) -{ - struct fotg210_ep *ep = - fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; - - switch (ctrl->bRequestType & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - fotg210_set_cxdone(fotg210); - break; - case USB_RECIP_INTERFACE: - fotg210_set_cxdone(fotg210); - break; - case USB_RECIP_ENDPOINT: - if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { - if (ep->wedged) { - fotg210_set_cxdone(fotg210); - break; - } - if (ep->stall) - fotg210_set_halt_and_wedge(&ep->ep, 0, 0); - } - fotg210_set_cxdone(fotg210); - break; - default: - fotg210_request_error(fotg210); - break; - } -} - -static int fotg210_is_epnstall(struct fotg210_ep *ep) -{ - struct fotg210_udc *fotg210 = ep->fotg210; - u32 value; - void __iomem *reg; - - reg = (ep->dir_in) ? - fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : - fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); - value = ioread32(reg); - return value & INOUTEPMPSR_STL_EP ? 1 : 0; -} - -static void fotg210_get_status(struct fotg210_udc *fotg210, - struct usb_ctrlrequest *ctrl) -{ - u8 epnum; - - switch (ctrl->bRequestType & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - fotg210->ep0_data = cpu_to_le16(1 << USB_DEVICE_SELF_POWERED); - break; - case USB_RECIP_INTERFACE: - fotg210->ep0_data = cpu_to_le16(0); - break; - case USB_RECIP_ENDPOINT: - epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; - if (epnum) - fotg210->ep0_data = - cpu_to_le16(fotg210_is_epnstall(fotg210->ep[epnum]) - << USB_ENDPOINT_HALT); - else - fotg210_request_error(fotg210); - break; - - default: - fotg210_request_error(fotg210); - return; /* exit */ - } - - fotg210->ep0_req->buf = &fotg210->ep0_data; - fotg210->ep0_req->length = 2; - - spin_unlock(&fotg210->lock); - fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC); - spin_lock(&fotg210->lock); -} - -static int fotg210_setup_packet(struct fotg210_udc *fotg210, - struct usb_ctrlrequest *ctrl) -{ - u8 *p = (u8 *)ctrl; - u8 ret = 0; - - fotg210_rdsetupp(fotg210, p); - - fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; - - if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { - u32 value = ioread32(fotg210->reg + FOTG210_DMCR); - fotg210->gadget.speed = value & DMCR_HS_EN ? - USB_SPEED_HIGH : USB_SPEED_FULL; - } - - /* check request */ - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { - switch (ctrl->bRequest) { - case USB_REQ_GET_STATUS: - fotg210_get_status(fotg210, ctrl); - break; - case USB_REQ_CLEAR_FEATURE: - fotg210_clear_feature(fotg210, ctrl); - break; - case USB_REQ_SET_FEATURE: - fotg210_set_feature(fotg210, ctrl); - break; - case USB_REQ_SET_ADDRESS: - fotg210_set_address(fotg210, ctrl); - break; - case USB_REQ_SET_CONFIGURATION: - fotg210_set_configuration(fotg210); - ret = 1; - break; - default: - ret = 1; - break; - } - } else { - ret = 1; - } - - return ret; -} - -static void fotg210_ep0out(struct fotg210_udc *fotg210) -{ - struct fotg210_ep *ep = fotg210->ep[0]; - - if (!list_empty(&ep->queue) && !ep->dir_in) { - struct fotg210_request *req; - - req = list_first_entry(&ep->queue, - struct fotg210_request, queue); - - if (req->req.length) - fotg210_start_dma(ep, req); - - if ((req->req.length - req->req.actual) < ep->ep.maxpacket) - fotg210_done(ep, req, 0); - } else { - pr_err("%s : empty queue\n", __func__); - } -} - -static void fotg210_ep0in(struct fotg210_udc *fotg210) -{ - struct fotg210_ep *ep = fotg210->ep[0]; - - if ((!list_empty(&ep->queue)) && (ep->dir_in)) { - struct fotg210_request *req; - - req = list_entry(ep->queue.next, - struct fotg210_request, queue); - - if (req->req.length) - fotg210_start_dma(ep, req); - - if (req->req.actual == req->req.length) - fotg210_done(ep, req, 0); - } else { - fotg210_set_cxdone(fotg210); - } -} - -static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) -{ - u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); - - value &= ~DISGR0_CX_COMABT_INT; - iowrite32(value, fotg210->reg + FOTG210_DISGR0); -} - -static void fotg210_in_fifo_handler(struct fotg210_ep *ep) -{ - struct fotg210_request *req = list_entry(ep->queue.next, - struct fotg210_request, queue); - - if (req->req.length) - fotg210_start_dma(ep, req); - fotg210_done(ep, req, 0); -} - -static void fotg210_out_fifo_handler(struct fotg210_ep *ep) -{ - struct fotg210_request *req = list_entry(ep->queue.next, - struct fotg210_request, queue); - int disgr1 = ioread32(ep->fotg210->reg + FOTG210_DISGR1); - - fotg210_start_dma(ep, req); - - /* Complete the request when it's full or a short packet arrived. - * Like other drivers, short_not_ok isn't handled. - */ - - if (req->req.length == req->req.actual || - (disgr1 & DISGR1_SPK_INT(ep->epnum - 1))) - fotg210_done(ep, req, 0); -} - -static irqreturn_t fotg210_irq(int irq, void *_fotg210) -{ - struct fotg210_udc *fotg210 = _fotg210; - u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); - u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); - - int_grp &= ~int_msk; - - spin_lock(&fotg210->lock); - - if (int_grp & DIGR_INT_G2) { - void __iomem *reg = fotg210->reg + FOTG210_DISGR2; - u32 int_grp2 = ioread32(reg); - u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); - u32 value; - - int_grp2 &= ~int_msk2; - - if (int_grp2 & DISGR2_USBRST_INT) { - usb_gadget_udc_reset(&fotg210->gadget, - fotg210->driver); - value = ioread32(reg); - value &= ~DISGR2_USBRST_INT; - iowrite32(value, reg); - pr_info("fotg210 udc reset\n"); - } - if (int_grp2 & DISGR2_SUSP_INT) { - value = ioread32(reg); - value &= ~DISGR2_SUSP_INT; - iowrite32(value, reg); - pr_info("fotg210 udc suspend\n"); - } - if (int_grp2 & DISGR2_RESM_INT) { - value = ioread32(reg); - value &= ~DISGR2_RESM_INT; - iowrite32(value, reg); - pr_info("fotg210 udc resume\n"); - } - if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { - value = ioread32(reg); - value &= ~DISGR2_ISO_SEQ_ERR_INT; - iowrite32(value, reg); - pr_info("fotg210 iso sequence error\n"); - } - if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { - value = ioread32(reg); - value &= ~DISGR2_ISO_SEQ_ABORT_INT; - iowrite32(value, reg); - pr_info("fotg210 iso sequence abort\n"); - } - if (int_grp2 & DISGR2_TX0BYTE_INT) { - fotg210_clear_tx0byte(fotg210); - value = ioread32(reg); - value &= ~DISGR2_TX0BYTE_INT; - iowrite32(value, reg); - pr_info("fotg210 transferred 0 byte\n"); - } - if (int_grp2 & DISGR2_RX0BYTE_INT) { - fotg210_clear_rx0byte(fotg210); - value = ioread32(reg); - value &= ~DISGR2_RX0BYTE_INT; - iowrite32(value, reg); - pr_info("fotg210 received 0 byte\n"); - } - if (int_grp2 & DISGR2_DMA_ERROR) { - value = ioread32(reg); - value &= ~DISGR2_DMA_ERROR; - iowrite32(value, reg); - } - } - - if (int_grp & DIGR_INT_G0) { - void __iomem *reg = fotg210->reg + FOTG210_DISGR0; - u32 int_grp0 = ioread32(reg); - u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); - struct usb_ctrlrequest ctrl; - - int_grp0 &= ~int_msk0; - - /* the highest priority in this source register */ - if (int_grp0 & DISGR0_CX_COMABT_INT) { - fotg210_clear_comabt_int(fotg210); - pr_info("fotg210 CX command abort\n"); - } - - if (int_grp0 & DISGR0_CX_SETUP_INT) { - if (fotg210_setup_packet(fotg210, &ctrl)) { - spin_unlock(&fotg210->lock); - if (fotg210->driver->setup(&fotg210->gadget, - &ctrl) < 0) - fotg210_set_cxstall(fotg210); - spin_lock(&fotg210->lock); - } - } - if (int_grp0 & DISGR0_CX_COMEND_INT) - pr_info("fotg210 cmd end\n"); - - if (int_grp0 & DISGR0_CX_IN_INT) - fotg210_ep0in(fotg210); - - if (int_grp0 & DISGR0_CX_OUT_INT) - fotg210_ep0out(fotg210); - - if (int_grp0 & DISGR0_CX_COMFAIL_INT) { - fotg210_set_cxstall(fotg210); - pr_info("fotg210 ep0 fail\n"); - } - } - - if (int_grp & DIGR_INT_G1) { - void __iomem *reg = fotg210->reg + FOTG210_DISGR1; - u32 int_grp1 = ioread32(reg); - u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); - int fifo; - - int_grp1 &= ~int_msk1; - - for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { - if (int_grp1 & DISGR1_IN_INT(fifo)) - fotg210_in_fifo_handler(fotg210->ep[fifo + 1]); - - if ((int_grp1 & DISGR1_OUT_INT(fifo)) || - (int_grp1 & DISGR1_SPK_INT(fifo))) - fotg210_out_fifo_handler(fotg210->ep[fifo + 1]); - } - } - - spin_unlock(&fotg210->lock); - - return IRQ_HANDLED; -} - -static void fotg210_disable_unplug(struct fotg210_udc *fotg210) -{ - u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); - - reg &= ~PHYTMSR_UNPLUG; - iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); -} - -static int fotg210_udc_start(struct usb_gadget *g, - struct usb_gadget_driver *driver) -{ - struct fotg210_udc *fotg210 = gadget_to_fotg210(g); - u32 value; - int ret; - - /* hook up the driver */ - fotg210->driver = driver; - - if (!IS_ERR_OR_NULL(fotg210->phy)) { - ret = otg_set_peripheral(fotg210->phy->otg, - &fotg210->gadget); - if (ret) - dev_err(fotg210->dev, "can't bind to phy\n"); - } - - /* enable device global interrupt */ - value = ioread32(fotg210->reg + FOTG210_DMCR); - value |= DMCR_GLINT_EN; - iowrite32(value, fotg210->reg + FOTG210_DMCR); - - return 0; -} - -static void fotg210_init(struct fotg210_udc *fotg210) -{ - u32 value; - - /* disable global interrupt and set int polarity to active high */ - iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, - fotg210->reg + FOTG210_GMIR); - - /* disable device global interrupt */ - value = ioread32(fotg210->reg + FOTG210_DMCR); - value &= ~DMCR_GLINT_EN; - iowrite32(value, fotg210->reg + FOTG210_DMCR); - - /* enable only grp2 irqs we handle */ - iowrite32(~(DISGR2_DMA_ERROR | DISGR2_RX0BYTE_INT | DISGR2_TX0BYTE_INT - | DISGR2_ISO_SEQ_ABORT_INT | DISGR2_ISO_SEQ_ERR_INT - | DISGR2_RESM_INT | DISGR2_SUSP_INT | DISGR2_USBRST_INT), - fotg210->reg + FOTG210_DMISGR2); - - /* disable all fifo interrupt */ - iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); - - /* disable cmd end */ - value = ioread32(fotg210->reg + FOTG210_DMISGR0); - value |= DMISGR0_MCX_COMEND; - iowrite32(value, fotg210->reg + FOTG210_DMISGR0); -} - -static int fotg210_udc_stop(struct usb_gadget *g) -{ - struct fotg210_udc *fotg210 = gadget_to_fotg210(g); - unsigned long flags; - - if (!IS_ERR_OR_NULL(fotg210->phy)) - return otg_set_peripheral(fotg210->phy->otg, NULL); - - spin_lock_irqsave(&fotg210->lock, flags); - - fotg210_init(fotg210); - fotg210->driver = NULL; - - spin_unlock_irqrestore(&fotg210->lock, flags); - - return 0; -} - -static const struct usb_gadget_ops fotg210_gadget_ops = { - .udc_start = fotg210_udc_start, - .udc_stop = fotg210_udc_stop, -}; - -/** - * fotg210_phy_event - Called by phy upon VBus event - * @nb: notifier block - * @action: phy action, is vbus connect or disconnect - * @data: the usb_gadget structure in fotg210 - * - * Called by the USB Phy when a cable connect or disconnect is sensed. - * - * Returns NOTIFY_OK or NOTIFY_DONE - */ -static int fotg210_phy_event(struct notifier_block *nb, unsigned long action, - void *data) -{ - struct usb_gadget *gadget = data; - - if (!gadget) - return NOTIFY_DONE; - - switch (action) { - case USB_EVENT_VBUS: - usb_gadget_vbus_connect(gadget); - return NOTIFY_OK; - case USB_EVENT_NONE: - usb_gadget_vbus_disconnect(gadget); - return NOTIFY_OK; - default: - return NOTIFY_DONE; - } -} - -static struct notifier_block fotg210_phy_notifier = { - .notifier_call = fotg210_phy_event, -}; - -int fotg210_udc_remove(struct platform_device *pdev) -{ - struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); - int i; - - usb_del_gadget_udc(&fotg210->gadget); - if (!IS_ERR_OR_NULL(fotg210->phy)) { - usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); - usb_put_phy(fotg210->phy); - } - iounmap(fotg210->reg); - free_irq(platform_get_irq(pdev, 0), fotg210); - - fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) - kfree(fotg210->ep[i]); - - if (!IS_ERR(fotg210->pclk)) - clk_disable_unprepare(fotg210->pclk); - - kfree(fotg210); - - return 0; -} - -int fotg210_udc_probe(struct platform_device *pdev) -{ - struct resource *res; - struct fotg210_udc *fotg210 = NULL; - struct device *dev = &pdev->dev; - int irq; - int ret = 0; - int i; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - pr_err("platform_get_resource error.\n"); - return -ENODEV; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - pr_err("could not get irq\n"); - return -ENODEV; - } - - /* initialize udc */ - fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); - if (fotg210 == NULL) - return -ENOMEM; - - fotg210->dev = dev; - - /* It's OK not to supply this clock */ - fotg210->pclk = devm_clk_get(dev, "PCLK"); - if (!IS_ERR(fotg210->pclk)) { - ret = clk_prepare_enable(fotg210->pclk); - if (ret) { - dev_err(dev, "failed to enable PCLK\n"); - goto err; - } - } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { - /* - * Percolate deferrals, for anything else, - * just live without the clocking. - */ - ret = -EPROBE_DEFER; - goto err; - } - - fotg210->phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); - if (IS_ERR(fotg210->phy)) { - ret = PTR_ERR(fotg210->phy); - if (ret == -EPROBE_DEFER) - goto err_pclk; - dev_info(dev, "no PHY found\n"); - fotg210->phy = NULL; - } else { - ret = usb_phy_init(fotg210->phy); - if (ret) - goto err_pclk; - dev_info(dev, "found and initialized PHY\n"); - } - - ret = -ENOMEM; - - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { - fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); - if (!fotg210->ep[i]) - goto err_alloc; - } - - fotg210->reg = ioremap(res->start, resource_size(res)); - if (fotg210->reg == NULL) { - dev_err(dev, "ioremap error\n"); - goto err_alloc; - } - - spin_lock_init(&fotg210->lock); - - platform_set_drvdata(pdev, fotg210); - - fotg210->gadget.ops = &fotg210_gadget_ops; - - fotg210->gadget.max_speed = USB_SPEED_HIGH; - fotg210->gadget.dev.parent = dev; - fotg210->gadget.dev.dma_mask = dev->dma_mask; - fotg210->gadget.name = udc_name; - - INIT_LIST_HEAD(&fotg210->gadget.ep_list); - - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { - struct fotg210_ep *ep = fotg210->ep[i]; - - if (i) { - INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); - list_add_tail(&fotg210->ep[i]->ep.ep_list, - &fotg210->gadget.ep_list); - } - ep->fotg210 = fotg210; - INIT_LIST_HEAD(&ep->queue); - ep->ep.name = fotg210_ep_name[i]; - ep->ep.ops = &fotg210_ep_ops; - usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); - - if (i == 0) { - ep->ep.caps.type_control = true; - } else { - ep->ep.caps.type_iso = true; - ep->ep.caps.type_bulk = true; - ep->ep.caps.type_int = true; - } - - ep->ep.caps.dir_in = true; - ep->ep.caps.dir_out = true; - } - usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40); - fotg210->gadget.ep0 = &fotg210->ep[0]->ep; - INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); - - fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, - GFP_KERNEL); - if (fotg210->ep0_req == NULL) - goto err_map; - - fotg210_init(fotg210); - - fotg210_disable_unplug(fotg210); - - ret = request_irq(irq, fotg210_irq, IRQF_SHARED, - udc_name, fotg210); - if (ret < 0) { - dev_err(dev, "request_irq error (%d)\n", ret); - goto err_req; - } - - if (!IS_ERR_OR_NULL(fotg210->phy)) - usb_register_notifier(fotg210->phy, &fotg210_phy_notifier); - - ret = usb_add_gadget_udc(dev, &fotg210->gadget); - if (ret) - goto err_add_udc; - - dev_info(dev, "version %s\n", DRIVER_VERSION); - - return 0; - -err_add_udc: - if (!IS_ERR_OR_NULL(fotg210->phy)) - usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); - free_irq(irq, fotg210); - -err_req: - fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); - -err_map: - iounmap(fotg210->reg); - -err_alloc: - for (i = 0; i < FOTG210_MAX_NUM_EP; i++) - kfree(fotg210->ep[i]); -err_pclk: - if (!IS_ERR(fotg210->pclk)) - clk_disable_unprepare(fotg210->pclk); - -err: - kfree(fotg210); - return ret; -} diff --git a/drivers/usb/fotg210/fotg210-udc.h b/drivers/usb/fotg210/fotg210-udc.h deleted file mode 100644 index fadb57ca8d7863..00000000000000 --- a/drivers/usb/fotg210/fotg210-udc.h +++ /dev/null @@ -1,252 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Faraday FOTG210 USB OTG controller - * - * Copyright (C) 2013 Faraday Technology Corporation - * Author: Yuan-Hsin Chen - */ - -#include - -#define FOTG210_MAX_NUM_EP 5 /* ep0...ep4 */ -#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo4 */ - -/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */ -#define FOTG210_GMIR 0xC4 -#define GMIR_INT_POLARITY 0x8 /*Active High*/ -#define GMIR_MHC_INT 0x4 -#define GMIR_MOTG_INT 0x2 -#define GMIR_MDEV_INT 0x1 - -/* Device Main Control Register(0x100) */ -#define FOTG210_DMCR 0x100 -#define DMCR_HS_EN (1 << 6) -#define DMCR_CHIP_EN (1 << 5) -#define DMCR_SFRST (1 << 4) -#define DMCR_GOSUSP (1 << 3) -#define DMCR_GLINT_EN (1 << 2) -#define DMCR_HALF_SPEED (1 << 1) -#define DMCR_CAP_RMWAKUP (1 << 0) - -/* Device Address Register(0x104) */ -#define FOTG210_DAR 0x104 -#define DAR_AFT_CONF (1 << 7) - -/* Device Test Register(0x108) */ -#define FOTG210_DTR 0x108 -#define DTR_TST_CLRFF (1 << 0) - -/* PHY Test Mode Selector register(0x114) */ -#define FOTG210_PHYTMSR 0x114 -#define PHYTMSR_TST_PKT (1 << 4) -#define PHYTMSR_TST_SE0NAK (1 << 3) -#define PHYTMSR_TST_KSTA (1 << 2) -#define PHYTMSR_TST_JSTA (1 << 1) -#define PHYTMSR_UNPLUG (1 << 0) - -/* Cx configuration and FIFO Empty Status register(0x120) */ -#define FOTG210_DCFESR 0x120 -#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo)) -#define DCFESR_CX_EMP (1 << 5) -#define DCFESR_CX_CLR (1 << 3) -#define DCFESR_CX_STL (1 << 2) -#define DCFESR_TST_PKDONE (1 << 1) -#define DCFESR_CX_DONE (1 << 0) - -/* Device IDLE Counter Register(0x124) */ -#define FOTG210_DICR 0x124 - -/* Device Mask of Interrupt Group Register (0x130) */ -#define FOTG210_DMIGR 0x130 -#define DMIGR_MINT_G0 (1 << 0) - -/* Device Mask of Interrupt Source Group 0(0x134) */ -#define FOTG210_DMISGR0 0x134 -#define DMISGR0_MCX_COMEND (1 << 3) -#define DMISGR0_MCX_OUT_INT (1 << 2) -#define DMISGR0_MCX_IN_INT (1 << 1) -#define DMISGR0_MCX_SETUP_INT (1 << 0) - -/* Device Mask of Interrupt Source Group 1 Register(0x138)*/ -#define FOTG210_DMISGR1 0x138 -#define DMISGR1_MF3_IN_INT (1 << 19) -#define DMISGR1_MF2_IN_INT (1 << 18) -#define DMISGR1_MF1_IN_INT (1 << 17) -#define DMISGR1_MF0_IN_INT (1 << 16) -#define DMISGR1_MF_IN_INT(fifo) (1 << (16 + (fifo))) -#define DMISGR1_MF3_SPK_INT (1 << 7) -#define DMISGR1_MF3_OUT_INT (1 << 6) -#define DMISGR1_MF2_SPK_INT (1 << 5) -#define DMISGR1_MF2_OUT_INT (1 << 4) -#define DMISGR1_MF1_SPK_INT (1 << 3) -#define DMISGR1_MF1_OUT_INT (1 << 2) -#define DMISGR1_MF0_SPK_INT (1 << 1) -#define DMISGR1_MF0_OUT_INT (1 << 0) -#define DMISGR1_MF_OUTSPK_INT(fifo) (0x3 << (fifo) * 2) - -/* Device Mask of Interrupt Source Group 2 Register (0x13C) */ -#define FOTG210_DMISGR2 0x13C -#define DMISGR2_MDMA_ERROR (1 << 8) -#define DMISGR2_MDMA_CMPLT (1 << 7) - -/* Device Interrupt group Register (0x140) */ -#define FOTG210_DIGR 0x140 -#define DIGR_INT_G2 (1 << 2) -#define DIGR_INT_G1 (1 << 1) -#define DIGR_INT_G0 (1 << 0) - -/* Device Interrupt Source Group 0 Register (0x144) */ -#define FOTG210_DISGR0 0x144 -#define DISGR0_CX_COMABT_INT (1 << 5) -#define DISGR0_CX_COMFAIL_INT (1 << 4) -#define DISGR0_CX_COMEND_INT (1 << 3) -#define DISGR0_CX_OUT_INT (1 << 2) -#define DISGR0_CX_IN_INT (1 << 1) -#define DISGR0_CX_SETUP_INT (1 << 0) - -/* Device Interrupt Source Group 1 Register (0x148) */ -#define FOTG210_DISGR1 0x148 -#define DISGR1_OUT_INT(fifo) (1 << ((fifo) * 2)) -#define DISGR1_SPK_INT(fifo) (1 << 1 << ((fifo) * 2)) -#define DISGR1_IN_INT(fifo) (1 << 16 << (fifo)) - -/* Device Interrupt Source Group 2 Register (0x14C) */ -#define FOTG210_DISGR2 0x14C -#define DISGR2_DMA_ERROR (1 << 8) -#define DISGR2_DMA_CMPLT (1 << 7) -#define DISGR2_RX0BYTE_INT (1 << 6) -#define DISGR2_TX0BYTE_INT (1 << 5) -#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4) -#define DISGR2_ISO_SEQ_ERR_INT (1 << 3) -#define DISGR2_RESM_INT (1 << 2) -#define DISGR2_SUSP_INT (1 << 1) -#define DISGR2_USBRST_INT (1 << 0) - -/* Device Receive Zero-Length Data Packet Register (0x150)*/ -#define FOTG210_RX0BYTE 0x150 -#define RX0BYTE_EP8 (1 << 7) -#define RX0BYTE_EP7 (1 << 6) -#define RX0BYTE_EP6 (1 << 5) -#define RX0BYTE_EP5 (1 << 4) -#define RX0BYTE_EP4 (1 << 3) -#define RX0BYTE_EP3 (1 << 2) -#define RX0BYTE_EP2 (1 << 1) -#define RX0BYTE_EP1 (1 << 0) - -/* Device Transfer Zero-Length Data Packet Register (0x154)*/ -#define FOTG210_TX0BYTE 0x154 -#define TX0BYTE_EP8 (1 << 7) -#define TX0BYTE_EP7 (1 << 6) -#define TX0BYTE_EP6 (1 << 5) -#define TX0BYTE_EP5 (1 << 4) -#define TX0BYTE_EP4 (1 << 3) -#define TX0BYTE_EP3 (1 << 2) -#define TX0BYTE_EP2 (1 << 1) -#define TX0BYTE_EP1 (1 << 0) - -/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */ -#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1)) -#define INOUTEPMPSR_MPS(mps) ((mps) & 0x2FF) -#define INOUTEPMPSR_STL_EP (1 << 11) -#define INOUTEPMPSR_RESET_TSEQ (1 << 12) - -/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */ -#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1)) - -/* Device Endpoint 1~4 Map Register (0x1A0) */ -#define FOTG210_EPMAP 0x1A0 -#define EPMAP_FIFONO(ep, dir) \ - ((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) -#define EPMAP_FIFONOMSK(ep, dir) \ - ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) - -/* Device FIFO Map Register (0x1A8) */ -#define FOTG210_FIFOMAP 0x1A8 -#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << (fifo) * 8) -#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << (fifo) * 8) -#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << (fifo) * 8) -#define FIFOMAP_NA(fifo) (0x3 << 4 << (fifo) * 8) -#define FIFOMAP_EPNO(ep) ((ep) << ((ep) - 1) * 8) -#define FIFOMAP_EPNOMSK(ep) (0xF << ((ep) - 1) * 8) - -/* Device FIFO Confuguration Register (0x1AC) */ -#define FOTG210_FIFOCF 0x1AC -#define FIFOCF_TYPE(type, fifo) ((type) << (fifo) * 8) -#define FIFOCF_BLK_SIN(fifo) (0x0 << (fifo) * 8 << 2) -#define FIFOCF_BLK_DUB(fifo) (0x1 << (fifo) * 8 << 2) -#define FIFOCF_BLK_TRI(fifo) (0x2 << (fifo) * 8 << 2) -#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4) -#define FIFOCF_BLKSZ_1024(fifo) (0x1 << (fifo) * 8 << 4) -#define FIFOCF_FIFO_EN(fifo) (0x1 << (fifo) * 8 << 5) - -/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */ -#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4) -#define FIBCR_BCFX 0x7FF -#define FIBCR_FFRST (1 << 12) - -/* Device DMA Target FIFO Number Register (0x1C0) */ -#define FOTG210_DMATFNR 0x1C0 -#define DMATFNR_ACC_CXF (1 << 4) -#define DMATFNR_ACC_F3 (1 << 3) -#define DMATFNR_ACC_F2 (1 << 2) -#define DMATFNR_ACC_F1 (1 << 1) -#define DMATFNR_ACC_F0 (1 << 0) -#define DMATFNR_ACC_FN(fifo) (1 << (fifo)) -#define DMATFNR_DISDMA 0 - -/* Device DMA Controller Parameter setting 1 Register (0x1C8) */ -#define FOTG210_DMACPSR1 0x1C8 -#define DMACPSR1_DMA_LEN(len) (((len) & 0xFFFF) << 8) -#define DMACPSR1_DMA_ABORT (1 << 3) -#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) -#define DMACPSR1_DMA_START (1 << 0) - -/* Device DMA Controller Parameter setting 2 Register (0x1CC) */ -#define FOTG210_DMACPSR2 0x1CC - -/* Device DMA Controller Parameter setting 3 Register (0x1CC) */ -#define FOTG210_CXPORT 0x1D0 - -struct fotg210_request { - struct usb_request req; - struct list_head queue; -}; - -struct fotg210_ep { - struct usb_ep ep; - struct fotg210_udc *fotg210; - - struct list_head queue; - unsigned stall:1; - unsigned wedged:1; - unsigned use_dma:1; - - unsigned char epnum; - unsigned char type; - unsigned char dir_in; - unsigned int maxp; - const struct usb_endpoint_descriptor *desc; -}; - -struct fotg210_udc { - spinlock_t lock; /* protect the struct */ - void __iomem *reg; - struct clk *pclk; - - unsigned long irq_trigger; - - struct device *dev; - struct usb_phy *phy; - struct usb_gadget gadget; - struct usb_gadget_driver *driver; - - struct fotg210_ep *ep[FOTG210_MAX_NUM_EP]; - - struct usb_request *ep0_req; /* for internal request */ - __le16 ep0_data; - u8 ep0_dir; /* 0/0x80 out/in */ - - u8 reenum; /* if re-enumeration */ -}; - -#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget) diff --git a/drivers/usb/fotg210/fotg210.h b/drivers/usb/fotg210/fotg210.h deleted file mode 100644 index ef79d8323d8979..00000000000000 --- a/drivers/usb/fotg210/fotg210.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __FOTG210_H -#define __FOTG210_H - -#ifdef CONFIG_USB_FOTG210_HCD -int fotg210_hcd_probe(struct platform_device *pdev); -int fotg210_hcd_remove(struct platform_device *pdev); -int fotg210_hcd_init(void); -void fotg210_hcd_cleanup(void); -#else -static inline int fotg210_hcd_probe(struct platform_device *pdev) -{ - return 0; -} -static inline int fotg210_hcd_remove(struct platform_device *pdev) -{ - return 0; -} -static inline int fotg210_hcd_init(void) -{ - return 0; -} -static inline void fotg210_hcd_cleanup(void) -{ -} -#endif - -#ifdef CONFIG_USB_FOTG210_UDC -int fotg210_udc_probe(struct platform_device *pdev); -int fotg210_udc_remove(struct platform_device *pdev); -#else -static inline int fotg210_udc_probe(struct platform_device *pdev) -{ - return 0; -} -static inline int fotg210_udc_remove(struct platform_device *pdev) -{ - return 0; -} -#endif - -#endif /* __FOTG210_H */ diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index b3006d8b04ab37..071cfd16106083 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -108,6 +108,48 @@ config USB_FUSB300 help Faraday usb device controller FUSB300 driver +config USB_FOTG210_UDC + depends on HAS_DMA + tristate "Faraday FOTG210 USB Peripheral Controller" + help + Faraday USB2.0 OTG controller which can be configured as + high speed or full speed USB device. This driver supppors + Bulk Transfer so far. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fotg210_udc". + +config USB_FOTG210_UDC_V1_11_0 + bool "Support FOTG210 v1.11.0 and newer" + default y if !MACH_A369 + depends on USB_FOTG210_UDC + +config USB_FOTG210_UDC_V1_26_0 + bool "Support FOTG210 v1.26.0 and newer" + select USB_FOTG210_UDC_V1_11_0 + depends on USB_FOTG210_UDC + +config USB_FOTG210_UDC_DOUBLE_FIFO + bool "Enable double FIFO support" + depends on USB_FOTG210_UDC + help + Faraday usb device controller work on Double FIFO. + +config USB_FOTG210_UDC_VDMA + bool "Enable VDMA support" + depends on USB_FOTG210_UDC + help + Faraday usb device controller work on VDMA. + This featrue supports after v1.20.0. + +config USB_FOTG210_UDC_HALF_SPEED + bool "Enable half speed support(For FPGA)" + depends on USB_FOTG210_UDC + help + Faraday usb device controller work on half speed. + When the system clock rate falls range of between + 15 MHz and 30 MHz, this needs to be enabled. + config USB_GR_UDC tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" depends on HAS_DMA diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 39daf36a2baa29..12f9e4c9eb0c57 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o obj-$(CONFIG_USB_MV_UDC) += mv_udc.o mv_udc-y := mv_udc_core.o obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o +obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o obj-$(CONFIG_USB_GR_UDC) += gr_udc.o obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c new file mode 100644 index 00000000000000..859b9a5a15a94a --- /dev/null +++ b/drivers/usb/gadget/udc/fotg210-udc.c @@ -0,0 +1,2326 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FOTG210 UDC Driver + * + * Copyright (C) 2013-2019 Faraday Technology Corporation + * + * Author: Faraday CTD/SD Dept. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fotg210.h" + +#define DRIVER_DESC "FOTG210 USB Device Controller Driver" + +/* Note: CONFIG_USB_FOTG210_UDC_DOUBLE_FIFO usage + * EP1 = FIFO 0~1, EP2 = FIFO 2~3 + * Please note that FIFO number is from 0~3, if user has configured EP numbers more than 2, + * user can't define CONFIG_USB_FOTG210_UDC_DOUBLE_FIFO + */ +//#define CONFIG_USB_FOTG210_UDC_DOUBLE_FIFO 1 + +/* read 8-byte setup packet from data port(0x1D0) */ +#define CX_CMD_FROM_DATAPORT 1 + +static const char udc_name[] = "fotg210_udc"; +static const char * const fotg210_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4"}; + +#ifdef CONFIG_USB_FOTG210_UDC_VDMA +static u32 vdma_overlap = 0; //To judge how many EPs use VDMA +#endif + + +static void fotg210_set_reg(struct fotg210_udc *fotg210, u16 offset, u32 value) +{ + u32 reg = ioread32(fotg210->reg + offset); + reg |= value; + iowrite32(reg, fotg210->reg + offset); +} + +static void fotg210_clear_reg(struct fotg210_udc *fotg210, u16 offset, u32 value) +{ + u32 reg = ioread32(fotg210->reg + offset); + reg &= ~value; + iowrite32(reg, fotg210->reg + offset); +} + +static void fotg210_disable_fifo_int(struct fotg210_ep *ep) +{ +#ifdef EXTEND_FIFO + if (ep->dir_in) + fotg210_set_reg(ep->fotg210, FOTG210_DMISGR1, + DMISGR1_MFN_IN_INT(ep->fifonum)); + else { + if (ep->fifonum > 7) + fotg210_set_reg(ep->fotg210, FOTG210_DMISGR2, + DMISGR2_MFN_OUTSPK_INT(ep->fifonum)); + else + fotg210_set_reg(ep->fotg210, FOTG210_DMISGR1, + DMISGR1_MFN_OUTSPK_INT(ep->fifonum)); + } + +#else //original code (F0~F3) + u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); + + if (ep->dir_in) + value |= DMISGR1_MFN_IN_INT(ep->fifonum); + else + value |= DMISGR1_MFN_OUTSPK_INT(ep->fifonum); + iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +#endif +} + +static void fotg210_enable_fifo_int(struct fotg210_ep *ep) +{ +#ifdef EXTEND_FIFO + if (ep->dir_in) + fotg210_clear_reg(ep->fotg210, FOTG210_DMISGR1, + DMISGR1_MFN_IN_INT(ep->fifonum)); + else { + if (ep->fifonum > 7) + fotg210_clear_reg(ep->fotg210, FOTG210_DMISGR2, + DMISGR2_MFN_OUTSPK_INT(ep->fifonum)); + else + fotg210_clear_reg(ep->fotg210, FOTG210_DMISGR1, + DMISGR1_MFN_OUTSPK_INT(ep->fifonum)); + } + +#else //original code (F0~F3) + u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); + + if (ep->dir_in) + value &= ~DMISGR1_MFN_IN_INT(ep->fifonum); + else + value &= ~DMISGR1_MFN_OUTSPK_INT(ep->fifonum); + iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +#endif +} + +static void fotg210_set_cxdone(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); + + value |= DCFESR_CX_DONE; + iowrite32(value, fotg210->reg + FOTG210_DCFESR); +} + +static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, + int status) +{ + list_del_init(&req->queue); + + /* don't modify queue heads during completion callback */ + if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) + req->req.status = -ESHUTDOWN; + else + req->req.status = status; + + spin_unlock(&ep->fotg210->lock); + if (req->req.complete) + usb_gadget_giveback_request(&ep->ep, &req->req); + spin_lock(&ep->fotg210->lock); + + if (ep->epnum) { + fotg210_disable_fifo_int(ep); + if (!list_empty(&ep->queue)) + fotg210_enable_fifo_int(ep); + } else { + fotg210_set_cxdone(ep->fotg210); + } +} + +static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; +#ifdef EXTEND_FIFO + u8 fifo_idx; + u32 val2, offset_FIFOMAP, offset_FIFOCF; +#endif + + /* Driver should map an ep to a fifo and then map the fifo + * to the ep. + */ + + /* map a fifo to an ep */ + if (ep->epnum > 4) { + val = ioread32(fotg210->reg + FOTG210_EPMAP2); + val &= ~EPMAP_FIFONOMSK(ep->epnum - 4, ep->dir_in); + val |= EPMAP_FIFONO(ep->fifonum, ep->epnum - 4, ep->dir_in); + iowrite32(val, fotg210->reg + FOTG210_EPMAP2); + } + else { + val = ioread32(fotg210->reg + FOTG210_EPMAP); + val &= ~EPMAP_FIFONOMSK(ep->epnum, ep->dir_in); + val |= EPMAP_FIFONO(ep->fifonum, ep->epnum, ep->dir_in); + iowrite32(val, fotg210->reg + FOTG210_EPMAP); + } + +#ifdef EXTEND_FIFO + fifo_idx = ep->fifonum; + + if (fifo_idx > 11) { + fifo_idx -= 12; + offset_FIFOMAP = FOTG210_FIFOMAP4; + offset_FIFOCF = FOTG210_FIFOCF4; + } + else if (fifo_idx > 7) { + fifo_idx -= 8; + offset_FIFOMAP = FOTG210_FIFOMAP3; + offset_FIFOCF = FOTG210_FIFOCF3; + } + else if (fifo_idx > 3) { + fifo_idx -= 4; + offset_FIFOMAP = FOTG210_FIFOMAP2; + offset_FIFOCF = FOTG210_FIFOCF2; + } + else { + offset_FIFOMAP = FOTG210_FIFOMAP; + offset_FIFOCF = FOTG210_FIFOCF; + } + + /* map the ep to the fifo */ + val = ioread32(fotg210->reg + offset_FIFOMAP); + val &= ~ FIFOMAP_EPNOMSK(fifo_idx); + val &= ~ FIFOMAP_NA(fifo_idx); + val |= FIFOMAP_EPNO(fifo_idx, ep->epnum); + val |= (ep->dir_in ? FIFOMAP_DIRIN(fifo_idx) : FIFOMAP_DIROUT(fifo_idx)); + iowrite32(val, fotg210->reg + offset_FIFOMAP); + + /* set the fifo configuration */ + val = ioread32(fotg210->reg + offset_FIFOCF); + val |= FIFOCF_TYPE(ep->type, fifo_idx); + #ifdef CONFIG_USB_FOTG210_UDC_DOUBLE_FIFO + val |= FIFOCF_BLK_DUB(fifo_idx); + //cross register + if ((ep->fifonum + 1) == 12) { + val2 = ioread32(fotg210->reg + FOTG210_FIFOCF4); + val2 |= FIFOCF_TYPE(ep->type, 0); + val2 |= FIFOCF_BLK_DUB(0); + iowrite32(val2, fotg210->reg + FOTG210_FIFOCF4); + } + else if ((ep->fifonum + 1) == 8) { + val2 = ioread32(fotg210->reg + FOTG210_FIFOCF3); + val2 |= FIFOCF_TYPE(ep->type, 0); + val2 |= FIFOCF_BLK_DUB(0); + iowrite32(val2, fotg210->reg + FOTG210_FIFOCF3); + } + else if ((ep->fifonum + 1) == 4) { + val2 = ioread32(fotg210->reg + FOTG210_FIFOCF2); + val2 |= FIFOCF_TYPE(ep->type, 0); + val2 |= FIFOCF_BLK_DUB(0); + iowrite32(val2, fotg210->reg + FOTG210_FIFOCF2); + } + //same register + else { + val |= FIFOCF_TYPE(ep->type, fifo_idx + 1); + val |= FIFOCF_BLK_DUB(fifo_idx + 1); + } + #endif + + if(ep->ep.maxpacket <= 512) + val &= ~ FIFOCF_BLKSZ_1024(fifo_idx); + else + val |= FIFOCF_BLKSZ_1024(fifo_idx); + + /* enable fifo */ + val |= FIFOCF_FIFO_EN(fifo_idx); + iowrite32(val, fotg210->reg + offset_FIFOCF); + +#else //original code (F0~F3) + + /* map the ep to the fifo */ + val = ioread32(fotg210->reg + FOTG210_FIFOMAP); + val &= ~FIFOMAP_EPNOMSK(ep->fifonum); + val &= ~FIFOMAP_NA(ep->fifonum); + val |= FIFOMAP_EPNO(ep->fifonum, ep->epnum); + val |= (ep->dir_in ? FIFOMAP_DIRIN(ep->fifonum) : FIFOMAP_DIROUT(ep->fifonum)); + iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); + + /* set the fifo configuration */ + val = ioread32(fotg210->reg + FOTG210_FIFOCF); + val |= FIFOCF_TYPE(ep->type, ep->fifonum); + #ifdef CONFIG_USB_FOTG210_UDC_DOUBLE_FIFO + val |= FIFOCF_TYPE(ep->type, ep->fifonum + 1); + val |= FIFOCF_BLK_DUB(ep->fifonum); + val |= FIFOCF_BLK_DUB(ep->fifonum + 1); + #endif + if(ep->ep.maxpacket <= 512) + val &= ~FIFOCF_BLKSZ_1024(ep->fifonum); + else + val |= FIFOCF_BLKSZ_1024(ep->fifonum); + + /* enable fifo */ + val |= FIFOCF_FIFO_EN(ep->fifonum); + iowrite32(val, fotg210->reg + FOTG210_FIFOCF); +#endif +} + +static void fotg210_set_mps(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; + u32 offset = ep->dir_in ? FOTG210_INEPMPSR(ep->epnum) : + FOTG210_OUTEPMPSR(ep->epnum); + + val = ioread32(fotg210->reg + offset); + val &= ~INOUTEPMPSR_MPS(0x7FF); + val |= INOUTEPMPSR_MPS(ep->ep.maxpacket); + if (ep->dir_in) { + val &= ~INEPMPSR_TX_NUM_HBW(3); + val |= INEPMPSR_TX_NUM_HBW(ep->ep.mult); + } + iowrite32(val, fotg210->reg + offset); +} + +static int fotg210_config_ep(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + + fotg210_set_mps(ep); + fotg210_fifo_ep_mapping(ep); + + fotg210->ep[ep->epnum] = ep; + + return 0; +} + +static int fotg210_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fotg210_ep *ep; + + ep = container_of(_ep, struct fotg210_ep, ep); + + ep->ep.desc = desc; + ep->epnum = usb_endpoint_num(desc); + ep->type = usb_endpoint_type(desc); + ep->dir_in = usb_endpoint_dir_in(desc); + ep->ep.maxpacket = usb_endpoint_maxp(desc); + ep->ep.mult = usb_endpoint_maxp_mult(desc); + + //Note: we assume each epnum only has one direction (dir_in or dir_out), not bi-direction + +#ifdef CONFIG_USB_FOTG210_UDC_DOUBLE_FIFO + //EP1 = FIFO 0~1, EP2 = FIFO 2~3 + ep->fifonum = (ep->epnum - 1) * 2; +#else + //Single FIFO: EP1 = FIFO 0, EP2 = FIFO 1, ... + ep->fifonum = (ep->epnum - 1); +#endif + + if (ep->fifonum >= FOTG210_MAX_FIFO_NUM) { + printk("ERROR!! *** FIFO number (F%d) is too large ***\n", ep->fifonum); + while(1); + } + + return fotg210_config_ep(ep); +} + +static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) +{ + struct fotg210_ep *ep = fotg210->ep[epnum]; + u32 value; + void __iomem *reg; + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(epnum); + + /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ + * bit. Controller wouldn't clear this bit. + */ + + value = ioread32(reg); + value |= INOUTEPMPSR_RESET_TSEQ; + iowrite32(value, reg); + + value = ioread32(reg); + value &= ~INOUTEPMPSR_RESET_TSEQ; + iowrite32(value, reg); +} + +static int fotg210_ep_release(struct fotg210_ep *ep) +{ + if (!ep->epnum) + return 0; + ep->epnum = 0; + ep->stall = 0; + ep->wedged = 0; + + return 0; +} + +static int fotg210_ep_disable(struct usb_ep *_ep) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + unsigned long flags; + + BUG_ON(!_ep); + + ep = container_of(_ep, struct fotg210_ep, ep); + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct fotg210_request, queue); + spin_lock_irqsave(&ep->fotg210->lock, flags); + fotg210_done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + } + + ep->ep.desc = NULL; + return fotg210_ep_release(ep); +} + +static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct fotg210_request *req; + + req = kzalloc(sizeof(struct fotg210_request), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void fotg210_ep_free_request(struct usb_ep *_ep, + struct usb_request *_req) +{ + struct fotg210_request *req; + + req = container_of(_req, struct fotg210_request, req); + kfree(req); +} + +#ifdef CONFIG_USB_FOTG210_UDC_VDMA +static void fotg210_set_vdma(struct fotg210_ep *ep, + dma_addr_t d, u32 len) +{ + u32 value; + struct fotg210_udc *fotg210 = ep->fotg210; + + if (ep->epnum) { +#ifdef EXTEND_FIFO + if (ep->fifonum > 3) { + /* set transfer length and direction */ + value = ioread32(fotg210->reg + FOTG210_VDMA_FNPS1_EX(ep->fifonum)); + value &= ~(VDMAFPS1_VDMA_LEN(0x1FFFF) | VDMAFPS1_VDMA_TYPE(1)); + value |= VDMAFPS1_VDMA_LEN(len) | VDMAFPS1_VDMA_TYPE(ep->dir_in); + iowrite32(value, fotg210->reg + FOTG210_VDMA_FNPS1_EX(ep->fifonum)); + + /* set VDMA memory address */ + iowrite32(d, fotg210->reg + FOTG210_VDMA_FNPS2_EX(ep->fifonum)); + + /* enable MVDMA_ERROR and MVDMA_CMPLT interrupt */ + if (ep->fifonum == FIFO15) + fotg210_clear_reg(fotg210, FOTG210_DMISGR4, + DMISGR4_MVDMA_CMPLT_FN(ep->fifonum) | + DMISGR4_MVDMA_ERROR_FN(ep->fifonum)); + else + fotg210_clear_reg(fotg210, FOTG210_DMISGR3, + DMISGR3_MVDMA_CMPLT_FN(ep->fifonum) | + DMISGR3_MVDMA_ERROR_FN(ep->fifonum)); + + /* enable VDMA */ + fotg210_set_reg(fotg210, FOTG210_VDMA_CTRL, VDMA_EN); + + /* start VDMA */ + fotg210_set_reg(fotg210, FOTG210_VDMA_FNPS1_EX(ep->fifonum), + VDMAFPS1_VDMA_START); + } + else +#endif + { + /* set transfer length and direction */ + value = ioread32(fotg210->reg + FOTG210_VDMA_FNPS1(ep->fifonum)); + value &= ~(VDMAFPS1_VDMA_LEN(0x1FFFF) | VDMAFPS1_VDMA_TYPE(1)); + value |= VDMAFPS1_VDMA_LEN(len) | VDMAFPS1_VDMA_TYPE(ep->dir_in); + iowrite32(value, fotg210->reg + FOTG210_VDMA_FNPS1(ep->fifonum)); + + /* set VDMA memory address */ + iowrite32(d, fotg210->reg + FOTG210_VDMA_FNPS2(ep->fifonum)); + + /* enable MVDMA_ERROR and MVDMA_CMPLT interrupt */ + fotg210_clear_reg(fotg210, FOTG210_DMISGR3, + DMISGR3_MVDMA_CMPLT_FN(ep->fifonum) | + DMISGR3_MVDMA_ERROR_FN(ep->fifonum)); + + /* enable VDMA */ + fotg210_set_reg(fotg210, FOTG210_VDMA_CTRL, VDMA_EN); + + /* start VDMA */ + fotg210_set_reg(fotg210, FOTG210_VDMA_FNPS1(ep->fifonum), + VDMAFPS1_VDMA_START); + } + } + + else { + /* set transfer length and direction */ + value = ioread32(fotg210->reg + FOTG210_VDMA_CXFPS1); + value &= ~(VDMAFPS1_VDMA_LEN(0x1FFFF) | VDMAFPS1_VDMA_TYPE(1)); + value |= VDMAFPS1_VDMA_LEN(len) | VDMAFPS1_VDMA_TYPE(ep->dir_in); + iowrite32(value, fotg210->reg + FOTG210_VDMA_CXFPS1); + + /* set VDMA memory address */ + iowrite32(d, fotg210->reg + FOTG210_VDMA_CXFPS2); + + /* enable MVDMA_ERROR and MVDMA_CMPLT interrupt */ + fotg210_clear_reg(fotg210, FOTG210_DMISGR3, + DMISGR3_MVDMA_CMPLT_CXF | DMISGR3_MVDMA_ERROR_CXF); + + /* enable VDMA */ + fotg210_set_reg(fotg210, FOTG210_VDMA_CTRL, VDMA_EN); + + /* start VDMA */ + fotg210_set_reg(fotg210, FOTG210_VDMA_CXFPS1, + VDMAFPS1_VDMA_START); + } +} + +static void fotg210_wait_vdma_done(struct fotg210_ep *ep) +{ + u32 value; + + if (ep->epnum) { +#ifdef EXTEND_FIFO + if (ep->fifonum == FIFO15) { + do { + value = ioread32(ep->fotg210->reg + FOTG210_DISGR4); + if (value & DISGR4_VDMA_ERROR_FN(ep->fifonum)) { + //write '1' clear + iowrite32(DISGR4_VDMA_ERROR_FN(ep->fifonum), + ep->fotg210->reg + FOTG210_DISGR4); + goto vdma_reset; + } + } while (!(value & DISGR4_VDMA_CMPLT_FN(ep->fifonum))); + + //write '1' clear + iowrite32(DISGR4_VDMA_CMPLT_FN(ep->fifonum), + ep->fotg210->reg + FOTG210_DISGR4); + } + else +#endif + { + do { + value = ioread32(ep->fotg210->reg + FOTG210_DISGR3); + if (value & DISGR3_VDMA_ERROR_FN(ep->fifonum)) { + //write '1' clear + iowrite32(DISGR3_VDMA_ERROR_FN(ep->fifonum), + ep->fotg210->reg + FOTG210_DISGR3); + goto vdma_reset; + } + } while (!(value & DISGR3_VDMA_CMPLT_FN(ep->fifonum))); + + //write '1' clear + iowrite32(DISGR3_VDMA_CMPLT_FN(ep->fifonum), + ep->fotg210->reg + FOTG210_DISGR3); + } + } + else { //EP0 + do { + value = ioread32(ep->fotg210->reg + FOTG210_DISGR3); + if (value & DISGR3_VDMA_ERROR_CXF) { + pr_info("fotg210 CXF VDMA error\n"); + //write '1' clear + iowrite32(DISGR3_VDMA_ERROR_CXF, + ep->fotg210->reg + FOTG210_DISGR3); + goto vdma_reset; + } + } while (!(value & DISGR3_VDMA_CMPLT_CXF)); + + //write '1' clear + iowrite32(DISGR3_VDMA_CMPLT_CXF, + ep->fotg210->reg + FOTG210_DISGR3); + } + return; + +vdma_reset: /* reset fifo */ + + if (ep->epnum) { +#ifdef EXTEND_FIFO + if (ep->fifonum > 3) + fotg210_set_reg(ep->fotg210, + FOTG210_FIBCR_EX(ep->fifonum), FIBCR_FFRST); + else +#endif + fotg210_set_reg(ep->fotg210, + FOTG210_FIBCR(ep->fifonum), FIBCR_FFRST); + } else { //EP0 + fotg210_set_reg(ep->fotg210, FOTG210_DCFESR, DCFESR_CX_CLR); + } +} + +/* If VDMA is completed, fotg210_irq_vdma_done() will be called */ +static void fotg210_start_vdma_wfi(struct fotg210_ep *ep, + struct fotg210_request *req, u32 length) +{ + dma_addr_t d; + u8 *buffer; + + buffer = req->req.buf + req->req.actual; + + d = dma_map_single(ep->fotg210->gadget.dev.parent, + buffer, length, + ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(ep->fotg210->gadget.dev.parent, d)) { + pr_err("dma_mapping_error\n"); + return; + } + + ep->irq_current_task->req = req; + ep->irq_current_task->transfer_length = length; + ep->irq_current_task->dma_addr = d; + + fotg210_set_vdma(ep, d, length); + vdma_overlap ++; +} + +/* Polling until VDMA is completed. Used for CX transfer */ +static void fotg210_start_vdma(struct fotg210_ep *ep, + struct fotg210_request *req, u32 length) +{ + dma_addr_t d; + u8 *buffer; + + buffer = req->req.buf + req->req.actual; + + d = dma_map_single(ep->fotg210->gadget.dev.parent, + buffer, length, + ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(ep->fotg210->gadget.dev.parent, d)) { + pr_err("dma_mapping_error\n"); + return; + } + + fotg210_set_vdma(ep, d, length); + + /* check if vdma is complete */ + fotg210_wait_vdma_done(ep); + + /* disable VDMA */ + if (vdma_overlap == 0) + fotg210_clear_reg(ep->fotg210, FOTG210_VDMA_CTRL, VDMA_EN); + + /* update actual transfer length */ + req->req.actual += length; + + dma_unmap_single(ep->fotg210->gadget.dev.parent, + d, length, ep->dir_in ? DMA_TO_DEVICE : + DMA_FROM_DEVICE); +} + +#else //not CONFIG_USB_FOTG210_UDC_VDMA + +static void fotg210_set_dma(struct fotg210_ep *ep, + dma_addr_t d, u32 len) +{ + u32 value; + struct fotg210_udc *fotg210 = ep->fotg210; + + /* set transfer length and direction */ + value = ioread32(fotg210->reg + FOTG210_DMACPSR1); + value &= ~(DMACPSR1_DMA_LEN(0x1FFFF) | DMACPSR1_DMA_TYPE(1)); + value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); + iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); + + /* set device DMA target FIFO number */ + value = ioread32(fotg210->reg + FOTG210_DMATFNR); + if (ep->epnum) { +#ifdef EXTEND_FIFO + if (ep->fifonum > 3) + value |= DMATFNR_ACC_FN_EX(ep->fifonum); + else +#endif + value |= DMATFNR_ACC_FN(ep->fifonum); + } + else + value |= DMATFNR_ACC_CXF; + iowrite32(value, fotg210->reg + FOTG210_DMATFNR); + + /* set DMA memory address */ + iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); + + /* enable MDMA_ERROR and MDMA_CMPLT interrupt */ + fotg210_clear_reg(fotg210, FOTG210_DMISGR2, + DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); + + /* start DMA */ + fotg210_set_reg(fotg210, FOTG210_DMACPSR1, DMACPSR1_DMA_START); +} + +static void fotg210_wait_dma_done(struct fotg210_ep *ep) +{ + u32 value; + + do { + value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); + if (value & DISGR2_DMA_ERROR) { +#ifndef CONFIG_USB_FOTG210_UDC_V1_11_0 + value &= ~DISGR2_DMA_ERROR; + iowrite32(value, + ep->fotg210->reg + FOTG210_DISGR2); +#else //write '1' clear + iowrite32(DISGR2_DMA_ERROR, + ep->fotg210->reg + FOTG210_DISGR2); +#endif + goto dma_reset; + } + if (value & DISGR2_USBRST_INT) + return; + } while (!(value & DISGR2_DMA_CMPLT)); + +#ifndef CONFIG_USB_FOTG210_UDC_V1_11_0 + value &= ~DISGR2_DMA_CMPLT; + iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); +#else + //write '1' clear + iowrite32(DISGR2_DMA_CMPLT, ep->fotg210->reg + FOTG210_DISGR2); +#endif + return; + +dma_reset: + value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); + value |= DMACPSR1_DMA_ABORT; + iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); + + /* reset fifo */ + if (ep->epnum) { +#ifdef EXTEND_FIFO + if (ep->fifonum > 3) + fotg210_set_reg(ep->fotg210, + FOTG210_FIBCR_EX(ep->fifonum), FIBCR_FFRST); + else +#endif + fotg210_set_reg(ep->fotg210, + FOTG210_FIBCR(ep->fifonum), FIBCR_FFRST); + } else { + fotg210_set_reg(ep->fotg210, FOTG210_DCFESR, DCFESR_CX_CLR); + } +} + +static void fotg210_start_dma(struct fotg210_ep *ep, + struct fotg210_request *req, u32 length) +{ + dma_addr_t d; + u8 *buffer; + + buffer = req->req.buf + req->req.actual; + + d = dma_map_single(ep->fotg210->gadget.dev.parent, + buffer, length, + ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(ep->fotg210->gadget.dev.parent, d)) { + pr_err("dma_mapping_error\n"); + return; + } + + fotg210_set_dma(ep, d, length); + + /* check if dma is done */ + fotg210_wait_dma_done(ep); + + /* disable DMA */ + iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); + + /* update actual transfer length */ + req->req.actual += length; + + dma_unmap_single(ep->fotg210->gadget.dev.parent, + d, length, ep->dir_in ? DMA_TO_DEVICE : + DMA_FROM_DEVICE); +} +#endif + +static void fotg210_ep0_queue(struct fotg210_ep *ep, + struct fotg210_request *req) +{ + if (ep->dir_in) { /* if IN */ + if (req->req.length) { +#ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_start_vdma(ep, req, req->req.length); //polling + //fotg210_start_vdma_wfi(ep, req, req->req.length); +#else + if (req->req.length > ep->ep.maxpacket) + fotg210_start_dma(ep, req, ep->ep.maxpacket); + else + fotg210_start_dma(ep, req, req->req.length); +#endif + } else { + pr_err("%s : req->req.length = 0x%x\n", + __func__, req->req.length); + } + if ((req->req.length == req->req.actual) || + (req->req.actual && (req->req.actual < ep->ep.maxpacket))) + fotg210_done(ep, req, 0); + } else { /* OUT */ + if (!req->req.length) { + fotg210_done(ep, req, 0); + } else { + /* For set_feature(TEST_PACKET) */ + if (ep->fotg210->ep0_length == 53) { + ep->dir_in = 1; +#ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_start_vdma(ep, req, req->req.length); +#else + fotg210_start_dma(ep, req, req->req.length); +#endif + ep->fotg210->ep0_length = 0; + } + else + fotg210_clear_reg(ep->fotg210, + FOTG210_DMISGR0, DMISGR0_MCX_OUT_INT); + } + } +} + +static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + unsigned long flags; + int request = 0; + + ep = container_of(_ep, struct fotg210_ep, ep); + req = container_of(_req, struct fotg210_request, req); + + if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&ep->fotg210->lock, flags); + + if (list_empty(&ep->queue)) + request = 1; + + list_add_tail(&req->queue, &ep->queue); + + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (!ep->epnum) /* ep0 */ + fotg210_ep0_queue(ep, req); + else if (request && !ep->stall) + fotg210_enable_fifo_int(ep); + + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + + return 0; +} + +static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + unsigned long flags; + + ep = container_of(_ep, struct fotg210_ep, ep); + req = container_of(_req, struct fotg210_request, req); + + spin_lock_irqsave(&ep->fotg210->lock, flags); + if (!list_empty(&ep->queue)) + fotg210_done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + + return 0; +} + +static void fotg210_set_epnstall(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 value; + void __iomem *reg; + + /* check if IN FIFO is empty before stall */ + if (ep->dir_in) { + do { + value = ioread32(fotg210->reg + FOTG210_DCFESR); + } while (!(value & DCFESR_FIFO_EMPTY(ep->fifonum))); + } + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); + value = ioread32(reg); + value |= INOUTEPMPSR_STL_EP; + iowrite32(value, reg); +} + +static void fotg210_clear_epnstall(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 value; + void __iomem *reg; + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); + value = ioread32(reg); + value &= ~INOUTEPMPSR_STL_EP; + iowrite32(value, reg); +} + +static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) +{ + struct fotg210_ep *ep; + struct fotg210_udc *fotg210; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct fotg210_ep, ep); + + fotg210 = ep->fotg210; + + spin_lock_irqsave(&ep->fotg210->lock, flags); + + if (!list_empty(&ep->queue)) { + ret = -EAGAIN; + goto out; + } + + if (value) { + fotg210_set_epnstall(ep); + ep->stall = 1; + if (wedge) + ep->wedged = 1; + } else { + fotg210_clear_epnstall(ep); + ep->stall = 0; + ep->wedged = 0; + } + +out: + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + return ret; +} + +static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) +{ + return fotg210_set_halt_and_wedge(_ep, value, 0); +} + +static int fotg210_ep_set_wedge(struct usb_ep *_ep) +{ + return fotg210_set_halt_and_wedge(_ep, 1, 1); +} + +static void fotg210_ep_fifo_flush(struct usb_ep *_ep) +{ +} + +static const struct usb_ep_ops fotg210_ep_ops = { + .enable = fotg210_ep_enable, + .disable = fotg210_ep_disable, + + .alloc_request = fotg210_ep_alloc_request, + .free_request = fotg210_ep_free_request, + + .queue = fotg210_ep_queue, + .dequeue = fotg210_ep_dequeue, + + .set_halt = fotg210_ep_set_halt, + .fifo_flush = fotg210_ep_fifo_flush, + .set_wedge = fotg210_ep_set_wedge, +}; + + +/* read 8-byte setup packet only */ +static void fotg210_rdsetupp(struct fotg210_udc *fotg210, + u8 *buffer) +{ + int i = 0; + u32 data; + u32 length = 8; +#ifdef CX_CMD_FROM_DATAPORT + u8 *tmp = buffer; + + iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); + + for (i = (length >> 2); i > 0; i--) { + data = ioread32(fotg210->reg + FOTG210_CXPORT); + pr_info(" 0x%x\n", data); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + *(tmp + 3) = (data >> 24) & 0xFF; + tmp = tmp + 4; + } + + switch (length % 4) { + case 1: + data = ioread32(fotg210->reg + FOTG210_CXPORT); + pr_info(" 0x%x\n", data); + *tmp = data & 0xFF; + break; + case 2: + data = ioread32(fotg210->reg + FOTG210_CXPORT); + pr_info(" 0x%x\n", data); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + break; + case 3: + data = ioread32(fotg210->reg + FOTG210_CXPORT); + pr_info(" 0x%x\n", data); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + break; + default: + break; + } + + iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); + +#else //read 8-byte by VDMA or DMA + u8 *tmp; + dma_addr_t d; + struct fotg210_ep *ep = fotg210->ep[0]; + + tmp = kmalloc(length, GFP_KERNEL); + d = dma_map_single(ep->fotg210->gadget.dev.parent, + tmp, length, DMA_FROM_DEVICE); + + if (dma_mapping_error(ep->fotg210->gadget.dev.parent, d)) { + pr_err("dma_mapping_error, can't read 8-byte setup packet!\n"); + return; + } + + #ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_set_vdma(ep, d, length); + + /* check if vdma is complete */ + fotg210_wait_vdma_done(ep); + + /* disable VDMA */ + if (vdma_overlap == 0) + fotg210_clear_reg(fotg210, FOTG210_VDMA_CTRL, VDMA_EN); + + #else + fotg210_set_dma(ep, d, length); + + /* check if dma is complete */ + fotg210_wait_dma_done(ep); + + /* disable DMA */ + iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); + #endif + + dma_unmap_single(ep->fotg210->gadget.dev.parent, + d, length, DMA_FROM_DEVICE); + memcpy(buffer, tmp, length); + kfree(tmp); + + //print and check value + tmp = buffer; + for(i = (length >> 2); i > 0; i --) { + data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 | *(tmp + 3) << 24; + pr_info(" 0x%x\n", data); + tmp = tmp + 4; + } +#endif +} + +static int fotg210_is_rmwkup(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DMCR); + + return value & DMCR_CAP_RMWAKUP ? 1 : 0; +} + +static void fotg210_set_rmwkup(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DMCR); + + value |= DMCR_CAP_RMWAKUP; + iowrite32(value, fotg210->reg + FOTG210_DMCR); +} + +static void fotg210_clear_rmwkup(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DMCR); + + value &= ~DMCR_CAP_RMWAKUP; + iowrite32(value, fotg210->reg + FOTG210_DMCR); +} + +static void fotg210_after_configuration(struct fotg210_udc *fotg210, u8 setted) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DAR); + + if (setted) + value |= DAR_AFT_CONF; + else + value &= ~DAR_AFT_CONF; + iowrite32(value, fotg210->reg + FOTG210_DAR); +} + +static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DAR); + + value &= ~0x7F; + value |= (addr & 0x7F); + iowrite32(value, fotg210->reg + FOTG210_DAR); +} + +static void fotg210_set_cxstall(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); + + value |= DCFESR_CX_STL; + iowrite32(value, fotg210->reg + FOTG210_DCFESR); +} + +static void fotg210_request_error(struct fotg210_udc *fotg210) +{ + fotg210_set_cxstall(fotg210); + pr_err("request error!!\n"); +} + +static void fotg210_set_configuration(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 i; + + if (!w_value) + fotg210_after_configuration(fotg210, 0); + else { + if (w_value == 1) { + fotg210_after_configuration(fotg210, 1); + for (i = 1; i < FOTG210_MAX_NUM_EP; i++) + fotg210_reset_tseq(fotg210, i); + } + } +} + +static void fotg210_set_address(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + + if (w_value >= 0x0100) { + fotg210_request_error(fotg210); + } else { + fotg210_set_dev_addr(fotg210, w_value); + fotg210_set_cxdone(fotg210); + } +} + +void gen_test_packet(u8 *tst_packet) +{ + u8 *tp = tst_packet; + + int i; + for (i = 0; i < 9; i++)/*JKJKJKJK x 9*/ + *tp++ = 0x00; + + for (i = 0; i < 8; i++) /* 8*AA */ + *tp++ = 0xAA; + + for (i = 0; i < 8; i++) /* 8*EE */ + *tp++ = 0xEE; + + *tp++ = 0xFE; + + for (i = 0; i < 11; i++) /* 11*FF */ + *tp++ = 0xFF; + + *tp++ = 0x7F; + *tp++ = 0xBF; + *tp++ = 0xDF; + *tp++ = 0xEF; + *tp++ = 0xF7; + *tp++ = 0xFB; + *tp++ = 0xFD; + *tp++ = 0xFC; + *tp++ = 0x7E; + *tp++ = 0xBF; + *tp++ = 0xDF; + *tp++ = 0xEF; + *tp++ = 0xF7; + *tp++ = 0xFB; + *tp++ = 0xFD; + *tp++ = 0x7E; +} + +static void fotg210_set_feature(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u32 val; + u8 tst_packet[53]; + + struct fotg210_ep *ep = + fotg210->ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch(w_value) { + case USB_DEVICE_REMOTE_WAKEUP: + fotg210_set_rmwkup(fotg210); + fotg210_set_cxdone(fotg210); + break; + + case USB_DEVICE_TEST_MODE: + switch (w_index >> 8) { + case TEST_J: + iowrite32(PHYTMSR_TST_JSTA, + fotg210->reg + FOTG210_PHYTMSR); + fotg210_set_cxdone(fotg210); + break; + case TEST_K: + iowrite32(PHYTMSR_TST_KSTA, + fotg210->reg + FOTG210_PHYTMSR); + fotg210_set_cxdone(fotg210); + break; + case TEST_SE0_NAK: + iowrite32(PHYTMSR_TST_SE0NAK, + fotg210->reg + FOTG210_PHYTMSR); + fotg210_set_cxdone(fotg210); + break; + case TEST_PACKET: + iowrite32(PHYTMSR_TST_PKT, + fotg210->reg + FOTG210_PHYTMSR); + fotg210_set_cxdone(fotg210); + + gen_test_packet(tst_packet); + + fotg210->ep0_req->buf = tst_packet; + fotg210->ep0_req->length = 53; + fotg210->ep0_length = 53; + + spin_unlock(&fotg210->lock); + fotg210_ep_queue(fotg210->gadget.ep0, + fotg210->ep0_req, GFP_KERNEL); + spin_lock(&fotg210->lock); + + //Test Packet Done set + val = ioread32(fotg210->reg + FOTG210_DCFESR); + val |= DCFESR_TST_PKDONE; + iowrite32(val, fotg210->reg + FOTG210_DCFESR); + break; + case TEST_FORCE_EN: + fotg210_set_cxdone(fotg210); + break; + default: + fotg210_request_error(fotg210); + break; + } + break; + + default: + fotg210_set_cxdone(fotg210); + break; + } + break; + + case USB_RECIP_INTERFACE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_ENDPOINT: + if (w_value == USB_ENDPOINT_HALT) { + if (w_index & USB_ENDPOINT_NUMBER_MASK) { + fotg210_set_epnstall(ep); + ep->stall = 1; + } + else + fotg210_set_cxstall(fotg210); + fotg210_set_cxdone(fotg210); + } + else + fotg210_request_error(fotg210); + break; + default: + fotg210_request_error(fotg210); + break; + } +} + +static void fotg210_clear_feature(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + + struct fotg210_ep *ep = + fotg210->ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + if (w_value == USB_DEVICE_REMOTE_WAKEUP) + fotg210_clear_rmwkup(fotg210); + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_INTERFACE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_ENDPOINT: + if (w_value == USB_ENDPOINT_HALT) { + if (w_index & USB_ENDPOINT_NUMBER_MASK) { + fotg210_reset_tseq(fotg210, ep->epnum); + if (ep->wedged) { + fotg210_set_cxdone(fotg210); + return; + } + if (ep->stall) { + ep->stall = 0; + fotg210_clear_epnstall(ep); + } + if (!list_empty(&ep->queue)) + fotg210_enable_fifo_int(ep); + } + fotg210_set_cxdone(fotg210); + } + else + fotg210_request_error(fotg210); + break; + default: + fotg210_request_error(fotg210); + break; + } +} + +static int fotg210_is_epnstall(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 value; + void __iomem *reg; + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); + value = ioread32(reg); + return value & INOUTEPMPSR_STL_EP ? 1 : 0; +} + +static void fotg210_get_status(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u8 epnum; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fotg210->ep0_data = 1 << USB_DEVICE_SELF_POWERED; + if (fotg210_is_rmwkup(fotg210)) + fotg210->ep0_data |= 1 << USB_DEVICE_REMOTE_WAKEUP; + break; + case USB_RECIP_INTERFACE: + fotg210->ep0_data = 0; + break; + case USB_RECIP_ENDPOINT: + epnum = w_index & USB_ENDPOINT_NUMBER_MASK; + if (epnum) + fotg210->ep0_data = + fotg210_is_epnstall(fotg210->ep[epnum]) + << USB_ENDPOINT_HALT; + else + fotg210_request_error(fotg210); + break; + + default: + fotg210_request_error(fotg210); + return; /* exit */ + } + + fotg210->ep0_req->buf = &fotg210->ep0_data; + fotg210->ep0_req->length = 2; + + spin_unlock(&fotg210->lock); + fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_KERNEL); + spin_lock(&fotg210->lock); +} + +static int fotg210_setup_packet(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u8 *p = (u8 *)ctrl; + u8 ret = 0; + + fotg210->ep[0]->dir_in = 0; + fotg210_rdsetupp(fotg210, p); + + fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; + + if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { + u32 value = ioread32(fotg210->reg + FOTG210_DMCR); + fotg210->gadget.speed = value & DMCR_HS_EN ? + USB_SPEED_HIGH : USB_SPEED_FULL; + } + + /* check request */ + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + fotg210_get_status(fotg210, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + fotg210_clear_feature(fotg210, ctrl); + break; + case USB_REQ_SET_FEATURE: + fotg210_set_feature(fotg210, ctrl); + break; + case USB_REQ_SET_ADDRESS: + fotg210_set_address(fotg210, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + fotg210_set_configuration(fotg210, ctrl); + ret = 1; + break; + default: + ret = 1; + break; + } + } else { + ret = 1; + } + + return ret; +} + +static void fotg210_ep0out(struct fotg210_udc *fotg210) +{ + struct fotg210_ep *ep = fotg210->ep[0]; + u32 length; + + if (!list_empty(&ep->queue) && !ep->dir_in) { + struct fotg210_request *req; + + req = list_first_entry(&ep->queue, + struct fotg210_request, queue); + + length = req->req.length - req->req.actual; + if (length) { +#ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_start_vdma(ep, req, length); //polling + //fotg210_start_vdma_wfi(ep, req, length); +#else + if (length > ep->ep.maxpacket) + fotg210_start_dma(ep, req, ep->ep.maxpacket); + else + fotg210_start_dma(ep, req, length); +#endif + } + if (req->req.length == req->req.actual) { + fotg210_done(ep, req, 0); + //mask CX_OUT_INT until ep0_queue() + fotg210_set_reg(fotg210, + FOTG210_DMISGR0, DMISGR0_MCX_OUT_INT); + } + } else { + pr_err("%s : empty queue\n", __func__); + } +} + +//In theory, VDMA mode will not call in this function +static void fotg210_ep0in(struct fotg210_udc *fotg210) +{ + struct fotg210_ep *ep = fotg210->ep[0]; + u32 length = 0; + + if ((!list_empty(&ep->queue)) && (ep->dir_in)) { + struct fotg210_request *req; + + req = list_entry(ep->queue.next, + struct fotg210_request, queue); + + length = req->req.length - req->req.actual; + if (length) { +#ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_start_vdma(ep, req, length); +#else + if (length > ep->ep.maxpacket) + fotg210_start_dma(ep, req, ep->ep.maxpacket); + else + fotg210_start_dma(ep, req, length); +#endif + } + + if (req->req.length == req->req.actual) + fotg210_done(ep, req, 0); + } else { + fotg210_set_cxdone(fotg210); + } +} + +static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); + + value |= DISGR0_CX_COMABT_INT; + iowrite32(value, fotg210->reg + FOTG210_DISGR0); +} + +#ifdef CONFIG_USB_FOTG210_UDC_VDMA +/* wait for interrupt (wfi) method, not polling method + * fotg210_irq_dma_done() will be called when dma complete + */ +static void fotg210_in_fifo_handler_wfi(struct fotg210_udc *fotg210, int fifo_int) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + int i; + + for (i = 1; i < FOTG210_MAX_NUM_EP; i++) { + ep = fotg210->ep[i]; + if (ep->fifonum == fifo_int) + break; + } + req = list_entry(ep->queue.next, struct fotg210_request, queue); + + if (req->req.length) { + fotg210_disable_fifo_int(ep); + fotg210_start_vdma_wfi(ep, req, req->req.length); + + #ifdef CX_CMD_FROM_DATAPORT + //Temporarily block EP0 setup interrupt, + //CPU is not allowed to obtain 8-byte setup command from data port when VDMA_EN is set + fotg210_set_reg(fotg210, FOTG210_DMISGR0, + DMISGR0_MCX_SETUP_INT); + #endif + } + else + fotg210_done(ep, req, 0); +} + +static void fotg210_out_fifo_handler_wfi(struct fotg210_udc *fotg210, int fifo_int) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + int i; + + for (i = 1; i < FOTG210_MAX_NUM_EP; i++) { + ep = fotg210->ep[i]; + if (ep->fifonum == fifo_int) + break; + } + req = list_entry(ep->queue.next, struct fotg210_request, queue); + + fotg210_disable_fifo_int(ep); + fotg210_start_vdma_wfi(ep, req, req->req.length); + + #ifdef CX_CMD_FROM_DATAPORT + //Temporarily block EP0 setup interrupt, + //CPU is not allowed to obtain 8-byte setup command from data port when VDMA_EN is set + fotg210_set_reg(fotg210, FOTG210_DMISGR0, + DMISGR0_MCX_SETUP_INT); + #endif +} + +static void fotg210_irq_vdma_done(struct fotg210_udc *fotg210, int fifo_cmplt, + int force_out_done) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + int i; + u32 value, rem_len; + + if (fifo_cmplt == CXFIFO) { + /* disable MVDMA_ERROR and MVDMA_CMPLT interrupt */ + fotg210_set_reg(fotg210, FOTG210_DMISGR3, + DMISGR3_MVDMA_CMPLT_CXF | DMISGR3_MVDMA_ERROR_CXF); + + ep = fotg210->ep[0]; + } + else { + /* disable MVDMA_ERROR and MVDMA_CMPLT interrupt */ +#ifdef EXTEND_FIFO + if (fifo_cmplt == FIFO15) + fotg210_set_reg(fotg210, FOTG210_DMISGR4, + DMISGR4_MVDMA_CMPLT_FN(fifo_cmplt) | + DMISGR4_MVDMA_ERROR_FN(fifo_cmplt)); + else +#endif + fotg210_set_reg(fotg210, FOTG210_DMISGR3, + DMISGR3_MVDMA_CMPLT_FN(fifo_cmplt) | + DMISGR3_MVDMA_ERROR_FN(fifo_cmplt)); + + /* find the ep */ + for (i = 1; i < FOTG210_MAX_NUM_EP; i++) { + ep = fotg210->ep[i]; + if (ep->fifonum == fifo_cmplt) + break; + } + } + req = ep->irq_current_task->req; + + /* update actual transfer length */ + if (fifo_cmplt == CXFIFO) + value = ioread32(fotg210->reg + FOTG210_VDMA_CXFPS1); + else { +#ifdef EXTEND_FIFO + if (ep->fifonum > 3) + value = ioread32(fotg210->reg + FOTG210_VDMA_FNPS1_EX(ep->fifonum)); + else +#endif + value = ioread32(fotg210->reg + FOTG210_VDMA_FNPS1(ep->fifonum)); + } + rem_len = GET_VDMAFPS1_VDMA_LEN(value); + req->req.actual += (ep->irq_current_task->transfer_length - rem_len); + + vdma_overlap --; + if (vdma_overlap == 0) { + /* disable VDMA */ + fotg210_clear_reg(fotg210, FOTG210_VDMA_CTRL, VDMA_EN); + + #ifdef CX_CMD_FROM_DATAPORT + //Restore EP0 setup interrupt, + //CPU is not allowed to obtain 8-byte setup command from data port when VDMA_EN is set + fotg210_clear_reg(fotg210, FOTG210_DMISGR0, + DMISGR0_MCX_SETUP_INT); + #endif + } + + dma_unmap_single(ep->fotg210->gadget.dev.parent, + ep->irq_current_task->dma_addr, + ep->irq_current_task->transfer_length, + ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (ep->dir_in) { + fotg210_done(ep, req, 0); + } + else { + /* finish out transfer */ + if (force_out_done) + fotg210_done(ep, req, 0); + else { + if ((req->req.length == req->req.actual) || + ((req->req.actual % ep->ep.maxpacket) != 0)) + fotg210_done(ep, req, 0); + } + } +} + +#else //not CONFIG_USB_FOTG210_UDC_VDMA +static void fotg210_in_fifo_handler(struct fotg210_udc *fotg210, int fifo_int) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + int i; + + for (i = 1; i < FOTG210_MAX_NUM_EP; i++) { + ep = fotg210->ep[i]; + if (ep->fifonum == fifo_int) + break; + } + req = list_entry(ep->queue.next, struct fotg210_request, queue); + + if (req->req.length) + fotg210_start_dma(ep, req, req->req.length); + fotg210_done(ep, req, 0); +} + +static void fotg210_out_fifo_handler(struct fotg210_udc *fotg210, int fifo_int) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + int i; + u32 reg, length; + + for (i = 1; i < FOTG210_MAX_NUM_EP; i++) { + ep = fotg210->ep[i]; + if (ep->fifonum == fifo_int) + break; + } + req = list_entry(ep->queue.next, struct fotg210_request, queue); + +#ifdef EXTEND_FIFO + if (fifo_int > 3) + reg = ioread32(fotg210->reg + FOTG210_FIBCR_EX(fifo_int)); + else +#endif + reg = ioread32(fotg210->reg + FOTG210_FIBCR(fifo_int)); + length = reg & FIBCR_BCFX; + + fotg210_start_dma(ep, req, length); + /* finish out transfer */ + if (req->req.length == req->req.actual || + length < ep->ep.maxpacket) + fotg210_done(ep, req, 0); +} +#endif + +static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); + + //Note: From HW version 1.26, this register change to W1C +#ifndef CONFIG_USB_FOTG210_UDC_V1_26_0 + value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 + | TX0BYTE_EP4); +#endif + iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); +} + +static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); +#ifdef CONFIG_USB_FOTG210_UDC_VDMA + struct fotg210_ep *ep; + int i; +#endif + + //Note: From HW version 1.26, this register change to W1C +#ifndef CONFIG_USB_FOTG210_UDC_V1_26_0 + value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 + | RX0BYTE_EP4); +#endif + iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); + +#ifdef CONFIG_USB_FOTG210_UDC_VDMA + //SW do: If device receive 0 byte from Host, make vdma finish + //Note: From HW version 1.30, vdma will be complete when device receive 0 byte + for (i = 1; i < FOTG210_MAX_NUM_EP; i++) { + ep = fotg210->ep[i]; + if ((value == (1 << (ep->epnum - 1))) && (!ep->dir_in)) + break; + } + +#ifdef EXTEND_FIFO + if (ep->fifonum > 3) + value = ioread32(fotg210->reg + FOTG210_VDMA_FNPS1_EX(ep->fifonum)); + else +#endif + value = ioread32(fotg210->reg + FOTG210_VDMA_FNPS1(ep->fifonum)); + //From HW version 1.30, START will be clear + if (value & VDMAFPS1_VDMA_START) + fotg210_irq_vdma_done(fotg210, ep->fifonum, 1); +#endif +} + +static void fotg210_go_suspend(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DMCR); + + value |= DMCR_GOSUSP; + iowrite32(value, fotg210->reg + FOTG210_DMCR); +} + + +static irqreturn_t fotg210_irq(int irq, void *_fotg210) +{ + struct fotg210_udc *fotg210 = _fotg210; + u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); + u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); + int fifo; + + int_grp &= ~int_msk; + + spin_lock(&fotg210->lock); + +//--------------------------------------------------------- G2 (0x14C: device) + if (int_grp & DIGR_INT_G2) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR2; + u32 int_grp2 = ioread32(reg); + u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); +#ifndef CONFIG_USB_FOTG210_UDC_V1_11_0 + u32 value; +#endif + int_grp2 &= ~int_msk2; + +#ifndef CONFIG_USB_FOTG210_UDC_V1_11_0 + if (int_grp2 & DISGR2_USBRST_INT) { + value = ioread32(reg); + value &= ~DISGR2_USBRST_INT; + iowrite32(value, reg); + fotg210->gadget.speed = USB_SPEED_UNKNOWN; + pr_info("fotg210 udc reset\n"); + } + if (int_grp2 & DISGR2_SUSP_INT) { + value = ioread32(reg); + value &= ~DISGR2_SUSP_INT; + iowrite32(value, reg); + pr_info("fotg210 udc suspend\n"); + } + if (int_grp2 & DISGR2_RESM_INT) { + value = ioread32(reg); + value &= ~DISGR2_RESM_INT; + iowrite32(value, reg); + pr_info("fotg210 udc resume\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { + value = ioread32(reg); + value &= ~DISGR2_ISO_SEQ_ERR_INT; + iowrite32(value, reg); + pr_info("fotg210 iso sequence error\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { + value = ioread32(reg); + value &= ~DISGR2_ISO_SEQ_ABORT_INT; + iowrite32(value, reg); + pr_info("fotg210 iso sequence abort\n"); + } + if (int_grp2 & DISGR2_TX0BYTE_INT) { + fotg210_clear_tx0byte(fotg210); + value = ioread32(reg); + value &= ~DISGR2_TX0BYTE_INT; + iowrite32(value, reg); + pr_info("fotg210 transferred 0 byte\n"); + } + if (int_grp2 & DISGR2_RX0BYTE_INT) { + fotg210_clear_rx0byte(fotg210); + value = ioread32(reg); + value &= ~DISGR2_RX0BYTE_INT; + iowrite32(value, reg); + pr_info("fotg210 received 0 byte\n"); + } + if (int_grp2 & DISGR2_DMA_ERROR) { + value = ioread32(reg); + value &= ~DISGR2_DMA_ERROR; + iowrite32(value, reg); + pr_info("fotg210 DMA error\n"); + } +#else //write '1' clear + if (int_grp2 & DISGR2_USBRST_INT) { + iowrite32(DISGR2_USBRST_INT, reg); + fotg210->gadget.speed = USB_SPEED_UNKNOWN; + iowrite32(0x0, fotg210->reg + FOTG210_FIFOCF); +#ifdef EXTEND_FIFO + iowrite32(0x0, fotg210->reg + FOTG210_FIFOCF2); + iowrite32(0x0, fotg210->reg + FOTG210_FIFOCF3); + iowrite32(0x0, fotg210->reg + FOTG210_FIFOCF4); +#endif +#ifdef CONFIG_USB_FOTG210_UDC_VDMA + vdma_overlap = 0; + /* disable VDMA */ + fotg210_clear_reg(fotg210, FOTG210_VDMA_CTRL, VDMA_EN); + + #ifdef CX_CMD_FROM_DATAPORT + //Restore EP0 setup interrupt, + fotg210_clear_reg(fotg210, FOTG210_DMISGR0, + DMISGR0_MCX_SETUP_INT); + #endif +#endif + pr_info("fotg210 udc reset\n"); + } + if (int_grp2 & DISGR2_SUSP_INT) { + iowrite32(DISGR2_SUSP_INT, reg); + fotg210_go_suspend(fotg210); + pr_info("fotg210 udc suspend\n"); + } + if (int_grp2 & DISGR2_RESM_INT) { + iowrite32(DISGR2_RESM_INT, reg); + pr_info("fotg210 udc resume\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { + iowrite32(DISGR2_ISO_SEQ_ERR_INT, reg); + pr_info("fotg210 iso sequence error\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { + iowrite32(DISGR2_ISO_SEQ_ABORT_INT, reg); + pr_info("fotg210 iso sequence abort\n"); + } + if (int_grp2 & DISGR2_TX0BYTE_INT) { + fotg210_clear_tx0byte(fotg210); + iowrite32(DISGR2_TX0BYTE_INT, reg); + pr_info("fotg210 transferred 0 byte\n"); + } + if (int_grp2 & DISGR2_RX0BYTE_INT) { + pr_info("fotg210 received 0 byte\n"); + fotg210_clear_rx0byte(fotg210); + iowrite32(DISGR2_RX0BYTE_INT, reg); + } + if (int_grp2 & DISGR2_DMA_ERROR) { + iowrite32(DISGR2_DMA_ERROR, reg); + pr_info("fotg210 DMA error\n"); + } +#endif + } + +//--------------------------------------------------------- G3 & G4 (0x328/0x338: VDMA) +#ifdef CONFIG_USB_FOTG210_UDC_VDMA + if (int_grp & DIGR_INT_G3) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR3; + u32 int_grp3 = ioread32(reg); + u32 int_msk3 = ioread32(fotg210->reg + FOTG210_DMISGR3); + + int_grp3 &= ~int_msk3; + + if (int_grp3 & DISGR3_VDMA_CMPLT_CXF) { + iowrite32(DISGR3_VDMA_CMPLT_CXF, reg); + //pr_info("fotg210 CXF VDMA complete\n"); + fotg210_irq_vdma_done(fotg210, CXFIFO, 0); + } + if (int_grp3 & DISGR3_VDMA_ERROR_CXF) { + iowrite32(DISGR3_VDMA_ERROR_CXF, reg); + pr_info("fotg210 CXF VDMA error\n"); + fotg210_irq_vdma_done(fotg210, CXFIFO, 0); + } + +#ifdef EXTEND_FIFO + for (fifo = FIFO0; fifo < FIFO15; fifo++) +#else + for (fifo = FIFO0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) +#endif + { + if (int_grp3 & DISGR3_VDMA_CMPLT_FN(fifo)) { + iowrite32(DISGR3_VDMA_CMPLT_FN(fifo), reg); + fotg210_irq_vdma_done(fotg210, fifo, 0); + } + + if (int_grp3 & DISGR3_VDMA_ERROR_FN(fifo)) { + iowrite32(DISGR3_VDMA_ERROR_FN(fifo), reg); + printk("fotg210 F%d VDMA error\n", fifo); + fotg210_irq_vdma_done(fotg210, fifo, 0); + } + } + } + +#ifdef EXTEND_FIFO + if (int_grp & DIGR_INT_G4) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR4; + u32 int_grp4 = ioread32(reg); + u32 int_msk4 = ioread32(fotg210->reg + FOTG210_DMISGR4); + int_grp4 &= ~int_msk4; + + if (int_grp4 & DISGR4_VDMA_CMPLT_FN(FIFO15)) { + iowrite32(DISGR4_VDMA_CMPLT_FN(FIFO15), reg); + fotg210_irq_vdma_done(fotg210, FIFO15, 0); + } + if (int_grp4 & DISGR4_VDMA_ERROR_FN(FIFO15)) { + iowrite32(DISGR4_VDMA_ERROR_FN(FIFO15), reg); + printk("fotg210 F%d VDMA error\n", FIFO15); + fotg210_irq_vdma_done(fotg210, FIFO15, 0); + } + } +#endif +#endif //CONFIG_USB_FOTG210_UDC_VDMA + +//--------------------------------------------------------- G0 (0x144: CX) + if (int_grp & DIGR_INT_G0) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR0; + u32 int_grp0 = ioread32(reg); + u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); + struct usb_ctrlrequest ctrl; + + int_grp0 &= ~int_msk0; + + /* the highest priority in this source register */ + if (int_grp0 & DISGR0_CX_COMABT_INT) { + fotg210_clear_comabt_int(fotg210); + pr_info("fotg210 CX command abort\n"); + } + + if (int_grp0 & DISGR0_CX_SETUP_INT) { + pr_info("fotg210 ep0 setup\n"); + if (fotg210_setup_packet(fotg210, &ctrl)) { + spin_unlock(&fotg210->lock); + if (fotg210->driver) { + if (fotg210->driver->setup( + &fotg210->gadget, + &ctrl) < 0) + fotg210_set_cxstall(fotg210); + } + spin_lock(&fotg210->lock); + } + } + if (int_grp0 & DISGR0_CX_COMEND_INT) + pr_info("fotg210 cmd end\n"); + + if (int_grp0 & DISGR0_CX_IN_INT) { + pr_info("fotg210 cxin\n"); + fotg210_ep0in(fotg210); + } + + if (int_grp0 & DISGR0_CX_OUT_INT) { + pr_info("fotg210 cxout\n"); + fotg210_ep0out(fotg210); + } + + if (int_grp0 & DISGR0_CX_COMFAIL_INT) { + fotg210_set_cxstall(fotg210); + pr_info("fotg210 ep0 fail\n"); + } + } + +//--------------------------------------------------------- G1 (0x148: FIFO) + if (int_grp & DIGR_INT_G1) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR1; + u32 int_grp1 = ioread32(reg); + u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); + + int_grp1 &= ~int_msk1; + +#ifdef EXTEND_FIFO + for (fifo = FIFO0; fifo < FIFO8; fifo++) { + if ((int_grp1 & DISGR1_FN_OUT_INT(fifo)) || + (int_grp1 & DISGR1_FN_SPK_INT(fifo))) + #ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_out_fifo_handler_wfi(fotg210, fifo); + #else + fotg210_out_fifo_handler(fotg210, fifo); + #endif + } + + for (fifo = FIFO0; fifo <= FIFO15; fifo++) { + if (int_grp1 & DISGR1_FN_IN_INT(fifo)) + #ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_in_fifo_handler_wfi(fotg210, fifo); + #else + fotg210_in_fifo_handler(fotg210, fifo); + #endif + } + +#else //original code (F0~F3) + for (fifo = FIFO0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { + if (int_grp1 & DISGR1_FN_IN_INT(fifo)) + #ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_in_fifo_handler_wfi(fotg210, fifo); + #else + fotg210_in_fifo_handler(fotg210, fifo); + #endif + + if ((int_grp1 & DISGR1_FN_OUT_INT(fifo)) || + (int_grp1 & DISGR1_FN_SPK_INT(fifo))) + #ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_out_fifo_handler_wfi(fotg210, fifo); + #else + fotg210_out_fifo_handler(fotg210, fifo); + #endif + } +#endif + } + +#ifdef EXTEND_FIFO + if (int_grp & DIGR_INT_G2) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR2; + u32 int_grp2 = ioread32(reg); + u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); + + int_grp2 &= ~int_msk2; + + for (fifo = FIFO8; fifo <= FIFO15; fifo++) { + if ((int_grp2 & DISGR2_FN_OUT_INT(fifo)) || + (int_grp2 & DISGR2_FN_SPK_INT(fifo))) + #ifdef CONFIG_USB_FOTG210_UDC_VDMA + fotg210_out_fifo_handler_wfi(fotg210, fifo); + #else + fotg210_out_fifo_handler(fotg210, fifo); + #endif + } + } +#endif + + spin_unlock(&fotg210->lock); + + return IRQ_HANDLED; +} + +static void fotg210_disable_unplug(struct fotg210_udc *fotg210) +{ + u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); + + reg &= ~PHYTMSR_UNPLUG; + iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); +} + +static int fotg210_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); +#ifdef CONFIG_USB_FOTG210_OTG + u32 value; +#endif + + /* hook up the driver */ + driver->driver.bus = NULL; + fotg210->driver = driver; + +#ifdef CONFIG_USB_FOTG210_OTG + value = ioread32(fotg210->reg + FOTG210_OTGCSR); + + /* if peripheral mode */ + if (value & OTGCSR_CROLE) { + printk(" OTGCSR: device role\n"); + //disable Host + value = ioread32(fotg210->reg + FOTG210_USBCMD); + if (value & USBCMD_RUN) { + value &= ~USBCMD_RUN; + iowrite32(value, fotg210->reg + FOTG210_USBCMD); + } + + //Mask Host interrupt + #ifdef CONFIG_INT_POLARITY_HIGH + iowrite32(GMIR_MHC_INT | GMIR_INT_POLARITY, fotg210->reg + FOTG210_GMIR); + #else + iowrite32(GMIR_MHC_INT, fotg210->reg + FOTG210_GMIR); + #endif + + /* clear device un-plug */ + fotg210_disable_unplug(fotg210); + + /* enable device global interrupt */ + fotg210_set_reg(fotg210, FOTG210_DMCR, DMCR_GLINT_EN); + } + /* if host mode */ + else { + printk(" OTGCSR: host role\n"); + //Mask device interrupt + #ifdef CONFIG_INT_POLARITY_HIGH + iowrite32(GMIR_MDEV_INT | GMIR_INT_POLARITY, fotg210->reg + FOTG210_GMIR); + #else + iowrite32(GMIR_MDEV_INT, fotg210->reg + FOTG210_GMIR); + #endif + } +#else + fotg210_disable_unplug(fotg210); + + /* enable device global interrupt */ + fotg210_set_reg(fotg210, FOTG210_DMCR, DMCR_GLINT_EN); +#endif + return 0; +} + +static void fotg210_init(struct fotg210_udc *fotg210) +{ + /* disable global interrupt and set int polarity to active high */ +#ifdef CONFIG_INT_POLARITY_HIGH + iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, + fotg210->reg + FOTG210_GMIR); +#else + iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT, + fotg210->reg + FOTG210_GMIR); +#endif + /* disable device global interrupt */ +#ifdef CONFIG_USB_FOTG210_UDC_HALF_SPEED + fotg210_set_reg(fotg210, FOTG210_DMCR, DMCR_HALF_SPEED); +#endif + fotg210_clear_reg(fotg210, FOTG210_DMCR, DMCR_GLINT_EN); + + /* disable all fifo interrupt */ + iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); + + /* disable cmd end */ + /* mask CX_OUT_INT until ep0_queue() */ + fotg210_set_reg(fotg210, FOTG210_DMISGR0, + DMISGR0_MCX_COMEND_INT | DMISGR0_MCX_OUT_INT); + + // on device mode, Dev_Idle on A369 always set, so we disable interrupt. + // on FPGA we have no such issue + // we also disable Dev_Wakeup_byVBUS + iowrite32((DMISGR2_MDEV_IDLE | DMISGR2_MDEV_WAKEUP_VBUS), + fotg210->reg + FOTG210_DMISGR2); + + /* set default value */ + iowrite32(0x33333333, fotg210->reg + FOTG210_EPMAP); + iowrite32(0x33333333, fotg210->reg + FOTG210_EPMAP2); +} + +static int fotg210_udc_stop(struct usb_gadget *g) +{ + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); + unsigned long flags; + + spin_lock_irqsave(&fotg210->lock, flags); + + fotg210_init(fotg210); + fotg210->driver = NULL; + + spin_unlock_irqrestore(&fotg210->lock, flags); + + return 0; +} + +static int fotg210_udc_pullup(struct usb_gadget *g, int is_on) +{ + return 0; +} + +static const struct usb_gadget_ops fotg210_gadget_ops = { + .pullup = fotg210_udc_pullup, + .udc_start = fotg210_udc_start, + .udc_stop = fotg210_udc_stop, +}; + +static int fotg210_udc_remove(struct platform_device *pdev) +{ + struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); + int i; + + usb_del_gadget_udc(&fotg210->gadget); + iounmap(fotg210->reg); + free_irq(platform_get_irq(pdev, 0), fotg210); + + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); + kfree(fotg210); + + return 0; +} + +static int fotg210_udc_probe(struct platform_device *pdev) +{ + struct resource *res, *ires; + struct fotg210_udc *fotg210 = NULL; + struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; + int ret = 0; + int i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("platform_get_resource error.\n"); + return -ENODEV; + } + + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!ires) { + pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); + return -ENODEV; + } + + ret = -ENOMEM; + + /* initialize udc */ + fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); + if (fotg210 == NULL) + goto err; + + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); + if (_ep[i] == NULL) + goto err_alloc; + fotg210->ep[i] = _ep[i]; + } + + fotg210->reg = ioremap(res->start, resource_size(res)); + if (fotg210->reg == NULL) { + pr_err("ioremap error.\n"); + goto err_alloc; + } + + spin_lock_init(&fotg210->lock); + + platform_set_drvdata(pdev, fotg210); + + fotg210->gadget.ops = &fotg210_gadget_ops; + + fotg210->gadget.max_speed = USB_SPEED_HIGH; + fotg210->gadget.name = udc_name; + + INIT_LIST_HEAD(&fotg210->gadget.ep_list); + + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + struct fotg210_ep *ep = fotg210->ep[i]; + + if (i) { + INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); + list_add_tail(&fotg210->ep[i]->ep.ep_list, + &fotg210->gadget.ep_list); + } + ep->fotg210 = fotg210; + INIT_LIST_HEAD(&ep->queue); + ep->ep.name = fotg210_ep_name[i]; + ep->ep.ops = &fotg210_ep_ops; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); + + if (i == 0) { + ep->ep.caps.type_control = true; + } else { + ep->ep.caps.type_iso = true; + ep->ep.caps.type_bulk = true; + ep->ep.caps.type_int = true; + } + + ep->ep.caps.dir_in = true; + ep->ep.caps.dir_out = true; + } + usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40); + fotg210->gadget.ep0 = &fotg210->ep[0]->ep; + INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); + + fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, + GFP_KERNEL); + if (fotg210->ep0_req == NULL) + goto err_map; + + fotg210_init(fotg210); + + ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, + udc_name, fotg210); + if (ret < 0) { + pr_err("request_irq error (%d)\n", ret); + goto err_req; + } + + ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); + if (ret) + goto err_add_udc; + + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + struct fotg210_ep *ep = fotg210->ep[i]; + + ep->irq_current_task = kzalloc(sizeof(struct fotg210_irq_task), GFP_KERNEL); + if (ep->irq_current_task == NULL) { + pr_err("irq_current_task kzalloc error.\n"); + goto err_add_udc; + } + + ep->irq_current_task->transfer_length = 0; + ep->irq_current_task->req = NULL; + } + + dev_info(&pdev->dev, "%s probe\n", udc_name); + + return 0; + +err_add_udc: + free_irq(ires->start, fotg210); + +err_req: + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); + +err_map: + iounmap(fotg210->reg); + +err_alloc: + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); + kfree(fotg210); + +err: + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id fotg210_udc_dt_ids[] = { + { .compatible = "faraday,fotg210_udc" }, + { } +}; + +MODULE_DEVICE_TABLE(of, fotg210_udc_dt_ids); +#endif + +static struct platform_driver fotg210_driver = { + .driver = { + .name = (char *)udc_name, + .of_match_table = of_match_ptr(fotg210_udc_dt_ids), + }, + .probe = fotg210_udc_probe, + .remove = fotg210_udc_remove, +}; + +module_platform_driver(fotg210_driver); + +MODULE_AUTHOR("Faraday Technology Corporation"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/gadget/udc/fotg210.h b/drivers/usb/gadget/udc/fotg210.h new file mode 100644 index 00000000000000..dfa46e7b4c86b2 --- /dev/null +++ b/drivers/usb/gadget/udc/fotg210.h @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Faraday FOTG210 USB UDC driver + * + * Copyright (C) 2013-2019 Faraday Technology Corporation + * Author: Faraday CTD/SD Dept. + */ + +#include + +/* HW extend the FIFO number from F0~F3 to F0~F15 + * HW version >= v1.30.0 */ +//#define EXTEND_FIFO 1 + +#define FOTG210_MAX_NUM_EP (4 + 1) /* ep1~4 + ep0 */ +#ifdef EXTEND_FIFO +#define FOTG210_MAX_FIFO_NUM 16 /* fifo0...fifo15 */ +#else +#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo3 */ +#endif + +#define FIFO0 0 +#define FIFO1 1 +#define FIFO2 2 +#define FIFO3 3 +#define CXFIFO (FOTG210_MAX_FIFO_NUM) +#ifdef EXTEND_FIFO +#define FIFO4 4 +#define FIFO5 5 +#define FIFO6 6 +#define FIFO7 7 +#define FIFO8 8 +#define FIFO9 9 +#define FIFO10 10 +#define FIFO11 11 +#define FIFO12 12 +#define FIFO13 13 +#define FIFO14 14 +#define FIFO15 15 +#endif + +#ifdef CONFIG_USB_FOTG210_OTG +/* Host controller register */ +#define FOTG210_USBCMD 0x10 +#define USBCMD_RUN (1 << 0) + +/* OTG Control Status Register (0x80) */ +#define FOTG210_OTGCSR 0x80 +#define OTGCSR_ID (1 << 21) +#define OTGCSR_CROLE (1 << 20) +#define OTGCSR_VBUS_VLD (1 << 19) +#define OTGCSR_B_HNP_EN (1 << 1) +#define OTGCSR_B_BUS_REQ (1 << 0) +#endif + +/* Global Mask of HC/OTG/DEV interrupt Register (0xC4) */ +#define FOTG210_GMIR 0xC4 +#define GMIR_INT_POLARITY (1 << 3) /* Active High */ +#define GMIR_MHC_INT (1 << 2) +#define GMIR_MOTG_INT (1 << 1) +#define GMIR_MDEV_INT (1 << 0) + +/* Device Main Control Register (0x100) */ +#define FOTG210_DMCR 0x100 +#define DMCR_HS_EN (1 << 6) +#define DMCR_CHIP_EN (1 << 5) +#define DMCR_SFRST (1 << 4) +#define DMCR_GOSUSP (1 << 3) +#define DMCR_GLINT_EN (1 << 2) +#define DMCR_HALF_SPEED (1 << 1) /* set 1 when SCLK < 30MHz */ +#define DMCR_CAP_RMWAKUP (1 << 0) + +/* Device Address Register (0x104) */ +#define FOTG210_DAR 0x104 +#define DAR_AFT_CONF (1 << 7) + +/* Device Test Register (0x108) */ +#define FOTG210_DTR 0x108 +#define DTR_TST_CLRFF (1 << 0) + +/* PHY Test Mode Selector register (0x114) */ +#define FOTG210_PHYTMSR 0x114 +#define PHYTMSR_TST_PKT (1 << 4) +#define PHYTMSR_TST_SE0NAK (1 << 3) +#define PHYTMSR_TST_KSTA (1 << 2) +#define PHYTMSR_TST_JSTA (1 << 1) +#define PHYTMSR_UNPLUG (1 << 0) + +/* Cx configuration and FIFO Empty Status register (0x120) */ +#define FOTG210_DCFESR 0x120 +#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo)) +#define DCFESR_CX_EMP (1 << 5) +#define DCFESR_CX_CLR (1 << 3) +#define DCFESR_CX_STL (1 << 2) +#define DCFESR_TST_PKDONE (1 << 1) +#define DCFESR_CX_DONE (1 << 0) + +/* Device IDLE Counter Register (0x124) */ +#define FOTG210_DICR 0x124 + +/* Device Mask of Interrupt Group Register (0x130) */ +#define FOTG210_DMIGR 0x130 +#define DMIGR_MINT_G4 (1 << 4) +#define DMIGR_MINT_G3 (1 << 3) +#define DMIGR_MINT_G2 (1 << 2) +#define DMIGR_MINT_G1 (1 << 1) +#define DMIGR_MINT_G0 (1 << 0) + +/* Device Mask of Interrupt Source Group 0 (0x134) */ +#define FOTG210_DMISGR0 0x134 +#define DMISGR0_MCX_COMEND_INT (1 << 3) +#define DMISGR0_MCX_OUT_INT (1 << 2) +#define DMISGR0_MCX_IN_INT (1 << 1) +#define DMISGR0_MCX_SETUP_INT (1 << 0) + +/* Device Mask of Interrupt Source Group 1 Register (0x138)*/ +#define FOTG210_DMISGR1 0x138 +#define DMISGR1_MFN_IN_INT(fifo) (1 << (16 + (fifo))) +#define DMISGR1_MFN_OUTSPK_INT(fifo) (0x3 << (fifo) * 2) + +/* Device Mask of Interrupt Source Group 2 Register (0x13C) */ +#define FOTG210_DMISGR2 0x13C +#define DMISGR2_MDEV_WAKEUP_VBUS (1 << 10) +#define DMISGR2_MDEV_IDLE (1 << 9) +#define DMISGR2_MDMA_ERROR (1 << 8) +#define DMISGR2_MDMA_CMPLT (1 << 7) +#ifdef EXTEND_FIFO +#define DMISGR2_MFN_OUTSPK_INT(fifo) (0x3 << 16 << (((fifo) - 8) * 2)) +#endif + +/* Device Interrupt group Register (0x140) */ +#define FOTG210_DIGR 0x140 +#define DIGR_INT_G4 (1 << 4) +#define DIGR_INT_G3 (1 << 3) +#define DIGR_INT_G2 (1 << 2) +#define DIGR_INT_G1 (1 << 1) +#define DIGR_INT_G0 (1 << 0) + +/* Device Interrupt Source Group 0 Register (0x144) */ +#define FOTG210_DISGR0 0x144 +#define DISGR0_CX_COMABT_INT (1 << 5) +#define DISGR0_CX_COMFAIL_INT (1 << 4) +#define DISGR0_CX_COMEND_INT (1 << 3) +#define DISGR0_CX_OUT_INT (1 << 2) +#define DISGR0_CX_IN_INT (1 << 1) +#define DISGR0_CX_SETUP_INT (1 << 0) + +/* Device Interrupt Source Group 1 Register (0x148) */ +#define FOTG210_DISGR1 0x148 +#define DISGR1_FN_IN_INT(fifo) (1 << 16 << (fifo)) +#define DISGR1_FN_OUT_INT(fifo) (1 << ((fifo) * 2)) +#define DISGR1_FN_SPK_INT(fifo) (1 << 1 << ((fifo) * 2)) + +/* Device Interrupt Source Group 2 Register (0x14C) */ +#define FOTG210_DISGR2 0x14C +#define DISGR2_DMA_ERROR (1 << 8) +#define DISGR2_DMA_CMPLT (1 << 7) +#define DISGR2_RX0BYTE_INT (1 << 6) +#define DISGR2_TX0BYTE_INT (1 << 5) +#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4) +#define DISGR2_ISO_SEQ_ERR_INT (1 << 3) +#define DISGR2_RESM_INT (1 << 2) +#define DISGR2_SUSP_INT (1 << 1) +#define DISGR2_USBRST_INT (1 << 0) +#ifdef EXTEND_FIFO +#define DISGR2_FN_OUT_INT(fifo) (1 << 16 << (((fifo) - 8) * 2)) +#define DISGR2_FN_SPK_INT(fifo) (1 << 17 << (((fifo) - 8) * 2)) +#endif + +/* Device Receive Zero-Length Data Packet Register (0x150)*/ +#define FOTG210_RX0BYTE 0x150 +#define RX0BYTE_EP8 (1 << 7) +#define RX0BYTE_EP7 (1 << 6) +#define RX0BYTE_EP6 (1 << 5) +#define RX0BYTE_EP5 (1 << 4) +#define RX0BYTE_EP4 (1 << 3) +#define RX0BYTE_EP3 (1 << 2) +#define RX0BYTE_EP2 (1 << 1) +#define RX0BYTE_EP1 (1 << 0) + +/* Device Transfer Zero-Length Data Packet Register (0x154)*/ +#define FOTG210_TX0BYTE 0x154 +#define TX0BYTE_EP8 (1 << 7) +#define TX0BYTE_EP7 (1 << 6) +#define TX0BYTE_EP6 (1 << 5) +#define TX0BYTE_EP5 (1 << 4) +#define TX0BYTE_EP4 (1 << 3) +#define TX0BYTE_EP3 (1 << 2) +#define TX0BYTE_EP2 (1 << 1) +#define TX0BYTE_EP1 (1 << 0) + +/* Device IN Endpoint x MaxPacketSize Register (0x160+4*(x-1), x=1~8) */ +#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1)) +#define INOUTEPMPSR_MPS(mps) ((mps) & 0x7FF) +#define INOUTEPMPSR_STL_EP (1 << 11) +#define INOUTEPMPSR_RESET_TSEQ (1 << 12) +#define INEPMPSR_TX_NUM_HBW(num) ((num) << 13) +#define INEPMPSR_TX0BYTE (1 << 15) + +/* Device OUT Endpoint x MaxPacketSize Register (0x180+4*(x-1), x=1~8) */ +#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1)) + +/* Device Endpoint 1~4 Map Register (0x1A0) */ +#define FOTG210_EPMAP 0x1A0 +#define EPMAP_FIFONO(fifo, ep, dir) \ + (((fifo) << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) +#ifndef EXTEND_FIFO +#define EPMAP_FIFONOMSK(ep, dir) \ + ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) +#else +#define EPMAP_FIFONOMSK(ep, dir) \ + ((0xF << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) +#endif + +/* Device Endpoint 5~8 Map Register (0x1A4) */ +#define FOTG210_EPMAP2 0x1A4 + +/* Device FIFO 0~3 Map Register (0x1A8) */ +#define FOTG210_FIFOMAP 0x1A8 +#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << ((fifo) * 8)) +#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << ((fifo) * 8)) +#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << ((fifo) * 8)) +#define FIFOMAP_NA(fifo) (0x3 << 4 << ((fifo) * 8)) +#define FIFOMAP_EPNO(fifo, ep) ((ep) << ((fifo) * 8)) +#define FIFOMAP_EPNOMSK(fifo) (0xF << ((fifo) * 8)) +#ifdef EXTEND_FIFO +/* Device FIFO 4~7 Map Register (0x1D8) */ +#define FOTG210_FIFOMAP2 0x1D8 +/* Device FIFO 8~11 Map Register (0x1E0) */ +#define FOTG210_FIFOMAP3 0x1E0 +/* Device FIFO 12~15 Map Register (0x1E8) */ +#define FOTG210_FIFOMAP4 0x1E8 +#endif + +/* Device FIFO 0~3 Confuguration Register (0x1AC) */ +#define FOTG210_FIFOCF 0x1AC +#define FIFOCF_TYPE(type, fifo) ((type) << ((fifo) * 8)) +#define FIFOCF_BLK_SIN(fifo) (0x0 << ((fifo) * 8) << 2) +#define FIFOCF_BLK_DUB(fifo) (0x1 << ((fifo) * 8) << 2) +#define FIFOCF_BLK_TRI(fifo) (0x2 << ((fifo) * 8) << 2) +#define FIFOCF_BLKSZ_512(fifo) (0x0 << ((fifo) * 8) << 4) +#define FIFOCF_BLKSZ_1024(fifo) (0x1 << ((fifo) * 8) << 4) +#define FIFOCF_FIFO_EN(fifo) (0x1 << ((fifo) * 8) << 5) +#ifdef EXTEND_FIFO +/* Device FIFO 4~7 Confuguration Register (0x1DC) */ +#define FOTG210_FIFOCF2 0x1DC +/* Device FIFO 8~11 Confuguration Register (0x1E4) */ +#define FOTG210_FIFOCF3 0x1E4 +/* Device FIFO 12~15 Confuguration Register (0x1EC) */ +#define FOTG210_FIFOCF4 0x1EC +#endif + +/* Device FIFO 0~3 Instruction and Byte Count Register (0x1B0+4*n, n=0~3) */ +#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4) +#define FIBCR_BCFX 0x7FF +#define FIBCR_FFRST (1 << 12) +#ifdef EXTEND_FIFO +/* Device FIFO 4~15 Instruction and Byte Count Register (0x1F0+4*(n-4), n=4~15) */ +#define FOTG210_FIBCR_EX(fifo) (0x1F0 + ((fifo) -4) * 4) +#endif + +/* Device DMA Target FIFO Number Register (0x1C0) */ +#define FOTG210_DMATFNR 0x1C0 +#define DMATFNR_ACC_CXF (1 << 4) +#define DMATFNR_ACC_FN(fifo) (1 << (fifo)) +#define DMATFNR_DISDMA 0 +#ifdef EXTEND_FIFO +#define DMATFNR_ACC_FN_EX(fifo) (1 << ((fifo) + 1)) +#endif + +/* Device DMA Controller Parameter setting 1 Register (0x1C8) */ +#define FOTG210_DMACPSR1 0x1C8 +#define DMACPSR1_DMA_LEN(len) (((len) & 0x1FFFF) << 8) +#define DMACPSR1_DMA_ABORT (1 << 3) +#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) +#define DMACPSR1_DMA_START (1 << 0) + +/* Device DMA Controller Parameter setting 2 Register (0x1CC) */ +#define FOTG210_DMACPSR2 0x1CC + +/* Device DMA Controller Parameter setting 3 Register (0x1D0) */ +#define FOTG210_CXPORT 0x1D0 + +// --------------- VIRTUAL_DMA (VDMA) -----------------------// +/* Device Virtual DMA CXF Parameter setting 1 Register (0x300) */ +#define FOTG210_VDMA_CXFPS1 0x300 +/* Device Virtual DMA CXF Parameter setting 2 Register (0x304) */ +#define FOTG210_VDMA_CXFPS2 0x304 + +/* Device Virtual DMA FIFO 0~3 Parameter setting 1 Register (0x308+8*n, n=0~3) */ +#define FOTG210_VDMA_FNPS1(fifo) (0x308 + (fifo) * 8) +#define VDMAFPS1_VDMA_LEN(len) (((len) & 0x1FFFF) << 8) +#define VDMAFPS1_VDMA_IO (1 << 2) +#define VDMAFPS1_VDMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) +#define VDMAFPS1_VDMA_START (1 << 0) +#define GET_VDMAFPS1_VDMA_LEN(p) (((p) & (0x1FFFF << 8)) >> 8) +#ifdef EXTEND_FIFO +/* Device Virtual DMA FIFO 4~15 Parameter setting 1 Register (0x350+8*(n-4), n=4~15) */ +#define FOTG210_VDMA_FNPS1_EX(fifo) (0x350 + ((fifo) - 4) * 8) +#endif + +/* Device Virtual DMA FIFO 0~3 Parameter setting 2 Register (0x30C+8*n, n=0~3) */ +#define FOTG210_VDMA_FNPS2(fifo) (0x30C + (fifo) * 8) +#ifdef EXTEND_FIFO +/* Device Virtual DMA FIFO 4~15 Parameter setting 2 Register (0x354+8*(n-4), n=4~15) */ +#define FOTG210_VDMA_FNPS2_EX(fifo) (0x354 + ((fifo) - 4) * 8) +#endif + +/* Device Interrupt Source Group 3 Register (0x328) */ +#define FOTG210_DISGR3 0x328 +#define DISGR3_VDMA_ERROR_FN(fifo) (1 << ((fifo) + 17)) +#define DISGR3_VDMA_ERROR_CXF (1 << 16) +#define DISGR3_VDMA_CMPLT_FN(fifo) (1 << ((fifo) + 1)) +#define DISGR3_VDMA_CMPLT_CXF (1 << 0) + +/* Device Mask of Interrupt Source Group 3 Register (0x32C) */ +#define FOTG210_DMISGR3 0x32C +#define DMISGR3_MVDMA_ERROR_FN(fifo) (1 << ((fifo) + 17)) +#define DMISGR3_MVDMA_ERROR_CXF (1 << 16) +#define DMISGR3_MVDMA_CMPLT_FN(fifo) (1 << ((fifo) + 1)) +#define DMISGR3_MVDMA_CMPLT_CXF (1 << 0) + +/* Device Virtual DMA Control Register (0x330) */ +#define FOTG210_VDMA_CTRL 0x330 +#define VDMA_EN (1 << 0) + +/* Device Interrupt Source Group 4 Register (0x338) */ +#define FOTG210_DISGR4 0x338 +#define DISGR4_L1_INT (1 << 0) +#ifdef EXTEND_FIFO +#define DISGR4_VDMA_ERROR_FN(fifo) (1 << 24 << ((fifo) - 15)) +#define DISGR4_VDMA_CMPLT_FN(fifo) (1 << 16 << ((fifo) - 15)) +#endif + +/* Device Mask of Interrupt Source Group 4 Register (0x33C) */ +#define FOTG210_DMISGR4 0x33C +#define DMISGR4_ML1_INT (1 << 0) +#ifdef EXTEND_FIFO +#define DMISGR4_MVDMA_ERROR_FN(fifo) (1 << 24 << ((fifo) - 15)) +#define DMISGR4_MVDMA_CMPLT_FN(fifo) (1 << 16 << ((fifo) - 15)) +#endif + + +struct fotg210_request { + struct usb_request req; + struct list_head queue; +}; + +struct fotg210_irq_task { + dma_addr_t dma_addr; + u32 transfer_length; + struct fotg210_request *req; +}; + +struct fotg210_ep { + struct usb_ep ep; + struct fotg210_udc *fotg210; + + struct list_head queue; + unsigned stall:1; + unsigned wedged:1; + unsigned use_dma:1; + + unsigned char epnum; + unsigned char type; /* transfer type: bulk/interrupt/iso. */ + unsigned char dir_in; + unsigned char fifonum; /* This ep map to which FIFO number (FIFO 0~3) */ + struct fotg210_irq_task *irq_current_task; +}; + +struct fotg210_udc { + spinlock_t lock; /* protect the struct */ + void __iomem *reg; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct fotg210_ep *ep[FOTG210_MAX_NUM_EP]; + + struct usb_request *ep0_req; /* for internal request */ + __le16 ep0_data; + u32 ep0_length; /* for set_feature(TEST_PACKET) */ +}; + +#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 8d799d23c476e1..1bdd2bb4bfe0ad 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -389,6 +389,17 @@ config USB_ISP1362_HCD To compile this driver as a module, choose M here: the module will be called isp1362-hcd. +config USB_FOTG210_HCD + tristate "FOTG210 HCD support" + depends on USB && HAS_DMA && HAS_IOMEM + ---help--- + Faraday FOTG210 is an OTG controller which can be configured as + an USB2.0 host. It is designed to meet USB2.0 EHCI specification + with minor modification. + + To compile this driver as a module, choose M here: the + module will be called fotg210-hcd. + config USB_MAX3421_HCD tristate "MAX3421 HCD (USB-over-SPI) support" depends on USB && SPI diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6d8ee264c9b2bf..2c8a61be7e466c 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -84,5 +84,6 @@ obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o obj-$(CONFIG_USB_EHCI_MV) += ehci-mv.o obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o +obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o obj-$(CONFIG_USB_XEN_HCD) += xen-hcd.o diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c similarity index 78% rename from drivers/usb/fotg210/fotg210-hcd.c rename to drivers/usb/host/fotg210-hcd.c index 51ac93a2eb98e5..83a1c032f94c90 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1,16 +1,25 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* Faraday FOTG210 EHCI-like driver - * - * Copyright (c) 2013 Faraday Technology Corporation +/* + * Faraday FOTG210 EHCI-like driver * - * Author: Yuan-Hsin Chen - * Feng-Hsin Chiang - * Po-Yu Chuang + * Copyright (c) 2013-2019 Faraday Technology Corporation * * Most of code borrowed from the Linux-3.7 EHCI driver + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include -#include #include #include #include @@ -32,15 +41,15 @@ #include #include #include -#include -#include +#include +#include #include #include #include -#include "fotg210.h" - +#define DRIVER_AUTHOR "Faraday Technology Corporation" +#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" static const char hcd_name[] = "fotg210_hcd"; #undef FOTG210_URB_TRACE @@ -77,7 +86,12 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); #define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) -#include "fotg210-hcd.h" +#include "fotg210.h" +//lichun@2019, When device disconnect from port, do HC_RESET +static int unplug_do_hc_rst = 0; + +/*-------------------------------------------------------------------------*/ +/* following is part of ehci-dbg.c */ #define fotg210_dbg(fotg210, fmt, args...) \ dev_dbg(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) @@ -174,6 +188,21 @@ dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) itd->index[6], itd->index[7]); } +static void __maybe_unused +dbg_sitd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_sitd *sitd) +{ + fotg210_dbg(fotg210, "%s [%d] sitd %p, next %08x, urb %p\n", + label, sitd->frame, sitd, hc32_to_cpu(fotg210, sitd->hw_next), + sitd->urb); + fotg210_dbg(fotg210, + " addr %08x sched %04x result %08x buf %08x %08x\n", + hc32_to_cpu(fotg210, sitd->hw_fullspeed_ep), + hc32_to_cpu(fotg210, sitd->hw_uframe), + hc32_to_cpu(fotg210, sitd->hw_results), + hc32_to_cpu(fotg210, sitd->hw_buf[0]), + hc32_to_cpu(fotg210, sitd->hw_buf[1])); +} + static int __maybe_unused dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) { @@ -408,17 +437,17 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, temp = snprintf(next, size, "\n\t%p%c%s len=%d %08x urb %p", td, mark, ({ char *tmp; - switch ((scratch>>8)&0x03) { - case 0: + switch ((scratch>>8)&0x03) { + case 0: tmp = "out"; break; - case 1: + case 1: tmp = "in"; break; - case 2: + case 2: tmp = "setup"; break; - default: + default: tmp = "?"; break; } tmp; }), @@ -606,6 +635,16 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); p = p.itd->itd_next; break; + case Q_TYPE_SITD: + temp = scnprintf(next, size, + " sitd%d-%04x/%p", + p.sitd->stream->interval, + hc32_to_cpup(fotg210, &p.sitd->hw_uframe) + & 0x0000ffff, + p.sitd); + tag = Q_NEXT_TYPE(fotg210, p.sitd->hw_next); + p = p.sitd->sitd_next; + break; } size -= temp; next += temp; @@ -847,24 +886,39 @@ static int debug_registers_open(struct inode *inode, struct file *file) static inline void create_debug_files(struct fotg210_hcd *fotg210) { struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; - struct dentry *root; - root = debugfs_create_dir(bus->bus_name, fotg210_debug_root); + fotg210->debug_dir = debugfs_create_dir(bus->bus_name, + fotg210_debug_root); + if (!fotg210->debug_dir) + return; + + if (!debugfs_create_file("async", S_IRUGO, fotg210->debug_dir, bus, + &debug_async_fops)) + goto file_error; + + if (!debugfs_create_file("periodic", S_IRUGO, fotg210->debug_dir, bus, + &debug_periodic_fops)) + goto file_error; + + if (!debugfs_create_file("registers", S_IRUGO, fotg210->debug_dir, bus, + &debug_registers_fops)) + goto file_error; + + return; - debugfs_create_file("async", S_IRUGO, root, bus, &debug_async_fops); - debugfs_create_file("periodic", S_IRUGO, root, bus, - &debug_periodic_fops); - debugfs_create_file("registers", S_IRUGO, root, bus, - &debug_registers_fops); +file_error: + debugfs_remove_recursive(fotg210->debug_dir); } static inline void remove_debug_files(struct fotg210_hcd *fotg210) { - struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; - - debugfs_remove(debugfs_lookup(bus->bus_name, fotg210_debug_root)); + debugfs_remove_recursive(fotg210->debug_dir); } +/* above is part of ehci-dbg.c */ + +/*-------------------------------------------------------------------------*/ + /* handshake - spin reading hc until handshake completes or fails * @ptr: address of hc register to be read * @mask: bits to look at in result of read @@ -885,15 +939,18 @@ static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, u32 mask, u32 done, int usec) { u32 result; - int ret; - ret = readl_poll_timeout_atomic(ptr, result, - ((result & mask) == done || - result == U32_MAX), 1, usec); - if (result == U32_MAX) /* card removed */ - return -ENODEV; - - return ret; + do { + result = fotg210_readl(fotg210, ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; } /* Force HC to halt state from unknown (EHCI spec section 2.3). @@ -990,6 +1047,9 @@ static void start_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); +/*-------------------------------------------------------------------------*/ +/* following is part of ehci-timer.c */ + /* Set a bit in the USBCMD register */ static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) { @@ -1047,6 +1107,7 @@ static unsigned event_delays_ns[] = { 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */ 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */ + 1000 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DELAY_HCD_RESET */ }; /* Enable a pending hrtimer event */ @@ -1230,6 +1291,9 @@ static void start_free_itds(struct fotg210_hcd *fotg210) fotg210->last_itd_to_free = list_entry( fotg210->cached_itd_list.prev, struct fotg210_itd, itd_list); + fotg210->last_sitd_to_free = list_entry( + fotg210->cached_sitd_list.prev, + struct fotg210_sitd, sitd_list); fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true); } } @@ -1238,9 +1302,12 @@ static void start_free_itds(struct fotg210_hcd *fotg210) static void end_free_itds(struct fotg210_hcd *fotg210) { struct fotg210_itd *itd, *n; + struct fotg210_sitd *sitd, *sn; - if (fotg210->rh_state < FOTG210_RH_RUNNING) + if (fotg210->rh_state < FOTG210_RH_RUNNING) { fotg210->last_itd_to_free = NULL; + fotg210->last_sitd_to_free = NULL; + } list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) { list_del(&itd->itd_list); @@ -1248,8 +1315,15 @@ static void end_free_itds(struct fotg210_hcd *fotg210) if (itd == fotg210->last_itd_to_free) break; } + list_for_each_entry_safe(sitd, sn, &fotg210->cached_sitd_list, sitd_list) { + list_del(&sitd->sitd_list); + dma_pool_free(fotg210->sitd_pool, sitd, sitd->sitd_dma); + if (sitd == fotg210->last_sitd_to_free) + break; + } - if (!list_empty(&fotg210->cached_itd_list)) + if (!list_empty(&fotg210->cached_itd_list) || + !list_empty(&fotg210->cached_sitd_list)) start_free_itds(fotg210); } @@ -1286,7 +1360,7 @@ static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) */ status = fotg210_readl(fotg210, &fotg210->regs->status); if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { - INCR(fotg210->stats.lost_iaa); + COUNT(fotg210->stats.lost_iaa); fotg210_writel(fotg210, STS_IAA, &fotg210->regs->status); } @@ -1317,6 +1391,28 @@ static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) true); } +/* 20150829 BC: do HC_Reset */ +static void fotg210_set_hc_reset(struct fotg210_hcd *fotg210) +{ + u32 regcommand, k, temp; + + fotg210_dbg(fotg210, "Reset HC, start.....\n"); + regcommand = readl(&fotg210->regs->command); + + writel(CMD_RESET, &fotg210->regs->command); + k = 0; + temp = CMD_RESET; + while (((temp & CMD_RESET)) && (k <= 100)) { + mdelay(3); + k++; + temp = readl(&fotg210->regs->command); + } + writel(INTR_MASK, &fotg210->regs->intr_enable); + writel(regcommand, &fotg210->regs->command); + mdelay(1); + writel(regcommand | CMD_RUN, &fotg210->regs->command); + fotg210_dbg(fotg210, "Reset HC, recover.....\n"); +} /* Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by @@ -1333,6 +1429,7 @@ static void (*event_handlers[])(struct fotg210_hcd *) = { fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */ fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */ + fotg210_set_hc_reset, /* FOTG210_HRTIMER_DELAY_HCD_RESET */ }; static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) @@ -1366,11 +1463,291 @@ static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) return HRTIMER_NORESTART; } -#define fotg210_bus_suspend NULL -#define fotg210_bus_resume NULL +/* above is part of ehci-timer.c */ + +/*-------------------------------------------------------------------------*/ +/* following is part of ehci-hub.c */ + +/* + * EHCI Root Hub ... the nonsharable stuff + * + * Registers don't need cpu_to_le32, that happens transparently + */ + +/*-------------------------------------------------------------------------*/ +#ifdef CONFIG_PM + +static int fotg210_port_change(struct fotg210_hcd *fotg210) +{ + /* First check if the controller indicates a change event */ + + if (fotg210_readl(fotg210, &fotg210->regs->status) & STS_PCD) + return 1; + + /* + * Not all controllers appear to update this while going from D3 to D0, + * so check the individual port status registers as well + */ + + if (fotg210_readl(fotg210, &fotg210->regs->port_status) & PORT_CSC) + return 1; + + return 0; +} + +static void fotg210_adjust_port_wakeup_flags(struct fotg210_hcd *fotg210, + bool suspending, bool do_wakeup) +{ + /* If remote wakeup is enabled for the root hub but disabled + * for the controller, we must adjust all the port wakeup flags + * when the controller is suspended or resumed. In all other + * cases they don't need to be changed. + */ + if (!fotg210_to_hcd(fotg210)->self.root_hub->do_remote_wakeup || do_wakeup) + return; + + spin_lock_irq(&fotg210->lock); + + /* Does the root hub have a port wakeup pending? */ + if (!suspending && fotg210_port_change(fotg210)) + usb_hcd_resume_root_hub(fotg210_to_hcd(fotg210)); + + spin_unlock_irq(&fotg210->lock); +} + +static int fotg210_bus_suspend(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + int port; + int mask; + int changed; + + fotg210_dbg(fotg210, "suspend root hub\n"); + + if (time_before(jiffies, fotg210->next_statechange)) + msleep(5); + + /* stop the schedules */ + fotg210_quiesce(fotg210); + + spin_lock_irq(&fotg210->lock); + if (fotg210->rh_state < FOTG210_RH_RUNNING) + goto done; + + /* Once the controller is stopped, port resumes that are already + * in progress won't complete. Hence if remote wakeup is enabled + * for the root hub and any ports are in the middle of a resume or + * remote wakeup, we must fail the suspend. + */ + if (hcd->self.root_hub->do_remote_wakeup) { + if (fotg210->resuming_ports) { + spin_unlock_irq(&fotg210->lock); + fotg210_dbg(fotg210, "suspend failed because a port is resuming\n"); + return -EBUSY; + } + } + + /* Unlike other USB host controller types, EHCI doesn't have + * any notion of "global" or bus-wide suspend. The driver has + * to manually suspend all the active unsuspended ports, and + * then manually resume them in the bus_resume() routine. + */ + fotg210->bus_suspended = 0; + changed = 0; + port = HCS_N_PORTS(fotg210->hcs_params); + while (port--) { + u32 __iomem *reg = &fotg210->regs->port_status; + u32 t1 = fotg210_readl(fotg210, reg) & ~PORT_RWC_BITS; + u32 t2 = t1; + + /* keep track of which ports we suspend */ + if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) { + t2 |= PORT_SUSPEND; + set_bit(port, &fotg210->bus_suspended); + } + + if (t1 != t2) { + fotg210_dbg(fotg210, "port %d, %08x -> %08x\n", + port + 1, t1, t2); + fotg210_writel(fotg210, t2, reg); + changed = 1; + } + } + + spin_unlock_irq(&fotg210->lock); + + /* Apparently some devices need a >= 1-uframe delay here */ + if (fotg210->bus_suspended) + udelay(150); + + /* turn off now-idle HC */ + fotg210_halt(fotg210); + + spin_lock_irq(&fotg210->lock); + if (fotg210->enabled_hrtimer_events & BIT(FOTG210_HRTIMER_POLL_DEAD)) + fotg210_handle_controller_death(fotg210); + if (fotg210->rh_state != FOTG210_RH_RUNNING) + goto done; + fotg210->rh_state = FOTG210_RH_SUSPENDED; + + end_unlink_async(fotg210); + unlink_empty_async(fotg210); + fotg210_handle_intr_unlinks(fotg210); + end_free_itds(fotg210); + + /* allow remote wakeup */ + mask = INTR_MASK; + if (!hcd->self.root_hub->do_remote_wakeup) + mask &= ~STS_PCD; + fotg210_writel(fotg210, mask, &fotg210->regs->intr_enable); + fotg210_readl(fotg210, &fotg210->regs->intr_enable); + + done: + fotg210->next_statechange = jiffies + msecs_to_jiffies(10); + fotg210->enabled_hrtimer_events = 0; + fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; + spin_unlock_irq(&fotg210->lock); + + hrtimer_cancel(&fotg210->hrtimer); + return 0; +} + + +/* caller has locked the root hub, and should reset/reinit on error */ +static int fotg210_bus_resume(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp; + u32 power_okay; + int i; + unsigned long resume_needed = 0; + + if (time_before(jiffies, fotg210->next_statechange)) + msleep(5); + spin_lock_irq(&fotg210->lock); + if (!HCD_HW_ACCESSIBLE(hcd) || fotg210->shutdown) + goto shutdown; + + if (unlikely(fotg210->debug)) { + if (!dbgp_reset_prep(hcd)) + fotg210->debug = NULL; + else + dbgp_external_startup(hcd); + } + + /* Ideally and we've got a real resume here, and no port's power + * was lost. (For PCI, that means Vaux was maintained.) But we + * could instead be restoring a swsusp snapshot -- so that BIOS was + * the last user of the controller, not reset/pm hardware keeping + * state we gave to it. + */ + power_okay = fotg210_readl(fotg210, &fotg210->regs->intr_enable); + fotg210_dbg(fotg210, "resume root hub%s\n", + power_okay ? "" : " after power loss"); + + /* at least some APM implementations will try to deliver + * IRQs right away, so delay them until we're ready. + */ + fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); + + /* re-init operational registers */ + fotg210_writel(fotg210, fotg210->periodic_dma, &fotg210->regs->frame_list); + fotg210_writel(fotg210, (u32) fotg210->async->qh_dma, &fotg210->regs->async_next); + + //FOTG210 patch resume, move these codes later + /* restore CMD_RUN, framelist size, and irq threshold */ +// fotg210->command |= CMD_RUN; +// fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); +// fotg210->rh_state = FOTG210_RH_RUNNING; + + /* + * According to Bugzilla #8190, the port status for some controllers + * will be wrong without a delay. At their wrong status, the port + * is enabled, but not suspended neither resumed. + */ + i = HCS_N_PORTS(fotg210->hcs_params); + while (i--) { + temp = fotg210_readl(fotg210, &fotg210->regs->port_status); + if ((temp & PORT_PE) && + !(temp & (PORT_SUSPEND | PORT_RESUME))) { + fotg210_dbg(fotg210, "Port status(0x%x) is wrong\n", temp); + spin_unlock_irq(&fotg210->lock); + msleep(8); + spin_lock_irq(&fotg210->lock); + break; + } + } + + if (fotg210->shutdown) + goto shutdown; + + /* manually resume the ports we suspended during bus_suspend() */ + i = HCS_N_PORTS(fotg210->hcs_params); + while (i--) { + temp = fotg210_readl(fotg210, &fotg210->regs->port_status); + temp &= ~(PORT_RWC_BITS); + if (test_bit(i, &fotg210->bus_suspended) && + (temp & PORT_SUSPEND)) { + temp |= PORT_RESUME; + set_bit(i, &resume_needed); + } + fotg210_writel(fotg210, temp, &fotg210->regs->port_status); + } + + /* msleep for 20ms only if code is trying to resume port */ + if (resume_needed) { + spin_unlock_irq(&fotg210->lock); + msleep(20); + spin_lock_irq(&fotg210->lock); + if (fotg210->shutdown) + goto shutdown; + } + + i = HCS_N_PORTS(fotg210->hcs_params); + while (i--) { + temp = fotg210_readl(fotg210, &fotg210->regs->port_status); + if (test_bit(i, &resume_needed)) { + temp &= ~(PORT_RWC_BITS | PORT_RESUME); + fotg210_writel(fotg210, temp, &fotg210->regs->port_status); + fotg210_dbg(fotg210, "resumed port %d\n", i + 1); + } + } + + //FOTG210 patch resume + /* restore CMD_RUN, framelist size, and irq threshold */ + fotg210->command |= CMD_RUN; + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + fotg210->rh_state = FOTG210_RH_RUNNING; + //~FOTG210 patch resume + + fotg210->next_statechange = jiffies + msecs_to_jiffies(5); + spin_unlock_irq(&fotg210->lock); + + /* Now we can safely re-enable irqs */ + spin_lock_irq(&fotg210->lock); + if (fotg210->shutdown) + goto shutdown; + fotg210_writel(fotg210, INTR_MASK, &fotg210->regs->intr_enable); + (void) fotg210_readl(fotg210, &fotg210->regs->intr_enable); + spin_unlock_irq(&fotg210->lock); + + return 0; + + shutdown: + spin_unlock_irq(&fotg210->lock); + return -ESHUTDOWN; +} + +#else + +#define fotg210_bus_suspend NULL +#define fotg210_bus_resume NULL +#endif /* CONFIG_PM */ + +/*-------------------------------------------------------------------------*/ static int check_reset_complete(struct fotg210_hcd *fotg210, int index, - u32 __iomem *status_reg, int port_status) + u32 __iomem *status_reg, int port_status) { if (!(port_status & PORT_CONNECT)) return port_status; @@ -1416,6 +1793,16 @@ static int fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) temp = fotg210_readl(fotg210, &fotg210->regs->port_status); + // 20170428 BC: Sometimes USB subsystem will not clear PORT_CSC and PORT_PEC events when PORT_RESET stalled. + if((temp & PORT_RESET) !=0) { + if((temp & mask) != 0) { + printk("Forced clear PORT_CSC & PORT_PEC events\n"); + fotg210_writel(fotg210, temp, &fotg210->regs->port_status); + + unplug_do_hc_rst = 1; //lichun@add + } + } + /* * Return status information even for ports with OWNER set. * Otherwise hub_wq wouldn't see the disconnect event when a @@ -1429,7 +1816,11 @@ static int fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) buf[0] |= 1 << 1; status = STS_PCD; } - /* FIXME autosuspend idle root hubs */ + + /* If a resume is in progress, make sure it can finish */ + if (fotg210->resuming_ports) + mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25)); + spin_unlock_irqrestore(&fotg210->lock, flags); return status ? retval : 0; } @@ -1457,6 +1848,77 @@ static void fotg210_hub_descriptor(struct fotg210_hcd *fotg210, desc->wHubCharacteristics = cpu_to_le16(temp); } +//lichun@add, USB2 PET Test Packet +static void fotg210_set_test_packet(struct fotg210_hcd *fotg210) +{ + u8 *tst_pkt; + int i, idx; + u32 temp; + dma_addr_t d; + + tst_pkt = kzalloc(53, GFP_KERNEL); + if (!tst_pkt) { + printk("Could not alloc test packet size\n"); + return; + } + memset(tst_pkt, 0, 53); + + idx = 0; + for (i = 0; i < 9; i++)/*JKJKJKJK x 9*/ + tst_pkt[idx++] = 0x00; + + for (i = 0; i < 8; i++) /* JJKKJJKK*8, 8*AA */ + tst_pkt[idx++] = 0xAA; + + for (i = 0; i < 8; i++) /* JJJJKKKK*8, 8*EE */ + tst_pkt[idx++] = 0xEE; + + tst_pkt[idx++] = 0xFE; /* JJJJJJJKKKKKKK *8 */ + + for (i = 0; i < 11; i++) /* 11*FF */ + tst_pkt[idx++] = 0xFF; + + tst_pkt[idx++] = 0x7F; // JJJJJJJK *8 + tst_pkt[idx++] = 0xBF; + tst_pkt[idx++] = 0xDF; + tst_pkt[idx++] = 0xEF; + tst_pkt[idx++] = 0xF7; + tst_pkt[idx++] = 0xFB; + tst_pkt[idx++] = 0xFD; + tst_pkt[idx++] = 0xFC; + tst_pkt[idx++] = 0x7E; // {JKKKKKKK * 10}, JK + tst_pkt[idx++] = 0xBF; + tst_pkt[idx++] = 0xDF; + tst_pkt[idx++] = 0xEF; + tst_pkt[idx++] = 0xF7; + tst_pkt[idx++] = 0xFB; + tst_pkt[idx++] = 0xFD; + tst_pkt[idx++] = 0x7E; + + d = dma_map_single(fotg210_to_hcd(fotg210)->self.controller, + tst_pkt, 53, DMA_TO_DEVICE); + + //set DMA transfer length and direction (0x1c8) + fotg210_writel(fotg210, (53 << 8) | 0x02, &fotg210->regs->dev_dma_cps1); + + //set FIFO for CX (0x1c0) + fotg210_writel(fotg210, 0x10, &fotg210->regs->dev_dma_tfn); + + //set DMA address (0x1cc) + fotg210_writel(fotg210, d, &fotg210->regs->dev_dma_cps2); + + //start DMA (0x1c8) + temp = fotg210_readl(fotg210, &fotg210->regs->dev_dma_cps1); + temp |= (1 << 0); + fotg210_writel(fotg210, temp, &fotg210->regs->dev_dma_cps1); + + //wait until DMA done + do { + temp = fotg210_readl(fotg210, &fotg210->regs->dev_dma_cps1); + } while(temp & 0x1); +} +//~lichun + static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { @@ -1467,6 +1929,7 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, unsigned long flags; int retval = 0; unsigned selector; + u32 count; //lichun@add /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. @@ -1528,8 +1991,8 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, fotg210_writel(fotg210, temp | PORT_CSC, status_reg); break; case USB_PORT_FEAT_C_OVER_CURRENT: - fotg210_writel(fotg210, temp | OTGISR_OVC, - &fotg210->regs->otgisr); + fotg210_writel(fotg210, OTGISR_OVC, + &fotg210->regs->otgisr); break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ @@ -1555,6 +2018,17 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status = 0; temp = fotg210_readl(fotg210, status_reg); + //lichun@2019, When device disconnect from port, do HC_RESET + if (temp & (PORT_CONNECT | PORT_CSC | PORT_PE | PORT_PEC)) + /*unplug_do_hc_rst = 1*/; + else { + if (unplug_do_hc_rst) { + fotg210_set_hc_reset(fotg210); + unplug_do_hc_rst = 0; + } + } + //~lichun@2019 + /* wPortChange bits */ if (temp & PORT_CSC) status |= USB_PORT_STAT_C_CONNECTION << 16; @@ -1573,6 +2047,7 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* resume signaling for 20 msec */ fotg210->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); + usb_hcd_start_port_resume(&hcd->self, wIndex); /* check the port again */ mod_timer(&fotg210_to_hcd(fotg210)->rh_timer, fotg210->reset_done[wIndex]); @@ -1584,6 +2059,7 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, clear_bit(wIndex, &fotg210->suspended_ports); set_bit(wIndex, &fotg210->port_c_suspend); fotg210->reset_done[wIndex] = 0; + usb_hcd_end_port_resume(&hcd->self, wIndex); /* stop resume signaling */ temp = fotg210_readl(fotg210, status_reg); @@ -1595,8 +2071,8 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, PORT_RESUME, 0, 2000);/* 2ms */ if (retval != 0) { fotg210_err(fotg210, - "port %d resume error %d\n", - wIndex + 1, retval); + "port %d resume error %d\n", + wIndex + 1, retval); goto error; } temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); @@ -1612,8 +2088,8 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* force reset to complete */ fotg210_writel(fotg210, - temp & ~(PORT_RWC_BITS | PORT_RESET), - status_reg); + temp & ~(PORT_RWC_BITS | PORT_RESET), + status_reg); /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... */ @@ -1621,17 +2097,18 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, PORT_RESET, 0, 1000); if (retval != 0) { fotg210_err(fotg210, "port %d reset error %d\n", - wIndex + 1, retval); + wIndex + 1, retval); goto error; } + //lichun@add, we disable RUN before port_reset, enable RUN here. + writel(readl(&fotg210->regs->command) | + CMD_RUN, &fotg210->regs->command); + while((readl(&fotg210->regs->status) & STS_HALT)) ; + /* see what we found out */ temp = check_reset_complete(fotg210, wIndex, status_reg, fotg210_readl(fotg210, status_reg)); - - /* restart schedule */ - fotg210->command |= CMD_RUN; - fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); } if (!(temp & (PORT_RESUME|PORT_RESET))) { @@ -1658,6 +2135,7 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (temp & PORT_CONNECT) { status |= USB_PORT_STAT_CONNECTION; status |= fotg210_port_speed(fotg210, temp); + status |= USB_PORT_STAT_POWER; //FOTG210 patch resume } if (temp & PORT_PE) status |= USB_PORT_STAT_ENABLE; @@ -1671,6 +2149,7 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, fotg210->reset_done[wIndex] = 0; if (temp & PORT_PE) set_bit(wIndex, &fotg210->port_c_suspend); + usb_hcd_end_port_resume(&hcd->self, wIndex); } temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); @@ -1725,6 +2204,30 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * which can be fine if this root hub has a * transaction translator built in. */ + //lichun@add + /* Stop controller before port_reset */ + writel(readl(&fotg210->regs->command) & ~CMD_RUN, + &fotg210->regs->command); + + /* re-init operational registers */ + fotg210_writel(fotg210, fotg210->periodic_dma, + &fotg210->regs->frame_list); + fotg210_writel(fotg210, (u32) fotg210->async->qh_dma, + &fotg210->regs->async_next); + + count = 0; + temp1 = 0; + while (((temp1 & STS_HALT) == 0) && (count <= 1000)) { + udelay(125); + count ++; + temp1 = readl(&fotg210->regs->status); + } + //printk("Reset %d\n",count); + if (count >= 1000) { + printk("Host cannot enter HALT state, recover.....\n"); + fotg210_set_hc_reset(fotg210); + } + fotg210_dbg(fotg210, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; @@ -1752,12 +2255,13 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_lock_irqsave(&fotg210->lock, flags); /* Put all enabled ports into suspend */ - temp = fotg210_readl(fotg210, status_reg) & + //lichun@marked, FOTG210 doesn't need this step + /*temp = fotg210_readl(fotg210, status_reg) & ~PORT_RWC_BITS; if (temp & PORT_PE) fotg210_writel(fotg210, temp | PORT_SUSPEND, status_reg); - + */ spin_unlock_irqrestore(&fotg210->lock, flags); fotg210_halt(fotg210); spin_lock_irqsave(&fotg210->lock, flags); @@ -1765,6 +2269,18 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = fotg210_readl(fotg210, status_reg); temp |= selector << 16; fotg210_writel(fotg210, temp, status_reg); + + printk("Enter Test Mode: %d, Port_id=%d\n", + selector, wIndex + 1); + //lichun@add, USB2 PET Test Packet, using ehset.c + if (selector == TEST_PACKET) { + fotg210_set_test_packet(fotg210); + + printk("... Set Test Packet Done\n"); + temp = fotg210_readl(fotg210, status_reg); + temp |= (1 << 20); + fotg210_writel(fotg210, temp, status_reg); + } break; default: @@ -1794,6 +2310,11 @@ static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, return 0; } +/* above is part of ehci-hub.c */ + +/*-------------------------------------------------------------------------*/ +/* following is part of ehci-mem.c */ + /* There's basically three types of memory: * - data used only by the HCD ... kmalloc is fine * - async and periodic schedules, shared by HC and HCD ... these @@ -1859,9 +2380,10 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, if (!qh) goto done; qh->hw = (struct fotg210_qh_hw *) - dma_pool_zalloc(fotg210->qh_pool, flags, &dma); + dma_pool_alloc(fotg210->qh_pool, flags, &dma); if (!qh->hw) goto fail; + memset(qh->hw, 0, sizeof(*qh->hw)); qh->qh_dma = dma; INIT_LIST_HEAD(&qh->qtd_list); @@ -1905,6 +2427,9 @@ static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) dma_pool_destroy(fotg210->itd_pool); fotg210->itd_pool = NULL; + dma_pool_destroy(fotg210->sitd_pool); + fotg210->sitd_pool = NULL; + if (fotg210->periodic) dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, fotg210->periodic_size * sizeof(u32), @@ -1952,8 +2477,18 @@ static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) if (!fotg210->itd_pool) goto fail; + /* SITD for full/low speed split ISO transfers */ + fotg210->sitd_pool = dma_pool_create("fotg210_sitd", + fotg210_to_hcd(fotg210)->self.controller, + sizeof(struct fotg210_sitd), + 32 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */); + if (!fotg210->sitd_pool) { + goto fail; + } + /* Hardware periodic table */ - fotg210->periodic = + fotg210->periodic = (__le32 *) dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, fotg210->periodic_size * sizeof(__le32), &fotg210->periodic_dma, 0); @@ -1974,6 +2509,12 @@ static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) fotg210_mem_cleanup(fotg210); return -ENOMEM; } + +/* above is part of ehci-mem.c */ + +/*-------------------------------------------------------------------------*/ +/* following is part of ehci-q.c */ + /* EHCI hardware queue manipulation ... the core. QH/QTD manipulation. * * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" @@ -1981,7 +2522,7 @@ static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) * buffers needed for the larger number). We use one QH per endpoint, queue * multiple urbs (all three types) per endpoint. URBs may need several qtds. * - * ISO traffic uses "ISO TD" (itd) records, and (along with + * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with * interrupts) needs careful scheduling. Performance improvements can be * an ongoing challenge. That's in "ehci-sched.c". * @@ -2210,12 +2751,12 @@ __acquires(fotg210->lock) } if (unlikely(urb->unlinked)) { - INCR(fotg210->stats.unlink); + COUNT(fotg210->stats.unlink); } else { /* report non-error and short read status as zero */ if (status == -EINPROGRESS || status == -EREMOTEIO) status = 0; - INCR(fotg210->stats.complete); + COUNT(fotg210->stats.complete); } #ifdef FOTG210_URB_TRACE @@ -2511,6 +3052,11 @@ static unsigned qh_completions(struct fotg210_hcd *fotg210, return count; } +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +/* ... and packet size, for any kind of endpoint descriptor */ +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + /* reverse of qh_urb_transaction: free a list of TDs. * used for cleanup after errors, before HC sees an URB's TDs. */ @@ -2596,7 +3142,7 @@ static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ - maxpacket = usb_maxpacket(urb->dev, urb->pipe); + maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); /* * buffer gets wrapped in one or more qtds; @@ -2696,7 +3242,7 @@ static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, * any previous qh and cancel its urbs first; endpoints are * implicitly reset then (data toggle too). * That'd mean updating how usbcore talks to HCDs. (2.7?) - */ +*/ /* Each QH holds a qtd list; a QH is used for everything except iso. @@ -2710,11 +3256,9 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, gfp_t flags) { struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); - struct usb_host_endpoint *ep; u32 info1 = 0, info2 = 0; int is_input, type; int maxp = 0; - int mult; struct usb_tt *tt = urb->dev->tt; struct fotg210_qh_hw *hw; @@ -2729,15 +3273,14 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, is_input = usb_pipein(urb->pipe); type = usb_pipetype(urb->pipe); - ep = usb_pipe_endpoint(urb->dev, urb->pipe); - maxp = usb_endpoint_maxp(&ep->desc); - mult = usb_endpoint_maxp_mult(&ep->desc); + maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input); /* 1024 byte maxpacket is a hardware ceiling. High bandwidth * acts like up to 3KB, but is built from smaller packets. */ - if (maxp > 1024) { - fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", maxp); + if (max_packet(maxp) > 1024) { + fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", + max_packet(maxp)); goto done; } @@ -2751,7 +3294,8 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, */ if (type == PIPE_INTERRUPT) { qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, - is_input, 0, mult * maxp)); + is_input, 0, + hb_mult(maxp) * max_packet(maxp))); qh->start = NO_FRAME; if (urb->dev->speed == USB_SPEED_HIGH) { @@ -2788,7 +3332,7 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, think_time = tt ? tt->think_time : 0; qh->tt_usecs = NS_TO_US(think_time + usb_calc_bus_time(urb->dev->speed, - is_input, 0, maxp)); + is_input, 0, max_packet(maxp))); qh->period = urb->interval; if (qh->period > fotg210->periodic_size) { qh->period = fotg210->periodic_size; @@ -2804,7 +3348,7 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, switch (urb->dev->speed) { case USB_SPEED_LOW: info1 |= QH_LOW_SPEED; - fallthrough; + /* FALL THROUGH */ case USB_SPEED_FULL: /* EPS 0 means "full" */ @@ -2851,11 +3395,11 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, * to help them do so. So now people expect to use * such nonconformant devices with Linux too; sigh. */ - info1 |= maxp << 16; + info1 |= max_packet(maxp) << 16; info2 |= (FOTG210_TUNE_MULT_HS << 30); } else { /* PIPE_INTERRUPT */ - info1 |= maxp << 16; - info2 |= mult << 30; + info1 |= max_packet(maxp) << 16; + info2 |= hb_mult(maxp) << 30; } break; default: @@ -3258,6 +3802,12 @@ static void scan_async(struct fotg210_hcd *fotg210) ++fotg210->async_unlink_cycle; } } + +/* above is part of ehci-q.c */ + +/*-------------------------------------------------------------------------*/ +/* following is part of ehci-sched.c */ + /* EHCI scheduled transaction support: interrupt, iso, split iso * These are called "periodic" transactions in the EHCI spec. * @@ -3272,7 +3822,7 @@ static void scan_async(struct fotg210_hcd *fotg210) static int fotg210_get_frame(struct usb_hcd *hcd); /* periodic_next_shadow - return "next" pointer on shadow list - * @periodic: host pointer to qh/itd + * @periodic: host pointer to qh/itd/sitd * @tag: hardware tag for type of this record */ static union fotg210_shadow *periodic_next_shadow(struct fotg210_hcd *fotg210, @@ -3283,8 +3833,11 @@ static union fotg210_shadow *periodic_next_shadow(struct fotg210_hcd *fotg210, return &periodic->qh->qh_next; case Q_TYPE_FSTN: return &periodic->fstn->fstn_next; - default: + case Q_TYPE_ITD: return &periodic->itd->itd_next; + // case Q_TYPE_SITD: + default: + return &periodic->sitd->sitd_next; } } @@ -3371,6 +3924,27 @@ static unsigned short periodic_usecs(struct fotg210_hcd *fotg210, hw_p = &q->itd->hw_next; q = &q->itd->itd_next; break; + case Q_TYPE_SITD: + /* is it in the S-mask? (count SPLIT, DATA) */ + if (q->sitd->hw_uframe & cpu_to_hc32(fotg210, + 1 << uframe)) { + if (q->sitd->hw_fullspeed_ep & + cpu_to_hc32(fotg210, 1<<31)) + usecs += q->sitd->stream->usecs; + else /* worst case for OUT start-split */ + usecs += HS_USECS_ISO (188); + } + + /* ... C-mask? (count CSPLIT, DATA) */ + if (q->sitd->hw_uframe & + cpu_to_hc32(fotg210, 1 << (8 + uframe))) { + /* worst case for IN complete-split */ + usecs += q->sitd->stream->c_usecs; + } + + hw_p = &q->sitd->hw_next; + q = &q->sitd->sitd_next; + break; } } if (usecs > fotg210->uframe_periodic_max) @@ -3433,6 +4007,20 @@ static int tt_no_collision(struct fotg210_hcd *fotg210, unsigned period, type = Q_NEXT_TYPE(fotg210, hw->hw_next); here = here.qh->qh_next; continue; + case Q_TYPE_SITD: + if (same_tt(dev, here.sitd->urb->dev)) { + u16 mask; + + mask = hc32_to_cpu(fotg210, here.sitd + ->hw_uframe); + /* FIXME assumes no gap for IN! */ + mask |= mask >> 8; + if (mask & uf_mask) + break; + } + type = Q_NEXT_TYPE(fotg210, here.sitd->hw_next); + here = here.sitd->sitd_next; + continue; /* case Q_TYPE_FSTN: */ default: fotg210_dbg(fotg210, @@ -3920,12 +4508,14 @@ static void iso_stream_init(struct fotg210_hcd *fotg210, struct fotg210_iso_stream *stream, struct usb_device *dev, int pipe, unsigned interval) { + static const u8 smask_out [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f }; + u32 buf1; unsigned epnum, maxp; int is_input; long bandwidth; unsigned multi; - struct usb_host_endpoint *ep; + u32 hostp_speed; //speed of host root hub port /* * this might be a "high bandwidth" highspeed endpoint, @@ -3933,35 +4523,78 @@ static void iso_stream_init(struct fotg210_hcd *fotg210, */ epnum = usb_pipeendpoint(pipe); is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; - ep = usb_pipe_endpoint(dev, pipe); - maxp = usb_endpoint_maxp(&ep->desc); + maxp = usb_maxpacket(dev, pipe, !is_input); if (is_input) buf1 = (1 << 11); else buf1 = 0; - multi = usb_endpoint_maxp_mult(&ep->desc); - buf1 |= maxp; - maxp *= multi; + hostp_speed = fotg210_port_speed(fotg210, 0); + /* knows about ITD vs SITD */ + if ((dev->speed == USB_SPEED_HIGH) || + (hostp_speed != USB_PORT_STAT_HIGH_SPEED)) { + if (dev->speed == USB_SPEED_HIGH) + stream->highspeed = 1; - stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); - stream->buf1 = cpu_to_hc32(fotg210, buf1); - stream->buf2 = cpu_to_hc32(fotg210, multi); + maxp = max_packet(maxp); + multi = hb_mult(maxp); + buf1 |= maxp; + maxp *= multi; + + stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); + stream->buf1 = cpu_to_hc32(fotg210, buf1); + stream->buf2 = cpu_to_hc32(fotg210, multi); + + /* usbfs wants to report the average usecs per frame tied up + * when transfers on this endpoint are scheduled ... + */ + if (hostp_speed != USB_PORT_STAT_HIGH_SPEED) { //device is full speed + interval <<= 3; + stream->usecs = NS_TO_US(usb_calc_bus_time( + dev->speed, is_input, 1, maxp)); + // convert from FS to HS, use 125us as unit + stream->usecs /= 8; + } + else + stream->usecs = HS_USECS_ISO(maxp); + bandwidth = stream->usecs * 8; + bandwidth /= interval; - /* usbfs wants to report the average usecs per frame tied up - * when transfers on this endpoint are scheduled ... - */ - if (dev->speed == USB_SPEED_FULL) { - interval <<= 3; - stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, - is_input, 1, maxp)); - stream->usecs /= 8; } else { - stream->highspeed = 1; + u32 addr; + int think_time; + int hs_transfers; + + addr = dev->ttport << 24; + if ((dev->tt->hub != + fotg210_to_hcd(fotg210)->self.root_hub)) + addr |= dev->tt->hub->devnum << 16; + addr |= epnum << 8; + addr |= dev->devnum; stream->usecs = HS_USECS_ISO(maxp); + think_time = dev->tt ? dev->tt->think_time : 0; + stream->tt_usecs = NS_TO_US(think_time + usb_calc_bus_time( + dev->speed, is_input, 1, maxp)); + hs_transfers = max (1u, (maxp + 187) / 188); + if (is_input) { + u32 tmp; + + addr |= 1 << 31; + stream->c_usecs = stream->usecs; + stream->usecs = HS_USECS_ISO(1); + stream->raw_mask = 1; + + /* c-mask as specified in USB 2.0 11.18.4 3.c */ + tmp = (1 << (hs_transfers + 2)) - 1; + stream->raw_mask |= tmp << (8 + 2); + } else + stream->raw_mask = smask_out[hs_transfers - 1]; + bandwidth = stream->usecs + stream->c_usecs; + bandwidth /= interval << 3; + + /* stream->splits gets created from raw_mask later */ + stream->address = cpu_to_hc32(fotg210, addr); } - bandwidth = stream->usecs * 8; - bandwidth /= interval; stream->bandwidth = bandwidth; stream->udev = dev; @@ -4014,8 +4647,10 @@ static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets, gfp_t mem_flags) { struct fotg210_iso_sched *iso_sched; + int size = sizeof(*iso_sched); - iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags); + size += packets * sizeof(struct fotg210_iso_packet); + iso_sched = kzalloc(size, mem_flags); if (likely(iso_sched != NULL)) INIT_LIST_HEAD(&iso_sched->td_list); @@ -4147,6 +4782,69 @@ static inline int itd_slot_ok(struct fotg210_hcd *fotg210, u32 mod, u32 uframe, return 1; } +static inline int sitd_slot_ok(struct fotg210_hcd *fotg210, u32 mod, + struct fotg210_iso_stream *stream, u32 uframe, + struct fotg210_iso_sched *sched, u32 period_uframes) +{ + u32 mask, tmp; + u32 frame, uf; + + mask = stream->raw_mask << (uframe & 7); + + /* for IN, don't wrap CSPLIT into the next frame */ + if (mask & ~0xffff) + return 0; + + /* check bandwidth */ + uframe %= period_uframes; + frame = uframe >> 3; + + /* tt must be idle for start(s), any gap, and csplit. + * assume scheduling slop leaves 10+% for control/bulk. + */ + if (!tt_no_collision(fotg210, period_uframes >> 3, + stream->udev, frame, mask)) + return 0; + + /* this multi-pass logic is simple, but performance may + * suffer when the schedule data isn't cached. + */ + do { + u32 max_used; + + frame = uframe >> 3; + uf = uframe & 7; + + /* check starts (OUT uses more than one) */ + max_used = fotg210->uframe_periodic_max - stream->usecs; + for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) { + if (periodic_usecs (fotg210, frame, uf) > max_used) + return 0; + } + + /* for IN, check CSPLIT */ + if (stream->c_usecs) { + uf = uframe & 7; + max_used = fotg210->uframe_periodic_max - stream->c_usecs; + do { + tmp = 1 << uf; + tmp <<= 8; + if ((stream->raw_mask & tmp) == 0) + continue; + if (periodic_usecs (fotg210, frame, uf) + > max_used) + return 0; + } while (++uf < 8); + } + + /* we know urb->interval is 2^N uframes */ + uframe += period_uframes; + } while (uframe < mod); + + stream->splits = cpu_to_hc32(fotg210, stream->raw_mask << (uframe & 7)); + return 1; +} + /* This scheduler plans almost as far into the future as it has actual * periodic schedule slots. (Affected by TUNE_FLS, which defaults to * "as small as possible" to be cache-friendlier.) That limits the size @@ -4166,9 +4864,16 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, int status; unsigned mod = fotg210->periodic_size << 3; struct fotg210_iso_sched *sched = urb->hcpriv; + u32 hostp_speed; //speed of host root hub port + hostp_speed = fotg210_port_speed(fotg210, 0); period = urb->interval; span = sched->span; + if ((!stream->highspeed) && + (hostp_speed == USB_PORT_STAT_HIGH_SPEED)) { + period <<= 3; + span <<= 3; + } if (span > mod - SCHEDULE_SLOP) { fotg210_dbg(fotg210, "iso request %p too long\n", urb); @@ -4238,9 +4943,18 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, do { start--; /* check schedule: enough space? */ - if (itd_slot_ok(fotg210, mod, start, + if ((stream->highspeed) || + (hostp_speed != USB_PORT_STAT_HIGH_SPEED)) { + if (itd_slot_ok(fotg210, mod, start, stream->usecs, period)) - done = 1; + done = 1; + } else { + if ((start % 8) >= 6) + continue; + if (sitd_slot_ok(fotg210, mod, stream, + start, sched, period)) + done = 1; + } } while (start > next && !done); /* no room in the schedule */ @@ -4460,12 +5174,13 @@ static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) /* HC need not update length with this error */ if (!(t & FOTG210_ISOC_BABBLE)) { - desc->actual_length = FOTG210_ITD_LENGTH(t); + desc->actual_length = + fotg210_itdlen(urb, desc, t); urb->actual_length += desc->actual_length; } } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { desc->status = 0; - desc->actual_length = FOTG210_ITD_LENGTH(t); + desc->actual_length = fotg210_itdlen(urb, desc, t); urb->actual_length += desc->actual_length; } else { /* URB was too late */ @@ -4575,11 +5290,377 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, return status; } +/* + * "Split ISO TDs" ... used for USB 1.1 devices going through the + * TTs in USB 2.0 hubs. These need microframe scheduling. + */ + +static inline void +sitd_sched_init( + struct fotg210_hcd *fotg210, + struct fotg210_iso_sched *iso_sched, + struct fotg210_iso_stream *stream, + struct urb *urb) +{ + unsigned i; + dma_addr_t dma = urb->transfer_dma; + + /* how many frames are needed for these transfers */ + iso_sched->span = urb->number_of_packets * stream->interval; + + /* figure out per-frame sitd fields that we'll need later + * when we fit new sitds into the schedule. + */ + for (i = 0; i < urb->number_of_packets; i++) { + struct fotg210_iso_packet *packet = &iso_sched->packet [i]; + unsigned length; + dma_addr_t buf; + u32 trans; + + length = urb->iso_frame_desc [i].length & 0x03ff; + buf = dma + urb->iso_frame_desc [i].offset; + + trans = SITD_STS_ACTIVE; + if (((i + 1) == urb->number_of_packets) + && !(urb->transfer_flags & URB_NO_INTERRUPT)) + trans |= SITD_IOC; + trans |= length << 16; + packet->transaction = cpu_to_hc32(fotg210, trans); + + /* might need to cross a buffer page within a td */ + packet->bufp = buf; + packet->buf1 = (buf + length) & ~0x0fff; + if (packet->buf1 != (buf & ~(u64)0x0fff)) + packet->cross = 1; + + /* OUT uses multiple start-splits */ + if (stream->bEndpointAddress & USB_DIR_IN) + continue; + length = (length + 187) / 188; + if (length > 1) /* BEGIN vs ALL */ + length |= 1 << 3; + packet->buf1 |= length; + } +} + +static int +sitd_urb_transaction( + struct fotg210_iso_stream *stream, + struct fotg210_hcd *fotg210, + struct urb *urb, + gfp_t mem_flags) +{ + struct fotg210_sitd *sitd; + dma_addr_t sitd_dma; + int i; + struct fotg210_iso_sched *iso_sched; + unsigned long flags; + + iso_sched = iso_sched_alloc (urb->number_of_packets, mem_flags); + if (iso_sched == NULL) + return -ENOMEM; + + sitd_sched_init(fotg210, iso_sched, stream, urb); + + /* allocate/init sITDs */ + spin_lock_irqsave (&fotg210->lock, flags); + for (i = 0; i < urb->number_of_packets; i++) { + + /* NOTE: for now, we don't try to handle wraparound cases + * for IN (using sitd->hw_backpointer, like a FSTN), which + * means we never need two sitds for full speed packets. + */ + + /* + * Use siTDs from the free list, but not siTDs that may + * still be in use by the hardware. + */ + if (likely(!list_empty(&stream->free_list))) { + sitd = list_first_entry(&stream->free_list, + struct fotg210_sitd, sitd_list); + if (sitd->frame == fotg210->now_frame) + goto alloc_sitd; + list_del (&sitd->sitd_list); + sitd_dma = sitd->sitd_dma; + } else { +alloc_sitd: + spin_unlock_irqrestore (&fotg210->lock, flags); + sitd = dma_pool_alloc (fotg210->sitd_pool, mem_flags, + &sitd_dma); + spin_lock_irqsave (&fotg210->lock, flags); + if (!sitd) { + iso_sched_free(stream, iso_sched); + spin_unlock_irqrestore(&fotg210->lock, flags); + return -ENOMEM; + } + } + + memset (sitd, 0, sizeof *sitd); + sitd->sitd_dma = sitd_dma; + list_add (&sitd->sitd_list, &iso_sched->td_list); + } + + /* temporarily store schedule info in hcpriv */ + urb->hcpriv = iso_sched; + urb->error_count = 0; + + spin_unlock_irqrestore (&fotg210->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static inline void sitd_patch( + struct fotg210_hcd *fotg210, + struct fotg210_iso_stream *stream, + struct fotg210_sitd *sitd, + struct fotg210_iso_sched *iso_sched, + unsigned index) +{ + struct fotg210_iso_packet *uf = &iso_sched->packet [index]; + u64 bufp = uf->bufp; + + sitd->hw_next = FOTG210_LIST_END(fotg210); + sitd->hw_fullspeed_ep = stream->address; + sitd->hw_uframe = stream->splits; + sitd->hw_results = uf->transaction; + sitd->hw_backpointer = FOTG210_LIST_END(fotg210); + + bufp = uf->bufp; + sitd->hw_buf[0] = cpu_to_hc32(fotg210, bufp); + sitd->hw_buf_hi[0] = cpu_to_hc32(fotg210, bufp >> 32); + + sitd->hw_buf[1] = cpu_to_hc32(fotg210, uf->buf1); + if (uf->cross) + bufp += 4096; + sitd->hw_buf_hi[1] = cpu_to_hc32(fotg210, bufp >> 32); + sitd->index = index; +} + +static inline void +sitd_link(struct fotg210_hcd *fotg210, unsigned frame, struct fotg210_sitd *sitd) +{ + /* note: sitd ordering could matter (CSPLIT then SSPLIT) */ + sitd->sitd_next = fotg210->pshadow [frame]; + sitd->hw_next = fotg210->periodic [frame]; + fotg210->pshadow [frame].sitd = sitd; + sitd->frame = frame; + wmb (); + fotg210->periodic[frame] = cpu_to_hc32(fotg210, sitd->sitd_dma | Q_TYPE_SITD); +} + +/* fit urb's sitds into the selected schedule slot; activate as needed */ +static void sitd_link_urb( + struct fotg210_hcd *fotg210, + struct urb *urb, + unsigned mod, + struct fotg210_iso_stream *stream) +{ + int packet; + unsigned next_uframe; + struct fotg210_iso_sched *sched = urb->hcpriv; + struct fotg210_sitd *sitd; + + next_uframe = stream->next_uframe; + + if (list_empty(&stream->td_list)) { + /* usbfs ignores TT bandwidth */ + fotg210_to_hcd(fotg210)->self.bandwidth_allocated + += stream->bandwidth; + fotg210_dbg (fotg210, + "sched devp %s ep%d%s-iso [%d] %dms/%04x\n", + urb->dev->devpath, stream->bEndpointAddress & 0x0f, + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", + (next_uframe >> 3) & (fotg210->periodic_size - 1), + stream->interval, hc32_to_cpu(fotg210, stream->splits)); + } + + /* fill sITDs frame by frame */ + for (packet = 0, sitd = NULL; + packet < urb->number_of_packets; + packet++) { + + /* ASSERT: we have all necessary sitds */ + BUG_ON (list_empty (&sched->td_list)); + + /* ASSERT: no itds for this endpoint in this frame */ + + sitd = list_entry (sched->td_list.next, + struct fotg210_sitd, sitd_list); + list_move_tail (&sitd->sitd_list, &stream->td_list); + sitd->stream = stream; + sitd->urb = urb; + + sitd_patch(fotg210, stream, sitd, sched, packet); + sitd_link(fotg210, (next_uframe >> 3) & (fotg210->periodic_size - 1), + sitd); + + next_uframe += stream->interval << 3; + } + stream->next_uframe = next_uframe & (mod - 1); + + /* don't need that schedule data any more */ + iso_sched_free (stream, sched); + urb->hcpriv = NULL; + + ++fotg210->isoc_count; + enable_periodic(fotg210); +} + +/*-------------------------------------------------------------------------*/ + +#define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \ + | SITD_STS_XACT | SITD_STS_MMF) + +/* Process and recycle a completed SITD. Return true iff its urb completed, + * and hence its completion callback probably added things to the hardware + * schedule. + * + * Note that we carefully avoid recycling this descriptor until after any + * completion callback runs, so that it won't be reused quickly. That is, + * assuming (a) no more than two urbs per frame on this endpoint, and also + * (b) only this endpoint's completions submit URBs. It seems some silicon + * corrupts things if you reuse completed descriptors very quickly... + */ +static bool sitd_complete(struct fotg210_hcd *fotg210, struct fotg210_sitd *sitd) +{ + struct urb *urb = sitd->urb; + struct usb_iso_packet_descriptor *desc; + u32 t; + int urb_index = -1; + struct fotg210_iso_stream *stream = sitd->stream; + struct usb_device *dev; + bool retval = false; + + urb_index = sitd->index; + desc = &urb->iso_frame_desc [urb_index]; + t = hc32_to_cpup(fotg210, &sitd->hw_results); + + /* report transfer status */ + if (t & SITD_ERRS) { + urb->error_count++; + if (t & SITD_STS_DBE) + desc->status = usb_pipein (urb->pipe) + ? -ENOSR /* hc couldn't read */ + : -ECOMM; /* hc couldn't write */ + else if (t & SITD_STS_BABBLE) + desc->status = -EOVERFLOW; + else /* XACT, MMF, etc */ + desc->status = -EPROTO; + } else { + desc->status = 0; + desc->actual_length = desc->length - SITD_LENGTH(t); + urb->actual_length += desc->actual_length; + } + + /* handle completion now? */ + if ((urb_index + 1) != urb->number_of_packets) + goto done; + + /* ASSERT: it's really the last sitd for this urb + list_for_each_entry (sitd, &stream->td_list, sitd_list) + BUG_ON (sitd->urb == urb); + */ + + /* give urb back to the driver; completion often (re)submits */ + dev = urb->dev; + fotg210_urb_done(fotg210, urb, 0); + retval = true; + urb = NULL; + + --fotg210->isoc_count; + disable_periodic(fotg210); + + if (list_is_singular(&stream->td_list)) { + fotg210_to_hcd(fotg210)->self.bandwidth_allocated + -= stream->bandwidth; + fotg210_dbg (fotg210, + "deschedule devp %s ep%d%s-iso\n", + dev->devpath, stream->bEndpointAddress & 0x0f, + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); + } + +done: + sitd->urb = NULL; + + /* Add to the end of the free list for later reuse */ + list_move_tail(&sitd->sitd_list, &stream->free_list); + + /* Recycle the siTDs when the pipeline is empty (ep no longer in use) */ + if (list_empty(&stream->td_list)) { + list_splice_tail_init(&stream->free_list, + &fotg210->cached_sitd_list); + start_free_itds(fotg210); + } + + return retval; +} + + +static int sitd_submit(struct fotg210_hcd *fotg210, struct urb *urb, + gfp_t mem_flags) +{ + int status = -EINVAL; + unsigned long flags; + struct fotg210_iso_stream *stream; + + if (fotg210_port_speed(fotg210, 0) != USB_PORT_STAT_HIGH_SPEED) //FS itd + return itd_submit(fotg210, urb, mem_flags); + + /* Get iso_stream head */ + stream = iso_stream_find (fotg210, urb); + if (stream == NULL) { + fotg210_dbg (fotg210, "can't get iso stream\n"); + return -ENOMEM; + } + if (urb->interval != stream->interval) { + fotg210_dbg (fotg210, "can't change iso interval %d --> %d\n", + stream->interval, urb->interval); + goto done; + } + +#ifdef FOTG210_URB_TRACE + fotg210_dbg (fotg210, + "submit %p dev%s ep%d%s-iso len %d\n", + urb, urb->dev->devpath, + usb_pipeendpoint (urb->pipe), + usb_pipein (urb->pipe) ? "in" : "out", + urb->transfer_buffer_length); +#endif + + /* allocate SITDs */ + status = sitd_urb_transaction (stream, fotg210, urb, mem_flags); + if (status < 0) { + fotg210_dbg (fotg210, "can't init sitds\n"); + goto done; + } + + /* schedule ... need to lock */ + spin_lock_irqsave (&fotg210->lock, flags); + if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { + status = -ESHUTDOWN; + goto done_not_linked; + } + status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); + if (unlikely(status)) + goto done_not_linked; + status = iso_stream_schedule(fotg210, urb, stream); + if (status == 0) + sitd_link_urb (fotg210, urb, fotg210->periodic_size << 3, stream); + else + usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); +done_not_linked: + spin_unlock_irqrestore (&fotg210->lock, flags); +done: + return status; +} + static inline int scan_frame_queue(struct fotg210_hcd *fotg210, unsigned frame, unsigned now_frame, bool live) { unsigned uf; bool modified; + unsigned fmask = fotg210->periodic_size - 1; union fotg210_shadow q, *q_p; __hc32 type, *hw_p; @@ -4627,10 +5708,41 @@ static inline int scan_frame_queue(struct fotg210_hcd *fotg210, unsigned frame, modified = itd_complete(fotg210, q.itd); q = *q_p; break; + case Q_TYPE_SITD: + /* If this SITD is still active, leave it for + * later processing ... check the next entry. + * No need to check for activity unless the + * frame is current. + */ + if (((frame == now_frame) || + (((frame + 1) & fmask) == now_frame)) + && live + && (q.sitd->hw_results & + SITD_ACTIVE(fotg210))) { + + q_p = &q.sitd->sitd_next; + hw_p = &q.sitd->hw_next; + type = Q_NEXT_TYPE(fotg210, + q.sitd->hw_next); + q = *q_p; + break; + } + + /* Take finished SITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. + */ + *q_p = q.sitd->sitd_next; + *hw_p = q.sitd->hw_next; + type = Q_NEXT_TYPE(fotg210, q.sitd->hw_next); + wmb(); + modified = sitd_complete(fotg210, q.sitd); + q = *q_p; + break; default: fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", type, frame, q.ptr); - fallthrough; + /* FALL THROUGH */ case Q_TYPE_QH: case Q_TYPE_FSTN: /* End of the iTDs and siTDs */ @@ -4681,9 +5793,14 @@ static void scan_isoc(struct fotg210_hcd *fotg210) fotg210->next_frame = now_frame; } +/* above is part of ehci-sched.c */ + +/*-------------------------------------------------------------------------*/ +/* following is part of ehci-sysfs.c */ + /* Display / Set uframe_periodic_max */ -static ssize_t uframe_periodic_max_show(struct device *dev, +static ssize_t show_uframe_periodic_max(struct device *dev, struct device_attribute *attr, char *buf) { struct fotg210_hcd *fotg210; @@ -4695,7 +5812,7 @@ static ssize_t uframe_periodic_max_show(struct device *dev, } -static ssize_t uframe_periodic_max_store(struct device *dev, +static ssize_t store_uframe_periodic_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fotg210_hcd *fotg210; @@ -4762,7 +5879,8 @@ static ssize_t uframe_periodic_max_store(struct device *dev, return ret; } -static DEVICE_ATTR_RW(uframe_periodic_max); +static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, + store_uframe_periodic_max); static inline int create_sysfs_files(struct fotg210_hcd *fotg210) { @@ -4777,6 +5895,11 @@ static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) device_remove_file(controller, &dev_attr_uframe_periodic_max); } + +/* above is part of ehci-sysfs.c */ + +/*-------------------------------------------------------------------------*/ + /* On some systems, leaving remote wakeup enabled prevents system shutdown. * The firmware seems to think that powering off is a wakeup event! * This routine turns off remote wakeup and everything else, on all ports. @@ -4929,6 +6052,7 @@ static int hcd_fotg210_init(struct usb_hcd *hcd) fotg210->periodic_size = DEFAULT_I_TDPS; INIT_LIST_HEAD(&fotg210->intr_qh_list); INIT_LIST_HEAD(&fotg210->cached_itd_list); + INIT_LIST_HEAD(&fotg210->cached_sitd_list); if (HCC_PGM_FRAMELISTLEN(hcc_params)) { /* periodic schedule size can be smaller than default */ @@ -4996,7 +6120,7 @@ static int hcd_fotg210_init(struct usb_hcd *hcd) fotg210->command = temp; /* Accept arbitrarily long scatter-gather lists */ - if (!hcd->localmem_pool) + if (!(hcd->driver->flags & HCD_LOCAL_MEM)) hcd->self.sg_tablesize = ~0; return 0; } @@ -5006,6 +6130,7 @@ static int fotg210_run(struct usb_hcd *hcd) { struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); u32 temp; + u32 hcc_params; hcd->uses_new_polling = 1; @@ -5020,7 +6145,7 @@ static int fotg210_run(struct usb_hcd *hcd) * hcc_params controls whether fotg210->regs->segment must (!!!) * be used; it constrains QH/ITD/SITD and QTD locations. * dma_pool consistent memory always uses segment zero. - * streaming mappings for I/O buffers, like dma_map_single(), + * streaming mappings for I/O buffers, like pci_map_single(), * can return segments above 4GB, if the device allows. * * NOTE: the dma mask is visible through dev->dma_mask, so @@ -5028,7 +6153,7 @@ static int fotg210_run(struct usb_hcd *hcd) * Scsi_Host.highmem_io, and so forth. It's readonly to all * host side drivers though. */ - fotg210_readl(fotg210, &fotg210->caps->hcc_params); + hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); /* * Philips, Intel, and maybe others need CMD_RUN before the @@ -5117,9 +6242,20 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); u32 status, masked_status, pcd_status = 0, cmd; int bh; + u32 temp; spin_lock(&fotg210->lock); +#ifdef CONFIG_USB_FOTG210_OTG + /* check if host mode */ + status = fotg210_readl(fotg210, &fotg210->regs->otgcsr); + if (status & OTGCSR_CROLE) { + //peripheral mode + spin_unlock(&fotg210->lock); + return IRQ_NONE; + } +#endif + status = fotg210_readl(fotg210, &fotg210->regs->status); /* e.g. cardbus physical eject */ @@ -5154,9 +6290,9 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) /* normal [4.15.1.2] or error [4.15.1.1] completion */ if (likely((status & (STS_INT|STS_ERR)) != 0)) { if (likely((status & STS_ERR) == 0)) - INCR(fotg210->stats.normal); + COUNT(fotg210->stats.normal); else - INCR(fotg210->stats.error); + COUNT(fotg210->stats.error); bh = 1; } @@ -5181,7 +6317,7 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) if (cmd & CMD_IAAD) fotg210_dbg(fotg210, "IAA with IAAD still set?\n"); if (fotg210->async_iaa) { - INCR(fotg210->stats.iaa); + COUNT(fotg210->stats.iaa); end_unlink_async(fotg210); } else fotg210_dbg(fotg210, "IAA with nothing unlinked?\n"); @@ -5191,6 +6327,7 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) if (status & STS_PCD) { int pstatus; u32 __iomem *status_reg = &fotg210->regs->port_status; + u32 i = 0, j; /* kick root hub later */ pcd_status = status; @@ -5199,7 +6336,54 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) if (fotg210->rh_state == FOTG210_RH_SUSPENDED) usb_hcd_resume_root_hub(hcd); + //lichun@add + /* When device disconnect, old version fotg210 HW will clear RUN bit + * and assert port change interrupt. We enable RUN bit here. + */ + if (!(cmd & CMD_RUN)) { + temp = readl(&fotg210->regs->status); + while ((!(temp & STS_HALT)) && (i < 1000)) { + udelay(125); + i++; + temp = readl(&fotg210->regs->status); + } + + if (i < 1000) { + writel(readl(&fotg210->regs->command) | CMD_RUN, + &fotg210->regs->command); + + j = 0; + temp = readl(&fotg210->regs->status); + while ((temp & STS_HALT) && (j < 10)) { + udelay(125); + j++; + temp = readl(&fotg210->regs->status); + } + if (j > 0) + printk("polling NOT HALT times.....%d\n", j); + } + else { + printk("Host cannot exit HALT state, recover.....\n"); +/* 20150829 BC: delay hc_reset, let AP knows usb disconnect, will call fotg210_set_hc_reset() */ +#if 1 + writel(0x0, &fotg210->regs->intr_enable); + fotg210_enable_event(fotg210, + FOTG210_HRTIMER_DELAY_HCD_RESET, true); +#else + fotg210_set_hc_reset(fotg210); +#endif + } + } + pstatus = fotg210_readl(fotg210, status_reg); + //lichun@2019, if not do HC_RESET when device disconnect, do it at here + if (unplug_do_hc_rst && (pstatus & PORT_CSC)) { + if (pstatus & PORT_CONNECT) { + printk("PCD, set HC_Reset\n"); + fotg210_set_hc_reset(fotg210); + unplug_do_hc_rst = 0; + } + } if (test_bit(0, &fotg210->suspended_ports) && ((pstatus & PORT_RESUME) || @@ -5215,6 +6399,7 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25); set_bit(0, &fotg210->resuming_ports); fotg210_dbg(fotg210, "port 1 remote wakeup\n"); + usb_hcd_start_port_resume(&hcd->self, 0); mod_timer(&hcd->rh_timer, fotg210->reset_done[0]); } } @@ -5274,7 +6459,7 @@ static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, */ if (urb->transfer_buffer_length > (16 * 1024)) return -EMSGSIZE; - fallthrough; + /* FALLTHROUGH */ /* case PIPE_BULK: */ default: if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) @@ -5287,7 +6472,10 @@ static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, return intr_submit(fotg210, urb, &qtd_list, mem_flags); case PIPE_ISOCHRONOUS: - return itd_submit(fotg210, urb, mem_flags); + if (urb->dev->speed == USB_SPEED_HIGH) //the testing device + return itd_submit(fotg210, urb, mem_flags); + else + return sitd_submit(fotg210, urb, mem_flags); } } @@ -5350,7 +6538,7 @@ static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) break; case PIPE_ISOCHRONOUS: - /* itd... */ + /* itd or sitd ... */ /* wait till next completion, do it then. */ /* completion irqs can wait up to 1024 msec, */ @@ -5407,7 +6595,7 @@ static void fotg210_endpoint_disable(struct usb_hcd *hcd, */ if (tmp) start_unlink_async(fotg210, qh); - fallthrough; + /* FALL THROUGH */ case QH_STATE_UNLINK: /* wait for hw to finish? */ case QH_STATE_UNLINK_WAIT: idle_timeout: @@ -5421,7 +6609,7 @@ static void fotg210_endpoint_disable(struct usb_hcd *hcd, qh_destroy(fotg210, qh); break; } - fallthrough; + /* else FALL THROUGH */ default: /* caller was supposed to have unlinked any requests; * that's not our job. just leak this memory. @@ -5485,11 +6673,108 @@ static int fotg210_get_frame(struct usb_hcd *hcd) fotg210->periodic_size; } +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +/* suspend/resume, section 4.3 */ + +/* These routines handle the generic parts of controller suspend/resume */ + +int fotg210_suspend(struct usb_hcd *hcd, bool do_wakeup) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + + if (time_before(jiffies, fotg210->next_statechange)) + msleep(10); + + /* + * Root hub was already suspended. Disable IRQ emission and + * mark HW unaccessible. The PM and USB cores make sure that + * the root hub is either suspended or stopped. + */ + fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup); + + spin_lock_irq(&fotg210->lock); + fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); + (void) fotg210_readl(fotg210, &fotg210->regs->intr_enable); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + spin_unlock_irq(&fotg210->lock); + + return 0; +} + +/* Returns 0 if power was preserved, 1 if power was lost */ +int fotg210_resume(struct usb_hcd *hcd, bool force_reset) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + + if (time_before(jiffies, fotg210->next_statechange)) + msleep(100); + + /* Mark hardware accessible again as we are back to full power by now */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + if (fotg210->shutdown) + return 0; /* Controller is dead */ + + /* + * If CF is still set and reset isn't forced + * then we maintained suspend power. + * Just undo the effect of fotg210_suspend(). + */ + if (!force_reset) { + int mask = INTR_MASK; + + fotg210_prepare_ports_for_controller_resume(fotg210); + + spin_lock_irq(&fotg210->lock); + if (fotg210->shutdown) + goto skip; + + if (!hcd->self.root_hub->do_remote_wakeup) + mask &= ~STS_PCD; + fotg210_writel(fotg210, mask, &fotg210->regs->intr_enable); + fotg210_readl(fotg210, &fotg210->regs->intr_enable); + skip: + spin_unlock_irq(&fotg210->lock); + return 0; + } + + /* + * Else reset, to cope with power loss or resume from hibernation + * having let the firmware kick in during reboot. + */ + usb_root_hub_lost_power(hcd->self.root_hub); + (void) fotg210_halt(fotg210); + (void) fotg210_reset(fotg210); + + spin_lock_irq(&fotg210->lock); + if (fotg210->shutdown) + goto skip; + + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + fotg210_readl(fotg210, &fotg210->regs->command); /* unblock posted writes */ + + fotg210->rh_state = FOTG210_RH_SUSPENDED; + spin_unlock_irq(&fotg210->lock); + + return 1; +} + +#endif + +/*-------------------------------------------------------------------------*/ + /* The EHCI in ChipIdea HDRC cannot be a separate module or device, * because its registers (and irq) are shared between host/gadget/otg * functions and in order to facilitate role switching we cannot * give the fotg210 driver exclusive access to those. */ +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); static const struct hc_driver fotg210_fotg210_hc_driver = { .description = hcd_name, @@ -5500,7 +6785,7 @@ static const struct hc_driver fotg210_fotg210_hc_driver = { * generic hardware linkage */ .irq = fotg210_irq, - .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2, /* * basic lifecycle operations @@ -5539,31 +6824,37 @@ static const struct hc_driver fotg210_fotg210_hc_driver = { static void fotg210_init(struct fotg210_hcd *fotg210) { +#ifndef CONFIG_USB_FOTG210_OTG u32 value; - +#ifdef CONFIG_MACH_LEO_VP iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, &fotg210->regs->gmir); +#else + iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT, // | GMIR_INT_POLARITY, + &fotg210->regs->gmir); +#endif value = ioread32(&fotg210->regs->otgcsr); value &= ~OTGCSR_A_BUS_DROP; value |= OTGCSR_A_BUS_REQ; iowrite32(value, &fotg210->regs->otgcsr); +#endif } -/* +/** * fotg210_hcd_probe - initialize faraday FOTG210 HCDs * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. */ -int fotg210_hcd_probe(struct platform_device *pdev) +static int fotg210_hcd_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct usb_hcd *hcd; struct resource *res; int irq; - int retval; + int retval = -ENODEV; struct fotg210_hcd *fotg210; if (usb_disabled()) @@ -5571,93 +6862,72 @@ int fotg210_hcd_probe(struct platform_device *pdev) pdev->dev.power.power_state = PMSG_ON; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "Found HC with no IRQ. Check %s setup!\n", + dev_name(dev)); + return -ENODEV; + } + + irq = res->start; hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, dev_name(dev)); if (!hcd) { - dev_err(dev, "failed to create hcd\n"); + dev_err(dev, "failed to create hcd with err %d\n", retval); retval = -ENOMEM; goto fail_create_hcd; } - hcd->has_tt = 1; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hcd->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(hcd->regs)) { retval = PTR_ERR(hcd->regs); - goto failed_put_hcd; + goto failed; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); + hcd->has_tt = 1; fotg210 = hcd_to_fotg210(hcd); fotg210->caps = hcd->regs; - /* It's OK not to supply this clock */ - fotg210->pclk = clk_get(dev, "PCLK"); - if (!IS_ERR(fotg210->pclk)) { - retval = clk_prepare_enable(fotg210->pclk); - if (retval) { - dev_err(dev, "failed to enable PCLK\n"); - goto failed_put_hcd; - } - } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { - /* - * Percolate deferrals, for anything else, - * just live without the clocking. - */ - retval = PTR_ERR(fotg210->pclk); - goto failed_dis_clk; - } - retval = fotg210_setup(hcd); if (retval) - goto failed_dis_clk; + goto failed; fotg210_init(fotg210); retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) { dev_err(dev, "failed to add hcd with err %d\n", retval); - goto failed_dis_clk; + goto failed; } device_wakeup_enable(hcd->self.controller); - platform_set_drvdata(pdev, hcd); return retval; -failed_dis_clk: - if (!IS_ERR(fotg210->pclk)) { - clk_disable_unprepare(fotg210->pclk); - clk_put(fotg210->pclk); - } -failed_put_hcd: +failed: usb_put_hcd(hcd); fail_create_hcd: dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); return retval; } -/* +/** * fotg210_hcd_remove - shutdown processing for EHCI HCDs * @dev: USB Host Controller being removed * */ -int fotg210_hcd_remove(struct platform_device *pdev) +static int fotg210_hcd_remove(struct platform_device *pdev) { - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); - if (!IS_ERR(fotg210->pclk)) { - clk_disable_unprepare(fotg210->pclk); - clk_put(fotg210->pclk); - } + if (!hcd) + return 0; usb_remove_hcd(hcd); usb_put_hcd(hcd); @@ -5665,28 +6935,67 @@ int fotg210_hcd_remove(struct platform_device *pdev) return 0; } -int __init fotg210_hcd_init(void) +#ifdef CONFIG_OF +static const struct of_device_id fotg210_hcd_dt_ids[] = { + { .compatible = "faraday,fotg210_hcd" }, + { } +}; + +MODULE_DEVICE_TABLE(of, fotg210_hcd_dt_ids); +#endif + +static struct platform_driver fotg210_hcd_driver = { + .driver = { + .name = "fotg210-hcd", + .of_match_table = of_match_ptr(fotg210_hcd_dt_ids), + }, + .probe = fotg210_hcd_probe, + .remove = fotg210_hcd_remove, +}; + +static int __init fotg210_hcd_init(void) { + int retval = 0; + if (usb_disabled()) return -ENODEV; + pr_info("%s: " DRIVER_DESC "\n", hcd_name); set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); - pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n", + pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n", hcd_name, sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd), - sizeof(struct fotg210_itd)); + sizeof(struct fotg210_itd), + sizeof(struct fotg210_sitd)); fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); + if (!fotg210_debug_root) { + retval = -ENOENT; + goto err_debug; + } - return 0; + retval = platform_driver_register(&fotg210_hcd_driver); + if (retval < 0) + goto clean; + return retval; + +clean: + debugfs_remove(fotg210_debug_root); + fotg210_debug_root = NULL; +err_debug: + clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); + return retval; } +module_init(fotg210_hcd_init); -void __exit fotg210_hcd_cleanup(void) +static void __exit fotg210_hcd_cleanup(void) { + platform_driver_unregister(&fotg210_hcd_driver); debugfs_remove(fotg210_debug_root); clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); } +module_exit(fotg210_hcd_cleanup); diff --git a/drivers/usb/fotg210/fotg210-hcd.h b/drivers/usb/host/fotg210.h similarity index 88% rename from drivers/usb/fotg210/fotg210-hcd.h rename to drivers/usb/host/fotg210.h index 0781442b7a24ab..26e27d339f8f0d 100644 --- a/drivers/usb/fotg210/fotg210-hcd.h +++ b/drivers/usb/host/fotg210.h @@ -69,6 +69,7 @@ enum fotg210_hrtimer_event { FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ + FOTG210_HRTIMER_DELAY_HCD_RESET,/* delay hc_reset, let AP knows usb disconnect */ FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ }; #define FOTG210_HRTIMER_NO_EVENT 99 @@ -131,9 +132,11 @@ struct fotg210_hcd { /* one per controller */ unsigned uframe_periodic_max; - /* list of itds completed while now_frame was still active */ + /* list of itds & sitds completed while now_frame was still active */ struct list_head cached_itd_list; struct fotg210_itd *last_itd_to_free; + struct list_head cached_sitd_list; + struct fotg210_sitd *last_sitd_to_free; /* per root hub port */ unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; @@ -162,6 +165,7 @@ struct fotg210_hcd { /* one per controller */ struct dma_pool *qh_pool; /* qh per active urb */ struct dma_pool *qtd_pool; /* one or more per qh */ struct dma_pool *itd_pool; /* itd per iso urb */ + struct dma_pool *sitd_pool; /* sitd per split iso urb */ unsigned random_frame; unsigned long next_statechange; @@ -177,13 +181,13 @@ struct fotg210_hcd { /* one per controller */ /* irq statistics */ #ifdef FOTG210_STATS struct fotg210_stats stats; -# define INCR(x) ((x)++) +# define COUNT(x) ((x)++) #else -# define INCR(x) do {} while (0) +# define COUNT(x) #endif - /* silicon clock */ - struct clk *pclk; + /* debug files */ + struct dentry *debug_dir; }; /* convert between an HCD pointer and the corresponding FOTG210_HCD */ @@ -280,26 +284,38 @@ struct fotg210_regs { #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC) - u32 reserved2[19]; + u32 reserved2[19]; /* OTGCSR: offet 0x70 */ - u32 otgcsr; + u32 otgcsr; #define OTGCSR_HOST_SPD_TYP (3 << 22) +#define OTGCSR_ID (1 << 21) +#define OTGCSR_CROLE (1 << 20) #define OTGCSR_A_BUS_DROP (1 << 5) #define OTGCSR_A_BUS_REQ (1 << 4) /* OTGISR: offset 0x74 */ - u32 otgisr; + u32 otgisr; #define OTGISR_OVC (1 << 10) - u32 reserved3[15]; + u32 reserved3[15]; /* GMIR: offset 0xB4 */ - u32 gmir; + u32 gmir; #define GMIR_INT_POLARITY (1 << 3) /*Active High*/ #define GMIR_MHC_INT (1 << 2) #define GMIR_MOTG_INT (1 << 1) #define GMIR_MDEV_INT (1 << 0) + + u32 reserved4[62]; + /* Device DMA Target FIFO Number Register (0x1C0) */ + u32 dev_dma_tfn; + /* Device DMA Controller Parameter setting 0 Register (0x1C4) */ + u32 dev_dma_cps0; + /* Device DMA Controller Parameter setting 1 Register (0x1C8) */ + u32 dev_dma_cps1; + /* Device DMA Controller Parameter setting 2 Register (0x1CC) */ + u32 dev_dma_cps2; }; /*-------------------------------------------------------------------------*/ @@ -389,6 +405,7 @@ struct fotg210_qtd { union fotg210_shadow { struct fotg210_qh *qh; /* Q_TYPE_QH */ struct fotg210_itd *itd; /* Q_TYPE_ITD */ + struct fotg210_sitd *sitd; /* Q_TYPE_SITD */ struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ __hc32 *hw_next; /* (all types) */ void *ptr; @@ -487,7 +504,7 @@ struct fotg210_iso_packet { struct fotg210_iso_sched { struct list_head td_list; unsigned span; - struct fotg210_iso_packet packet[]; + struct fotg210_iso_packet packet[0]; }; /* @@ -565,6 +582,49 @@ struct fotg210_itd { unsigned frame; /* where scheduled */ unsigned pg; unsigned index[8]; /* in urb->iso_frame_desc */ +} __aligned(64); + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.4 + * siTD, aka split-transaction isochronous Transfer Descriptor + * ... describe full speed iso xfers through TT in hubs + * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD) + */ +struct fotg210_sitd { + /* first part defined by EHCI spec */ + __hc32 hw_next; +/* uses bit field macros above - see EHCI 0.95 Table 3-8 */ + __hc32 hw_fullspeed_ep; /* EHCI table 3-9 */ + __hc32 hw_uframe; /* EHCI table 3-10 */ + __hc32 hw_results; /* EHCI table 3-11 */ +#define SITD_IOC (1 << 31) /* interrupt on completion */ +#define SITD_PAGE (1 << 30) /* buffer 0/1 */ +#define SITD_LENGTH(x) (((x) >> 16) & 0x3ff) +#define SITD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define SITD_STS_ERR (1 << 6) /* error from TT */ +#define SITD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define SITD_STS_BABBLE (1 << 4) /* device was babbling */ +#define SITD_STS_XACT (1 << 3) /* illegal IN response */ +#define SITD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define SITD_STS_STS (1 << 1) /* split transaction state */ + +#define SITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, SITD_STS_ACTIVE) + + __hc32 hw_buf[2]; /* EHCI table 3-12 */ + __hc32 hw_backpointer; /* EHCI table 3-13 */ + __hc32 hw_buf_hi[2]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t sitd_dma; + union fotg210_shadow sitd_next; /* ptr to periodic q entry */ + + struct urb *urb; + struct fotg210_iso_stream *stream; /* endpoint's queue */ + struct list_head sitd_list; /* list of stream's sitds */ + unsigned frame; + unsigned index; } __aligned(32); /*-------------------------------------------------------------------------*/ @@ -683,6 +743,19 @@ static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) return fotg210_readl(fotg210, &fotg210->regs->frame_index); } +#if 1 +#define fotg210_itdlen(urb, desc, t) ({ \ + FOTG210_ITD_LENGTH(t); \ +}) + +#else //For very old controller version, e.g. A369 on-board +#define fotg210_itdlen(urb, desc, t) ({ \ + usb_pipein((urb)->pipe) ? \ + (desc)->length - FOTG210_ITD_LENGTH(t) : \ + FOTG210_ITD_LENGTH(t); \ +}) +#endif + /*-------------------------------------------------------------------------*/ #endif /* __LINUX_FOTG210_H */ From 2c260c4814d239afc2e5fee1829bd59797125173 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Wed, 15 Feb 2023 23:00:51 -0500 Subject: [PATCH 38/41] BL808 USB Support patch 2 of 5: Patches to make faraday authored fotg210 drivers for 5.10 work on linux 6.2 --- drivers/usb/gadget/udc/fotg210-udc.c | 25 +++++++++++++++------- drivers/usb/host/Kconfig | 2 +- drivers/usb/host/fotg210-hcd.c | 31 ++++++++++++++++------------ 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c index 859b9a5a15a94a..af61ec33ad867a 100644 --- a/drivers/usb/gadget/udc/fotg210-udc.c +++ b/drivers/usb/gadget/udc/fotg210-udc.c @@ -22,6 +22,16 @@ #define DRIVER_DESC "FOTG210 USB Device Controller Driver" +/* + * Test Mode Selectors + * See USB 2.0 spec Table 9-7 + */ +#define TEST_J 1 +#define TEST_K 2 +#define TEST_SE0_NAK 3 +#define TEST_PACKET 4 +#define TEST_FORCE_EN 5 + /* Note: CONFIG_USB_FOTG210_UDC_DOUBLE_FIFO usage * EP1 = FIFO 0~1, EP2 = FIFO 2~3 * Please note that FIFO number is from 0~3, if user has configured EP numbers more than 2, @@ -2042,7 +2052,6 @@ static int fotg210_udc_start(struct usb_gadget *g, #endif /* hook up the driver */ - driver->driver.bus = NULL; fotg210->driver = driver; #ifdef CONFIG_USB_FOTG210_OTG @@ -2170,11 +2179,11 @@ static int fotg210_udc_remove(struct platform_device *pdev) static int fotg210_udc_probe(struct platform_device *pdev) { - struct resource *res, *ires; + struct resource *res; struct fotg210_udc *fotg210 = NULL; struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; int ret = 0; - int i; + int i, irq; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -2182,9 +2191,9 @@ static int fotg210_udc_probe(struct platform_device *pdev) return -ENODEV; } - ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!ires) { - pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + pr_err("Couldn't get IRQ.\n"); return -ENODEV; } @@ -2255,7 +2264,7 @@ static int fotg210_udc_probe(struct platform_device *pdev) fotg210_init(fotg210); - ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, + ret = request_irq(irq, fotg210_irq, IRQF_SHARED, udc_name, fotg210); if (ret < 0) { pr_err("request_irq error (%d)\n", ret); @@ -2284,7 +2293,7 @@ static int fotg210_udc_probe(struct platform_device *pdev) return 0; err_add_udc: - free_irq(ires->start, fotg210); + free_irq(irq, fotg210); err_req: fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1bdd2bb4bfe0ad..8e8db71021a547 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -392,7 +392,7 @@ config USB_ISP1362_HCD config USB_FOTG210_HCD tristate "FOTG210 HCD support" depends on USB && HAS_DMA && HAS_IOMEM - ---help--- + help Faraday FOTG210 is an OTG controller which can be configured as an USB2.0 host. It is designed to meet USB2.0 EHCI specification with minor modification. diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 83a1c032f94c90..ea7d648d904035 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -55,6 +55,16 @@ static const char hcd_name[] = "fotg210_hcd"; #undef FOTG210_URB_TRACE #define FOTG210_STATS +/* + * Test Mode Selectors + * See USB 2.0 spec Table 9-7 + */ +#define TEST_J 1 +#define TEST_K 2 +#define TEST_SE0_NAK 3 +#define TEST_PACKET 4 +#define TEST_FORCE_ENABLE 5 + /* magic numbers that can affect system performance */ #define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ @@ -3142,7 +3152,7 @@ static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ - maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); + maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe)); /* * buffer gets wrapped in one or more qtds; @@ -3273,7 +3283,7 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, is_input = usb_pipein(urb->pipe); type = usb_pipetype(urb->pipe); - maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input); + maxp = usb_maxpacket(urb->dev, urb->pipe); /* 1024 byte maxpacket is a hardware ceiling. High bandwidth * acts like up to 3KB, but is built from smaller packets. @@ -4523,7 +4533,7 @@ static void iso_stream_init(struct fotg210_hcd *fotg210, */ epnum = usb_pipeendpoint(pipe); is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; - maxp = usb_maxpacket(dev, pipe, !is_input); + maxp = usb_maxpacket(dev, pipe); if (is_input) buf1 = (1 << 11); else @@ -6120,7 +6130,7 @@ static int hcd_fotg210_init(struct usb_hcd *hcd) fotg210->command = temp; /* Accept arbitrarily long scatter-gather lists */ - if (!(hcd->driver->flags & HCD_LOCAL_MEM)) + if (!hcd->localmem_pool) hcd->self.sg_tablesize = ~0; return 0; } @@ -6785,7 +6795,7 @@ static const struct hc_driver fotg210_fotg210_hc_driver = { * generic hardware linkage */ .irq = fotg210_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, /* * basic lifecycle operations @@ -6862,14 +6872,9 @@ static int fotg210_hcd_probe(struct platform_device *pdev) pdev->dev.power.power_state = PMSG_ON; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "Found HC with no IRQ. Check %s setup!\n", - dev_name(dev)); - return -ENODEV; - } - - irq = res->start; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, dev_name(dev)); From e520ae338a5dcd499b1ef7c984296be5f0c8b55e Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Mon, 20 Feb 2023 16:28:32 -0500 Subject: [PATCH 39/41] BL808 usb support patch 3 of 5: DTS changes. --- .../boot/dts/bouffalolab/bl808-pine64-ox64.dts | 2 +- .../boot/dts/bouffalolab/bl808-sipeed-m1s.dts | 2 +- arch/riscv/boot/dts/bouffalolab/bl808.dtsi | 14 ++++++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts index c5b1e86310d983..73a869240da3c0 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-pine64-ox64.dts @@ -59,7 +59,7 @@ status = "okay"; }; -&ehci0 { +&usb1 { status = "okay"; }; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts index ecdce792132336..e177771e93020f 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts +++ b/arch/riscv/boot/dts/bouffalolab/bl808-sipeed-m1s.dts @@ -73,7 +73,7 @@ status = "okay"; }; -&ehci0 { +&usb1 { status = "okay"; }; diff --git a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi index 73a4e055c7a9e6..fcb62c916e83fc 100644 --- a/arch/riscv/boot/dts/bouffalolab/bl808.dtsi +++ b/arch/riscv/boot/dts/bouffalolab/bl808.dtsi @@ -127,8 +127,18 @@ status = "disabled"; }; - ehci0: usb@20072000 { - compatible = "generic-ehci"; + usb0: usb_hcd@20072000 { + compatible = "faraday,fotg210_hcd"; + reg = <0x20072000 0x1000>; + interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 + BFLB_IPC_DEVICE_USB + IRQ_TYPE_EDGE_RISING>; + clocks = <&xtal>; + status = "disabled"; + }; + + usb1: usb_udc@20072000 { + compatible = "faraday,fotg210_udc"; reg = <0x20072000 0x1000>; interrupts-extended = <&ipclic BFLB_IPC_SOURCE_M0 BFLB_IPC_DEVICE_USB From b77f15db46eb73bb307e924804c06bf586eca660 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Fri, 10 Mar 2023 10:27:59 -0500 Subject: [PATCH 40/41] BL808 USB Support patch 4 of 5: Minimal enable of USB drivers. Only turn on host and peripheral drivers. --- arch/riscv/configs/bl808_defconfig | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index 9e234dcb5ec0d7..bf42d192a995ff 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -140,11 +140,13 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y CONFIG_HID_CHICONY=y +CONFIG_USB_SUPPORT=y CONFIG_USB=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_HCD_PLATFORM=y -CONFIG_USB_STORAGE=y -CONFIG_USB_SERIAL=y +CONFIG_USB_FOTG210_HCD=y +CONFIG_USB_GADGET=y +CONFIG_USB_FOTG210_UDC=y +CONFIG_USB_FOTG210_UDC_V1_26_0=y +CONFIG_USB_FOTG210_UDC_VDMA=y CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y From 540e9abc22e4f65e2fd52f56f34ec76c35ac4311 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Fri, 10 Mar 2023 10:30:36 -0500 Subject: [PATCH 41/41] BL808 USB Support patch 5 of 5: Usb configuration with useful host and gadget drivers, so that normally expected devices work out of the box. --- arch/riscv/configs/bl808_defconfig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/arch/riscv/configs/bl808_defconfig b/arch/riscv/configs/bl808_defconfig index bf42d192a995ff..7407448699c165 100644 --- a/arch/riscv/configs/bl808_defconfig +++ b/arch/riscv/configs/bl808_defconfig @@ -143,10 +143,40 @@ CONFIG_HID_CHICONY=y CONFIG_USB_SUPPORT=y CONFIG_USB=y CONFIG_USB_FOTG210_HCD=y +CONFIG_USB_ACM=m +CONFIG_USB_WDM=m +CONFIG_USB_STORAGE=m +CONFIG_USBIP_CORE=m +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y CONFIG_USB_GADGET=y CONFIG_USB_FOTG210_UDC=y CONFIG_USB_FOTG210_UDC_V1_26_0=y CONFIG_USB_FOTG210_UDC_VDMA=y +CONFIG_USB_CONFIGFS=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_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_LB_SS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_PRINTER=y +CONFIG_USB_ETH=m +CONFIG_USB_G_NCM=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_MULTI_CDC=y +CONFIG_USB_G_HID=m CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y