diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml index 0753289fba844c..6b653bf5d00032 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml @@ -37,6 +37,15 @@ allOf: - items: - const: clk_out_sd1 - const: clk_in_sd1 + - if: + properties: + compatible: + contains: + const: elvees,mcom03-sdhci-8.9a + then: + required: + - resets + - elvees,ctrl-id properties: compatible: @@ -88,6 +97,12 @@ properties: description: For this device it is strongly suggested to include arasan,soc-ctl-syscon. + - items: + - const: elvees,mcom03-sdhci-8.9a # Elvees SDHCI 8.9a PHY + - const: arasan,sdhci-8.9a + description: + For this device it is strongly suggested to include + arasan,soc-ctl-syscon. reg: maxItems: 1 @@ -112,6 +127,17 @@ properties: phy-names: const: phy_arasan + resets: + maxItems: 1 + description: + Handle to reset input. + + elvees,ctrl-id: + maxItems: 1 + enum: [0, 1] + description: + ID of SDHCI controller. + arasan,soc-ctl-syscon: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -152,6 +178,11 @@ properties: description: The MIO bank number in which the command and data lines are configured. + elvees,broken-hs: + $ref: /schemas/types.yaml#/definitions/flag + description: + When present, denotes that HS mode is not supported. + dependencies: clock-output-names: [ '#clock-cells' ] '#clock-cells': [ clock-output-names ] diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 78423bfc808e19..a05cb0e81fadd1 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -154,6 +154,11 @@ config ARCH_KEEMBAY help This enables support for Intel Movidius SoC code-named Keem Bay. +config ARCH_MCOM03 + bool "ELVEES MCom-03 SoC" + help + This enables support for ELVEES MCom-03 SoC + config ARCH_MEDIATEK bool "MediaTek SoC Family" select ARM_GIC diff --git a/arch/arm64/configs/mcom03_defconfig b/arch/arm64/configs/mcom03_defconfig new file mode 100644 index 00000000000000..618cb07ddacf47 --- /dev/null +++ b/arch/arm64/configs/mcom03_defconfig @@ -0,0 +1,144 @@ +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_MCOM03=y +# CONFIG_ARM64_ERRATUM_832075 is not set +# CONFIG_ARM64_ERRATUM_1024718 is not set +# CONFIG_CAVIUM_ERRATUM_22375 is not set +# CONFIG_CAVIUM_ERRATUM_23154 is not set +# CONFIG_CAVIUM_ERRATUM_27456 is not set +# CONFIG_CAVIUM_ERRATUM_30115 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set +# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set +# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set +# CONFIG_HISILICON_ERRATUM_161600802 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=4 +CONFIG_HZ_100=y +CONFIG_SECCOMP=y +CONFIG_KEXEC=y +# CONFIG_ARM64_HW_AFDBM is not set +# CONFIG_ARM64_PAN is not set +# CONFIG_ARM64_LSE_ATOMICS is not set +# CONFIG_ARM64_VHE is not set +# CONFIG_ARM64_UAO is not set +# CONFIG_ARM64_RAS_EXTN is not set +# CONFIG_ARM64_SVE is not set +# CONFIG_EFI is not set +CONFIG_HIBERNATION=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPUFREQ_DT=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA256_ARM64=y +CONFIG_CRYPTO_SHA512_ARM64=y +CONFIG_CRYPTO_CHACHA20_NEON=y +CONFIG_CRYPTO_AES_ARM64_BS=y +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_BINFMT_MISC=m +CONFIG_KSM=y +CONFIG_MEMORY_FAILURE=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_CMA=y +CONFIG_FRAME_VECTOR=y +CONFIG_NET=y +CONFIG_UNIX=y +# CONFIG_WIRELESS is not set +# CONFIG_UEVENT_HELPER is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=1 +CONFIG_SERIAL_8250_RUNTIME_UARTS=1 +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +CONFIG_MFD_SYSCON=y +CONFIG_DRM=y +# CONFIG_USB_SUPPORT is not set +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_COMMON_CLK_XGENE is not set +CONFIG_DW_APB_TIMER_OF=y +# CONFIG_ARM_ARCH_TIMER_EVTSTREAM is not set +# CONFIG_FSL_ERRATUM_A008585 is not set +# CONFIG_HISILICON_ERRATUM_161010101 is not set +# CONFIG_ARM64_ERRATUM_858921 is not set +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_XFS_FS=m +CONFIG_XFS_POSIX_ACL=y +CONFIG_BTRFS_FS=m +CONFIG_F2FS_FS=m +# CONFIG_DNOTIFY is not set +CONFIG_FUSE_FS=m +CONFIG_CUSE=m +CONFIG_OVERLAY_FS=m +CONFIG_TMPFS=y +CONFIG_HUGETLBFS=y +CONFIG_CONFIGFS_FS=y +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_PRINTK_TIME=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_RCU_TRACE is not set +CONFIG_FUNCTION_TRACER=y +CONFIG_PREEMPTIRQ_EVENTS=y +CONFIG_IRQSOFF_TRACER=y +CONFIG_SCHED_TRACER=y +CONFIG_FTRACE_SYSCALLS=y +# CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_MEMTEST=y +# CONFIG_STRICT_DEVMEM is not set diff --git a/arch/arm64/configs/mcom03haps_defconfig b/arch/arm64/configs/mcom03haps_defconfig new file mode 100644 index 00000000000000..18a7891e74e53e --- /dev/null +++ b/arch/arm64/configs/mcom03haps_defconfig @@ -0,0 +1,101 @@ +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_IRQ_TIME_ACCOUNTING=y +# CONFIG_CPU_ISOLATION is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_MULTIUSER is not set +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_FHANDLE is not set +# CONFIG_BUG is not set +# CONFIG_BASE_FULL is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +# CONFIG_SHMEM is not set +# CONFIG_AIO is not set +# CONFIG_ADVISE_SYSCALLS is not set +# CONFIG_MEMBARRIER is not set +# CONFIG_KALLSYMS is not set +# CONFIG_RSEQ is not set +CONFIG_EMBEDDED=y +CONFIG_PERF_EVENTS=y +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SLOB=y +# CONFIG_SLAB_MERGE_DEFAULT is not set +CONFIG_ARCH_MCOM03=y +# CONFIG_ARM64_ERRATUM_832075 is not set +# CONFIG_ARM64_ERRATUM_1024718 is not set +# CONFIG_CAVIUM_ERRATUM_22375 is not set +# CONFIG_CAVIUM_ERRATUM_23154 is not set +# CONFIG_CAVIUM_ERRATUM_27456 is not set +# CONFIG_CAVIUM_ERRATUM_30115 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set +# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set +# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set +# CONFIG_HISILICON_ERRATUM_161600802 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set +CONFIG_HZ_100=y +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +# CONFIG_HARDEN_BRANCH_PREDICTOR is not set +# CONFIG_HARDEN_EL2_VECTORS is not set +# CONFIG_ARM64_SSBD is not set +# CONFIG_ARM64_HW_AFDBM is not set +# CONFIG_ARM64_PAN is not set +# CONFIG_ARM64_LSE_ATOMICS is not set +# CONFIG_ARM64_VHE is not set +# CONFIG_ARM64_UAO is not set +# CONFIG_ARM64_RAS_EXTN is not set +# CONFIG_ARM64_SVE is not set +# CONFIG_EFI is not set +# CONFIG_SUSPEND is not set +# CONFIG_STACKPROTECTOR is not set +# CONFIG_VMAP_STACK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLOCK is not set +# CONFIG_COREDUMP is not set +# CONFIG_SPARSEMEM_VMEMMAP is not set +# CONFIG_COMPACTION is not set +CONFIG_FRAME_VECTOR=y +CONFIG_NET=y +CONFIG_UNIX=y +# CONFIG_WIRELESS is not set +# CONFIG_UEVENT_HELPER is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_ALLOW_DEV_COREDUMP is not set +# CONFIG_INPUT is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=1 +CONFIG_SERIAL_8250_RUNTIME_UARTS=1 +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +CONFIG_DRM=y +# CONFIG_USB_SUPPORT is not set +# CONFIG_VIRTIO_MENU is not set +# CONFIG_COMMON_CLK_XGENE is not set +# CONFIG_ARM_ARCH_TIMER_EVTSTREAM is not set +# CONFIG_FSL_ERRATUM_A008585 is not set +# CONFIG_HISILICON_ERRATUM_161010101 is not set +# CONFIG_ARM64_ERRATUM_858921 is not set +# CONFIG_IOMMU_SUPPORT is not set +# CONFIG_FILE_LOCKING is not set +# CONFIG_DNOTIFY is not set +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_DEBUG_FS=y +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_FTRACE is not set +# CONFIG_RUNTIME_TESTING_MENU is not set +# CONFIG_STRICT_DEVMEM is not set diff --git a/arch/arm64/configs/mcom03rtl_defconfig b/arch/arm64/configs/mcom03rtl_defconfig new file mode 100644 index 00000000000000..19f3a39be980df --- /dev/null +++ b/arch/arm64/configs/mcom03rtl_defconfig @@ -0,0 +1,97 @@ +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_CROSS_MEMORY_ATTACH is not set +# CONFIG_CPU_ISOLATION is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_MULTIUSER is not set +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_FHANDLE is not set +# CONFIG_BUG is not set +# CONFIG_BASE_FULL is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +# CONFIG_SHMEM is not set +# CONFIG_AIO is not set +# CONFIG_ADVISE_SYSCALLS is not set +# CONFIG_MEMBARRIER is not set +# CONFIG_KALLSYMS is not set +# CONFIG_RSEQ is not set +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SLOB=y +# CONFIG_SLAB_MERGE_DEFAULT is not set +CONFIG_ARCH_MCOM03=y +# CONFIG_ARM64_ERRATUM_832075 is not set +# CONFIG_ARM64_ERRATUM_1024718 is not set +# CONFIG_CAVIUM_ERRATUM_22375 is not set +# CONFIG_CAVIUM_ERRATUM_23154 is not set +# CONFIG_CAVIUM_ERRATUM_27456 is not set +# CONFIG_CAVIUM_ERRATUM_30115 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set +# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set +# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set +# CONFIG_HISILICON_ERRATUM_161600802 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +# CONFIG_HARDEN_BRANCH_PREDICTOR is not set +# CONFIG_HARDEN_EL2_VECTORS is not set +# CONFIG_ARM64_SSBD is not set +# CONFIG_ARM64_HW_AFDBM is not set +# CONFIG_ARM64_PAN is not set +# CONFIG_ARM64_LSE_ATOMICS is not set +# CONFIG_ARM64_VHE is not set +# CONFIG_ARM64_UAO is not set +# CONFIG_ARM64_RAS_EXTN is not set +# CONFIG_ARM64_SVE is not set +# CONFIG_EFI is not set +# CONFIG_SUSPEND is not set +# CONFIG_STACKPROTECTOR is not set +# CONFIG_VMAP_STACK is not set +# CONFIG_BLOCK is not set +# CONFIG_COREDUMP is not set +# CONFIG_SPARSEMEM_VMEMMAP is not set +# CONFIG_COMPACTION is not set +CONFIG_NET=y +CONFIG_UNIX=y +# CONFIG_WIRELESS is not set +# CONFIG_UEVENT_HELPER is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_ALLOW_DEV_COREDUMP is not set +# CONFIG_INPUT is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=1 +CONFIG_SERIAL_8250_RUNTIME_UARTS=1 +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_ELVEES_P2M=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +CONFIG_DRM=y +# CONFIG_USB_SUPPORT is not set +# CONFIG_VIRTIO_MENU is not set +# CONFIG_COMMON_CLK_XGENE is not set +# CONFIG_ARM_ARCH_TIMER_EVTSTREAM is not set +# CONFIG_FSL_ERRATUM_A008585 is not set +# CONFIG_HISILICON_ERRATUM_161010101 is not set +# CONFIG_ARM64_ERRATUM_858921 is not set +# CONFIG_IOMMU_SUPPORT is not set +# CONFIG_FILE_LOCKING is not set +# CONFIG_DNOTIFY is not set +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_ENABLE_MUST_CHECK is not set +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_FTRACE is not set +# CONFIG_RUNTIME_TESTING_MENU is not set +# CONFIG_STRICT_DEVMEM is not set diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index a0c6e88bebe084..16909230104b44 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -62,7 +62,7 @@ config DW_APB_TIMER Enables the support for the dw_apb timer. config DW_APB_TIMER_OF - bool + bool "DW APB OF timer driver" select DW_APB_TIMER select TIMER_OF diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index fc38db64a6b48a..385ac7efc702a5 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -198,6 +198,20 @@ static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = { .hiword_update = false, }; +static const struct sdhci_arasan_soc_ctl_map mcom03_soc_ctl_map[] = { + { + .baseclkfreq = { .reg = 0x40, .width = 8, .shift = 8 }, + .clockmultiplier = { .reg = 0, .width = -1, .shift = -1}, + .hiword_update = false, + }, + { + .baseclkfreq = { .reg = 0x7c, .width = 8, .shift = 8 }, + .clockmultiplier = { .reg = 0, .width = -1, .shift = -1}, + .hiword_update = false, + }, + { /* sentinel */ } +}; + /** * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers * @@ -1229,6 +1243,12 @@ static struct sdhci_arasan_of_data intel_keembay_sdio_data = { .clk_ops = &arasan_clk_ops, }; +static struct sdhci_arasan_of_data elvees_mcom03_sdhci_data = { + .soc_ctl_map = &mcom03_soc_ctl_map, + .pdata = &sdhci_arasan_cqe_pdata, + .clk_ops = &arasan_clk_ops, +}; + static const struct of_device_id sdhci_arasan_of_match[] = { /* SoC-specific compatible strings w/ soc_ctl_map */ { @@ -1255,6 +1275,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = { .compatible = "intel,keembay-sdhci-5.1-sdio", .data = &intel_keembay_sdio_data, }, + { + .compatible = "elvees,mcom03-sdhci-8.9a", + .data = &elvees_mcom03_sdhci_data, + }, /* Generic compatible below here */ { .compatible = "arasan,sdhci-8.9a", @@ -1532,6 +1556,7 @@ static int sdhci_arasan_add_host(struct sdhci_arasan_data *sdhci_arasan) static int sdhci_arasan_probe(struct platform_device *pdev) { int ret; + int ctrl_id; const struct of_device_id *match; struct device_node *node; struct clk *clk_xin; @@ -1617,6 +1642,17 @@ static int sdhci_arasan_probe(struct platform_device *pdev) host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; } + /* Set proper soc_ctl_map for MCom-03 */ + if (of_device_is_compatible(pdev->dev.of_node, + "elvees,mcom03-sdhci-8.9a")) { + ret = device_property_read_u32(&pdev->dev, "elvees,ctrl-id", + &ctrl_id); + if (ret) + goto clk_disable_all; + + sdhci_arasan->soc_ctl_map = &mcom03_soc_ctl_map[ctrl_id]; + } + sdhci_arasan_update_baseclkfreq(host); ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev); diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index fad9a2c77fa7cc..4409f29c073d0c 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -31,6 +31,7 @@ source "drivers/net/ethernet/amd/Kconfig" source "drivers/net/ethernet/apm/Kconfig" source "drivers/net/ethernet/apple/Kconfig" source "drivers/net/ethernet/aquantia/Kconfig" +source "drivers/net/ethernet/arasan/Kconfig" source "drivers/net/ethernet/arc/Kconfig" source "drivers/net/ethernet/atheros/Kconfig" source "drivers/net/ethernet/aurora/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index f8f38dcb5f8a05..6885d5b47082db 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_NET_VENDOR_AMD) += amd/ obj-$(CONFIG_NET_XGENE) += apm/ obj-$(CONFIG_NET_VENDOR_APPLE) += apple/ obj-$(CONFIG_NET_VENDOR_AQUANTIA) += aquantia/ +obj-$(CONFIG_NET_VENDOR_ARASAN) += arasan/ obj-$(CONFIG_NET_VENDOR_ARC) += arc/ obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/ obj-$(CONFIG_NET_VENDOR_AURORA) += aurora/ diff --git a/drivers/net/ethernet/arasan/Kconfig b/drivers/net/ethernet/arasan/Kconfig new file mode 100644 index 00000000000000..ce0297774ffc6c --- /dev/null +++ b/drivers/net/ethernet/arasan/Kconfig @@ -0,0 +1,31 @@ +# +# Arasan device configuration +# + +config NET_VENDOR_ARASAN + bool "Arasan devices" + default n + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Arasan cards. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_ARASAN + +config ARASAN_GEMAC + tristate "Arasan Gigabit Ethernet support" + depends on HAS_IOMEM + select PHYLIB + default n + ---help--- + This driver supports the Arasan Gigabit Ethernet (GEMAC) adapter. + + To compile this driver as a module, choose M here. The module + will be called arasan-gemac. + +endif # NET_VENDOR_ARASAN diff --git a/drivers/net/ethernet/arasan/Makefile b/drivers/net/ethernet/arasan/Makefile new file mode 100644 index 00000000000000..e10e44f7a5b7c8 --- /dev/null +++ b/drivers/net/ethernet/arasan/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for Arasan network device drivers. +# + +obj-$(CONFIG_ARASAN_GEMAC) += arasan-gemac.o diff --git a/drivers/net/ethernet/arasan/arasan-gemac.c b/drivers/net/ethernet/arasan/arasan-gemac.c new file mode 100644 index 00000000000000..f3269040252a77 --- /dev/null +++ b/drivers/net/ethernet/arasan/arasan-gemac.c @@ -0,0 +1,1409 @@ +/* + * Copyright 2007, 2008 SMSC + * Copyright 2015 ELVEES NeoTek CJSC + * Copyright 2017-2020 RnD Center "ELVEES", JSC + * + * Based on the driver for smsc9420 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arasan-gemac.h" + +#define ARASAN_GEMAC_FEATURES (PHY_GBIT_FEATURES | SUPPORTED_FIBRE | \ + SUPPORTED_BNC) + +#define print_reg(reg) netdev_info(pd->dev, \ + "offset 0x%x : value 0x%x\n", \ + reg, \ + arasan_gemac_readl(pd, reg)) + +void arasan_gemac_dump_regs(struct arasan_gemac_pdata *pd) +{ + netdev_info(pd->dev, "Arasan GEMAC register dump:\n"); + + print_reg(DMA_CONFIGURATION); + print_reg(DMA_CONTROL); + print_reg(DMA_STATUS_AND_IRQ); + print_reg(DMA_INTERRUPT_ENABLE); + print_reg(DMA_TRANSMIT_AUTO_POLL_COUNTER); + print_reg(DMA_TRANSMIT_POLL_DEMAND); + print_reg(DMA_RECEIVE_POLL_DEMAND); + print_reg(DMA_TRANSMIT_BASE_ADDRESS); + print_reg(DMA_RECEIVE_BASE_ADDRESS); + print_reg(DMA_MISSED_FRAME_COUNTER); + print_reg(DMA_STOP_FLUSH_COUNTER); + print_reg(DMA_RECEIVE_INTERRUPT_MITIGATION); + print_reg(DMA_CURRENT_TRANSMIT_DESCRIPTOR_POINTER); + print_reg(DMA_CURRENT_TRANSMIT_BUFFER_POINTER); + print_reg(DMA_CURRENT_RECEIVE_DESCRIPTOR_POINTER); + print_reg(DMA_CURRENT_RECEIVE_BUFFER_POINTER); + + print_reg(MAC_GLOBAL_CONTROL); + print_reg(MAC_TRANSMIT_CONTROL); + print_reg(MAC_RECEIVE_CONTROL); + print_reg(MAC_ADDRESS_CONTROL); + print_reg(MAC_ADDRESS1_HIGH); + print_reg(MAC_ADDRESS1_MED); + print_reg(MAC_ADDRESS1_LOW); + print_reg(MAC_INTERRUPT_STATUS); + print_reg(MAC_INTERRUPT_ENABLE); +} + +static void arasan_gemac_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + + strlcpy(info->driver, pd->pdev->dev.driver->name, sizeof(info->driver)); + strlcpy(info->version, UTS_RELEASE, sizeof(info->version)); +} + +static u32 arasan_gemac_get_msglevel(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + + return pd->msg_enable; +} + +static void arasan_gemac_set_msglevel(struct net_device *dev, u32 val) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + + pd->msg_enable = val; +} + +static int arasan_gemac_nway_reset(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + + if (!pd->phy_dev) + return -ENODEV; + + return genphy_restart_aneg(pd->phy_dev); +} + +static const struct ethtool_ops arasan_gemac_ethtool_ops = { + .get_drvinfo = arasan_gemac_get_drvinfo, + .get_msglevel = arasan_gemac_get_msglevel, + .set_msglevel = arasan_gemac_set_msglevel, + .get_link = ethtool_op_get_link, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .nway_reset = arasan_gemac_nway_reset, +}; + +static void arasan_gemac_set_hwaddr(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + + u8 *dev_addr = dev->dev_addr; + + arasan_gemac_writel(pd, MAC_ADDRESS1_LOW, + MAC_ADDRESS1_LOW_SIXTH_BYTE(dev_addr[5]) | + MAC_ADDRESS1_LOW_FIFTH_BYTE(dev_addr[4])); + + arasan_gemac_writel(pd, MAC_ADDRESS1_MED, + MAC_ADDRESS1_MED_FOURTH_BYTE(dev_addr[3]) | + MAC_ADDRESS1_MED_THIRD_BYTE(dev_addr[2])); + + arasan_gemac_writel(pd, MAC_ADDRESS1_HIGH, + MAC_ADDRESS1_HIGH_SECOND_BYTE(dev_addr[1]) | + MAC_ADDRESS1_HIGH_FIRST_BYTE(dev_addr[0])); +} + +static void arasan_gemac_get_hwaddr(struct arasan_gemac_pdata *pd) +{ + netdev_info(pd->dev, "Using random hw address\n"); + eth_hw_addr_random(pd->dev); +} + +static void arasan_gemac_dma_soft_reset(struct arasan_gemac_pdata *pd) +{ + /* Reset the DMA controller to the default state */ + arasan_gemac_writel(pd, DMA_CONFIGURATION, + DMA_CONFIGURATION_SOFT_RESET); + + /* FIXME + * mdelay or msleep ? + */ + mdelay(10); + + /* Write the default value to deassert the reset signal */ + arasan_gemac_writel(pd, DMA_CONFIGURATION, + DMA_CONFIGURATION_BURST_LENGTH(4)); +} + +static void arasan_gemac_setup_frame_limits(struct arasan_gemac_pdata *pd, + int mtu) +{ + /* FIXME: extra_sz = 12 for 3 VLAN TAG ??? */ + const int extra_sz = 0; + int sz = mtu_to_frame_sz(mtu); + /* Frame length violation is set if received frame exceed max */ + arasan_gemac_writel(pd, MAC_MAXIMUM_FRAME_SIZE, sz + extra_sz); + + /* Jabber error is set if received frame exceeds jabber size */ + arasan_gemac_writel(pd, MAC_RECEIVE_JABBER_SIZE, sz + extra_sz); + + /* EOP will be sent if transmitted frame exceeds jabber size */ + arasan_gemac_writel(pd, MAC_TRANSMIT_JABBER_SIZE, sz + extra_sz); +} + +static void arasan_gemac_setup_fifo_thresholds(struct arasan_gemac_pdata *pd) +{ + /* limitation required by vendor */ + const int max = ARASAN_FIFO_SZ - 8; + + /* FIXME: It can damp difference between DMA and GEMAC speed. + * DMA has been stopped if it crosses full threshold. + * At this time GEMAC still transmit data to a link and + * DMA can be resumed when GEMAC crosses empty threshold. + * Because GEMAC still transmits it can flush FIFO before DMA + * brings new data, thus packet will be dropped. + * This scenario hasn't been confirmed. */ + const int min = 8; + + /* each location is 32 bits */ + arasan_gemac_writel(pd, MAC_TRANSMIT_FIFO_ALMOST_FULL, max); + arasan_gemac_writel(pd, MAC_TRANSMIT_FIFO_ALMOST_EMPTY_THRESHOLD, min); +} + +static void arasan_gemac_init(struct arasan_gemac_pdata *pd) +{ + u32 reg; + + arasan_gemac_writel(pd, MAC_ADDRESS_CONTROL, 1); + + reg = arasan_gemac_readl(pd, MAC_RECEIVE_CONTROL); + reg |= MAC_RECEIVE_CONTROL_STORE_AND_FORWARD; + arasan_gemac_writel(pd, MAC_RECEIVE_CONTROL, reg); + + arasan_gemac_setup_fifo_thresholds(pd); + + arasan_gemac_setup_frame_limits(pd, pd->dev->mtu); + + arasan_gemac_set_hwaddr(pd->dev); +} + +static int arasan_gemac_alloc_rx_desc(struct arasan_gemac_pdata *pd, int index) +{ + struct sk_buff *skb; + dma_addr_t mapping; + int len; + bool last = index == (RX_RING_SIZE - 1); + + skb = netdev_alloc_skb(pd->dev, mtu_to_buf_sz(pd->dev->mtu)); + if (unlikely(!skb)) + return -ENOMEM; + + len = skb_tailroom(skb); + + mapping = dma_map_single(&pd->pdev->dev, skb_tail_pointer(skb), len, + DMA_FROM_DEVICE); + + if (dma_mapping_error(&pd->pdev->dev, mapping)) { + dev_kfree_skb_any(skb); + netdev_warn(pd->dev, "dma_map_single failed!\n"); + return -ENOMEM; + } + + pd->rx_buffers[index].skb = skb; + pd->rx_buffers[index].mapping = mapping; + + /* check if we are at the last descriptor and need to set EOR */ + pd->rx_ring[index].misc = last ? DMA_RDES1_EOR | len : len; + pd->rx_ring[index].buffer1 = mapping + NET_IP_ALIGN; + + /* ensures that descriptor has been initialized */ + dma_wmb(); + + /* assign ownership to DMAC */ + pd->rx_ring[index].status = DMA_RDES0_OWN_BIT; + + /* Strictly speaking, a barrier is required here. + * Caller should provide it. + */ + + return 0; +} + +static void arasan_gemac_free_rx_desc(struct arasan_gemac_pdata *pd, int index) +{ + struct arasan_gemac_ring_info *desc = &pd->rx_buffers[index]; + int len; + + if (desc->skb) { + len = skb_tailroom(desc->skb); + WARN_ON(len == 0); + dma_unmap_single(&pd->pdev->dev, desc->mapping, len, + DMA_FROM_DEVICE); + dev_kfree_skb_any(desc->skb); + + desc->skb = NULL; + desc->mapping = 0; + } +} + +static void arasan_gemac_free_tx_desc(struct arasan_gemac_pdata *pd, int index) +{ + struct arasan_gemac_ring_info *desc = &pd->tx_buffers[index]; + + if (desc->skb) { + WARN_ON(!desc->mapping); + dma_unmap_single(&pd->pdev->dev, desc->mapping, desc->skb->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(desc->skb); + + desc->skb = NULL; + desc->mapping = 0; + } +} + +static void arasan_gemac_free_tx_ring(struct arasan_gemac_pdata *pd) +{ + int i; + int dma_sz = TX_RING_SIZE * sizeof(struct arasan_gemac_dma_desc); + + if (pd->tx_buffers) { + for (i = 0; i < TX_RING_SIZE; i++) + arasan_gemac_free_tx_desc(pd, i); + + kfree(pd->tx_buffers); + pd->tx_buffers = NULL; + } + + if (pd->tx_ring) { + dma_free_coherent(&pd->pdev->dev, dma_sz, pd->tx_ring, + pd->tx_dma_addr); + pd->tx_ring = NULL; + } + + pd->tx_ring_head = 0; + pd->tx_ring_tail = 0; +} + +static void arasan_gemac_free_rx_ring(struct arasan_gemac_pdata *pd) +{ + int i; + int dma_sz = RX_RING_SIZE * sizeof(struct arasan_gemac_dma_desc); + + if (pd->rx_buffers) { + for (i = 0; i < RX_RING_SIZE; i++) + arasan_gemac_free_rx_desc(pd, i); + + kfree(pd->rx_buffers); + pd->rx_buffers = NULL; + } + + if (pd->rx_ring) { + dma_free_coherent(&pd->pdev->dev, dma_sz, pd->rx_ring, + pd->rx_dma_addr); + pd->rx_ring = NULL; + } + + pd->rx_ring_head = 0; + pd->rx_ring_tail = 0; +} + +static int arasan_gemac_alloc_tx_ring(struct arasan_gemac_pdata *pd) +{ + int dma_sz = TX_RING_SIZE * sizeof(struct arasan_gemac_dma_desc); + int cpu_sz = TX_RING_SIZE * sizeof(struct arasan_gemac_ring_info); + + pd->tx_ring = dma_zalloc_coherent(&pd->pdev->dev, dma_sz, + &pd->tx_dma_addr, GFP_KERNEL); + if (!pd->tx_ring) + return -ENOMEM; + + pd->tx_buffers = kzalloc(cpu_sz, GFP_KERNEL); + + if (!pd->tx_buffers) + return -ENOMEM; + + pd->tx_ring_head = 0; + pd->tx_ring_tail = 0; + + /* Memory barrier is required here and is provided by writel(). */ + arasan_gemac_writel(pd, DMA_TRANSMIT_BASE_ADDRESS, pd->tx_dma_addr); + + return 0; +} + +static int arasan_gemac_alloc_rx_ring(struct arasan_gemac_pdata *pd) +{ + int i; + int dma_sz = RX_RING_SIZE * sizeof(struct arasan_gemac_dma_desc); + int cpu_sz = RX_RING_SIZE * sizeof(struct arasan_gemac_ring_info); + + pd->rx_ring = dma_zalloc_coherent(&pd->pdev->dev, dma_sz, + &pd->rx_dma_addr, GFP_KERNEL); + if (!pd->rx_ring) + return -ENOMEM; + + pd->rx_buffers = kzalloc(cpu_sz, GFP_KERNEL); + + if (!pd->rx_buffers) + return -ENOMEM; + + /* now allocate the entire ring of skbs */ + for (i = 0; i < RX_RING_SIZE; i++) { + if (arasan_gemac_alloc_rx_desc(pd, i)) { + netdev_err(pd->dev, + "failed to allocate rx skb %d\n", i); + return -ENOMEM; + } + } + + pd->rx_ring_head = 0; + pd->rx_ring_tail = 0; + + /* Memory barrier is required here and is provided by writel(). */ + arasan_gemac_writel(pd, DMA_RECEIVE_BASE_ADDRESS, pd->rx_dma_addr); + + return 0; +} + +static inline void arasan_gemac_tx_update_stats(struct net_device *dev, + u32 status, u32 length) +{ + if (unlikely(status & 0x7fffffff)) { + dev->stats.tx_errors++; + } else { + dev->stats.tx_packets++; + dev->stats.tx_bytes += (length & 0xFFF); + } +} + +/* Check for completed dma transfers, update stats and free skbs */ +static bool arasan_gemac_try_complete_tx(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + int freed = 0; + unsigned long flags; + + /* try_complete_tx() can be called in IRQ handler or start_xmit() */ + if (!spin_trylock_irqsave(&pd->tx_freelock, flags)) + return false; + + do { + u32 status, misc; + + int tail = pd->tx_ring_tail; + + /* synchronize with start_xmit() */ + int head = smp_load_acquire(&pd->tx_ring_head); + + if (tail == head) + break; + + /* ensures that CPU sees actual state */ + dma_rmb(); + + status = pd->tx_ring[tail].status; + misc = pd->tx_ring[tail].misc; + + /* Check if DMA still owns this descriptor */ + if (unlikely(DMA_TDES0_OWN_BIT & status)) + break; + + arasan_gemac_tx_update_stats(dev, status, misc); + + arasan_gemac_free_tx_desc(pd, tail); + freed++; + + /* synchronize for start_xmit() */ + smp_store_release(&pd->tx_ring_tail, (tail + 1) % TX_RING_SIZE); + } while (true); + + spin_unlock_irqrestore(&pd->tx_freelock, flags); + return freed > 0; +} + +/* Transmit packet */ +static int arasan_gemac_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + dma_addr_t mapping; + int head, tail; + u32 tmp_desc1; + + arasan_gemac_try_complete_tx(dev); + + head = pd->tx_ring_head; + + /* synchronize with complete_tx() */ + tail = smp_load_acquire(&pd->tx_ring_tail); + + WARN_ON(pd->tx_ring[head].status & DMA_TDES0_OWN_BIT); + + mapping = dma_map_single(&pd->pdev->dev, skb->data, + skb->len, DMA_TO_DEVICE); + + if (dma_mapping_error(&pd->pdev->dev, mapping)) { + netdev_warn(dev, "dma_map_single failed, dropping packet\n"); + return NETDEV_TX_BUSY; + } + + /* skb_tx_timestamp() should be called before + * preparing the descriptor, because at this time the DMA can work + * without kicking. + */ + + skb_tx_timestamp(skb); + + pd->tx_buffers[head].skb = skb; + pd->tx_buffers[head].mapping = mapping; + + if (unlikely(((head + 2) % TX_RING_SIZE) == tail)) + netif_stop_queue(pd->dev); + + tmp_desc1 = (DMA_TDES1_LS | DMA_TDES1_FS | ((u32)skb->len & 0xFFF)); + + /* check if we are at the last descriptor and need to set EOR */ + if (unlikely(head == (TX_RING_SIZE - 1))) + tmp_desc1 |= DMA_TDES1_EOR; + + pd->tx_ring[head].buffer1 = mapping; + pd->tx_ring[head].misc = tmp_desc1; + + /* ensures that descriptor has been initialized */ + dma_wmb(); + + /* assign ownership to DMAC */ + pd->tx_ring[head].status = DMA_TDES0_OWN_BIT; + + /* synchronize head for complete_tx() */ + smp_store_release(&pd->tx_ring_head, (head + 1) % TX_RING_SIZE); + + /* Memory barrier is required here and is provided by writel(). + * kick the DMA + */ + arasan_gemac_writel(pd, DMA_TRANSMIT_POLL_DEMAND, 1); + + return NETDEV_TX_OK; +} + +static void arasan_gemac_alloc_new_rx_buffers(struct arasan_gemac_pdata *pd) +{ + while (pd->rx_ring_tail != pd->rx_ring_head) { + WARN_ON(pd->rx_buffers[pd->rx_ring_tail].skb); + + if (arasan_gemac_alloc_rx_desc(pd, pd->rx_ring_tail)) + break; + + pd->rx_ring_tail = (pd->rx_ring_tail + 1) % RX_RING_SIZE; + } +} + +static void arasan_gemac_rx_handoff(struct arasan_gemac_pdata *pd, + const int index, const u32 status) +{ + struct net_device *dev = pd->dev; + struct sk_buff *skb; + u16 packet_length = (status & 0x3fff); + + /* remove crc from packet lendth */ + packet_length -= 4; + + dev->stats.rx_packets++; + dev->stats.rx_bytes += packet_length; + + dma_unmap_single(&pd->pdev->dev, pd->rx_buffers[index].mapping, + packet_length, DMA_FROM_DEVICE); + + skb = pd->rx_buffers[index].skb; + + pd->rx_buffers[index].skb = NULL; + pd->rx_buffers[index].mapping = 0; + + skb_reserve(skb, NET_IP_ALIGN); + skb_put(skb, packet_length); + + skb->protocol = eth_type_trans(skb, dev); + + netif_receive_skb(skb); +} + +static void arasan_gemac_rx_count_stats(struct net_device *dev, u32 desc_status) +{ + if (unlikely(!((desc_status & DMA_RDES0_FD) && + (desc_status & DMA_RDES0_LD)))) + dev->stats.rx_length_errors++; +} + +static int arasan_gemac_rx_poll(struct napi_struct *napi, int budget) +{ + struct arasan_gemac_pdata *pd = + container_of(napi, struct arasan_gemac_pdata, napi); + + struct net_device *dev = pd->dev; + u32 drop_frame_cnt, dma_intr_ena, status; + int work_done; + + for (work_done = 0; work_done < budget; work_done++) { + /* ensures that CPU sees actual state */ + dma_rmb(); + + status = pd->rx_ring[pd->rx_ring_head].status; + + /* stop if DMAC owns this dma descriptor */ + if (status & DMA_RDES0_OWN_BIT) + break; + + arasan_gemac_rx_count_stats(dev, status); + arasan_gemac_rx_handoff(pd, pd->rx_ring_head, status); + pd->rx_ring_head = (pd->rx_ring_head + 1) % RX_RING_SIZE; + } + + arasan_gemac_alloc_new_rx_buffers(pd); + + drop_frame_cnt = arasan_gemac_readl(pd, DMA_MISSED_FRAME_COUNTER); + dev->stats.rx_dropped += drop_frame_cnt; + + /* Memory barrier is required here and is provided by writel(). + * Kick RXDMA. + */ + arasan_gemac_writel(pd, DMA_RECEIVE_POLL_DEMAND, 1); + + if (work_done < budget) { + napi_complete(&pd->napi); + /* re-enable RX DMA interrupts */ + dma_intr_ena = arasan_gemac_readl(pd, DMA_INTERRUPT_ENABLE); + dma_intr_ena |= DMA_INTERRUPT_ENABLE_RECEIVE_DONE; + arasan_gemac_writel(pd, DMA_INTERRUPT_ENABLE, dma_intr_ena); + } + return work_done; +} + +static int arasan_gemac_try_up_tx_threshold(struct arasan_gemac_pdata *pd) +{ + int threshold; + /* if threshold is equal max threshold that GEMAC will work + * in Store and Forward mode. */ + const int maxthreshold = 1518; + + /* get current threshold */ + threshold = arasan_gemac_readl(pd, MAC_TRANSMIT_PACKET_START_THRESHOLD); + + if (threshold >= maxthreshold) + return false; + + threshold = min(maxthreshold, threshold + 32); + + arasan_gemac_writel(pd, MAC_TRANSMIT_PACKET_START_THRESHOLD, threshold); + return true; +} + +static void arasan_gemac_set_threshold(struct arasan_gemac_pdata *pd) +{ + int tx_tr, rx_tr; + + /* set initial TX threshold recommended by vendor */ + switch (pd->phy_dev->speed) { + case SPEED_10: + tx_tr = 64; + break; + case SPEED_100: + tx_tr = 128; + break; + case SPEED_1000: + default: + tx_tr = 1024; + } + + /* no obvious rules for RX threshold */ + rx_tr = 64; + + arasan_gemac_writel(pd, MAC_TRANSMIT_PACKET_START_THRESHOLD, tx_tr); + arasan_gemac_writel(pd, MAC_RECEIVE_PACKET_START_THRESHOLD, rx_tr); + + /* Underrun interrupt is enabled to adjust TX threshold + * if underrun condition occurs */ + arasan_gemac_writel(pd, MAC_INTERRUPT_ENABLE, + MAC_INTERRUPT_ENABLE_UNDERRUN); +} + +void arasan_gemac_mac_interrupt(struct arasan_gemac_pdata *pd) +{ + u32 sts, irq, clr = 0; + + sts = arasan_gemac_readl(pd, MAC_INTERRUPT_STATUS); + + if (sts & MAC_IRQ_STATUS_UNDERRUN) { + clr |= MAC_IRQ_STATUS_UNDERRUN; + /* Underrun condition occurs when DMA doesn't have time + * for deliver rest part of packet to FIFO. We can increase + * GEMAC start transmitting threshold. + * TODO: Inform upper layer that packet has been dropped. */ + if (!arasan_gemac_try_up_tx_threshold(pd)) { + irq = arasan_gemac_readl(pd, MAC_INTERRUPT_ENABLE); + irq &= ~MAC_INTERRUPT_ENABLE_UNDERRUN; + arasan_gemac_writel(pd, MAC_INTERRUPT_ENABLE, irq); + } + } + + arasan_gemac_writel(pd, MAC_INTERRUPT_STATUS, clr); +} + +static irqreturn_t arasan_gemac_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct arasan_gemac_pdata *pd = netdev_priv(dev); + u32 int_sts, ints_to_clear; + + int_sts = arasan_gemac_readl(pd, DMA_STATUS_AND_IRQ); + + ints_to_clear = 0; + + if (int_sts & DMA_STATUS_AND_IRQ_TRANS_DESC_UNAVAIL) { + ints_to_clear |= DMA_STATUS_AND_IRQ_TRANS_DESC_UNAVAIL; + + if (arasan_gemac_try_complete_tx(dev)) + netif_wake_queue(pd->dev); + } + + if (int_sts & DMA_STATUS_AND_IRQ_RECEIVE_DONE) { + /* mask RX DMAC interrupts */ + u32 dma_intr_ena = arasan_gemac_readl(pd, DMA_INTERRUPT_ENABLE); + + dma_intr_ena &= (~DMA_INTERRUPT_ENABLE_RECEIVE_DONE); + arasan_gemac_writel(pd, DMA_INTERRUPT_ENABLE, dma_intr_ena); + + ints_to_clear |= DMA_STATUS_AND_IRQ_RECEIVE_DONE; + napi_schedule(&pd->napi); + } + + if (int_sts & DMA_STATUS_AND_IRQ_MAC_INTERRUPT) { + ints_to_clear |= DMA_STATUS_AND_IRQ_MAC_INTERRUPT; + arasan_gemac_mac_interrupt(pd); + } + + if (ints_to_clear) + arasan_gemac_writel(pd, DMA_STATUS_AND_IRQ, ints_to_clear); + + return IRQ_HANDLED; +} + +static void arasan_gemac_stop_tx_dma(struct arasan_gemac_pdata *pd) +{ + u32 reg; + int timeout = 1000; + + /* disable TX DMAC */ + reg = arasan_gemac_readl(pd, DMA_CONTROL); + reg &= ~DMA_CONTROL_START_TRANSMIT_DMA; + arasan_gemac_writel(pd, DMA_CONTROL, reg); + + /* Wait max 20 ms for transmit process to stop */ + while (--timeout) { + reg = arasan_gemac_readl(pd, DMA_STATUS_AND_IRQ); + if (!DMA_STATUS_AND_IRQ_TRANSMIT_DMA_STATE(reg)) + break; + usleep_range(10, 20); + } + + if (!timeout) + netdev_warn(pd->dev, "TX DMAC failed to stop\n"); + + /* ACK Tx DMAC stop bit */ + arasan_gemac_writel(pd, DMA_STATUS_AND_IRQ, + DMA_STATUS_AND_IRQ_TX_DMA_STOPPED); +} + +static void arasan_gemac_start_tx_dma(struct arasan_gemac_pdata *pd) +{ + u32 reg; + + reg = arasan_gemac_readl(pd, DMA_CONTROL); + reg |= DMA_CONTROL_START_TRANSMIT_DMA; + arasan_gemac_writel(pd, DMA_CONTROL, reg); +} + +static void arasan_gemac_stop_tx_mac(struct arasan_gemac_pdata *pd) +{ + u32 reg; + + /* mask TX DMAC interrupts */ + reg = arasan_gemac_readl(pd, DMA_INTERRUPT_ENABLE); + reg &= ~DMA_INTERRUPT_ENABLE_TRANSMIT_DONE; + arasan_gemac_writel(pd, DMA_INTERRUPT_ENABLE, reg); + + /* writel() guarantees that interrupts will be masked + * before stopping MAC TX + */ + + reg = arasan_gemac_readl(pd, MAC_TRANSMIT_CONTROL); + reg &= ~MAC_TRANSMIT_CONTROL_TRANSMIT_ENABLE; + arasan_gemac_writel(pd, MAC_TRANSMIT_CONTROL, reg); +} + +static void arasan_gemac_start_tx_mac(struct arasan_gemac_pdata *pd) +{ + u32 reg; + + reg = arasan_gemac_readl(pd, MAC_TRANSMIT_CONTROL); + reg |= MAC_TRANSMIT_CONTROL_TRANSMIT_ENABLE; + arasan_gemac_writel(pd, MAC_TRANSMIT_CONTROL, reg); +} + +static void arasan_gemac_stop_tx(struct arasan_gemac_pdata *pd) +{ + arasan_gemac_stop_tx_dma(pd); + arasan_gemac_stop_tx_mac(pd); +} + +static void arasan_gemac_stop_rx_dma(struct arasan_gemac_pdata *pd) +{ + u32 reg; + int timeout = 1000; + + /* stop RX DMAC */ + reg = arasan_gemac_readl(pd, DMA_CONTROL); + reg &= ~DMA_CONTROL_START_RECEIVE_DMA; + arasan_gemac_writel(pd, DMA_CONTROL, reg); + + /* Wait max 20 ms for receive process to stop */ + while (--timeout) { + reg = arasan_gemac_readl(pd, DMA_STATUS_AND_IRQ); + if (!DMA_STATUS_AND_IRQ_RECEIVE_DMA_STATE(reg)) + break; + usleep_range(10, 20); + } + + if (!timeout) + netdev_warn(pd->dev, "RX DMAC failed to stop\n"); + + /* ACK the Rx DMAC stop bit */ + arasan_gemac_writel(pd, DMA_STATUS_AND_IRQ, + DMA_STATUS_AND_IRQ_RX_DMA_STOPPED); +} + +static void arasan_gemac_start_rx_dma(struct arasan_gemac_pdata *pd) +{ + u32 reg; + + reg = arasan_gemac_readl(pd, DMA_CONTROL); + reg |= DMA_CONTROL_START_RECEIVE_DMA; + arasan_gemac_writel(pd, DMA_CONTROL, reg); +} + +static void arasan_gemac_stop_rx_mac(struct arasan_gemac_pdata *pd) +{ + u32 reg; + + /* mask RX DMAC interrupts */ + reg = arasan_gemac_readl(pd, DMA_INTERRUPT_ENABLE); + reg &= ~DMA_INTERRUPT_ENABLE_RECEIVE_DONE; + arasan_gemac_writel(pd, DMA_INTERRUPT_ENABLE, reg); + + /* writel() guarantees that interrupts will be masked + * before stopping RX MAC. + */ + + reg = arasan_gemac_readl(pd, MAC_RECEIVE_CONTROL); + reg &= ~MAC_RECEIVE_CONTROL_RECEIVE_ENABLE; + arasan_gemac_writel(pd, MAC_RECEIVE_CONTROL, reg); +} + +static void arasan_gemac_start_rx_mac(struct arasan_gemac_pdata *pd) +{ + u32 reg; + + reg = arasan_gemac_readl(pd, MAC_RECEIVE_CONTROL); + reg |= MAC_RECEIVE_CONTROL_RECEIVE_ENABLE; + arasan_gemac_writel(pd, MAC_RECEIVE_CONTROL, reg); +} + +static void arasan_gemac_stop_rx(struct arasan_gemac_pdata *pd) +{ + arasan_gemac_stop_rx_mac(pd); + arasan_gemac_stop_rx_dma(pd); +} + +static void arasan_gemac_start_rx(struct arasan_gemac_pdata *pd) +{ + arasan_gemac_start_rx_mac(pd); + arasan_gemac_start_rx_dma(pd); +} + +static void arasan_gemac_start_tx(struct arasan_gemac_pdata *pd) +{ + arasan_gemac_start_tx_mac(pd); + arasan_gemac_start_tx_dma(pd); +} + +static void arasan_gemac_stop_mac(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + + netif_tx_disable(dev); + napi_disable(&pd->napi); + + arasan_gemac_stop_tx(pd); + arasan_gemac_free_tx_ring(pd); + + arasan_gemac_stop_rx(pd); + arasan_gemac_free_rx_ring(pd); + + arasan_gemac_dma_soft_reset(pd); +} + +static int arasan_gemac_stop(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + + arasan_gemac_stop_mac(dev); + + phy_stop(pd->phy_dev); + /* TODO: We should somehow power down PHY */ + + phy_disconnect(pd->phy_dev); + mdiobus_unregister(pd->mii_bus); + mdiobus_free(pd->mii_bus); + + return 0; +} + +static int arasan_gemac_get_gpio(struct platform_device *pdev, char *name) +{ + int rc, gpio; + struct device_node *np = pdev->dev.of_node; + + if (!np) + return -ENODEV; + + gpio = of_get_named_gpio(np, name, 0); + if (!gpio_is_valid(gpio)) + return gpio; + + rc = devm_gpio_request_one(&pdev->dev, gpio, + GPIOF_OUT_INIT_LOW, name); + + if (rc) + return rc; + + return gpio; +} + +static void arasan_gemac_reset_phy(struct platform_device *pdev) +{ + int gpio; + + gpio = arasan_gemac_get_gpio(pdev, "phy-reset-gpios"); + if (!gpio_is_valid(gpio)) { + dev_warn(&pdev->dev, "Failed to get phy-reset-gpios\n"); + return; + } + + /* FIXME + * 20 msec is actually too much for phy resetting. But if we set + * the reset time less than 20 msec check patch script is failed. + */ + + msleep(20); + gpio_set_value(gpio, 1); +} + +static int arasan_gemac_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct arasan_gemac_pdata *pd = bus->priv; + int value; + + arasan_gemac_writel(pd, MAC_MDIO_CONTROL, + MAC_MDIO_CONTROL_READ_WRITE(1) | + MAC_MDIO_CONTROL_REG_ADDR(regnum) | + MAC_MDIO_CONTROL_PHY_ADDR(mii_id) | + MAC_MDIO_CONTROL_START_FRAME(1)); + + /* wait for end of transfer */ + while ((arasan_gemac_readl(pd, MAC_MDIO_CONTROL) >> 15)) + cpu_relax(); + + value = arasan_gemac_readl(pd, MAC_MDIO_DATA); + + return value; +} + +static int arasan_gemac_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ + struct arasan_gemac_pdata *pd = bus->priv; + + arasan_gemac_writel(pd, MAC_MDIO_DATA, value); + + arasan_gemac_writel(pd, MAC_MDIO_CONTROL, + MAC_MDIO_CONTROL_START_FRAME(1) | + MAC_MDIO_CONTROL_PHY_ADDR(mii_id) | + MAC_MDIO_CONTROL_REG_ADDR(regnum) | + MAC_MDIO_CONTROL_READ_WRITE(0)); + + /* wait for end of transfer */ + while ((arasan_gemac_readl(pd, MAC_MDIO_CONTROL) >> 15)) + cpu_relax(); + + return 0; +} + +/* Reconfigure Arasan GEMAC according to speed and duplex value */ +static void arasan_gemac_reconfigure(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + struct phy_device *phydev = pd->phy_dev; + u32 reg; + + reg = arasan_gemac_readl(pd, MAC_GLOBAL_CONTROL); + reg &= ~(MAC_GLOBAL_CONTROL_SPEED(3) | + MAC_GLOBAL_CONTROL_DUPLEX_MODE(1)); + + switch (phydev->duplex) { + case DUPLEX_HALF: + break; + case DUPLEX_FULL: + reg |= MAC_GLOBAL_CONTROL_DUPLEX_MODE(DUPLEX_FULL); + break; + default: + netdev_err(dev, "Unknown duplex (%d)\n", phydev->duplex); + return; + } + + switch (phydev->speed) { + case SPEED_100: + reg |= MAC_GLOBAL_CONTROL_SPEED(1); + break; + case SPEED_1000: + reg |= MAC_GLOBAL_CONTROL_SPEED(2); + break; + default: + netdev_err(dev, "Unknown speed (%d)\n", phydev->speed); + return; + } + arasan_gemac_set_threshold(pd); + + arasan_gemac_writel(pd, MAC_GLOBAL_CONTROL, reg); +} + +static void arasan_gemac_handle_link_change(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + struct phy_device *phydev = pd->phy_dev; + unsigned long flags; + + int status_change = 0; + + spin_lock_irqsave(&pd->lock, flags); + + if ((phydev->link) && + ((pd->speed != phydev->speed) || (pd->duplex != phydev->duplex))) { + arasan_gemac_reconfigure(dev); + pd->speed = phydev->speed; + pd->duplex = phydev->duplex; + status_change = 1; + } + + if (phydev->link != pd->link) { + if (!phydev->link) { + pd->speed = 0; + pd->duplex = -1; + } + pd->link = phydev->link; + status_change = 1; + } + + spin_unlock_irqrestore(&pd->lock, flags); + + if (status_change) { + if (phydev->link) { + netif_carrier_on(dev); + netdev_info(dev, "link up (%d/%s)\n", + phydev->speed, + phydev->duplex == DUPLEX_FULL ? + "Full" : "Half"); + } else { + netif_carrier_off(dev); + netdev_info(dev, "link down\n"); + } + } +} + +static int arasan_gemac_mii_probe(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + struct phy_device *phydev; + + phydev = phy_find_first(pd->mii_bus); + if (!phydev) { + netdev_err(dev, "no PHY found\n"); + return -ENXIO; + } + + phydev = phy_connect(dev, phydev_name(phydev), + arasan_gemac_handle_link_change, + pd->phy_interface); + + if (IS_ERR(phydev)) { + netdev_err(dev, "Could not attach to PHY\n"); + return PTR_ERR(phydev); + } + + netdev_info(dev, + "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", + phydev->drv->name, phydev_name(phydev), phydev->irq); + + phydev->supported &= ARASAN_GEMAC_FEATURES; + if (pd->phy_interface == PHY_INTERFACE_MODE_MII) + phydev->supported &= ~PHY_1000BT_FEATURES; + + phydev->advertising = phydev->supported; + + pd->link = 0; + pd->speed = 0; + pd->duplex = -1; + pd->phy_dev = phydev; + + return 0; +} + +static int arasan_gemac_mii_init(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + struct device_node *np; + int err = -ENXIO; + u32 divisor; + + pd->mii_bus = mdiobus_alloc(); + if (!pd->mii_bus) { + err = -ENOMEM; + goto err_out; + } + + pd->mii_bus->name = "arasan-gemac-mii-bus"; + pd->mii_bus->read = &arasan_gemac_mdio_read; + pd->mii_bus->write = &arasan_gemac_mdio_write; + /* TODO: pd->mii_bus->reset also should be implemented to allow + * reset of Ethernet PHY from user space (see MII-TOOL utility) + */ + + /* Maximum allowing MDC clock is 2.5 MHz */ + divisor = DIV_ROUND_UP(clk_get_rate(pd->hclk), 2500000); + arasan_gemac_writel(pd, MAC_MDIO_CLOCK_DIVISION_CONTROL, divisor); + + snprintf(pd->mii_bus->id, MII_BUS_ID_SIZE, "%s-0x%x", + pd->pdev->name, pd->pdev->id); + + pd->mii_bus->priv = pd; + pd->mii_bus->parent = &pd->dev->dev; + + np = pd->pdev->dev.of_node; + if (np) { + /* try dt phy registration */ + err = of_mdiobus_register(pd->mii_bus, np); + + if (err) { + netdev_err(dev, + "Failed to register mdio bus, error: %d\n", + err); + goto err_out_free_mdiobus; + } + } else { + netdev_err(dev, "Missing device tree node\n"); + goto err_out_free_mdiobus; + } + + err = arasan_gemac_mii_probe(dev); + if (err) + goto err_out_unregister_bus; + + return 0; + +err_out_unregister_bus: + mdiobus_unregister(pd->mii_bus); +err_out_free_mdiobus: + mdiobus_free(pd->mii_bus); +err_out: + return err; +} + +int arasan_gemac_start_mac(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + int result; + + result = arasan_gemac_alloc_tx_ring(pd); + if (result) { + netdev_err(pd->dev, "Failed to Initialize tx dma ring\n"); + goto err; + } + + result = arasan_gemac_alloc_rx_ring(pd); + if (result) { + netdev_err(pd->dev, "Failed to Initialize rx dma ring\n"); + goto err; + } + + arasan_gemac_init(pd); + + napi_enable(&pd->napi); + + /* Enable interrupts */ + arasan_gemac_writel(pd, DMA_INTERRUPT_ENABLE, + DMA_INTERRUPT_ENABLE_RECEIVE_DONE | + DMA_INTERRUPT_ENABLE_TRANS_DESC_UNAVAIL | + DMA_INTERRUPT_ENABLE_MAC); + + /* Enable packet transmission */ + arasan_gemac_start_tx(pd); + + /* Enable packet reception */ + arasan_gemac_start_rx(pd); + + netif_start_queue(dev); + + return 0; +err: + /* explicit cleanup even if something is partially initialized */ + arasan_gemac_free_tx_ring(pd); + arasan_gemac_free_rx_ring(pd); + return result; +} + +/* Open the Ethernet interface */ +static int arasan_gemac_open(struct net_device *dev) +{ + struct arasan_gemac_pdata *pd = netdev_priv(dev); + int res; + + res = arasan_gemac_start_mac(dev); + if (res) + return res; + + res = arasan_gemac_mii_init(dev); + if (res) { + netdev_err(dev, "Failed to initialize Phy\n"); + arasan_gemac_stop_mac(dev); + return res; + } + /* schedule a link state check */ + phy_start(pd->phy_dev); + + return 0; +} + +static int arasan_gemac_set_mac_address(struct net_device *dev, void *addr) +{ + if (netif_running(dev)) + return -EBUSY; + + /* sa_family is validated by calling code */ + ether_addr_copy(dev->dev_addr, ((struct sockaddr *)addr)->sa_data); + arasan_gemac_set_hwaddr(dev); + + return 0; +} + +static int arasan_gemac_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu > ARASAN_JUMBO_MTU || new_mtu < 68) + return -EINVAL; + + if (netif_running(dev)) + arasan_gemac_stop_mac(dev); + + dev->mtu = new_mtu; + + if (netif_running(dev)) + arasan_gemac_start_mac(dev); + + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void arasan_gemac_poll_controller(struct net_device *dev) +{ + unsigned long flags; + + local_irq_save(flags); + arasan_gemac_interrupt(dev->irq, dev); + local_irq_restore(flags); +} +#endif + +static const struct net_device_ops arasan_gemac_netdev_ops = { + .ndo_open = arasan_gemac_open, + .ndo_stop = arasan_gemac_stop, + .ndo_start_xmit = arasan_gemac_start_xmit, + .ndo_set_mac_address = arasan_gemac_set_mac_address, + .ndo_change_mtu = arasan_gemac_change_mtu, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = arasan_gemac_poll_controller, +#endif +}; + +#if defined(CONFIG_OF) +static const struct of_device_id arasan_gemac_dt_ids[] = { + { .compatible = "elvees,arasan-gemac" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, arasan_gemac_dt_ids); +#endif + +static int arasan_gemac_probe(struct platform_device *pdev) +{ + struct resource *regs; + struct net_device *dev; + struct arasan_gemac_pdata *pd; + int res; + const char *mac; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENOENT; + + /* Allocate and set up ethernet device */ + dev = alloc_etherdev(sizeof(struct arasan_gemac_pdata)); + if (!dev) + return -ENOMEM; + + pd = netdev_priv(dev); + pd->pdev = pdev; + pd->dev = dev; + spin_lock_init(&pd->lock); + spin_lock_init(&pd->tx_freelock); + + /* Try to get and enable Arasan GEMAC hclk */ + pd->hclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pd->hclk)) { + res = PTR_ERR(pd->hclk); + dev_err(&pdev->dev, + "failed to get Arasan GEMAC hclk (%u)\n", res); + goto err_free_dev; + } + + res = clk_prepare_enable(pd->hclk); + if (res) { + dev_err(&pdev->dev, + "failed to enable Arasan GEMAC hclk (%u)\n", res); + goto err_free_dev; + } + + /* physical base address */ + dev->base_addr = regs->start; + pd->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); + if (!pd->regs) { + res = -ENOMEM; + goto err_disable_clocks; + } + + /* Install the interrupt handler */ + dev->irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, dev->irq, arasan_gemac_interrupt, + 0, dev->name, dev); + if (res) + goto err_disable_clocks; + + arasan_gemac_reset_phy(pdev); + + dev->netdev_ops = &arasan_gemac_netdev_ops; + dev->ethtool_ops = &arasan_gemac_ethtool_ops; + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + netif_napi_add(dev, &pd->napi, arasan_gemac_rx_poll, NAPI_WEIGHT); + + res = of_get_phy_mode(pdev->dev.of_node); + if (res < 0) + goto err_disable_clocks; + + if (res != PHY_INTERFACE_MODE_MII && res != PHY_INTERFACE_MODE_GMII) { + dev_err(&pdev->dev, "\"%s\" PHY interface is not supported\n", + phy_modes(res)); + res = -ENODEV; + goto err_disable_clocks; + } + + pd->phy_interface = res; + + mac = of_get_mac_address(pdev->dev.of_node); + if (mac) + ether_addr_copy(pd->dev->dev_addr, mac); + else + arasan_gemac_get_hwaddr(pd); + + /* Register the network interface */ + res = register_netdev(dev); + if (res) + goto err_disable_clocks; + + netif_carrier_off(dev); + + arasan_gemac_dma_soft_reset(pd); + + /* Display ethernet banner */ + netdev_info(dev, "Arasan GEMAC ethernet at 0x%08lx int=%d (%pM)\n", + dev->base_addr, dev->irq, dev->dev_addr); + + return 0; + +err_disable_clocks: + clk_disable_unprepare(pd->hclk); +err_free_dev: + free_netdev(dev); + + return res; +} + +static int arasan_gemac_remove(struct platform_device *pdev) +{ + struct net_device *dev; + struct arasan_gemac_pdata *pd; + + dev = platform_get_drvdata(pdev); + if (!dev) + return 0; + + pd = netdev_priv(dev); + + unregister_netdev(dev); + clk_disable_unprepare(pd->hclk); + free_netdev(dev); + + return 0; +} + +static struct platform_driver arasan_gemac_driver = { + .driver = { + .name = "arasan-gemac", + .of_match_table = of_match_ptr(arasan_gemac_dt_ids), + }, + .probe = arasan_gemac_probe, + .remove = arasan_gemac_remove, +}; + +module_platform_driver(arasan_gemac_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Arasan GEMAC ethernet driver"); +MODULE_AUTHOR("Dmitriy Zagrebin "); diff --git a/drivers/net/ethernet/arasan/arasan-gemac.h b/drivers/net/ethernet/arasan/arasan-gemac.h new file mode 100644 index 00000000000000..96497c88a82015 --- /dev/null +++ b/drivers/net/ethernet/arasan/arasan-gemac.h @@ -0,0 +1,203 @@ +/* + * Copyright 2015 ELVEES NeoTek CJSC + * Copyright 2017 RnD Center "ELVEES", JSC + * + * 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. + */ + +#ifndef _ARASAN_GEMAC_H +#define _ARASAN_GEMAC_H + +/* GEMAC TX descriptor can describe 4K buffer. + * But currently some unexplored bugs are observed if we set Jumbo frame + * more than 3500 bytes. This bugs lead to lack of transmission. */ +#define ARASAN_JUMBO_MTU 3500U + +/* GEMAC FIFO depth in 32 bit words */ +#define ARASAN_FIFO_SZ 1024 + +#define mtu_to_frame_sz(x) ((x) + VLAN_ETH_HLEN) +#define mtu_to_buf_sz(x) (mtu_to_frame_sz(x) + NET_IP_ALIGN + 4) + +#define TX_RING_SIZE (128) +#define RX_RING_SIZE (128) +#define NAPI_WEIGHT (64) + +/* Arasan GEMAC register offsets */ + +#define DMA_CONFIGURATION 0x0000 +#define DMA_CONTROL 0x0004 +#define DMA_STATUS_AND_IRQ 0x0008 +#define DMA_INTERRUPT_ENABLE 0x000C +#define DMA_TRANSMIT_AUTO_POLL_COUNTER 0x0010 +#define DMA_TRANSMIT_POLL_DEMAND 0x0014 +#define DMA_RECEIVE_POLL_DEMAND 0x0018 +#define DMA_TRANSMIT_BASE_ADDRESS 0x001C +#define DMA_RECEIVE_BASE_ADDRESS 0x0020 +#define DMA_MISSED_FRAME_COUNTER 0x0024 +#define DMA_STOP_FLUSH_COUNTER 0x0028 +#define DMA_RECEIVE_INTERRUPT_MITIGATION 0x002C +#define DMA_CURRENT_TRANSMIT_DESCRIPTOR_POINTER 0x0030 +#define DMA_CURRENT_TRANSMIT_BUFFER_POINTER 0x0034 +#define DMA_CURRENT_RECEIVE_DESCRIPTOR_POINTER 0x0038 +#define DMA_CURRENT_RECEIVE_BUFFER_POINTER 0x003C + +#define MAC_GLOBAL_CONTROL 0x0100 +#define MAC_TRANSMIT_CONTROL 0x0104 +#define MAC_RECEIVE_CONTROL 0x0108 +#define MAC_MAXIMUM_FRAME_SIZE 0x010C +#define MAC_TRANSMIT_JABBER_SIZE 0x0110 +#define MAC_RECEIVE_JABBER_SIZE 0x0114 +#define MAC_ADDRESS_CONTROL 0x0118 +#define MAC_MDIO_CLOCK_DIVISION_CONTROL 0x011C +#define MAC_ADDRESS1_HIGH 0x0120 +#define MAC_ADDRESS1_MED 0x0124 +#define MAC_ADDRESS1_LOW 0x0128 +#define MAC_ADDRESS2_HIGH 0x012C +#define MAC_ADDRESS2_MED 0x0130 +#define MAC_ADDRESS2_LOW 0x0134 +#define MAC_ADDRESS3_HIGH 0x0138 +#define MAC_ADDRESS3_MED 0x013C +#define MAC_ADDRESS3_LOW 0x0140 +#define MAC_ADDRESS4_HIGH 0x0144 +#define MAC_ADDRESS4_MED 0x0148 +#define MAC_ADDRESS4_LOW 0x014C +#define MAC_HASH_TABLE1 0x0150 +#define MAC_HASH_TABLE2 0x0154 +#define MAC_HASH_TABLE3 0x0158 +#define MAC_HASH_TABLE4 0x015C + +#define MAC_MDIO_CONTROL 0x01A0 +#define MAC_MDIO_DATA 0x01A4 +#define MAC_RX_STATCTR_CONTROL 0x01A8 +#define MAC_RX_STATCTR_DATA_HIGH 0x01AC +#define MAC_RX_STATCTR_DATA_LOW 0x01B0 +#define MAC_TX_STATCTR_CONTROL 0x01B4 +#define MAC_TX_STATCTR_DATA_HIGH 0x01B8 +#define MAC_TX_STATCTR_DATA_LOW 0x01BC +#define MAC_TRANSMIT_FIFO_ALMOST_FULL 0x01C0 +#define MAC_TRANSMIT_PACKET_START_THRESHOLD 0x01C4 +#define MAC_RECEIVE_PACKET_START_THRESHOLD 0x01C8 +#define MAC_TRANSMIT_FIFO_ALMOST_EMPTY_THRESHOLD 0x01CC +#define MAC_INTERRUPT_STATUS 0x01E0 +#define MAC_INTERRUPT_ENABLE 0x01E4 +#define MAC_VLAN_TPID1 0x01E8 +#define MAC_VLAN_TPID2 0x01EC +#define MAC_VLAN_TPID3 0x01F0 + +/* Arasan GEMAC register fields */ + +#define DMA_CONFIGURATION_SOFT_RESET BIT(0) +#define DMA_CONFIGURATION_BURST_LENGTH(VAL) ((VAL) << 1) +#define DMA_CONFIGURATION_WAIT_FOR_DONE BIT(16) + +#define DMA_CONTROL_START_TRANSMIT_DMA BIT(0) +#define DMA_CONTROL_START_RECEIVE_DMA BIT(1) + +#define DMA_STATUS_AND_IRQ_TRANSMIT_DONE BIT(0) +#define DMA_STATUS_AND_IRQ_TRANS_DESC_UNAVAIL BIT(1) +#define DMA_STATUS_AND_IRQ_TX_DMA_STOPPED BIT(2) +#define DMA_STATUS_AND_IRQ_RECEIVE_DONE BIT(4) +#define DMA_STATUS_AND_IRQ_RX_DMA_STOPPED BIT(6) +#define DMA_STATUS_AND_IRQ_MAC_INTERRUPT BIT(8) +#define DMA_STATUS_AND_IRQ_TRANSMIT_DMA_STATE(VAL) (((VAL) & 0x7000) >> 16) +#define DMA_STATUS_AND_IRQ_RECEIVE_DMA_STATE(VAL) (((VAL) & 0xf0000) >> 20) + +#define DMA_INTERRUPT_ENABLE_TRANSMIT_DONE BIT(0) +#define DMA_INTERRUPT_ENABLE_TRANS_DESC_UNAVAIL BIT(1) +#define DMA_INTERRUPT_ENABLE_RECEIVE_DONE BIT(4) +#define DMA_INTERRUPT_ENABLE_MAC BIT(8) + +#define MAC_GLOBAL_CONTROL_SPEED(VAL) ((VAL) << 0) +#define MAC_GLOBAL_CONTROL_DUPLEX_MODE(VAL) ((VAL) << 2) + +#define MAC_TRANSMIT_CONTROL_TRANSMIT_ENABLE BIT(0) + +#define MAC_RECEIVE_CONTROL_RECEIVE_ENABLE BIT(0) +#define MAC_RECEIVE_CONTROL_STORE_AND_FORWARD BIT(3) + +#define MAC_ADDRESS1_LOW_SIXTH_BYTE(VAL) ((VAL) << 8) +#define MAC_ADDRESS1_LOW_FIFTH_BYTE(VAL) ((VAL) << 0) +#define MAC_ADDRESS1_MED_FOURTH_BYTE(VAL) ((VAL) << 8) +#define MAC_ADDRESS1_MED_THIRD_BYTE(VAL) ((VAL) << 0) +#define MAC_ADDRESS1_HIGH_SECOND_BYTE(VAL) ((VAL) << 8) +#define MAC_ADDRESS1_HIGH_FIRST_BYTE(VAL) ((VAL) << 0) + +#define MAC_MDIO_CONTROL_READ_WRITE(VAL) ((VAL) << 10) +#define MAC_MDIO_CONTROL_REG_ADDR(VAL) ((VAL) << 5) +#define MAC_MDIO_CONTROL_PHY_ADDR(VAL) ((VAL) << 0) +#define MAC_MDIO_CONTROL_START_FRAME(VAL) ((VAL) << 15) + +#define MAC_INTERRUPT_ENABLE_UNDERRUN BIT(0) +#define MAC_IRQ_STATUS_UNDERRUN BIT(0) + +/* DMA descriptor fields */ + +#define DMA_RDES0_OWN_BIT BIT(31) +#define DMA_RDES0_FD BIT(30) +#define DMA_RDES0_LD BIT(29) +#define DMA_RDES1_EOR BIT(26) + +#define DMA_TDES0_OWN_BIT BIT(31) +#define DMA_TDES1_IOC BIT(31) +#define DMA_TDES1_LS BIT(30) +#define DMA_TDES1_FS BIT(29) +#define DMA_TDES1_EOR BIT(26) + +#define arasan_gemac_readl(port, reg) readl((port)->regs + (reg)) +#define arasan_gemac_writel(port, reg, value) \ + writel((value), (port)->regs + (reg)) + +struct arasan_gemac_dma_desc { + u32 status; + u32 misc; + u32 buffer1; + u32 buffer2; +}; + +struct arasan_gemac_ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + +struct arasan_gemac_pdata { + void __iomem *regs; + + struct platform_device *pdev; + struct net_device *dev; + + /* driver lock */ + spinlock_t lock; + + struct clk *hclk; + + struct arasan_gemac_dma_desc *rx_ring; + struct arasan_gemac_dma_desc *tx_ring; + struct arasan_gemac_ring_info *tx_buffers; + struct arasan_gemac_ring_info *rx_buffers; + + dma_addr_t rx_dma_addr; + dma_addr_t tx_dma_addr; + + /* lock for descriptor completion */ + spinlock_t tx_freelock; + int tx_ring_head, tx_ring_tail; + int rx_ring_head, rx_ring_tail; + + struct napi_struct napi; + + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + unsigned int link; + unsigned int speed; + unsigned int duplex; + u32 msg_enable; + + phy_interface_t phy_interface; + int phy_irq[PHY_MAX_ADDR]; +}; + +#endif /* _ARASAN_GEMAC_H */ diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 28f22e58639c6c..47e901bbfbb81e 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1361,6 +1361,12 @@ config SERIAL_ARC_NR_PORTS Set this to the number of serial ports you want the driver to support. +config SERIAL_ELVEES_P2M + tristate "ELVEES P2M serial driver" + select SERIAL_CORE + help + Serial driver for printing messages to memory. + config SERIAL_RP2 tristate "Comtrol RocketPort EXPRESS/INFINITY support" depends on PCI diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index caf167f0c10a62..bc5e080c0a54ba 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_SERIAL_TEGRA_TCU) += tegra-tcu.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o obj-$(CONFIG_SERIAL_ARC) += arc_uart.o +obj-$(CONFIG_SERIAL_ELVEES_P2M) += elvees-p2m.o obj-$(CONFIG_SERIAL_RP2) += rp2.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o obj-$(CONFIG_SERIAL_FSL_LINFLEXUART) += fsl_linflexuart.o diff --git a/drivers/tty/serial/elvees-p2m.c b/drivers/tty/serial/elvees-p2m.c new file mode 100644 index 00000000000000..296d7b2eafa845 --- /dev/null +++ b/drivers/tty/serial/elvees-p2m.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ELVEES serial driver for printing messages into memory + * + * Copyright 2019 RnD Center "ELVEES", JSC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define P2M_SERIAL_DEV_NAME "ttyP2M" + +static struct uart_port p2m_port; + +static struct console p2m_console; + +#define DRIVER_NAME "p2m" + +static struct uart_driver p2m_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = P2M_SERIAL_DEV_NAME, + .major = 0, + .minor = 0, + .nr = 1, + .cons = &p2m_console, +}; + +static void p2m_stop_rx(struct uart_port *port) +{ +} + +static void p2m_stop_tx(struct uart_port *port) +{ +} + +static unsigned int p2m_tx_empty(struct uart_port *port) +{ + return TIOCSER_TEMT; +} + +static void p2m_start_tx(struct uart_port *port) +{ +} + +static unsigned int p2m_get_mctrl(struct uart_port *port) +{ + /* + * Pretend we have a Modem status reg and following bits are + * always set, to satify the serial core state machine + * (DSR) Data Set Ready + * (CTS) Clear To Send + * (CAR) Carrier Detect + */ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void p2m_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static void p2m_break_ctl(struct uart_port *port, int break_state) +{ +} + +static int p2m_startup(struct uart_port *port) +{ + return 0; +} + +/* This is not really needed */ +static void p2m_shutdown(struct uart_port *port) +{ +} + +static void p2m_set_termios(struct uart_port *port, struct ktermios *new, + struct ktermios *old) +{ +} + +static const char *p2m_type(struct uart_port *port) +{ + return NULL; +} + +static void p2m_release_port(struct uart_port *port) +{ +} + +static int p2m_request_port(struct uart_port *port) +{ + return 0; +} + +static int p2m_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return 0; +} + +static void p2m_config_port(struct uart_port *port, int flags) +{ +} + +static const struct uart_ops p2m_serial_pops = { + .tx_empty = p2m_tx_empty, + .set_mctrl = p2m_set_mctrl, + .get_mctrl = p2m_get_mctrl, + .stop_tx = p2m_stop_tx, + .start_tx = p2m_start_tx, + .stop_rx = p2m_stop_rx, + .break_ctl = p2m_break_ctl, + .startup = p2m_startup, + .shutdown = p2m_shutdown, + .set_termios = p2m_set_termios, + .type = p2m_type, + .release_port = p2m_release_port, + .request_port = p2m_request_port, + .config_port = p2m_config_port, + .verify_port = p2m_verify_port, +}; + +static int p2m_console_setup(struct console *co, char *options) +{ + return 0; +} + +/* The following аlgorithm to print messages during RTL simulation is assumed: + * 1. The p2m driver puts message into shared buffer. + * 2. The p2m driver writes shared buffer address to print agent register. + * 3. Print agent prints message in RTL simulation environment. + */ + +#define P2M_SHARED_BUFFER_OFFSET 0x800 +#define P2M_AGENT_BUFFER_ADDR 0xC +#define P2M_AGENT_MAX_STRING_SIZE 4096 + +static void p2m_putstr(struct uart_port *port, const char *s, + unsigned int count) +{ + unsigned int i; + + if (count >= P2M_AGENT_MAX_STRING_SIZE) + count = P2M_AGENT_MAX_STRING_SIZE - 1; + + for (i = 0; i < count; i++, s++) + writeb(*s, port->membase + P2M_SHARED_BUFFER_OFFSET + i); + + /* Print agent requires that every message ends with null character */ + writeb(0, port->membase + P2M_SHARED_BUFFER_OFFSET + count); + + writel(port->mapbase + P2M_SHARED_BUFFER_OFFSET, + port->membase + P2M_AGENT_BUFFER_ADDR); +} + +static void p2m_console_write(struct console *co, const char *s, + unsigned int count) +{ + unsigned long flags; + + spin_lock_irqsave(&p2m_port.lock, flags); + p2m_putstr(&p2m_port, s, count); + spin_unlock_irqrestore(&p2m_port.lock, flags); +} + +static struct console p2m_console = { + .name = P2M_SERIAL_DEV_NAME, + .write = p2m_console_write, + .device = uart_console_device, + .setup = p2m_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &p2m_uart_driver +}; + +static void p2m_early_write(struct console *con, const char *s, + unsigned int n) +{ + struct earlycon_device *dev = con->data; + + p2m_putstr(&dev->port, s, n); +} + +static int __init p2m_early_console_setup(struct earlycon_device *dev, + const char *opt) +{ + dev->con->write = p2m_early_write; + + return 0; +} +OF_EARLYCON_DECLARE(p2m_uart, "elvees,p2m", p2m_early_console_setup); + +static int p2m_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + + if (!np) + return -ENODEV; + + p2m_port.membase = of_iomap(np, 0); + if (!p2m_port.membase) + return -ENXIO; + + p2m_port.dev = &pdev->dev; + p2m_port.iotype = UPIO_MEM; + p2m_port.flags = UPF_BOOT_AUTOCONF; + p2m_port.line = 0; + p2m_port.ops = &p2m_serial_pops; + + return uart_add_one_port(&p2m_uart_driver, &p2m_port); +} + +static int p2m_remove(struct platform_device *pdev) +{ + /* This will never be called */ + return 0; +} + +static const struct of_device_id p2m_dt_ids[] = { + { .compatible = "elvees,p2m" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, p2m_dt_ids); + +static struct platform_driver p2m_platform_driver = { + .probe = p2m_probe, + .remove = p2m_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = p2m_dt_ids, + }, +}; + +static int __init p2m_init(void) +{ + int ret; + + ret = uart_register_driver(&p2m_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&p2m_platform_driver); + if (ret) + uart_unregister_driver(&p2m_uart_driver); + + return ret; +} + +static void __exit p2m_exit(void) +{ + platform_driver_unregister(&p2m_platform_driver); + uart_unregister_driver(&p2m_uart_driver); +} + +module_init(p2m_init); +module_exit(p2m_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_DESCRIPTION("ELVEES serial driver for printing messages into memory"); diff --git a/mm/Kconfig b/mm/Kconfig index 390165ffbb0fc2..d810bfb6b47480 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -807,7 +807,7 @@ config VMAP_PFN bool config FRAME_VECTOR - bool + bool "Frame vector support" config ARCH_USES_HIGH_VMA_FLAGS bool