diff --git a/Documentation/devicetree/bindings/edac/phytium-e2000-edac.txt b/Documentation/devicetree/bindings/edac/phytium-pe220x-edac.txt similarity index 77% rename from Documentation/devicetree/bindings/edac/phytium-e2000-edac.txt rename to Documentation/devicetree/bindings/edac/phytium-pe220x-edac.txt index 6f49a13d88bdeff30a1ec6481f511bea51e841ee..ddbf14b018968b2384bd8da552a0fa00333d3b8d 100644 --- a/Documentation/devicetree/bindings/edac/phytium-e2000-edac.txt +++ b/Documentation/devicetree/bindings/edac/phytium-pe220x-edac.txt @@ -1,4 +1,4 @@ -* Phytium E2000 SoC EDAC node +* Phytium Pe220x SoC EDAC node EDAC node is defined to describe on-chip error detection and correction. The follow error types are supported: @@ -8,13 +8,13 @@ The follow error types are supported: The following section describes the EDAC DT node binding. Required properties: -- compatible: Shall be "phytium,e2000-edac". -- reg: Shall be the E2000 RAS resource. +- compatible: Shall be "phytium,pe220x-edac". +- reg: Shall be the Pe220x RAS resource. - interrupts: Interrupt-specifier for RAS error IRQ(s). Example: edac: edac@32b28000 { - compatible = "phytium,e2000-edac"; + compatible = "phytium,pe220x-edac"; reg = <0x0 0x32b28000 0x0 0x1000>, <0x0 0x31400000 0x0 0x1000>, <0x0 0x31401000 0x0 0x1000>; diff --git a/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.txt b/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.txt new file mode 100644 index 0000000000000000000000000000000000000000..82180b38bb6b623d5b5c2eea486a29044ad8adb0 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.txt @@ -0,0 +1,28 @@ +Phytium INTx interrupt controller (IXIC) + +This is a pseudo interrupt controller to handle PCI legacy interrupt on +some Phytium SoCs, which sits between the PCI INTx devices and the GIC +and forwards the 4 INTx input signals to 4 adjacent GICv3 SPIs. + +Required properties: + +- compatible : "phytium,ixic" +- reg : Specifies two regions of the register set, which + are called 'ctr' and 'hpb'. +- interrupt-controller : Identifies the node as an interrupt controller. +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value must be 3. +- intx-spi-base : The SPI number of the first SPI of the 4 adjacent + ones the IXIC forwards its interrupts to. + +Example: + ixic: interrupt-controller@29000000 { + compatible = "phytium,ixic"; + reg-names = "ctr", "hpb"; + reg = <0x0 0x29000000 0x0 0x00060000>, + <0x0 0x29100000 0x0 0x00002000>; + interrupt-controller; + interrupt-parent = <&gic>; + #interrupt-cells = <3>; + intx-spi-base = <28>; + }; diff --git a/Documentation/devicetree/bindings/mmc/phytium,sdci.yaml b/Documentation/devicetree/bindings/mmc/phytium,sdci.yaml new file mode 100755 index 0000000000000000000000000000000000000000..1364583c90e8670ec9fd145b6f8527d20822d158 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/phytium,sdci.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/phytium,sdci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium SDCI Controller Binding + +maintainers: + - Chen Baozi + +allOf: + - $ref: mmc-controller.yaml# + +properties: + compatible: + enum: + - phytium,sdci + + reg: + maxItems: 1 + + interrupts: + minItems: 3 + maxItems: 3 + + clocks: + minItems: 1 + items: + - description: core clock + + clock-names: + minItems: 1 + items: + - const: phytium_sdc_clk + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + sdci: sdci@28207c00 { + compatible = "phytium,sdci"; + reg = <0x0 0x28207c00 0x0 0x100>; + interrupts = , + , + ; + clocks = <&sysclk_600mhz>; + clock-names = "phytium_sdc_clk"; + }; + +... diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt index 0b61a90f1592bfd6a31534fc41bfb955b477e5f7..955293ed2aafe13ea4da1dd9888d7ec3914bba17 100644 --- a/Documentation/devicetree/bindings/net/macb.txt +++ b/Documentation/devicetree/bindings/net/macb.txt @@ -16,6 +16,8 @@ Required properties: Use "cdns,zynq-gem" Xilinx Zynq-7xxx SoC. Use "cdns,zynqmp-gem" for Zynq Ultrascale+ MPSoC. Use "sifive,fu540-c000-gem" for SiFive FU540-C000 SoC. + Use "cdns,phytium-gem-1.0" for GEM version 1.0 on Phytium SoCs + Use "cdns,phytium-gem-2.0" for GEM version 2.0 on Phytium SoCs Or the generic form: "cdns,emac". - reg: Address and length of the register set for the device For "sifive,fu540-c000-gem", second range is required to specify the diff --git a/Documentation/devicetree/bindings/net/phytium,gmac.yaml b/Documentation/devicetree/bindings/net/phytium,gmac.yaml new file mode 100755 index 0000000000000000000000000000000000000000..486f10a80c278e7e5ad45a2c5c542cc6e6b6ed1b --- /dev/null +++ b/Documentation/devicetree/bindings/net/phytium,gmac.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/net/phytium,gmac.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Phytium 10/100/1000 Ethernet driver (GMAC) + +maintainers: + - Chen Baozi + +# We need a select here so we don't match all nodes with 'snps,dwmac' +select: + properties: + compatible: + contains: + enum: + - phytium,gmac + required: + - compatible + +allOf: + - $ref: "snps,dwmac.yaml#" + +properties: + compatible: + - items: + - enum: + - phytium,gmac + + clock-frequency: true + +required: + - compatible + - clock-frequency + +unevaluatedProperties: false + +examples: + - | + gmac0: eth@2820c000 { + compatible = "phytium,gmac"; + reg = <0x0 0x2820c000 0x0 0x2000>; + interrupts = ; + clock-frequency = <250000000>; + + snps,pbl = <16>; + snps,fixed-burst; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <128>; + tx-fifo-depth = <4096>; + rx-fifo-depth = <4096>; + max-frame-size = <9000>; + }; diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index 11a6fdb657c93a0154812908411973b770eff46c..1a3ceabd2eca69f0a716ae6b6ef1034320bc74fe 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -56,6 +56,7 @@ properties: - amlogic,meson8m2-dwmac - amlogic,meson-gxbb-dwmac - amlogic,meson-axg-dwmac + - phytium,gmac - snps,dwmac - snps,dwmac-3.50a - snps,dwmac-3.610 diff --git a/arch/arm64/boot/dts/phytium/pe220x.dtsi b/arch/arm64/boot/dts/phytium/pe220x.dtsi index 291a92d4241d241257b779b88d9ac63c6690d8df..098db19768e8fb5a27f4872b74e25afb86696a32 100644 --- a/arch/arm64/boot/dts/phytium/pe220x.dtsi +++ b/arch/arm64/boot/dts/phytium/pe220x.dtsi @@ -224,20 +224,20 @@ ddma1: ddma@28004000 { }; qspi0: spi@28008000 { - compatible = "phytium,qspi"; - reg = <0x0 0x28008000 0x0 0x1000>, - <0x0 0x0 0x0 0x0fffffff>; + compatible = "phytium,qspi-nor"; + reg = <0x0 0x28008000 0x0 0x1000>, + <0x0 0x0 0x0 0x0fffffff>; reg-names = "qspi", "qspi_mm"; - clocks = <&sysclk_50mhz>; + clocks = <&sysclk_300mhz>; + status = "disabled"; #address-cells = <1>; #size-cells = <0>; - status = "disabled"; - flash@0 { /* Note: By default, chip select 0 is for BIOS, so be careful when enabling this node!!! */ - reg = <0>; + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; spi-rx-bus-width = <1>; spi-max-frequency = <50000000>; - status = "disabled"; }; }; @@ -989,7 +989,7 @@ pcie: pcie@40000000 { }; edac: edac@32b28000 { - compatible = "phytium,e2000-edac"; + compatible = "phytium,pe220x-edac"; reg = <0x0 0x32b28000 0x0 0x1000>, <0x0 0x31400000 0x0 0x1000>, <0x0 0x31401000 0x0 0x1000>; diff --git a/arch/arm64/configs/phytium_defconfig b/arch/arm64/configs/phytium_defconfig index 94b0df21099329f43d0170adb9422061de676a96..9fffde46dd1961af48b4baf9cc3f9160bf3bd348 100644 --- a/arch/arm64/configs/phytium_defconfig +++ b/arch/arm64/configs/phytium_defconfig @@ -85,6 +85,8 @@ CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CMDLINE_PARTITION=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_KSM=y CONFIG_MEMORY_FAILURE=y @@ -146,20 +148,22 @@ CONFIG_CAN_FLEXCAN=m CONFIG_CAN_PHYTIUM=m CONFIG_CAN_PHYTIUM_PLATFORM=m CONFIG_CAN_PHYTIUM_PCI=m -CONFIG_BT=m +CONFIG_BT=y +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m CONFIG_BT_HIDP=m # CONFIG_BT_LE is not set CONFIG_BT_LEDS=y # CONFIG_BT_DEBUGFS is not set CONFIG_BT_HCIBTUSB=m -CONFIG_BT_HCIUART=m -CONFIG_BT_HCIUART_LL=y -CONFIG_BT_HCIUART_BCM=y -CONFIG_BT_HCIUART_QCA=y -CONFIG_CFG80211=m -CONFIG_MAC80211=m +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_3WIRE=y +CONFIG_CFG80211=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=y CONFIG_MAC80211_LEDS=y -CONFIG_RFKILL=m +CONFIG_RFKILL=y CONFIG_NET_9P=y CONFIG_NET_9P_VIRTIO=y CONFIG_NFC=m @@ -190,6 +194,7 @@ CONFIG_BRCMSTB_GISB_ARB=y CONFIG_SIMPLE_PM_BUS=y CONFIG_VEXPRESS_CONFIG=y CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_CFI_ADV_OPTIONS=y @@ -202,7 +207,6 @@ CONFIG_MTD_DATAFLASH=y CONFIG_MTD_SST25L=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_NAND_DENALI_DT=y -CONFIG_MTD_NAND_PHYTIUM_PCI=m CONFIG_MTD_NAND_PHYTIUM_PLAT=m CONFIG_MTD_SPI_NOR=y CONFIG_SPI_PHYTIUM_QUADSPI=y @@ -259,6 +263,7 @@ CONFIG_ATL1C=m CONFIG_BCMGENET=m CONFIG_BNX2X=m CONFIG_MACB=y +CONFIG_MACB_PCI=y CONFIG_THUNDER_NIC_PF=y # CONFIG_NET_VENDOR_HISILICON is not set # CONFIG_NET_VENDOR_HUAWEI is not set @@ -280,7 +285,7 @@ CONFIG_QCOM_EMAC=m CONFIG_RMNET=m CONFIG_SMC91X=y CONFIG_SMSC911X=y -CONFIG_STMMAC_ETH=m +CONFIG_STMMAC_ETH=y CONFIG_AQUANTIA_PHY=y CONFIG_BROADCOM_PHY=m CONFIG_MARVELL_PHY=m @@ -295,24 +300,38 @@ CONFIG_MOTORCOMM_PHY=y CONFIG_MDIO_BITBANG=y CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_PPP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y CONFIG_USB_PEGASUS=m CONFIG_USB_RTL8150=m CONFIG_USB_RTL8152=m CONFIG_USB_LAN78XX=m -CONFIG_USB_USBNET=m +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_NCM=m CONFIG_USB_NET_DM9601=m CONFIG_USB_NET_SR9800=m CONFIG_USB_NET_SMSC75XX=m CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_NET1080=m CONFIG_USB_NET_PLUSB=m CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_QMI_WWAN=y CONFIG_ATH10K=m CONFIG_ATH10K_PCI=m CONFIG_BRCMFMAC=m CONFIG_MWIFIEX=m CONFIG_MWIFIEX_PCIE=m +CONFIG_RTL_CARDS=m CONFIG_WL18XX=m CONFIG_WLCORE_SDIO=m +CONFIG_RTL8821CS=m CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_ADC=m CONFIG_KEYBOARD_GPIO=y @@ -352,15 +371,18 @@ CONFIG_TCG_TIS_I2C_INFINEON=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MUX=y CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_I2C_GPIO=m CONFIG_I2C_PHYTIUM_PCI=y CONFIG_I2C_PHYTIUM_PLATFORM=y -CONFIG_I3C=m +CONFIG_I3C=y +CONFIG_CDNS_I3C_MASTER=y CONFIG_SPI=y CONFIG_SPI_BITBANG=m CONFIG_SPI_PHYTIUM_PLAT=y CONFIG_SPI_PHYTIUM_PCI=y -CONFIG_SPI_SPIDEV=m +CONFIG_SPI_PHYTIUM_QSPI=y +CONFIG_SPI_SPIDEV=y CONFIG_SPMI=y CONFIG_PINCTRL=y CONFIG_PINCTRL_SINGLE=y @@ -404,6 +426,8 @@ CONFIG_MFD_BD9571MWV=y CONFIG_MFD_AXP20X_I2C=y CONFIG_MFD_HI6421_PMIC=y CONFIG_MFD_MAX77620=y +CONFIG_MFD_PHYTIUM_I2S_LSD=y +CONFIG_MFD_PHYTIUM_I2S_MMD=y CONFIG_MFD_RK808=y CONFIG_MFD_SEC_CORE=y CONFIG_MFD_ROHM_BD718XX=y @@ -479,13 +503,12 @@ CONFIG_LOGO=y # CONFIG_LOGO_LINUX_VGA16 is not set CONFIG_SOUND=y CONFIG_SND=y -CONFIG_SND_HDA_INTEL=m -CONFIG_SND_HDA_PHYTIUM=m +CONFIG_SND_HDA_PHYTIUM=y CONFIG_SND_HDA_HWDEP=y CONFIG_SND_HDA_INPUT_BEEP=y CONFIG_SND_HDA_PATCH_LOADER=y -CONFIG_SND_HDA_CODEC_REALTEK=m -CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_HDMI=y CONFIG_SND_SOC=y CONFIG_SND_SOC_FSL_SAI=m CONFIG_SND_SOC_PHYTIUM_I2S=y @@ -530,7 +553,7 @@ CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_HCD_PLATFORM=y CONFIG_USB_OHCI_HCD=y CONFIG_USB_OHCI_HCD_PLATFORM=y -CONFIG_USB_ACM=m +CONFIG_USB_ACM=y CONFIG_USB_STORAGE=y CONFIG_USB_MUSB_HDRC=y CONFIG_USB_DWC3=y @@ -540,8 +563,10 @@ CONFIG_USB_CHIPIDEA_UDC=y CONFIG_USB_CHIPIDEA_HOST=y CONFIG_USB_ISP1760=y CONFIG_USB_PHYTIUM=y -CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CH341=y CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_OPTION=y CONFIG_USB_HSIC_USB3503=y CONFIG_NOP_USB_XCEIV=y CONFIG_USB_ULPI=y @@ -579,7 +604,7 @@ CONFIG_MMC_DW_HI3798CV200=y CONFIG_MMC_DW_K3=y CONFIG_MMC_SDHCI_XENON=y CONFIG_MMC_SDHCI_AM654=y -CONFIG_LEDS_CLASS=y +# CONFIG_MMC_PHYTIUM_MCI_PCI is not set CONFIG_LEDS_GPIO=y CONFIG_LEDS_PWM=y CONFIG_LEDS_SYSCON=y @@ -676,6 +701,7 @@ CONFIG_MPL3115=m CONFIG_PWM=y CONFIG_PWM_CROS_EC=m CONFIG_PWM_PHYTIUM=m +CONFIG_PHYTIUM_IXIC=y CONFIG_PHY_XGENE=y CONFIG_PHY_FSL_IMX8MQ_USB=y CONFIG_PHY_MIXEL_MIPI_DPHY=m @@ -745,7 +771,6 @@ CONFIG_CRYPTO_DEV_HISI_ZIP=m CONFIG_CRYPTO_DEV_HISI_HPRE=m CONFIG_CRYPTO_DEV_AMLOGIC_GXL=m CONFIG_INDIRECT_PIO=y -CONFIG_CRC_CCITT=m CONFIG_DMA_CMA=y CONFIG_DMA_PERNUMA_CMA=y CONFIG_CMA_SIZE_MBYTES=32 diff --git a/drivers/gpio/gpio-phytium-pci.c b/drivers/gpio/gpio-phytium-pci.c index d79ecc3997e1ff7770bb5affc912e9e538a18019..8bac22bd39b3efd5a4326ad95a48fbf114ccbea0 100644 --- a/drivers/gpio/gpio-phytium-pci.c +++ b/drivers/gpio/gpio-phytium-pci.c @@ -47,6 +47,8 @@ static int phytium_gpio_pci_probe(struct pci_dev *pdev, const struct pci_device_ if (err < 0) goto out; + pci_set_master(pdev); + gpio->irq[0] = pdev->irq; if (gpio->irq < 0) dev_warn(dev, "no irq is found.\n"); diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 3c24bf45263ce80b99bbc3193296e8b22b5c9d6a..45d9ac4dbbc9637fcda9944264d5209fc255feca 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -599,4 +599,12 @@ config MST_IRQ help Support MStar Interrupt Controller. +config PHYTIUM_IXIC + bool "Phytium SoC PCI Legacy Interrupt Controller" + depends on ARCH_PHYTIUM + select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY + help + This enables support PCI Legacy Interrupt on Phytium SoC. + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 94c2885882ee195cf37099aa6cab8105a7414949..337859dd39dec34a30bec8b7d891e002349374c0 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -114,3 +114,4 @@ obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-loongson-pch-pic.o obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o +obj-$(CONFIG_PHYTIUM_IXIC) += irq-phytium-ixic.o diff --git a/drivers/irqchip/irq-phytium-ixic.c b/drivers/irqchip/irq-phytium-ixic.c new file mode 100755 index 0000000000000000000000000000000000000000..7862df80fb46cf14f388438f7fb29fb75bdf1461 --- /dev/null +++ b/drivers/irqchip/irq-phytium-ixic.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Phytium PCIe legacy INTx interrupt controller + * + * Copyright (c) 2020-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NUM_IRQS 4 + +#define CTR_BANK_NUM 6 +#define CTR_BANK_SIZE 0x10000 +#define CTR_BANK_ISTATUS_LOCAL 0x184 + +#define HPB_INTX_STATUS_0 0x0 +#define HPB_INTX_STATUS_1 0x1000 + +struct ixic_irq_data { + void __iomem *ctr; + void __iomem *hpb; + u32 spi_base; +}; + +static void phytium_ixic_irq_eoi(struct irq_data *d) +{ + struct ixic_irq_data *data = irq_data_get_irq_chip_data(d); + unsigned int intx = irqd_to_hwirq(d); + u32 gstatus = readl(data->hpb) | (readl(data->hpb + HPB_INTX_STATUS_1) << 12); + u32 imask, istatus; + int i; + + WARN_ON(intx >= NUM_IRQS); + imask = 1 << (3 - intx); + istatus = (1 << intx) << 24; + for (i = 0; i < CTR_BANK_NUM; i++, gstatus >>= 4) { + if (gstatus & imask) + writel(istatus, data->ctr + CTR_BANK_SIZE*i + CTR_BANK_ISTATUS_LOCAL); + } + + irq_chip_eoi_parent(d); +} + +static struct irq_chip phytium_ixic_irq_chip = { + .name = "IXIU", + .irq_eoi = phytium_ixic_irq_eoi, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND, +}; + +static int phytium_ixic_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct ixic_irq_data *info = domain->host_data; + + if (is_of_node(fwspec->fwnode)) { + if (fwspec->param_count != 3) + return -EINVAL; + + if (fwspec->param[0] != GIC_SPI) + return -EINVAL; /* No PPI should point to this domain */ + + *hwirq = fwspec->param[1] - info->spi_base; + *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; + } else { + if (fwspec->param_count != 2) + return -EINVAL; + *hwirq = fwspec->param[0] - info->spi_base; + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + } + + return 0; +} + +static int phytium_ixic_alloc(struct irq_domain *dom, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + struct ixic_irq_data *info = dom->host_data; + irq_hw_number_t hwirq; + + /* We assume the device use the parent's format directly */ + parent_fwspec = *fwspec; + if (is_of_node(dom->parent->fwnode)) { + if (fwspec->param_count != 3) + return -EINVAL; /* Not GIC compliant */ + if (fwspec->param[0] != GIC_SPI) + return -EINVAL; /* No PPI should point to this domain */ + + /* Get the local hwirq of IXIC */ + hwirq = fwspec->param[1] - info->spi_base; + } else { + hwirq = fwspec->param[0] - info->spi_base; + } + WARN_ON(nr_irqs != 1); + irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &phytium_ixic_irq_chip, info); + + parent_fwspec.fwnode = dom->parent->fwnode; + return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec); +} + +static const struct irq_domain_ops ixic_domain_ops = { + .translate = phytium_ixic_translate, + .alloc = phytium_ixic_alloc, + .free = irq_domain_free_irqs_common, +}; + +static struct ixic_irq_data *phytium_ixic_init(const struct fwnode_handle *fwnode, + struct resource *ctr, struct resource *hpb) +{ + struct ixic_irq_data *data; + int err; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + if (fwnode_property_read_u32_array(fwnode, "intx-spi-base", + &data->spi_base, 1)) { + err = -ENODEV; + goto out_free; + } + + data->ctr = ioremap(ctr->start, resource_size(ctr)); + if (!data->ctr) { + err = -ENODEV; + goto out_free; + } + + data->hpb = ioremap(hpb->start, resource_size(hpb)); + if (!data->hpb) { + err = -ENODEV; + goto out_free; + } + + return data; + +out_free: + kfree(data); + return ERR_PTR(err); +} + +static int __init phytium_ixic_dt_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *pd, *d; + struct ixic_irq_data *data; + struct resource ctr, hpb; + + if (!parent) { + pr_err("%pOF: no parent, giving up\n", node); + return -ENODEV; + } + + pd = irq_find_host(parent); + if (!pd) { + pr_err("%pOF: unable to obtain parent domain\n", node); + return -ENXIO; + } + + if (of_address_to_resource(node, 0, &ctr)) { + pr_err("%pOF: failed to parse 'ctr' memory resource\n", node); + return -ENXIO; + } + + if (of_address_to_resource(node, 1, &hpb)) { + pr_err("%pOF: failed to parse 'hpb' memory resource\n", node); + return -ENXIO; + } + + data = phytium_ixic_init(of_node_to_fwnode(node), &ctr, &hpb); + if (IS_ERR(data)) + return PTR_ERR(data); + + d = irq_domain_add_hierarchy(pd, 0, NUM_IRQS, node, &ixic_domain_ops, data); + if (!d) { + pr_err("%pOF: failed to allocate domain\n", node); + goto out_unmap; + } + + pr_info("%pOF: %d interrupts forwarded to %pOF\n", node, NUM_IRQS, parent); + + return 0; + +out_unmap: + iounmap(data->ctr); + iounmap(data->hpb); + kfree(data); + return -ENOMEM; +} +IRQCHIP_DECLARE(ixic, "phytium,ixic", phytium_ixic_dt_init); + +#ifdef CONFIG_ACPI +static int phytium_ixic_acpi_probe(struct platform_device *pdev) +{ + struct irq_domain *domain; + struct ixic_irq_data *data; + struct resource *ctr, *hpb; + + ctr = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ctr) { + dev_err(&pdev->dev, "failed to parse 'ctr' memory resource\n"); + return -ENXIO; + } + + hpb = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!hpb) { + dev_err(&pdev->dev, "failed to parse 'hpb' memory resource\n"); + return -ENXIO; + } + + data = phytium_ixic_init(dev_fwnode(&pdev->dev), ctr, hpb); + if (IS_ERR(data)) + return PTR_ERR(data); + + domain = acpi_irq_create_hierarchy(0, NUM_IRQS, dev_fwnode(&pdev->dev), + &ixic_domain_ops, data); + if (!domain) { + dev_err(&pdev->dev, "failed to create IRQ domain\n"); + goto out_unmap; + } + + dev_info(&pdev->dev, "%d interrupts forwarded\n", NUM_IRQS); + + return 0; + +out_unmap: + iounmap(data->ctr); + iounmap(data->hpb); + kfree(data); + return -ENOMEM; +} + +static const struct acpi_device_id phytium_ixic_acpi_ids[] = { + { "PHYT0013" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, phytium_ixic_acpi_ids); + +static struct platform_driver phytium_ixic_driver = { + .driver = { + .name = "phytium-ixic", + .acpi_match_table = phytium_ixic_acpi_ids, + }, + .probe = phytium_ixic_acpi_probe, +}; +builtin_platform_driver(phytium_ixic_driver); +#endif diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index e311e7488d86b9589e4e0903acd6255fa2eaf6b3..11eb84df78a5027db2eed39900fa149e8f24561e 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -1103,6 +1103,18 @@ config MMC_OWL config MMC_SDHCI_EXTERNAL_DMA bool +config MMC_PHYTIUM_SDCI + tristate "Phytium SD Host Controller support" + depends on ARCH_PHYTIUM || COMPILE_TEST + default y if ARCH_PHYTIUM + help + This selects support for the SD/MMC Host Controller on + Phytium SoC family. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_PHYTIUM_MCI_PCI tristate "Phytium octopus PCI MultiMedia Card Interface support" depends on ARCH_PHYTIUM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 2669da038ed13be63388617156c2a15ce391d072..4a46cf083070bc566b2525a294adb48304ff4477 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o obj-$(CONFIG_MMC_TOSHIBA_PCI) += toshsd.o obj-$(CONFIG_MMC_BCM2835) += bcm2835.o obj-$(CONFIG_MMC_OWL) += owl-mmc.o +obj-$(CONFIG_MMC_PHYTIUM_SDCI) += phytium-sdci.o obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o diff --git a/drivers/mmc/host/phytium-mci.c b/drivers/mmc/host/phytium-mci.c index 8cb51875b42b642c6e6a6c1c2105055503ba596f..22ec14694c7999ce452cbd2e0f2ecc5d5745c9fe 100644 --- a/drivers/mmc/host/phytium-mci.c +++ b/drivers/mmc/host/phytium-mci.c @@ -134,7 +134,7 @@ static void phytium_mci_send_cmd(struct phytium_mci_host *host, u32 cmd, u32 arg rc = readl_relaxed_poll_timeout(host->base + MCI_STATUS, data, !(data & MCI_STATUS_CARD_BUSY), - 0, 5000 * 1000); + 0, 100 * 1000); if (rc == -ETIMEDOUT) pr_debug("%s %d, timeout mci_status: 0x%08x\n", __func__, __LINE__, data); @@ -143,7 +143,7 @@ static void phytium_mci_send_cmd(struct phytium_mci_host *host, u32 cmd, u32 arg rc = readl_relaxed_poll_timeout(host->base + MCI_CMD, data, !(data & MCI_CMD_START), - 0, 5000 * 1000); + 0, 100 * 1000); if (rc == -ETIMEDOUT) pr_debug("%s %d, timeout mci_cmd: 0x%08x\n", __func__, __LINE__, data); } @@ -849,7 +849,7 @@ static void phytium_mci_ops_request(struct mmc_host *mmc, struct mmc_request *mr rc = readl_relaxed_poll_timeout(host->base + MCI_STATUS, data, !(data & MCI_STATUS_CARD_BUSY), - 0, 2000 * 1000); + 0, 500 * 1000); if (rc == -ETIMEDOUT) pr_debug("%s %d, timeout mci_status: 0x%08x\n", __func__, __LINE__, data); diff --git a/drivers/mmc/host/phytium-sdci.c b/drivers/mmc/host/phytium-sdci.c new file mode 100755 index 0000000000000000000000000000000000000000..929162b8d5bf8ad31e6817a73bd73f9c79dc08f2 --- /dev/null +++ b/drivers/mmc/host/phytium-sdci.c @@ -0,0 +1,1433 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SD Card Interface dirver + * + * Copyright (c) 2019-2023, Phytium Technology Co.,Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "phytium-sdci.h" + +static const u32 cmd_ints_mask = SDCI_SDCI_NORMAL_ISER_ECC_EN | SDCI_SDCI_NORMAL_ISER_EEI_EN; +static const u32 data_ints_mask = SDCI_BD_ISER_ETRS_EN; +static const u32 err_ints_mask = SDCI_ERROR_ISER_ECTE_EN | SDCI_ERROR_ISR_CCRCE_EN | + SDCI_ERROR_ISR_CIR_EN | SDCI_ERROR_ISR_CNR_EN; + +static void hotplug_timer_func(struct timer_list *t); +static bool phytium_sdci_private_send_cmd(struct phytium_sdci_host *host, + u32 cmd, u32 resp_type, u32 arg); +static bool phytium_sdci_cmd_done(struct phytium_sdci_host *host, int events, + struct mmc_request *mrq, + struct mmc_command *cmd); +static bool phytium_sdci_data_xfer_done(struct phytium_sdci_host *host, + u32 events, struct mmc_request *mrq, + struct mmc_data *data); +static void phytium_sdci_cmd_next(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd); + +static int phytium_sdci_cmd13_process(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_data *data, + u32 wait_timeout_ms, + u32 send_once_time_ms); + +static int phytium_sd_error(struct phytium_sdci_host *host) +{ + int temp; + + temp = readl(host->base + SDCI_NORMAL_ISR); + dev_err(host->dev, "[%s %d]SDCI_NORMAL_ISR:%x\n", __func__, __LINE__, temp); + temp = readl(host->base + SDCI_BD_ISR); + temp = readl(host->base + SDCI_ERROR_ISR); + dev_err(host->dev, "[%s %d]SDCI_ERROR_ISR:%x\n", __func__, __LINE__, temp); + temp = readl(host->base + SDCI_BD_ISR); + dev_err(host->dev, "[%s %d]SDCI_BD_ISR:%x\n", __func__, __LINE__, temp); + temp = readl(host->base + SDCI_RESP0); + dev_err(host->dev, "[%s %d]SDCI_RESP0:%x\n", __func__, __LINE__, temp); + + return 0; +} + +static void sdr_set_bits(void __iomem *reg, u32 bs) +{ + u32 val; + + val = readl(reg); + val |= bs; + + writel(val, reg); +} + +static void sdr_clr_bits(void __iomem *reg, u32 bs) +{ + u32 val; + + val = readl(reg); + val &= ~bs; + + writel(val, reg); +} + +static void phytium_sdci_reset_hw(struct phytium_sdci_host *host) +{ + sdr_set_bits(host->base + SDCI_SOFTWARE, + SDCI_SOFTWARE_SRST); + sdr_clr_bits(host->base + SDCI_SOFTWARE, + SDCI_SOFTWARE_SRST); + while (!(readl(host->base + SDCI_STATUS) & SDCI_STATUS_IDIE)) + cpu_relax(); +} + +static void phytium_sdci_prepare_data(struct phytium_sdci_host *host, + struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + bool read; + + read = (data->flags & MMC_DATA_READ) != 0; + data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len, + read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); +} + +static void phytium_sdci_unprepare_data(struct phytium_sdci_host *host, + struct mmc_request *mrq) +{ + bool read; + struct mmc_data *data = mrq->data; + + read = (data->flags & MMC_DATA_READ) != 0; + dma_unmap_sg(host->dev, data->sg, data->sg_len, + read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); +} + +static void phytium_sdci_set_clk(struct phytium_sdci_host *host, + struct mmc_ios *ios) +{ + unsigned long clk_rate; + u32 div = 0xffffffff, div_reg; + + if (ios->clock) { + clk_rate = host->clk_rate; + div = ((clk_rate / (2 * ios->clock)) - 1); + div_reg = readl(host->base + SDCI_CLOCK_D); + if (div_reg == div) + return; + writel(div, host->base + SDCI_CLOCK_D); + writel(0, host->base + SDCI_SD_DRV); + writel(5, host->base + SDCI_SD_SAMP); + + sdr_set_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_SRST); + sdr_clr_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_SRST); + while (!(readl(host->base + SDCI_STATUS) & SDCI_STATUS_IDIE)) + cpu_relax(); + dev_dbg(host->dev, "host->clk_rate: %ld, ios->clock: %d\n", + host->clk_rate, ios->clock); + } +} + + +static inline u32 phytium_sdci_cmd_find_resp(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 resp; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_R1: + resp = 0x2; + break; + case MMC_RSP_R1B: + resp = 0x2; + break; + case MMC_RSP_R2: + resp = 0x1; + break; + case MMC_RSP_R3: + resp = 0x3; + break; + case MMC_RSP_NONE: + default: + resp = 0x0; + break; + } + + return resp; +} + +static inline u32 phytium_sdci_cmd_prepare_raw_cmd(struct phytium_sdci_host *host, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + /* + * rawcmd : + * trty << 14 | opcode << 8 | cmdw << 6 | cice << 4 | crce << 3 | resp + */ + u32 resp, rawcmd; + u32 opcode = cmd->opcode; + + resp = phytium_sdci_cmd_find_resp(host, mrq, cmd); + rawcmd = ((opcode << 8) | resp); + + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) + rawcmd = (rawcmd | (SDCI_CMD_TYPE_ADTC << 14)); + + return rawcmd; +} + +static void +phytium_sdci_unexpected_error_handler(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_data *data, + int err_type) +{ + unsigned long flags; + int error; + + spin_lock_irqsave(&host->lock, flags); + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + if (err_type & ERR_CARD_ABSENT) { + host->mmc->detect_change = 1; + dev_dbg(host->dev, "SD is absent when send cmd:%d\n", mrq->cmd->opcode); + } + + switch (err_type) { + case ERR_CARD_ABSENT: + error = -ENOMEDIUM; + break; + case ERR_TIMEOUT: + error = -ETIMEDOUT; + break; + case ERR_CMD_RESPONED: + error = -EIO; + break; + default: + error = -ETIMEDOUT; + break; + } + + if (data) { + data->error = error; + phytium_sdci_unprepare_data(host, mrq); + + if ((data->flags & MMC_DATA_READ) == MMC_DATA_READ || + (data->flags & MMC_DATA_WRITE) == MMC_DATA_WRITE) + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_TRS_R, mrq, data); + } else { + mrq->cmd->error = error; + } + + mmc_request_done(host->mmc, mrq); +} + +static bool phytium_sdci_start_data(struct phytium_sdci_host *host, struct mmc_request *mrq, + struct mmc_command *cmd, struct mmc_data *data) +{ + bool read, res; + u32 sg_dma_addrh, sg_dma_addrl; + u32 sd_block_addrh, sd_block_addrl; + u32 temp, timeout, sd_status; + u32 block_cnt = 0; + u32 sd_block_addr = cmd->arg; + u32 private_cmd, resp_type, arg; + u32 j, dma_len; + unsigned long deadline_time; + dma_addr_t dma_address; + struct scatterlist *sg; + int ret; + + WARN_ON(host->cmd); + host->cmd = cmd; + + WARN_ON(host->data); + host->data = data; + read = data->flags & MMC_DATA_READ; + + for_each_sg(data->sg, sg, data->sg_count, j) { + writel(0, host->base + SDCI_COMMAND); + + dma_address = sg_dma_address(sg); + sg_dma_addrh = (u32) (dma_address >> 32); + sg_dma_addrl = (u32) dma_address; + + dma_len = sg_dma_len(sg); + block_cnt = (dma_len / SD_BLOCK_SIZE); + + sd_block_addrh = 0; + sd_block_addrl = sd_block_addr; + + sdr_set_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_BDRST); + sdr_clr_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_BDRST); + writel(block_cnt, host->base + SDCI_BLK_CNT); + + if ((mrq->data->flags & MMC_DATA_READ) == MMC_DATA_READ) { + writel(sg_dma_addrl, host->base + SDCI_BD_RX); + writel(sg_dma_addrh, host->base + SDCI_BD_RX); + writel(sd_block_addrl, host->base + SDCI_BD_RX); + writel(sd_block_addrh, host->base + SDCI_BD_RX); + timeout = 100 * block_cnt; + } else { + timeout = 250 * block_cnt; + ret = phytium_sdci_cmd13_process(host, mrq, data, timeout, 1); + if (ret != SDCI_CMD13_OK) + return false; + + writel(sg_dma_addrl, host->base + SDCI_BD_TX); + writel(sg_dma_addrh, host->base + SDCI_BD_TX); + writel(sd_block_addrl, host->base + SDCI_BD_TX); + writel(sd_block_addrh, host->base + SDCI_BD_TX); + } + + deadline_time = jiffies + msecs_to_jiffies(timeout); + + temp = readl(host->base + SDCI_BD_ISR); + if ((mrq->data->flags & MMC_DATA_READ) == MMC_DATA_READ) { + while ((temp & SDCI_BD_ISR_TRS_R) != SDCI_BD_ISR_TRS_R) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CARD_ABSENT); + if (temp & SDCI_BD_ISR_DAIS) + writel(1, host->base + SDCI_BD_ISR); + return false; + } + + temp = readl(host->base + SDCI_BD_ISR); + if (time_after(jiffies, deadline_time)) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_TIMEOUT); + dev_err(host->dev, + "Read Data timeout:jiffies:0x%lx,dt_jiffies: 0x%lx, BD_isr_reg:0x%x,cmd:%d, REG_D0:0x%x\n", + jiffies, jiffies - deadline_time, temp, + cmd->opcode, readl(host->base + SDCI_STATUS)); + + return false; + } + } + } else { + while ((temp & SDCI_BD_ISR_TRS_W) != SDCI_BD_ISR_TRS_W) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CARD_ABSENT); + dev_err(host->dev, "[%s][%d]: Card absent ! cmd(%d)\n", + __func__, __LINE__, mrq->cmd->opcode); + return false; + } + + temp = readl(host->base + SDCI_BD_ISR); + if (time_after(jiffies, deadline_time)) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_TIMEOUT); + dev_err(host->dev, + "Write Date timeout: jiffies:0x%lx,dt_jiffies: 0x%lx,BD_isr_reg:0x%x\n", + jiffies, jiffies - deadline_time, temp); + return false; + } + } + } + writel(1, host->base + SDCI_BD_ISR); + writel(1, host->base + SDCI_NORMAL_ISR); + sd_block_addr = sd_block_addr + block_cnt; + + if (j < (data->sg_count - 1) && 1 < block_cnt) { + private_cmd = MMC_STOP_TRANSMISSION; + resp_type = 0x2; + arg = 0; + res = phytium_sdci_private_send_cmd(host, private_cmd, + resp_type, arg); + if (!res) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CARD_ABSENT); + writel(1, host->base + SDCI_BD_ISR); + dev_err(host->dev, + "[%s][%d]:Card absent ! private_cmd(%d)\n", + __func__, __LINE__, private_cmd); + } else { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CMD_RESPONED); + dev_err(host->dev, + "[%s][%d] cmd(%d) response errored\n", + __func__, __LINE__, mrq->cmd->opcode); + phytium_sd_error(host); + } + writel(1, host->base + SDCI_NORMAL_ISR); + return false; + } + writel(1, host->base + SDCI_NORMAL_ISR); + } + } + + host->is_multi_rw_only_one_blkcnt = false; + + if ((cmd->opcode == MMC_READ_MULTIPLE_BLOCK && block_cnt == 1) || + (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK && block_cnt == 1)) + host->is_multi_rw_only_one_blkcnt = true; + + phytium_sdci_cmd_done(host, SDCI_NORMAL_ISR_CC, mrq, cmd); + if ((mrq->data->flags & MMC_DATA_READ) == MMC_DATA_READ) + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_TRS_R, + mrq, data); + else + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_TRS_W, + mrq, data); + + return true; +} + +static int phytium_sdci_auto_cmd_done(struct phytium_sdci_host *host, + int events, struct mmc_command *cmd) +{ + u32 *rsp = cmd->resp; + + rsp[0] = readl(host->base + SDCI_RESP0); + + if (events & SDCI_NORMAL_ISR_CC) + cmd->error = 0; + else { + phytium_sdci_reset_hw(host); + dev_err(host->dev, + "%s: AUTO_CMD%d arg=%08X; rsp %08X; cmd_error=%d\n", + __func__, cmd->opcode, cmd->arg, rsp[0], cmd->error); + } + + return cmd->error; +} + +static void phytium_sdci_track_cmd_data(struct phytium_sdci_host *host, + struct mmc_command *cmd, + struct mmc_data *data) +{ + if (host->error) + dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n", + __func__, cmd->opcode, cmd->arg, host->error); +} + +static void phytium_sdci_request_done(struct phytium_sdci_host *host, + struct mmc_request *mrq) +{ + unsigned long flags; + + dev_dbg(host->dev, + "%s_%d:mrq->cmd->opcode:%d, mrq->cmd->arg:0x%x resp 0x%x 0x%x 0x%x 0x%x\n", + __func__, __LINE__, mrq->cmd->opcode, mrq->cmd->arg, + mrq->cmd->resp[0], mrq->cmd->resp[1], mrq->cmd->resp[2], + mrq->cmd->resp[3]); + + spin_lock_irqsave(&host->lock, flags); + host->mrq = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + phytium_sdci_track_cmd_data(host, mrq->cmd, mrq->data); + if (mrq->data) + phytium_sdci_unprepare_data(host, mrq); + mmc_request_done(host->mmc, mrq); +} + +static bool +phytium_sdci_auto_command_done(struct phytium_sdci_host *host, int events, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + u32 *rsp = cmd->resp; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->cmd = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + sdr_clr_bits(host->base + SDCI_NORMAL_ISER, cmd_ints_mask); + + rsp[0] = 0x900; + phytium_sdci_request_done(host, mrq); + return true; +} + +/* returns true if command is fully handled; returns false otherwise */ +static bool phytium_sdci_cmd_done(struct phytium_sdci_host *host, int events, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + bool done = false; + bool sbc_error; + unsigned long flags; + u32 *rsp = cmd->resp; + + if (mrq->sbc && cmd == mrq->cmd && + (events & SDCI_NORMAL_ISR_CC)) + phytium_sdci_auto_cmd_done(host, events, mrq->sbc); + + sbc_error = mrq->sbc && mrq->sbc->error; + + if (!sbc_error && !(events & (SDCI_NORMAL_ISR_CC | + SDCI_NORMAL_ISR_CR | + SDCI_NORMAL_ISR_TIMEOUT))) + return done; + + spin_lock_irqsave(&host->lock, flags); + done = !host->cmd; + host->cmd = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + if (done) + return true; + + sdr_clr_bits(host->base + SDCI_NORMAL_ISER, cmd_ints_mask); + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + rsp[0] = readl(host->base + SDCI_RESP0); + rsp[1] = readl(host->base + SDCI_RESP1); + rsp[2] = readl(host->base + SDCI_RESP2); + rsp[3] = readl(host->base + SDCI_RESP3); + } else + rsp[0] = readl(host->base + SDCI_RESP0); + + if (cmd->opcode == SD_SEND_RELATIVE_ADDR) + host->current_rca = rsp[0] & 0xFFFF0000; + } + + if (!sbc_error && + !(events & SDCI_NORMAL_ISR_CC) && + (events & SDCI_NORMAL_ISR_TIMEOUT)) + cmd->error = -ETIMEDOUT; + + if (cmd->error) + dev_dbg(host->dev, + "%s: cmd=%d arg=%08X; rsp %08X; cmd_error=%d\n", + __func__, cmd->opcode, cmd->arg, rsp[0], + cmd->error); + + phytium_sdci_cmd_next(host, mrq, cmd); + + return true; +} + +static bool set_databus_width(struct phytium_sdci_host *host) +{ + bool res; + u32 cmd, resp_type, arg; + + cmd = SD_APP_SET_BUS_WIDTH; + resp_type = 0x2; + arg = 0x2; + res = phytium_sdci_private_send_cmd(host, cmd, resp_type, arg); + if (!res) + return false; + + cmd = MMC_APP_CMD; + resp_type = 0x2; + arg = host->current_rca; + res = phytium_sdci_private_send_cmd(host, cmd, resp_type, arg); + if (!res) + return false; + + return true; +} + + +static void phytium_sdci_start_command(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 rawcmd; + struct mmc_data *data = mrq->data; + dma_addr_t dma_adtc_buf; + u32 dma_bufh, dma_bufl, block_cnt = 0; + + WARN_ON(host->cmd); + host->cmd = cmd; + + cmd->error = 0; + rawcmd = phytium_sdci_cmd_prepare_raw_cmd(host, mrq, cmd); + if (cmd->opcode == MMC_STOP_TRANSMISSION || + cmd->opcode == MMC_SEND_STATUS) + writel(1, host->base + SDCI_ERROR_ISR); + sdr_set_bits(host->base + SDCI_NORMAL_ISER, cmd_ints_mask); + writel(rawcmd, host->base + SDCI_COMMAND); + + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) { + WARN_ON(host->data); + host->data = data; + + dma_adtc_buf = host->dma_rx.bd_addr; + dma_bufh = (u32) (dma_adtc_buf >> 32); + dma_bufl = (u32) dma_adtc_buf; + block_cnt = mrq->data->blocks; + sdr_set_bits(host->base + SDCI_BD_ISER, data_ints_mask); + writel(block_cnt, host->base + SDCI_BLK_CNT); + + if ((mrq->data->flags & MMC_DATA_READ) == MMC_DATA_READ) { + writel(dma_bufl, host->base + SDCI_BD_RX); + writel(dma_bufh, host->base + SDCI_BD_RX); + writel(cmd->arg, host->base + SDCI_BD_RX); + writel(0, host->base + SDCI_BD_RX); + } else { + writel(dma_bufl, host->base + SDCI_BD_TX); + writel(dma_bufh, host->base + SDCI_BD_TX); + writel(cmd->arg, host->base + SDCI_BD_TX); + writel(0, host->base + SDCI_BD_TX); + } + } else { + writel(cmd->arg, host->base + SDCI_ARGUMENT); + } +} + +static void phytium_sdci_cmd_next(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + if (cmd->error || (mrq->sbc && mrq->sbc->error)) + phytium_sdci_request_done(host, mrq); + else if (cmd == mrq->sbc) + phytium_sdci_start_command(host, mrq, mrq->cmd); + else if (!cmd->data) + phytium_sdci_request_done(host, mrq); +} + +static int phytium_sdci_cmd13_process(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_data *data, + u32 wait_timeout_ms, + u32 send_once_time_ms) +{ + u32 private_cmd, resp_type, arg, temp, sd_status; + unsigned long deadline_time; + bool res; + + deadline_time = jiffies + msecs_to_jiffies(wait_timeout_ms); + + do { + private_cmd = MMC_SEND_STATUS; + resp_type = 0x2; + arg = host->current_rca; + + res = phytium_sdci_private_send_cmd(host, private_cmd, resp_type, arg); + if (!res) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CARD_ABSENT); + dev_err(host->dev, + "[%s][%d] Card absent! private_cmd(%d)\n", + __func__, __LINE__, private_cmd); + } else { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CMD_RESPONED); + + dev_err(host->dev, + "[%s][%d] private_cmd(%d) response errored\n", + __func__, __LINE__, private_cmd); + phytium_sd_error(host); + } + writel(1, host->base + SDCI_BD_ISR); + return SDCI_CMD13_FAILED; + } + + temp = readl(host->base + SDCI_RESP0); + + if (time_after(jiffies, deadline_time)) { + + if (mrq->cmd->opcode == MMC_SEND_STATUS) + return SDCI_CMD13_OK; + + dev_err(host->dev, + "SD card is not in transfer mode,timeout:%d,rsp[0]:%x\n", + wait_timeout_ms, temp); + + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_TIMEOUT); + phytium_sd_error(host); + return SDCI_CMD13_FAILED; + } + + writel(1, host->base + SDCI_NORMAL_ISR); + + if (CARD_TRAN_STATE != (temp & CARD_CURRENT_STATE) && send_once_time_ms) + mdelay(send_once_time_ms); + + } while (CARD_TRAN_STATE != (temp & CARD_CURRENT_STATE)); + + return SDCI_CMD13_OK; +} + +static void phytium_sdci_ops_request(struct mmc_host *mmc, + struct mmc_request *mrq) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + unsigned long flags; + bool res; + u32 status_sd; + int res_cmd13; + + host->error = 0; + WARN_ON(host->mrq); + host->mrq = mrq; + + dev_dbg(host->dev, "%s: mrq->cmd->opcode: %d, mrq->cmd->arg: 0x%x\n", + __func__, mrq->cmd->opcode, mrq->cmd->arg); + + if (mrq->cmd->opcode == MMC_SEND_STATUS && + (mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_ADTC) { + u32 status = readl(host->base + SDCI_STATUS); + + if (status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, NULL, + ERR_CARD_ABSENT); + return; + } + + res_cmd13 = phytium_sdci_cmd13_process(host, mrq, NULL, 400, 5); + if (res_cmd13 == SDCI_CMD13_FAILED) + return; + } else if (mrq->cmd->opcode == MMC_STOP_TRANSMISSION) { + status_sd = readl(host->base + SDCI_STATUS); + if (status_sd & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, NULL, + ERR_CARD_ABSENT); + return; + } + } + + if (mrq->data) { + phytium_sdci_prepare_data(host, mrq); + if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK || + mrq->cmd->opcode == MMC_READ_SINGLE_BLOCK || + mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || + mrq->cmd->opcode == MMC_WRITE_BLOCK) { + host->adtc_type = BLOCK_RW_ADTC; + phytium_sdci_start_data(host, mrq, + mrq->cmd, mrq->data); + return; + } + host->adtc_type = COMMOM_ADTC; + } + + if (mrq->cmd->opcode == SD_IO_RW_DIRECT || + mrq->cmd->opcode == SD_IO_SEND_OP_COND) { + spin_lock_irqsave(&host->lock, flags); + host->mrq = NULL; + host->cmd = NULL; + spin_unlock_irqrestore(&host->lock, flags); + mrq->cmd->error = -EINVAL; + mmc_request_done(host->mmc, mrq); + + return; + } + + if (mrq->cmd->opcode == SD_APP_SEND_SCR) { + res = set_databus_width(host); + if (!res) { + phytium_sdci_unexpected_error_handler(host, mrq, NULL, ERR_CMD_RESPONED); + return; + } + } + + /* if SBC is required, we have HW option and SW option. + * if HW option is enabled, and SBC does not have "special" flags, + * use HW option, otherwise use SW option + */ + if (mrq->sbc && + (!mmc_card_mmc(mmc->card) || (mrq->sbc->arg & 0xFFFF0000))) + phytium_sdci_start_command(host, mrq, mrq->sbc); + else + phytium_sdci_start_command(host, mrq, mrq->cmd); +} + +static void phytium_sdci_data_xfer_next(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_data *data) +{ + if (mmc_op_multi(mrq->cmd->opcode) && + mrq->stop && !mrq->stop->error && + !mrq->sbc && host->is_multi_rw_only_one_blkcnt) { + host->is_multi_rw_only_one_blkcnt = false; + phytium_sdci_auto_command_done(host, SDCI_NORMAL_ISR_CC, mrq, mrq->stop); + } else if (mmc_op_multi(mrq->cmd->opcode) && + mrq->stop && !mrq->stop->error && + !mrq->sbc) + phytium_sdci_start_command(host, mrq, mrq->stop); + else + phytium_sdci_request_done(host, mrq); +} + +static inline void get_data_buffer(struct mmc_data *data, + u32 *bytes, u32 **pointer) +{ + struct scatterlist *sg; + + sg = &data->sg[0]; + *bytes = sg->length; + *pointer = sg_virt(sg); +} + +static bool phytium_sdci_data_xfer_done(struct phytium_sdci_host *host, + u32 events, struct mmc_request *mrq, + struct mmc_data *data) +{ + struct mmc_command *stop = data->stop; + unsigned long flags; + bool done; + unsigned int check_data; + u32 sg_length, i; + u32 *sg_virt_addr; + + check_data = events & (SDCI_BD_ISR_TRS_R | SDCI_BD_ISR_TRS_W | SDCI_BD_ISR_EDTE); + + spin_lock_irqsave(&host->lock, flags); + done = !host->data; + if (check_data) + host->data = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + if (done) + return true; + + if (check_data || (stop && stop->error)) { + sdr_clr_bits(host->base + SDCI_BD_ISER, data_ints_mask); + dev_dbg(host->dev, "DMA stop\n"); + + if (((events & SDCI_BD_ISR_TRS_R) || + (events & SDCI_BD_ISR_TRS_W)) && + (!stop || !stop->error)) { + if ((mrq->cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC && + (host->adtc_type == COMMOM_ADTC)) { + get_data_buffer(data, &sg_length, + &host->sg_virt_addr); + sg_virt_addr = host->sg_virt_addr; + + for (i = 0; i < (sg_length/4); i++) { + *sg_virt_addr = host->dma_rx.buf[i]; + sg_virt_addr++; + } + } + data->bytes_xfered = data->blocks * data->blksz; + } else { + dev_dbg(host->dev, "interrupt events: %x\n", events); + phytium_sdci_reset_hw(host); + data->bytes_xfered = 0; + dev_dbg(host->dev, "%s: cmd=%d; blocks=%d", + __func__, mrq->cmd->opcode, data->blocks); + dev_dbg(host->dev, "data_error=%d xfer_size=%d\n", + (int)data->error, data->bytes_xfered); + } + + phytium_sdci_data_xfer_next(host, mrq, data); + done = true; + } + + return done; +} + + +static int phytium_sdci_card_busy(struct mmc_host *mmc) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + u32 status; + + /* check if any pin between dat[0:3] is low */ + status = readl(host->base + SDCI_STATUS); + if (((status >> 20) & 0xf) != 0xf) + return 1; + + return 0; +} + +static void phytium_sdci_request_timeout(struct work_struct *work) +{ + struct phytium_sdci_host *host; + + host = container_of(work, struct phytium_sdci_host, req_timeout.work); + dev_err(host->dev, "%s: aborting cmd/data/mrq\n", __func__); + if (host->mrq) { + dev_err(host->dev, "%s: aborting mrq=%p cmd=%d\n", __func__, + host->mrq, host->mrq->cmd->opcode); + if (host->cmd) { + dev_err(host->dev, "%s: aborting cmd=%d\n", + __func__, host->cmd->opcode); + phytium_sdci_cmd_done(host, SDCI_NORMAL_ISR_TIMEOUT, + host->mrq, host->cmd); + } else if (host->data) { + dev_err(host->dev, "%s: abort data: cmd%d; %d blocks\n", + __func__, host->mrq->cmd->opcode, + host->data->blocks); + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_EDTE, + host->mrq, host->data); + } + } +} + +static void hotplug_timer_func(struct timer_list *t) +{ + struct phytium_sdci_host *host; + u32 status; + + host = from_timer(host, t, hotplug_timer); + if (!host) + dev_err(host->dev, "%s: Not find host!\n", __func__); + status = readl(host->base + SDCI_STATUS); + + if (status & SDCI_STATUS_CDSL) { /* card absent */ + if (host->mmc->card) { + cancel_delayed_work(&host->mmc->detect); + mmc_detect_change(host->mmc, + msecs_to_jiffies(100)); + } + } else { /* card insert */ + cancel_delayed_work(&host->mmc->detect); + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + } +} + +static irqreturn_t phytium_sdci_irq(int irq, void *dev_id) +{ + struct phytium_sdci_host *host = (struct phytium_sdci_host *) dev_id; + unsigned long flags; + struct mmc_request *mrq; + struct mmc_command *cmd; + u32 events; + + if (!host) + return IRQ_NONE; + + spin_lock_irqsave(&host->lock, flags); + events = readl(host->base + SDCI_NORMAL_ISR); + /* clear interrupts */ + writel(1, host->base + SDCI_NORMAL_ISR); + + mrq = host->mrq; + cmd = host->cmd; + spin_unlock_irqrestore(&host->lock, flags); + + if (events & (SDCI_NORMAL_ISR_CR | SDCI_NORMAL_ISR_CI)) { + mod_timer(&host->hotplug_timer, + jiffies + usecs_to_jiffies(30000)); + goto irq_out; + } + + if (!(events & cmd_ints_mask)) + goto irq_out; + + if (!mrq) { + dev_err(host->dev, "%s: MRQ=NULL; events=%08X\n", + __func__, events); + WARN_ON(1); + goto irq_out; + } + + dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); + + if (cmd) + phytium_sdci_cmd_done(host, events, mrq, cmd); + +irq_out: + return IRQ_HANDLED; +} + +static irqreturn_t phytium_sdci_dma_irq(int irq, void *dev_id) +{ + struct phytium_sdci_host *host = (struct phytium_sdci_host *) dev_id; + unsigned long flags; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + u32 events; + + spin_lock_irqsave(&host->lock, flags); + events = readl(host->base + SDCI_BD_ISR); + writel(1, host->base + SDCI_BD_ISR); + + mrq = host->mrq; + cmd = host->cmd; + data = host->data; + spin_unlock_irqrestore(&host->lock, flags); + + if (!(events & data_ints_mask)) + goto dma_irq_out; + + if (!mrq) { + dev_err(host->dev, + "%s: MRQ=NULL; events=%08X\n", + __func__, events); + goto dma_irq_out; + } + + dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); + + if (data) + phytium_sdci_data_xfer_done(host, events, mrq, data); + +dma_irq_out: + return IRQ_HANDLED; +} + +static irqreturn_t phytium_sdci_err_irq(int irq, void *dev_id) +{ + struct phytium_sdci_host *host = (struct phytium_sdci_host *) dev_id; + unsigned long flags; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + u32 events; + + if (!host) + return IRQ_NONE; + + spin_lock_irqsave(&host->lock, flags); + events = readl(host->base + SDCI_ERROR_ISR); + mrq = host->mrq; + cmd = host->cmd; + data = host->data; + spin_unlock_irqrestore(&host->lock, flags); + + if (!(events&err_ints_mask)) + goto err_irq_out; + + if (!mrq) { + sdr_clr_bits(host->base + SDCI_NORMAL_ISER, SDCI_NORMAL_ISR_EI); + writel(1, host->base + SDCI_ERROR_ISR); + dev_err(host->dev, "%s: MRQ=NULL; events=%08X\n", __func__, events); + goto err_irq_out; + } + sdr_clr_bits(host->base + SDCI_NORMAL_ISER, SDCI_NORMAL_ISR_EI); + if (data) { + dev_err(host->dev, + "[%s][%d]: cmd(%d); %d read blocks, status:%x,flag:%x\n", + __func__, __LINE__, mrq->cmd->opcode, data->blocks, events, data->flags); + data->error = -ETIMEDOUT; + if ((data->flags & MMC_DATA_READ) == MMC_DATA_READ || + (data->flags & MMC_DATA_WRITE) == MMC_DATA_WRITE) + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_EDTE | SDCI_BD_ISR_TRS_R, + mrq, data); + mrq->cmd->error = -ETIMEDOUT; + mmc_request_done(host->mmc, mrq); + } else if (cmd) { + phytium_sdci_cmd_done(host, SDCI_NORMAL_ISR_TIMEOUT, mrq, cmd); + } + + writel(1, host->base + SDCI_NORMAL_ISR); + writel(1, host->base + SDCI_ERROR_ISR); +err_irq_out: + return IRQ_HANDLED; +} + +static void phytium_sdci_init_hw(struct phytium_sdci_host *host) +{ + u32 val; + + /* Reset */ + phytium_sdci_reset_hw(host); + + val = SDCI_SEN_CREFR_VAL | SDCI_SEN_DEBNCE_VAL; + writel(val, host->base + SDCI_SD_SEN); + + /* Disable and clear all interrupts */ + writel(0, host->base + SDCI_NORMAL_ISER); + writel(0, host->base + SDCI_ERROR_ISER); + writel(0, host->base + SDCI_BD_ISER); + + writel(1, host->base + SDCI_NORMAL_ISR); + writel(1, host->base + SDCI_ERROR_ISR); + writel(1, host->base + SDCI_BD_ISR); + + sdr_set_bits(host->base + SDCI_NORMAL_ISER, + SDCI_SDCI_NORMAL_ISER_ECI|SDCI_SDCI_NORMAL_ISER_ECR); + /* Configure default cmd timeout to 0.1(s)s = val/25M */ + val = SDCI_F_MAX / 10; + writel(val, host->base + SDCI_TIMEOUT_CMD); + writel(SDCI_TIMEOUT_DATA_VALUE, host->base + SDCI_TIMEOUT_DATA); + + val = 0x0F00; + writel(val, host->base + SDCI_CONTROLLER); + + dev_dbg(host->dev, "init hardware done!"); +} + +static void phytium_sdci_deinit_hw(struct phytium_sdci_host *host) +{ + /* Disable and clear all interrupts */ + writel(0, host->base + SDCI_NORMAL_ISER); + writel(0, host->base + SDCI_ERROR_ISER); + writel(0, host->base + SDCI_BD_ISER); + + writel(0, host->base + SDCI_NORMAL_ISR); + writel(0, host->base + SDCI_ERROR_ISR); + writel(0, host->base + SDCI_BD_ISR); +} + +static void phytium_sdci_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + + if (ios->bus_width == MMC_BUS_WIDTH_4) + mmc->caps = mmc->caps & (~MMC_CAP_4_BIT_DATA); + + /* Suspend/Resume will do power off/on */ + switch (ios->power_mode) { + case MMC_POWER_UP: + writel(SDCI_POWER_ON, host->base + SDCI_POWER); + break; + case MMC_POWER_ON: + phytium_sdci_set_clk(host, ios); + break; + case MMC_POWER_OFF: + writel(SDCI_POWER_OFF, host->base + SDCI_POWER); + break; + default: + break; + } +} + +static int phytium_sdci_get_cd(struct mmc_host *mmc) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + u32 status = readl(host->base + SDCI_STATUS); + + if (((status >> 19) & 0x1) == 0x1) + return 0; + + return 1; +} + +static void phytium_sdci_hw_reset(struct mmc_host *mmc) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + + sdr_set_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_SRST); + sdr_clr_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_SRST); + while (!(readl(host->base + SDCI_STATUS) & SDCI_STATUS_IDIE)) + cpu_relax(); +} + +static struct mmc_host_ops phytium_sdci_ops = { + .request = phytium_sdci_ops_request, + .set_ios = phytium_sdci_ops_set_ios, + .get_cd = phytium_sdci_get_cd, + .card_busy = phytium_sdci_card_busy, + .hw_reset = phytium_sdci_hw_reset, +}; + +static bool phytium_sdci_private_send_cmd(struct phytium_sdci_host *host, + u32 cmd, u32 resp_type, u32 arg) +{ + u32 temp, sd_cmd, sd_arg, sd_status; + unsigned long deadline_time; + + writel(1, host->base + SDCI_NORMAL_ISR); + writel(1, host->base + SDCI_ERROR_ISR); + + sd_cmd = (cmd << 8) | resp_type; + sd_arg = arg; + writel(sd_cmd, host->base + SDCI_COMMAND); + writel(sd_arg, host->base + SDCI_ARGUMENT); + + if (cmd == MMC_STOP_TRANSMISSION) + deadline_time = jiffies + msecs_to_jiffies(1000); + else + deadline_time = jiffies + msecs_to_jiffies(100); + + temp = readl(host->base + SDCI_NORMAL_ISR); + while ((temp & SDCI_NORMAL_ISR_CC) != SDCI_NORMAL_ISR_CC) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) + return false; + + temp = readl(host->base + SDCI_NORMAL_ISR); + if (time_after(jiffies, deadline_time)) + return false; + + if (cmd == MMC_STOP_TRANSMISSION) + mdelay(1); + } + + return true; +} + +static int phytium_sdci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct phytium_sdci_host *host; + struct resource *res; + int ret; + const struct acpi_device_id *match; + struct device *dev = &pdev->dev; + + /* Allocate MMC host for this device */ + mmc = mmc_alloc_host(sizeof(struct phytium_sdci_host), &pdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + ret = mmc_of_parse(mmc); + if (ret) + goto host_free; + + if (dev->of_node) { + host->src_clk = devm_clk_get(&pdev->dev, "phytium_sdc_clk"); + if (IS_ERR(host->src_clk)) { + ret = PTR_ERR(host->src_clk); + goto host_free; + } + + host->clk_rate = clk_get_rate(host->src_clk); + if (device_property_read_bool(dev, "no-dma-coherent")) + dev->dma_coherent = false; + } else if (has_acpi_companion(dev)) { + match = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!match) { + dev_err(dev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + + acpi_dma_configure(dev, DEV_DMA_NOT_SUPPORTED); + + host->clk_rate = 600000000; + } else { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + + dma_set_mask(dev, DMA_BIT_MASK(40)); + dma_set_coherent_mask(dev, DMA_BIT_MASK(40)); + + timer_setup(&host->hotplug_timer, hotplug_timer_func, 0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto host_free; + } + + host->irq = platform_get_irq(pdev, 1); + if (host->irq < 0) { + ret = -EINVAL; + goto host_free; + } + + host->irq_err = platform_get_irq(pdev, 2); + if (host->irq_err < 0) { + ret = -EINVAL; + goto host_free; + } + + host->irq_bd = platform_get_irq(pdev, 0); + if (host->irq_bd < 0) { + ret = -EINVAL; + goto host_free; + } + + host->dev = &pdev->dev; + host->mmc = mmc; + + if ((4 * SDCI_F_MAX) > host->clk_rate) + host->clk_div = 1; + else + host->clk_div = ((host->clk_rate / (2 * SDCI_F_MAX)) - 1); + + /* Set host parameters to mmc */ + mmc->f_min = SDCI_F_MIN; + mmc->f_max = (host->clk_rate / ((host->clk_div + 1) * 2)); + mmc->ops = &phytium_sdci_ops; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + + mmc->caps |= host->caps; + /* MMC core transfer sizes tunable parameters */ + mmc->max_segs = MAX_BD_NUM; + mmc->max_seg_size = 512 * 1024; + mmc->max_blk_size = 512; + mmc->max_req_size = 512 * 1024; + mmc->max_blk_count = mmc->max_req_size / 512; + + host->dma_rx.buf = dma_alloc_coherent(&pdev->dev, + MAX_BD_NUM, + &host->dma_rx.bd_addr, + GFP_KERNEL); + if (!host->dma_rx.buf) { + ret = -ENOMEM; + goto release_mem; + } + + host->cmd_timeout = msecs_to_jiffies(100); + host->data_timeout = msecs_to_jiffies(250); + + INIT_DELAYED_WORK(&host->req_timeout, phytium_sdci_request_timeout); + spin_lock_init(&host->lock); + + platform_set_drvdata(pdev, mmc); + phytium_sdci_init_hw(host); + + ret = devm_request_irq(&pdev->dev, host->irq, phytium_sdci_irq, + IRQF_SHARED, pdev->name, host); + if (ret) + goto release; + + ret = devm_request_irq(&pdev->dev, host->irq_err, phytium_sdci_err_irq, + IRQF_SHARED, pdev->name, host); + if (ret) + goto release; + + ret = devm_request_irq(&pdev->dev, host->irq_bd, phytium_sdci_dma_irq, + IRQF_SHARED, pdev->name, host); + if (ret) + goto release; + + ret = mmc_add_host(mmc); + if (ret) + goto release; + + return 0; + +release: + platform_set_drvdata(pdev, NULL); + phytium_sdci_deinit_hw(host); +release_mem: + if (host->dma_rx.buf) + dma_free_coherent(&pdev->dev, MAX_BD_NUM, + host->dma_rx.buf, + host->dma_rx.bd_addr); +host_free: + mmc_free_host(mmc); + + return ret; +} + +static int phytium_sdci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct phytium_sdci_host *host; + + mmc = platform_get_drvdata(pdev); + host = mmc_priv(mmc); + + cancel_delayed_work_sync(&host->req_timeout); + platform_set_drvdata(pdev, NULL); + mmc_remove_host(host->mmc); + phytium_sdci_deinit_hw(host); + + if (host->dma_rx.buf) + dma_free_coherent(&pdev->dev, MAX_BD_NUM, + host->dma_rx.buf, host->dma_rx.bd_addr); + + mmc_free_host(host->mmc); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int phytium_sdci_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_sdci_host *host = mmc_priv(mmc); + + phytium_sdci_deinit_hw(host); + return 0; +} + +static int phytium_sdci_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_sdci_host *host = mmc_priv(mmc); + + phytium_sdci_init_hw(host); + mmc->caps = mmc->caps | MMC_CAP_4_BIT_DATA; + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int phytium_sdci_runtime_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_sdci_host *host = mmc_priv(mmc); + + phytium_sdci_deinit_hw(host); + + return 0; +} + +static int phytium_sdci_runtime_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_sdci_host *host = mmc_priv(mmc); + + phytium_sdci_init_hw(host); + + return 0; +} + +static const struct dev_pm_ops phytium_sdci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytium_sdci_suspend, + phytium_sdci_resume) + SET_RUNTIME_PM_OPS(phytium_sdci_runtime_suspend, + phytium_sdci_runtime_resume, NULL) +}; +#else +#define phytium_sdci_dev_pm_ops NULL +#endif + +static const struct of_device_id phytium_sdci_of_ids[] = { + { .compatible = "phytium,sdci", }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_sdci_of_ids); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_sdci_acpi_ids[] = { + { .id = "PHYT0005" }, + { } +}; + +MODULE_DEVICE_TABLE(acpi, phytium_sdci_acpi_ids); +#else +#define phytium_sdci_acpi_ids NULL +#endif + +static struct platform_driver phytium_sdci_driver = { + .probe = phytium_sdci_probe, + .remove = phytium_sdci_remove, + .driver = { + .name = "sdci-phytium", + .of_match_table = phytium_sdci_of_ids, + .acpi_match_table = phytium_sdci_acpi_ids, + .pm = &phytium_sdci_dev_pm_ops, + }, +}; + +module_platform_driver(phytium_sdci_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Cheng Quan "); +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium SD Card Interface driver"); diff --git a/drivers/mmc/host/phytium-sdci.h b/drivers/mmc/host/phytium-sdci.h new file mode 100755 index 0000000000000000000000000000000000000000..be0e9aa65e2e6a0501888b409c5a94855109c2c5 --- /dev/null +++ b/drivers/mmc/host/phytium-sdci.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Phytium SD Card Interface driver + * + * Copyright (c) 2019-2023, Phytium Technology Co.,Ltd. + */ + +/*---------------------------------------------------------------------------*/ +/* Common Definition */ +/*---------------------------------------------------------------------------*/ +#define MAX_BD_NUM 0x1000 +#define SD_BLOCK_SIZE 512 + +/*---------------------------------------------------------------------------*/ +/* Register Offset */ +/*---------------------------------------------------------------------------*/ +#define SDCI_CONTROLLER 0x00 /* controller config reg */ +#define SDCI_ARGUMENT 0x04 /* argument reg */ +#define SDCI_COMMAND 0x08 /* command reg */ +#define SDCI_CLOCK_D 0x0C /* clock divide reg */ +#define SDCI_SOFTWARE 0x10 /* controller reset reg */ +#define SDCI_POWER 0X14 /* POWRE CONTROL REG */ +#define SDCI_TIMEOUT_CMD 0x18 /* cmd timeout config reg */ +#define SDCI_TIMEOUT_DATA 0x1C /* data timeout reg */ +#define SDCI_NORMAL_ISER 0x20 /* normal ISR config reg */ +#define SDCI_ERROR_ISER 0x24 /* erroe ISR config reg */ +#define SDCI_BD_ISER 0x28 /* BD ISR config reg */ +#define SDCI_CAPA 0x2C /* BD ISR config reg */ +#define SDCI_SD_DRV 0x30 /* SD card driving phase position reg */ +#define SDCI_SD_SAMP 0x34 /* SD card sampling phase position reg */ +#define SDCI_SD_SEN 0x38 /* SD card detection reg */ +#define SDCI_HDS_AXI 0x3C /* AXI boundary config reg */ +#define SDCI_BD_RX 0x40 /* BD rx addr reg */ +#define SDCI_BD_TX 0x60 /* BD tx addr reg */ +#define SDCI_BLK_CNT 0x80 /* r/w block num reg */ +#define SDCI_NORMAL_ISR 0xC0 /* normal ISR status reg */ +#define SDCI_ERROR_ISR 0xC4 /* error ISR status reg */ +#define SDCI_BD_ISR 0xC8 /* BD ISR status reg */ +#define SDCI_BD_STATUS 0xCC /* BD descriptor status reg */ +#define SDCI_STATUS 0xD0 /* status reg */ +#define SDCI_BLOCK 0xD4 /* block len reg */ +#define SDCI_RESP0 0xE0 /* response reg0 */ +#define SDCI_RESP1 0xE4 /* response reg1 */ +#define SDCI_RESP2 0xE8 /* response reg2 */ +#define SDCI_RESP3 0XEC /* response reg3 */ + +/*---------------------------------------------------------------------------*/ +/* Register Mask */ +/*---------------------------------------------------------------------------*/ +/* SDCI_CONTROLLER mask */ +#define SDCI_CONTROLLER_ECRCWR (0x1 << 0) /* RW */ +#define SDCI_CONTROLLER_ECRCRD (0x1 << 1) /* RW */ +#define SDCI_CONTROLLER_RESEDE (0x1 << 2) /* RW */ +#define SDCI_CONTROLLER_PERMDR (0x3 << 8) /* RW */ +#define SDCI_CONTROLLER_PERMDX (0x3 << 10) /* RW */ + +/* SDCI_SOFTWARE mask */ +#define SDCI_SOFTWARE_SRST (0x1 << 0) /* RW */ +#define SDCI_SOFTWARE_SCRST (0x1 << 1) /* RW */ +#define SDCI_SOFTWARE_BDRST (0x1 << 2) /* RW */ +#define SDCI_SOFTWARE_CFCLF (0x1 << 3) /* RW */ +#define SDCI_SOFTWARE_SDRST (0x1 << 4) /* RW */ + +/* SDCI_NORMAL_ISER mask */ +#define SDCI_SDCI_NORMAL_ISER_ECC_EN (0x1 << 0) /* RW */ +#define SDCI_SDCI_NORMAL_ISER_ECR (0x1 << 1) /* RW */ +#define SDCI_SDCI_NORMAL_ISER_ECI (0x1 << 2) /* RW */ +#define SDCI_SDCI_NORMAL_ISER_EEI_EN (0x1 << 15) /* RW */ + +/* SDCI_NORMAL_ISR mask */ +#define SDCI_NORMAL_ISR_CC (0x1 << 0) /* R */ +#define SDCI_NORMAL_ISR_CR (0x1 << 1) /* R */ +#define SDCI_NORMAL_ISR_CI (0x1 << 2) /* R */ +#define SDCI_NORMAL_ISR_TIMEOUT (0x1 << 3) /* R */ +#define SDCI_NORMAL_ISR_EI (0x1 << 15) /* R */ + +/* SDCI_ERROR_ISER mask */ +#define SDCI_ERROR_ISER_ECTE_EN (0x1 << 0) /* RW */ +#define SDCI_ERROR_ISR_CCRCE_EN (0x1 << 1) /* RW */ +#define SDCI_ERROR_ISR_CIR_EN (0x1 << 3) /* RW */ +#define SDCI_ERROR_ISR_CNR_EN (0x1 << 4) /* RW */ +/* SDCI_ERROR_ISR mask */ +#define SDCI_ERROR_ISR_CTE (0x1 << 0) /* R */ +#define SDCI_ERROR_ISR_CCRCE (0x1 << 1) /* R */ +#define SDCI_ERROR_ISR_CIR (0x1 << 3) /* R */ +#define SDCI_ERROR_ISR_CNR (0x1 << 4) /* R */ + +/* SDCI_BD_ISER mask */ +#define SDCI_BD_ISER_ETRS_EN (0x1 << 8) /* RW */ +#define SDCI_BD_ISER_DATFRAX_EN (0x1 << 7) /* RW */ + +/* SDCI_BD_ISR mask */ +#define SDCI_BD_ISR_TRS_W (0x1 << 0) /* R */ +#define SDCI_BD_ISR_TRS_R (0x1 << 8) /* R */ +#define SDCI_BD_ISR_EDTE (0x1 << 3) /* R */ +#define SDCI_BD_ISR_DAIS (0x1 << 15) /* R */ +#define SDCI_BD_ISR_DATFRAX (0x1 << 7) /* R */ + +/* SDCI_HDS_AXI mask */ +#define SDCI_HDS_AXI_AWDOMAIN (0x1 << 0) /* RW */ +#define SDCI_HDS_AXI_ARDOMAIN (0x1 << 12) /* RW */ +#define SDCI_HDS_AXI_AWCACHE (0x6 << 24) /* RW */ +#define SDCI_HDS_AXI_ARCACHE (0xB << 28) /* RW */ + +/* SDCI_STATUS mask */ +#define SDCI_STATUS_CMD_BUSY (0x0 << 0) /* R */ +#define SDCI_STATUS_CMD_READY (0x1 << 0) /* R */ +#define SDCI_STATUS_IDIE (0x1 << 12) /* R */ +#define SDCI_CARD_BUSY_IN_PRG (0x1 << 20) /* R D0 BUSY:0,IDLE:1 */ + +/* SDCI_STATUS */ +#define SDCI_STATUS_CDSL (0x1 << 19) /* R */ + +/*---------------------------------------------------------------------------*/ +/* Register Value */ +/*---------------------------------------------------------------------------*/ +#define SDCI_SD_DRV_VALUE 0 +#define SDCI_SD_SAMP_VALUE_MAX 50 +#define SDCI_SD_SAMP_VALUE_MIN 0 + +#define SDCI_TIMEOUT_CMD_VALUE 0xFFFFFFFF +#define SDCI_TIMEOUT_DATA_VALUE 0xFFFFFFFF +#define SDCI_POWER_ON 1 +#define SDCI_POWER_OFF 0 + +#define SDCI_CMD_TIMEOUT 10 +#define SDCI_DAT_TIMEOUT 5000 + +#define SDCI_CMD_TYPE_ADTC 0x2 + +#define SDCI_F_MIN 400000 +#define SDCI_F_MAX 25000000 + +#define SDCI_SEN_CREFR_VAL (0x1 << 1) +#define SDCI_SEN_DEBNCE_VAL (0xB << 8) + +#define CARD_CURRENT_STATE (0xF << 9) +#define CARD_PRG_STATE (0x7 << 9) +#define CARD_TRAN_STATE (0x4 << 9) + +#define SDCI_CMD13_OK 1 +#define SDCI_CMD13_FAILED 0 + +#define ERR_TIMEOUT (0x1 << 0) +#define ERR_CARD_ABSENT (0x1 << 1) +#define ERR_CMD_RESPONED (0x1 << 2) + +/*---------------------------------------------------------------------------*/ +/* Structure Type */ +/*---------------------------------------------------------------------------*/ +struct phytium_sdci_dma { + struct scatterlist *sg; + u32 *buf; + dma_addr_t bd_addr; + size_t bytes; +}; + +enum adtc_type { + COMMOM_ADTC = 0, + BLOCK_RW_ADTC, +}; + +struct phytium_sdci_host { + struct device *dev; + struct mmc_host *mmc; + u32 caps; + spinlock_t lock; + + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + int error; + + void __iomem *base; + + struct phytium_sdci_dma dma_rx; + struct phytium_sdci_dma dma_tx; + + u32 *sg_virt_addr; + enum adtc_type adtc_type; + + struct timer_list hotplug_timer; + + struct delayed_work req_timeout; + u32 cmd_timeout; + u32 data_timeout; + + int irq; + int irq_err; + int irq_bd; + + struct clk *src_clk; + unsigned long clk_rate; + unsigned long clk_div; + unsigned long real_rate; + + u32 current_rca; + bool is_multi_rw_only_one_blkcnt; +}; + diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 5b86336fe17073200635b790f50a91e18396a64c..80d1ca87d814183bdc4987017688ed793dc6faf0 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,12 @@ #define MACB_EXT_DESC #endif +enum irq_type { + IRQ_TYPE_INTX = 1, + IRQ_TYPE_MSI = 2, + IRQ_TYPE_MAX = 3, +}; + #define MACB_GREGS_NBR 16 #define MACB_GREGS_VERSION 2 #define MACB_MAX_QUEUES 8 @@ -173,6 +180,7 @@ #define GEM_DCFG12 0x02AC /* Design Config 12 */ #define GEM_USX_CONTROL 0x0A80 /* High speed PCS control register */ #define GEM_USX_STATUS 0x0A88 /* High speed PCS status register */ +#define GEM_TAIL_ENABLE 0x0E7C /* Phytium: Enable tail */ #define GEM_TXBDCTRL 0x04cc /* TX Buffer Descriptor control register */ #define GEM_RXBDCTRL 0x04d0 /* RX Buffer Descriptor control register */ @@ -207,6 +215,7 @@ #define GEM_IER(hw_q) (0x0600 + ((hw_q) << 2)) #define GEM_IDR(hw_q) (0x0620 + ((hw_q) << 2)) #define GEM_IMR(hw_q) (0x0640 + ((hw_q) << 2)) +#define GEM_TAIL(hw_q) (0x0e80 + ((hw_q) << 2)) /* Phytium: tail register */ #define GEM_SRC_SEL_LN 0x1C04 #define GEM_DIV_SEL0_LN 0x1C08 #define GEM_DIV_SEL1_LN 0x1C0C @@ -236,6 +245,8 @@ #define GEM_PHY_INT_CLEAR 0x1C8C #define GEM_PHY_INT_STATE 0x1C90 +#define GEM_INTX_IRQ_MASK 0x1C7C /* Phytium: irq mask */ + /* Bitfields in NCR */ #define MACB_LB_OFFSET 0 /* reserved */ #define MACB_LB_SIZE 1 @@ -738,6 +749,7 @@ #define MACB_CAPS_BD_RD_PREFETCH 0x00000080 #define MACB_CAPS_NEEDS_RSTONUBR 0x00000100 #define MACB_CAPS_SEL_CLK 0x00000200 +#define MACB_CAPS_TAILPTR 0x00001000 /* Phytium: tail register */ #define MACB_CAPS_CLK_HW_CHG 0x04000000 #define MACB_CAPS_MACB_IS_EMAC 0x08000000 #define MACB_CAPS_FIFO_MODE 0x10000000 @@ -1216,6 +1228,7 @@ struct macb_queue { unsigned int RBQS; unsigned int RBQP; unsigned int RBQPH; + unsigned int TAILADDR; unsigned int tx_head, tx_tail; struct macb_dma_desc *tx_ring; @@ -1394,6 +1407,24 @@ static inline bool gem_has_ptp(struct macb *bp) return !!(bp->caps & MACB_CAPS_GEM_HAS_PTP); } +enum phytium_type { + PHYTIUM_DEV_1P0 = 1, + PHYTIUM_DEV_2P0, + PHYTIUM_DEV_3P0, +}; + +struct phytium_platform_pdata { + int phytium_dev_type; + struct clk *txclk; + struct clk *rxclk; + struct clk *tsu_clk; + u32 caps; + int irq_type; + int irq[4]; + phy_interface_t phy_interface; + const struct property_entry *properties; +}; + /** * struct macb_platform_data - platform data for MACB Ethernet used for PCI registration * @pclk: platform clock @@ -1402,6 +1433,7 @@ static inline bool gem_has_ptp(struct macb *bp) struct macb_platform_data { struct clk *pclk; struct clk *hclk; + struct phytium_platform_pdata phytium_macb_pdata; }; #endif /* _MACB_H */ diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 99508d2194ea39cfd80b705bbb2811539f0de0cf..17d92bbafea4aef6a34992fb7f8b52822903e843 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -935,6 +935,19 @@ static void phytium_gem2p0_sel_clk(struct macb *bp, int speed) gem_readl(bp, HS_MAC_CONFIG))); } +static void phytium_gem3p0_sel_clk(struct macb *bp, int speed) +{ + if (speed == SPEED_100 || speed == SPEED_10) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_100M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (speed == SPEED_1000) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_1000M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (speed == SPEED_2500) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_2500M, + gem_readl(bp, HS_MAC_CONFIG))); +} + static void macb_mac_link_up(struct phylink_config *config, struct phy_device *phy, unsigned int mode, phy_interface_t interface, @@ -1073,8 +1086,14 @@ static int macb_phylink_connect(struct macb *bp) struct device_node *dn = bp->pdev->dev.of_node; struct net_device *dev = bp->dev; struct phy_device *phydev; + struct macb_platform_data *pdata = dev_get_platdata(&bp->pdev->dev); int ret; + if (pdata && pdata->phytium_macb_pdata.properties) { + phylink_start(bp->phylink); + return 0; + } + if (dn) ret = phylink_of_phy_connect(bp->phylink, dn, 0); @@ -1131,10 +1150,11 @@ static int macb_mii_probe(struct net_device *dev) bp->phylink_config.dev = &dev->dev; bp->phylink_config.type = PHYLINK_NETDEV; - if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII || + bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { bp->phylink_config.poll_fixed_state = true; bp->phylink_config.get_fixed_state = macb_get_pcs_fixed_state; - } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX || + } else if (bp->phy_interface == PHY_INTERFACE_MODE_10GBASER || bp->phy_interface == PHY_INTERFACE_MODE_USXGMII) { bp->phylink_config.poll_fixed_state = true; bp->phylink_config.get_fixed_state = macb_get_usx_pcs_fixed_state; @@ -2514,6 +2534,8 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev) wmb(); skb_tx_timestamp(skb); + if (bp->caps & MACB_CAPS_TAILPTR) + queue_writel(queue, TAILADDR, BIT(31) | macb_tx_ring_wrap(bp, queue->tx_head)); macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); if (CIRC_SPACE(queue->tx_head, queue->tx_tail, bp->tx_ring_size) < 1) @@ -2711,6 +2733,9 @@ static void gem_init_rings(struct macb *bp) queue->tx_head = 0; queue->tx_tail = 0; + if (bp->caps & MACB_CAPS_TAILPTR) + queue_writel(queue, TAILADDR, BIT(31) | queue->tx_head); + queue->rx_tail = 0; queue->rx_prepared_head = 0; @@ -2948,6 +2973,8 @@ static void macb_init_hw(struct macb *bp) bp->rx_frm_len_mask = MACB_RX_JFRMLEN_MASK; gem_writel(bp, AXI_PIPE, 0x1010); + if (bp->caps & MACB_CAPS_TAILPTR) + gem_writel(bp, TAIL_ENABLE, 0x80000001); if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII || bp->phy_interface == PHY_INTERFACE_MODE_5GBASER || @@ -4030,10 +4057,15 @@ static void macb_configure_caps(struct macb *bp, const struct macb_config *dt_conf) { u32 dcfg; + struct macb_platform_data *pdata; if (dt_conf) bp->caps = dt_conf->caps; + pdata = dev_get_platdata(&bp->pdev->dev); + if (pdata && pdata->phytium_macb_pdata.phytium_dev_type == PHYTIUM_DEV_3P0) + bp->caps |= pdata->phytium_macb_pdata.caps; + if (hw_is_gem(bp->regs, bp->native_io)) { bp->caps |= MACB_CAPS_MACB_IS_GEM; @@ -4086,24 +4118,172 @@ static void macb_probe_queues(void __iomem *mem, *num_queues = hweight32(*queue_mask); } -static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, - struct clk **hclk, struct clk **tx_clk, - struct clk **rx_clk, struct clk **tsu_clk) +#ifdef CONFIG_ACPI +static int phytium_clk_acpi_init(struct platform_device *pdev, struct clk **pclk, + struct clk **hclk, struct clk **tx_clk, + struct clk **rx_clk, struct clk **tsu_clk) { struct macb_platform_data *pdata; int err; - /* clk get not support ACPI */ - if (has_acpi_companion(&pdev->dev)) { - dev_info(&pdev->dev, "ACPI skip get macb clk\n"); + pdata = dev_get_platdata(&pdev->dev); + if (pdata) { + *pclk = pdata->pclk; + *hclk = pdata->hclk; + *tx_clk = pdata->phytium_macb_pdata.txclk; + *rx_clk = pdata->phytium_macb_pdata.rxclk; + *tsu_clk = pdata->phytium_macb_pdata.tsu_clk; + } else { *pclk = NULL; *hclk = NULL; *tx_clk = NULL; *rx_clk = NULL; *tsu_clk = NULL; - return 0; } + err = clk_prepare_enable(*pclk); + if (err) { + dev_err(&pdev->dev, "failed to enable pclk (%d)\n", err); + return err; + } + + err = clk_prepare_enable(*hclk); + if (err) { + dev_err(&pdev->dev, "failed to enable hclk (%d)\n", err); + goto err_disable_pclk; + } + + err = clk_prepare_enable(*tx_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable tx_clk (%d)\n", err); + goto err_disable_hclk; + } + + err = clk_prepare_enable(*rx_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable rx_clk (%d)\n", err); + goto err_disable_txclk; + } + + err = clk_prepare_enable(*tsu_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable tsu_clk (%d)\n", err); + goto err_disable_rxclk; + } + + return 0; + +err_disable_rxclk: + clk_disable_unprepare(*rx_clk); + +err_disable_txclk: + clk_disable_unprepare(*tx_clk); + +err_disable_hclk: + clk_disable_unprepare(*hclk); + +err_disable_pclk: + clk_disable_unprepare(*pclk); + + return err; +} +#endif + +static int phytium_clk_init(struct platform_device *pdev, struct clk **pclk, + struct clk **hclk, struct clk **tx_clk, + struct clk **rx_clk, struct clk **tsu_clk) +{ + struct macb_platform_data *pdata; + int err; + + pdata = dev_get_platdata(&pdev->dev); + if (pdata) { + *pclk = pdata->pclk; + *hclk = pdata->hclk; + *tx_clk = pdata->phytium_macb_pdata.txclk; + *rx_clk = pdata->phytium_macb_pdata.rxclk; + *tsu_clk = pdata->phytium_macb_pdata.tsu_clk; + } else { + *pclk = devm_clk_get(&pdev->dev, "pclk"); + *hclk = devm_clk_get(&pdev->dev, "hclk"); + *tx_clk = devm_clk_get_optional(&pdev->dev, "tx_clk"); + *rx_clk = devm_clk_get_optional(&pdev->dev, "rx_clk"); + *tsu_clk = devm_clk_get_optional(&pdev->dev, "tsu_clk"); + } + + if (IS_ERR_OR_NULL(*pclk)) + return dev_err_probe(&pdev->dev, + IS_ERR(*pclk) ? PTR_ERR(*pclk) : -ENODEV, + "failed to get pclk\n"); + + if (IS_ERR_OR_NULL(*hclk)) + return dev_err_probe(&pdev->dev, + IS_ERR(*hclk) ? PTR_ERR(*hclk) : -ENODEV, + "failed to get hclk\n"); + + if (IS_ERR(*tx_clk)) + return PTR_ERR(*tx_clk); + + if (IS_ERR(*rx_clk)) + return PTR_ERR(*rx_clk); + + if (IS_ERR(*tsu_clk)) + return PTR_ERR(*tsu_clk); + + err = clk_prepare_enable(*pclk); + if (err) { + dev_err(&pdev->dev, "failed to enable pclk (%d)\n", err); + return err; + } + + err = clk_prepare_enable(*hclk); + if (err) { + dev_err(&pdev->dev, "failed to enable hclk (%d)\n", err); + goto err_disable_pclk; + } + + err = clk_prepare_enable(*tx_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable tx_clk (%d)\n", err); + goto err_disable_hclk; + } + + err = clk_prepare_enable(*rx_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable rx_clk (%d)\n", err); + goto err_disable_txclk; + } + + err = clk_prepare_enable(*tsu_clk); + if (err) { + dev_err(&pdev->dev, "failed to enable tsu_clk (%d)\n", err); + goto err_disable_rxclk; + } + + return 0; + +err_disable_rxclk: + clk_disable_unprepare(*rx_clk); + +err_disable_txclk: + clk_disable_unprepare(*tx_clk); + +err_disable_hclk: + clk_disable_unprepare(*hclk); + +err_disable_pclk: + clk_disable_unprepare(*pclk); + + return err; +} + +static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, + struct clk **hclk, struct clk **tx_clk, + struct clk **rx_clk, struct clk **tsu_clk) +{ + struct macb_platform_data *pdata; + int err; + pdata = dev_get_platdata(&pdev->dev); if (pdata) { *pclk = pdata->pclk; @@ -4190,6 +4370,225 @@ static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, return err; } +static irqreturn_t phytium_macb_interrupt_intx(int irq, void *dev_id) +{ + struct macb *bp = dev_id; + u32 irq_mask; + int i; + + irq_mask = gem_readl(bp, INTX_IRQ_MASK); + if (unlikely(!irq_mask)) + return IRQ_NONE; + + gem_writel(bp, INTX_IRQ_MASK, irq_mask); + + for (i = 0; i < bp->num_queues; i++) { + if ((irq_mask & bp->queue_mask) & BIT(i)) + macb_interrupt(irq, &bp->queues[i]); + } + return IRQ_HANDLED; +} + +static int phytium_queue_request_irq(struct platform_device *pdev, unsigned int q) +{ + int err; + struct macb_queue *queue; + struct macb_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); + struct macb *bp = netdev_priv(dev); + + queue = &bp->queues[q]; + if (q == 0 && pdata->phytium_macb_pdata.irq_type == IRQ_TYPE_INTX) { + dev->irq = pdata->phytium_macb_pdata.irq[q]; + err = devm_request_irq(&pdev->dev, dev->irq, phytium_macb_interrupt_intx, + IRQF_SHARED, dev->name, bp); + if (err) { + dev_err(&pdev->dev, + "Unable to request dev %d (error %d)\n", + dev->irq, err); + } + } + + if (pdata->phytium_macb_pdata.irq_type == IRQ_TYPE_MSI) { + queue->irq = pdata->phytium_macb_pdata.irq[q]; + err = devm_request_irq(&pdev->dev, queue->irq, macb_interrupt, + 0, dev->name, queue); + if (err) { + dev_err(&pdev->dev, + "Unable to request IRQ %d (error %d)\n", + queue->irq, err); + } + } + + return err; +} + +static int phytium_init(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + unsigned int hw_q, q; + struct macb *bp = netdev_priv(dev); + struct macb_queue *queue; + int err; + u32 val, reg; + struct macb_platform_data *pdata; + + bp->tx_ring_size = DEFAULT_TX_RING_SIZE; + bp->rx_ring_size = DEFAULT_RX_RING_SIZE; + + pdata = dev_get_platdata(&pdev->dev); + + /* set the queue register mapping once for all: queue0 has a special + * register mapping but we don't want to test the queue index then + * compute the corresponding register offset at run time. + */ + for (hw_q = 0, q = 0; hw_q < MACB_MAX_QUEUES; ++hw_q) { + if (!(bp->queue_mask & (1 << hw_q))) + continue; + + queue = &bp->queues[q]; + queue->bp = bp; + netif_napi_add(dev, &queue->napi, macb_poll, NAPI_POLL_WEIGHT); + if (hw_q) { + queue->ISR = GEM_ISR(hw_q - 1); + queue->IER = GEM_IER(hw_q - 1); + queue->IDR = GEM_IDR(hw_q - 1); + queue->IMR = GEM_IMR(hw_q - 1); + queue->TBQP = GEM_TBQP(hw_q - 1); + queue->RBQP = GEM_RBQP(hw_q - 1); + queue->RBQS = GEM_RBQS(hw_q - 1); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + if (bp->hw_dma_cap & HW_DMA_CAP_64B) { + queue->TBQPH = GEM_TBQPH(hw_q - 1); + queue->RBQPH = GEM_RBQPH(hw_q - 1); + } +#endif + } else { + /* queue0 uses legacy registers */ + queue->ISR = MACB_ISR; + queue->IER = MACB_IER; + queue->IDR = MACB_IDR; + queue->IMR = MACB_IMR; + queue->TBQP = MACB_TBQP; + queue->RBQP = MACB_RBQP; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + if (bp->hw_dma_cap & HW_DMA_CAP_64B) { + queue->TBQPH = MACB_TBQPH; + queue->RBQPH = MACB_RBQPH; + } +#endif + } + + if (bp->caps & MACB_CAPS_TAILPTR) + queue->TAILADDR = GEM_TAIL(hw_q); + /* get irq: here we use the linux queue index, not the hardware + * queue index. the queue irq definitions in the device tree + * must remove the optional gaps that could exist in the + * hardware queue mask. + */ + if (pdata && pdata->phytium_macb_pdata.phytium_dev_type == PHYTIUM_DEV_3P0) { + err = phytium_queue_request_irq(pdev, q); + } else { + queue->irq = platform_get_irq(pdev, q); + err = devm_request_irq(&pdev->dev, queue->irq, macb_interrupt, + IRQF_SHARED, dev->name, queue); + if (err) { + dev_err(&pdev->dev, + "Unable to request IRQ %d (error %d)\n", + queue->irq, err); + return err; + } + } + + INIT_WORK(&queue->tx_error_task, macb_tx_error_task); + q++; + } + + dev->netdev_ops = &macb_netdev_ops; + + /* setup appropriated routines according to adapter type */ + if (macb_is_gem(bp)) { + bp->max_tx_length = GEM_MAX_TX_LEN; + bp->macbgem_ops.mog_alloc_rx_buffers = gem_alloc_rx_buffers; + bp->macbgem_ops.mog_free_rx_buffers = gem_free_rx_buffers; + bp->macbgem_ops.mog_init_rings = gem_init_rings; + bp->macbgem_ops.mog_rx = gem_rx; + dev->ethtool_ops = &gem_ethtool_ops; + } else { + bp->max_tx_length = MACB_MAX_TX_LEN; + bp->macbgem_ops.mog_alloc_rx_buffers = macb_alloc_rx_buffers; + bp->macbgem_ops.mog_free_rx_buffers = macb_free_rx_buffers; + bp->macbgem_ops.mog_init_rings = macb_init_rings; + bp->macbgem_ops.mog_rx = macb_rx; + dev->ethtool_ops = &macb_ethtool_ops; + } + + /* Set features */ + dev->hw_features = NETIF_F_SG; + + /* Check LSO capability */ + if (GEM_BFEXT(PBUF_LSO, gem_readl(bp, DCFG6))) + dev->hw_features |= MACB_NETIF_LSO; + + /* Checksum offload is only available on gem with packet buffer */ + if (macb_is_gem(bp) && !(bp->caps & MACB_CAPS_FIFO_MODE)) + dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + if (bp->caps & MACB_CAPS_SG_DISABLED) + dev->hw_features &= ~NETIF_F_SG; + dev->features = dev->hw_features; + + /* Check RX Flow Filters support. + * Max Rx flows set by availability of screeners & compare regs: + * each 4-tuple define requires 1 T2 screener reg + 3 compare regs + */ + reg = gem_readl(bp, DCFG8); + bp->max_tuples = min((GEM_BFEXT(SCR2CMP, reg) / 3), + GEM_BFEXT(T2SCR, reg)); + INIT_LIST_HEAD(&bp->rx_fs_list.list); + if (bp->max_tuples > 0) { + /* also needs one ethtype match to check IPv4 */ + if (GEM_BFEXT(SCR2ETH, reg) > 0) { + /* program this reg now */ + reg = 0; + reg = GEM_BFINS(ETHTCMP, (uint16_t)ETH_P_IP, reg); + gem_writel_n(bp, ETHT, SCRT2_ETHT, reg); + /* Filtering is supported in hw but don't enable it in kernel now */ + dev->hw_features |= NETIF_F_NTUPLE; + /* init Rx flow definitions */ + bp->rx_fs_list.count = 0; + spin_lock_init(&bp->rx_fs_lock); + } else { + bp->max_tuples = 0; + } + } + + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) { + val = 0; + if (phy_interface_mode_is_rgmii(bp->phy_interface)) + val = GEM_BIT(RGMII); + else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII && + (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII)) + val = MACB_BIT(RMII); + else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII)) + val = MACB_BIT(MII); + + if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN) + val |= MACB_BIT(CLKEN); + + macb_or_gem_writel(bp, USRIO, val); + } + + /* Set MII management clock divider */ + val = macb_mdc_clk_div(bp); + val |= macb_dbw(bp); + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII || + bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) + val |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL); + macb_writel(bp, NCFGR, val); + + return 0; +} + static int macb_init(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); @@ -4937,8 +5336,8 @@ static const struct macb_config phytium_gem1p0_config = { MACB_CAPS_BD_RD_PREFETCH | MACB_CAPS_SEL_CLK, .dma_burst_length = 16, - .clk_init = macb_clk_init, - .init = macb_init, + .clk_init = phytium_clk_init, + .init = phytium_init, .jumbo_max_len = 16360, .sel_clk_hw = phytium_gem1p0_sel_clk, }; @@ -4956,6 +5355,49 @@ static const struct macb_config phytium_gem2p0_config = { .sel_clk_hw = phytium_gem2p0_sel_clk, }; +static const struct macb_config phytium_gem3p0_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_SEL_CLK | + MACB_CAPS_TAILPTR, + .dma_burst_length = 16, + .clk_init = phytium_clk_init, + .init = phytium_init, + .jumbo_max_len = 10240, + .sel_clk_hw = phytium_gem3p0_sel_clk, +}; + +#ifdef CONFIG_ACPI +static const struct macb_config phytium_gem1p0_acpi_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_SEL_CLK, + .dma_burst_length = 16, + .clk_init = phytium_clk_acpi_init, + .init = phytium_init, + .jumbo_max_len = 10240, + .sel_clk_hw = phytium_gem1p0_sel_clk, +}; + +static const struct macb_config phytium_gem3p0_acpi_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_SEL_CLK | + MACB_CAPS_TAILPTR, + .dma_burst_length = 16, + .clk_init = phytium_clk_acpi_init, + .init = phytium_init, + .jumbo_max_len = 10240, + .sel_clk_hw = phytium_gem3p0_sel_clk, +}; +#endif + static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,at32ap7000-macb" }, { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config }, @@ -4975,6 +5417,7 @@ static const struct of_device_id macb_dt_ids[] = { { .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config }, { .compatible = "cdns,phytium-gem-1.0", .data = &phytium_gem1p0_config }, { .compatible = "cdns,phytium-gem-2.0", .data = &phytium_gem2p0_config }, + { .compatible = "cdns,phytium-gem-3.0", .data = &phytium_gem3p0_config }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, macb_dt_ids); @@ -4982,7 +5425,8 @@ MODULE_DEVICE_TABLE(of, macb_dt_ids); #ifdef CONFIG_ACPI static const struct acpi_device_id macb_acpi_ids[] = { - { .id = "PHYT0036", .driver_data = (kernel_ulong_t)&phytium_gem1p0_config }, + { .id = "PHYT0036", .driver_data = (kernel_ulong_t)&phytium_gem1p0_acpi_config }, + { .id = "PHYT0046", .driver_data = (kernel_ulong_t)&phytium_gem3p0_acpi_config }, { } }; @@ -5035,9 +5479,10 @@ static int macb_probe(struct platform_device *pdev) struct clk **) = macb_config->clk_init; int (*init)(struct platform_device *) = macb_config->init; struct device_node *np = pdev->dev.of_node; - struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL; + struct clk *pclk = NULL, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL; struct clk *tsu_clk = NULL; unsigned int queue_mask, num_queues; + struct macb_platform_data *pdata; bool native_io; struct net_device *dev; struct resource *regs; @@ -5046,6 +5491,15 @@ static int macb_probe(struct platform_device *pdev) struct macb *bp; int err, val; + pdata = dev_get_platdata(&pdev->dev); + if (pdata) { + if (pdata->phytium_macb_pdata.phytium_dev_type == PHYTIUM_DEV_3P0) { + macb_config = &phytium_gem3p0_config; + clk_init = macb_config->clk_init; + init = macb_config->init; + } + } + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(mem)) @@ -5178,10 +5632,14 @@ static int macb_probe(struct platform_device *pdev) } err = macb_get_phy_mode(pdev); - if (err < 0) - bp->phy_interface = PHY_INTERFACE_MODE_MII; - else + if (err < 0) { + if (pdata && pdata->phytium_macb_pdata.phytium_dev_type == PHYTIUM_DEV_3P0) + bp->phy_interface = pdata->phytium_macb_pdata.phy_interface; + else + bp->phy_interface = PHY_INTERFACE_MODE_MII; + } else { bp->phy_interface = err; + } bp->link = 0; bp->duplex = DUPLEX_UNKNOWN; @@ -5257,6 +5715,7 @@ static int macb_remove(struct platform_device *pdev) { struct net_device *dev; struct macb *bp; + struct macb_platform_data *pdata = dev_get_platdata(&pdev->dev); dev = platform_get_drvdata(pdev); @@ -5272,11 +5731,14 @@ static int macb_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); if (!pm_runtime_suspended(&pdev->dev)) { - clk_disable_unprepare(bp->tx_clk); - clk_disable_unprepare(bp->hclk); - clk_disable_unprepare(bp->pclk); - clk_disable_unprepare(bp->rx_clk); - clk_disable_unprepare(bp->tsu_clk); + if (!pdata) { + clk_disable_unprepare(bp->tx_clk); + clk_disable_unprepare(bp->hclk); + clk_disable_unprepare(bp->pclk); + clk_disable_unprepare(bp->rx_clk); + clk_disable_unprepare(bp->tsu_clk); + } + pm_runtime_set_suspended(&pdev->dev); } phylink_destroy(bp->phylink); diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c index 3593b310c325df63b2affcecee315db625570c21..63d8e3e01d946d577ea48cb2047898a800ec0c02 100644 --- a/drivers/net/ethernet/cadence/macb_pci.c +++ b/drivers/net/ethernet/cadence/macb_pci.c @@ -21,17 +21,166 @@ #define CDNS_VENDOR_ID 0x17cd #define CDNS_DEVICE_ID 0xe007 +#define PCI_DEVICE_ID_GMAC_3P0 0xdc3b +#define PCI_SUBDEVICE_ID_SGMII 0x1000 +#define PCI_SUBDEVICE_ID_1000BASEX 0x1001 +#define PCI_SUBDEVICE_ID_USXGMII 0x1004 +#define PCI_SUBDEVICE_ID_10GBASER 0x1005 #define GEM_PCLK_RATE 50000000 #define GEM_HCLK_RATE 50000000 +#define GEM_TXCLK_RATE 25000000 +#define GEM_RXCLK_RATE 25000000 +#define GEM_TSUCLK_RATE 300000000 + +static const u32 fixedlink[][5] = { + {0, 1, 1000, 1, 0}, + {0, 1, 2500, 1, 0}, + {0, 1, 5000, 1, 0}, + {0, 1, 10000, 1, 0}, +}; + +static const struct property_entry fl_properties[][2] = { + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[0]), {}}, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[1]), {}}, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[2]), {}}, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[3]), {}}, +}; + +static const struct phytium_platform_pdata phytium_sgmii_pdata = { + .phytium_dev_type = PHYTIUM_DEV_3P0, + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_USRIO_DISABLED | + MACB_CAPS_TAILPTR, + .phy_interface = PHY_INTERFACE_MODE_SGMII, +}; + +static const struct phytium_platform_pdata phytium_1000basex_pdata = { + .phytium_dev_type = PHYTIUM_DEV_3P0, + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_USRIO_DISABLED | + MACB_CAPS_TAILPTR, + .phy_interface = PHY_INTERFACE_MODE_SGMII, + .properties = fl_properties[0], +}; + +static const struct phytium_platform_pdata phytium_usxgmii_pdata = { + .phytium_dev_type = PHYTIUM_DEV_3P0, + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_USRIO_DISABLED | + MACB_CAPS_TAILPTR, + .phy_interface = PHY_INTERFACE_MODE_USXGMII, + .properties = fl_properties[3], +}; + +static int phytium_macb_pci_init(struct pci_dev *pdev, struct macb_platform_data *plat_data, + struct platform_device_info *plat_info, + struct phytium_platform_pdata *phytium_data) +{ + int i; + int err; + char clkname[20]; + + err = pci_alloc_irq_vectors(pdev, 4, 4, PCI_IRQ_MSI); + if (err < 0) { + dev_err(&pdev->dev, "err=%d, fialed to allocate MSI entry", err); + plat_data->phytium_macb_pdata.irq_type = IRQ_TYPE_INTX; + plat_data->phytium_macb_pdata.irq[0] = pdev->irq; + + } else { + plat_data->phytium_macb_pdata.irq_type = IRQ_TYPE_MSI; + for (i = 0; i < 4; i++) + plat_data->phytium_macb_pdata.irq[i] = pci_irq_vector(pdev, i); + } + + plat_data->phytium_macb_pdata.phytium_dev_type = phytium_data->phytium_dev_type; + plat_data->phytium_macb_pdata.caps = phytium_data->caps; + plat_data->phytium_macb_pdata.phy_interface = phytium_data->phy_interface; + if (phytium_data && phytium_data->properties) { + plat_info->fwnode = NULL; + plat_info->properties = phytium_data->properties; + plat_data->phytium_macb_pdata.properties = phytium_data->properties; + } + + snprintf(clkname, 20, "txclk:%02x", plat_info->id); + plat_data->phytium_macb_pdata.txclk = + clk_register_fixed_rate(&pdev->dev, clkname, NULL, 0, GEM_TXCLK_RATE); + if (IS_ERR(plat_data->phytium_macb_pdata.txclk)) { + err = PTR_ERR(plat_data->phytium_macb_pdata.txclk); + goto err_txclk_register; + } + + snprintf(clkname, 20, "rxclk:%02x", plat_info->id); + plat_data->phytium_macb_pdata.rxclk = + clk_register_fixed_rate(&pdev->dev, clkname, NULL, 0, GEM_RXCLK_RATE); + if (IS_ERR(plat_data->phytium_macb_pdata.rxclk)) { + err = PTR_ERR(plat_data->phytium_macb_pdata.rxclk); + goto err_rxclk_register; + } + + snprintf(clkname, 20, "tsuclk:%02x", plat_info->id); + plat_data->phytium_macb_pdata.tsu_clk = + clk_register_fixed_rate(&pdev->dev, clkname, NULL, 0, GEM_TSUCLK_RATE); + if (IS_ERR(plat_data->phytium_macb_pdata.tsu_clk)) { + err = PTR_ERR(plat_data->phytium_macb_pdata.tsu_clk); + goto err_tsuclk_register; + } + + return 0; + +err_tsuclk_register: + clk_unregister(plat_data->phytium_macb_pdata.rxclk); + +err_rxclk_register: + clk_unregister(plat_data->phytium_macb_pdata.txclk); + +err_txclk_register: + + return err; +} + +static void phytium_macb_pci_uninit(struct pci_dev *pdev) +{ + struct platform_device *plat_dev = pci_get_drvdata(pdev); + struct macb_platform_data *plat_data = dev_get_platdata(&plat_dev->dev); + + plat_dev->dev.dma_ops = NULL; + plat_dev->dev.iommu = NULL; + plat_dev->dev.iommu_group = NULL; + plat_dev->dev.dma_range_map = NULL; + + clk_unregister(plat_data->phytium_macb_pdata.txclk); + clk_unregister(plat_data->phytium_macb_pdata.rxclk); + clk_unregister(plat_data->phytium_macb_pdata.tsu_clk); + + if (plat_data->phytium_macb_pdata.properties) { + struct fwnode_handle *fw_node = dev_fwnode(&plat_dev->dev); + + if (fw_node) + fwnode_remove_software_node(fw_node); + fw_node = NULL; + } +} static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int err; struct platform_device *plat_dev; struct platform_device_info plat_info; + struct phytium_platform_pdata *phytium_data = NULL; struct macb_platform_data plat_data; struct resource res[2]; + char pclk_name[20] = "pclk"; + char hclk_name[20] = "hclk"; /* enable pci device */ err = pcim_enable_device(pdev); @@ -48,9 +197,6 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id) res[0].end = pci_resource_end(pdev, 0); res[0].name = PCI_DRIVER_NAME; res[0].flags = IORESOURCE_MEM; - res[1].start = pci_irq_vector(pdev, 0); - res[1].name = PCI_DRIVER_NAME; - res[1].flags = IORESOURCE_IRQ; dev_info(&pdev->dev, "EMAC physical base addr: %pa\n", &res[0].start); @@ -58,32 +204,48 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* set up macb platform data */ memset(&plat_data, 0, sizeof(plat_data)); + /* set up platform device info */ + memset(&plat_info, 0, sizeof(plat_info)); + plat_info.parent = &pdev->dev; + plat_info.fwnode = pdev->dev.fwnode; + plat_info.name = PLAT_DRIVER_NAME; + plat_info.id = pdev->devfn; + plat_info.res = res; + plat_info.num_res = ARRAY_SIZE(res); + plat_info.data = &plat_data; + plat_info.size_data = sizeof(plat_data); + plat_info.dma_mask = pdev->dma_mask; + if (pdev->vendor == PCI_VENDOR_ID_PHYTIUM) + phytium_data = (struct phytium_platform_pdata *)id->driver_data; + /* initialize clocks */ - plat_data.pclk = clk_register_fixed_rate(&pdev->dev, "pclk", NULL, 0, + if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) { + plat_info.id = (pdev->bus->number << 8) | pdev->devfn; + snprintf(pclk_name, 20, "pclk:%02x", plat_info.id); + snprintf(hclk_name, 20, "hclk:%02x", plat_info.id); + } + plat_data.pclk = clk_register_fixed_rate(&pdev->dev, pclk_name, NULL, 0, GEM_PCLK_RATE); if (IS_ERR(plat_data.pclk)) { err = PTR_ERR(plat_data.pclk); goto err_pclk_register; } - plat_data.hclk = clk_register_fixed_rate(&pdev->dev, "hclk", NULL, 0, + plat_data.hclk = clk_register_fixed_rate(&pdev->dev, hclk_name, NULL, 0, GEM_HCLK_RATE); if (IS_ERR(plat_data.hclk)) { err = PTR_ERR(plat_data.hclk); goto err_hclk_register; } - /* set up platform device info */ - memset(&plat_info, 0, sizeof(plat_info)); - plat_info.parent = &pdev->dev; - plat_info.fwnode = pdev->dev.fwnode; - plat_info.name = PLAT_DRIVER_NAME; - plat_info.id = pdev->devfn; - plat_info.res = res; - plat_info.num_res = ARRAY_SIZE(res); - plat_info.data = &plat_data; - plat_info.size_data = sizeof(plat_data); - plat_info.dma_mask = pdev->dma_mask; + if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) { + if (phytium_macb_pci_init(pdev, &plat_data, &plat_info, phytium_data)) + goto err_phytium_clk_register; + } + + res[1].start = pci_irq_vector(pdev, 0); + res[1].name = PCI_DRIVER_NAME; + res[1].flags = IORESOURCE_IRQ; /* register platform device */ plat_dev = platform_device_register_full(&plat_info); @@ -92,11 +254,21 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_plat_dev_register; } + if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) { + plat_dev->dev.dma_ops = (&pdev->dev)->dma_ops; + plat_dev->dev.iommu = (&pdev->dev)->iommu; + plat_dev->dev.iommu_group = (&pdev->dev)->iommu_group; + plat_dev->dev.dma_range_map = (&pdev->dev)->dma_range_map; + } pci_set_drvdata(pdev, plat_dev); return 0; err_plat_dev_register: + if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) + clk_unregister(plat_data.phytium_macb_pdata.tsu_clk); + +err_phytium_clk_register: clk_unregister(plat_data.hclk); err_hclk_register: @@ -113,11 +285,26 @@ static void macb_remove(struct pci_dev *pdev) clk_unregister(plat_data->pclk); clk_unregister(plat_data->hclk); + + if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) + phytium_macb_pci_uninit(pdev); platform_device_unregister(plat_dev); + + if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) + pci_free_irq_vectors(pdev); } static const struct pci_device_id dev_id_table[] = { { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC_3P0, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_SGMII), + .driver_data = (kernel_ulong_t)&phytium_sgmii_pdata}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC_3P0, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_1000BASEX), + .driver_data = (kernel_ulong_t)&phytium_1000basex_pdata}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC_3P0, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_USXGMII), + .driver_data = (kernel_ulong_t)&phytium_usxgmii_pdata}, { 0, } }; diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 53f14c5a9e02c58b53279741f2a69da352ca7557..3f2034ef863b8d1df94cdc78bb14b96ef169dd8b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -117,6 +117,16 @@ config DWMAC_OXNAS This selects the Oxford Semiconductor OXNASSoC glue layer support for the stmmac device driver. This driver is used for OX820. +config DWMAC_PHYTIUM + tristate "Phytium dwmac support" + default ARCH_PHYTIUM + depends on (OF || ACPI) && (ARCH_PHYTIUM || COMPILE_TEST) + help + Support for GMAC controller on Phytium SoCs. + + This selects the Phytium GMAC glue layer support for the + stmmac device driver. + config DWMAC_QCOM_ETHQOS tristate "Qualcomm ETHQOS support" default ARCH_QCOM diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 24e6145d4eae50822359ef9b1c138ab3a39ba170..04823f06c2e7843b04935bd5d1ccf762e4b50021 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o obj-$(CONFIG_DWMAC_OXNAS) += dwmac-oxnas.o +obj-$(CONFIG_DWMAC_PHYTIUM) += dwmac-phytium.o obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.o obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c new file mode 100755 index 0000000000000000000000000000000000000000..0dbf6eef7df5af29186cb78af9e271fff18bbb2c --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SWMAC specific glue layer + * + * Copyright (c) 2022-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "stmmac.h" +#include "stmmac_platform.h" + +static int phytium_get_mac_mode(struct fwnode_handle *fwnode) +{ + const char *pm; + int err, i; + + err = fwnode_property_read_string(fwnode, "mac-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phy_modes(i))) + return i; + } + + return -ENODEV; +} + +static int phytium_dwmac_acpi_phy(struct plat_stmmacenet_data *plat, + struct fwnode_handle *np, struct device *dev) +{ + plat->mdio_bus_data = devm_kzalloc(dev, + sizeof(struct stmmac_mdio_bus_data), + GFP_KERNEL); + + if (!plat->mdio_bus_data) + return -ENOMEM; + + return 0; +} + +static int phytium_dwmac_probe(struct platform_device *pdev) +{ + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); + struct plat_stmmacenet_data *plat; + struct stmmac_resources stmmac_res; + struct device_node *np = pdev->dev.of_node; + u64 clk_freq; + char clk_name[20]; + int ret; + + plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return -ENOMEM; + + plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); + if (!plat->dma_cfg) + return -ENOMEM; + + plat->axi = devm_kzalloc(&pdev->dev, sizeof(*plat->axi), GFP_KERNEL); + if (!plat->axi) + return -ENOMEM; + + plat->phy_interface = device_get_phy_mode(&pdev->dev); + if (plat->phy_interface < 0) + return plat->phy_interface; + + plat->interface = phytium_get_mac_mode(fwnode); + if (plat->interface < 0) + plat->interface = plat->phy_interface; + + /* Configure PHY if using device-tree */ + if (pdev->dev.of_node) { + plat->phy_node = of_parse_phandle(np, "phy-handle", 0); + plat->phylink_node = np; + } + + if (pdev->dev.of_node) { + plat->bus_id = of_alias_get_id(np, "ethernet"); + if (plat->bus_id < 0) + plat->bus_id = 0; + } else if (fwnode_property_read_u32(fwnode, "bus_id", &plat->bus_id)) { + plat->bus_id = 2; + } + + plat->phy_addr = -1; + plat->clk_csr = -1; + plat->has_gmac = 1; + plat->enh_desc = 1; + plat->bugged_jumbo = 1; + plat->pmt = 1; + plat->force_sf_dma_mode = 1; + + if (fwnode_property_read_u32(fwnode, "max-speed", &plat->max_speed)) + plat->max_speed = -1; + + if (fwnode_property_read_u32(fwnode, "max-frame-size", &plat->maxmtu)) + plat->maxmtu = JUMBO_LEN; + + if (fwnode_property_read_u32(fwnode, "snps,multicast-filter-bins", + &plat->multicast_filter_bins)) + plat->multicast_filter_bins = HASH_TABLE_SIZE; + + if (fwnode_property_read_u32(fwnode, "snps,perfect-filter-entries", + &plat->unicast_filter_entries)) + plat->unicast_filter_entries = 1; + + if (fwnode_property_read_u32(fwnode, "tx-fifo-depth", &plat->tx_fifo_size)) + plat->tx_fifo_size = 0x1000; + + if (fwnode_property_read_u32(fwnode, "rx-fifo-depth", &plat->rx_fifo_size)) + plat->rx_fifo_size = 0x1000; + + if (phytium_dwmac_acpi_phy(plat, fwnode, &pdev->dev)) + return -ENODEV; + + plat->rx_queues_to_use = 1; + plat->tx_queues_to_use = 1; + plat->rx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; + plat->tx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; + + if (fwnode_property_read_u64(fwnode, "clock-frequency", &clk_freq)) + clk_freq = 125000000; + + /* Set system clock */ + snprintf(clk_name, sizeof(clk_name), "%s-%d", "stmmaceth", plat->bus_id); + + plat->stmmac_clk = clk_register_fixed_rate(&pdev->dev, clk_name, NULL, 0, clk_freq); + if (IS_ERR(plat->stmmac_clk)) { + dev_warn(&pdev->dev, "Fail to register stmmac-clk\n"); + plat->stmmac_clk = NULL; + } + + ret = clk_prepare_enable(plat->stmmac_clk); + if (ret) { + clk_unregister_fixed_rate(plat->stmmac_clk); + return ret; + } + + plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk); + plat->clk_ptp_ref = NULL; + + if (fwnode_property_read_u32(fwnode, "snps,pbl", &plat->dma_cfg->pbl)) + plat->dma_cfg->pbl = 16; + + fwnode_property_read_u32(fwnode, "snps,txpbl", &plat->dma_cfg->txpbl); + fwnode_property_read_u32(fwnode, "snps,rxpbl", &plat->dma_cfg->rxpbl); + + plat->dma_cfg->pblx8 = !fwnode_property_read_bool(fwnode, "snps,no-pbl-x8"); + plat->dma_cfg->aal = fwnode_property_read_bool(fwnode, "snps,aal"); + plat->dma_cfg->fixed_burst = fwnode_property_read_bool(fwnode, "snps,fixed-burst"); + plat->dma_cfg->mixed_burst = fwnode_property_read_bool(fwnode, "snps,mixed-burst"); + + plat->axi->axi_lpi_en = false; + plat->axi->axi_xit_frm = false; + plat->axi->axi_wr_osr_lmt = 7; + plat->axi->axi_rd_osr_lmt = 7; + plat->axi->axi_blen[0] = 16; + + memset(&stmmac_res, 0, sizeof(stmmac_res)); + stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0); + stmmac_res.irq = platform_get_irq(pdev, 0); + if (stmmac_res.irq < 0) { + dev_err(&pdev->dev, "IRQ not found.\n"); + return -ENXIO; + } + stmmac_res.wol_irq = stmmac_res.irq; + stmmac_res.lpi_irq = -1; + + return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); +} + +int phytium_dwmac_remove(struct platform_device *pdev) +{ + int ret; + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + struct plat_stmmacenet_data *plat = priv->plat; + + ret = stmmac_pltfr_remove(pdev); + clk_unregister_fixed_rate(plat->stmmac_clk); + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id phytium_dwmac_of_match[] = { + { .compatible = "phytium,gmac" }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_dwmac_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_dwmac_acpi_ids[] = { + { .id = "PHYT0004" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, phytium_dwmac_acpi_ids); +#endif + +static struct platform_driver phytium_dwmac_driver = { + .probe = phytium_dwmac_probe, + .remove = phytium_dwmac_remove, + .driver = { + .name = "phytium-dwmac", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = of_match_ptr(phytium_dwmac_of_match), + .acpi_match_table = ACPI_PTR(phytium_dwmac_acpi_ids), + }, +}; +module_platform_driver(phytium_dwmac_driver); + +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium DWMAC specific glue layer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index ed601a7e46a0cdd24d9594caba1c879c352830c9..bd2757605d1576c558a5b9d496e73a80d559adc8 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -92,6 +92,10 @@ #define AT803X_DEBUG_REG_5 0x05 #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) +#define AT803X_DEBUG_REG_B 0x0B +#define AT803X_DEBUG_REG_B_HIBERNATION_ENABLE 0x1 +#define AT803X_DEBUG_REG_B_HIBERNATION_OFFSET 15 + #define AT803X_DEBUG_REG_1F 0x1F #define AT803X_DEBUG_PLL_ON BIT(2) #define AT803X_DEBUG_RGMII_1V8 BIT(3) @@ -202,6 +206,20 @@ static int at803x_enable_tx_delay(struct phy_device *phydev) AT803X_DEBUG_TX_CLK_DLY_EN); } +static inline int at803x_disable_hibernate(struct phy_device *phydev) +{ + int ret = 0; + u16 val = 0; + + ret = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_B); + if (ret < 0) + return ret; + + val = ret & 0xffff; + val &= (~(AT803X_DEBUG_REG_B_HIBERNATION_ENABLE << AT803X_DEBUG_REG_B_HIBERNATION_OFFSET)); + return phy_write(phydev, AT803X_DEBUG_DATA, val); +} + static int at803x_disable_rx_delay(struct phy_device *phydev) { return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, @@ -563,6 +581,10 @@ static int at803x_config_init(struct phy_device *phydev) { int ret; + ret = at803x_disable_hibernate(phydev); + if (ret < 0) + return ret; + /* The RX and TX delay default is: * after HW reset: RX delay enabled and TX delay disabled * after SW reset: RX delay enabled, while TX delay retains the diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e06fde258d407f8202dc5e56046df8a21c4505c0..65ef9bcf22554a04df0a5ba03f457f715605021a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -611,6 +611,19 @@ config SPI_PHYTIUM_PCI If unsure, say N. +config SPI_PHYTIUM_QSPI + tristate "Phytium Quad SPI controller" + depends on ARCH_PHYTIUM || COMPILE_TEST + depends on OF + depends on SPI_MEM + help + This enables support for Phytium Quad SPI flash controller. + + This driver does not support generic SPI. The implementation only + supports spi-mem interface. + + If unsure, say N. + config SPI_PIC32 tristate "Microchip PIC32 series SPI" depends on MACH_PIC32 || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index ccc12c8ac4ec4b411f1d6c4682551b32b26280ee..567bb20731064e14d2b97bc3ea0425e3d6ac4a3a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -85,7 +85,8 @@ obj-$(CONFIG_SPI_ORION) += spi-orion.o obj-$(CONFIG_SPI_PHYTIUM) += spi-phytium.o obj-$(CONFIG_SPI_PHYTIUM_PLAT) += spi-phytium-plat.o obj-$(CONFIG_SPI_PHYTIUM_PCI) += spi-phytium-pci.o -obj-$(CONFIG_SPI_PHYTIUM) += spi-phytium-dma.o +obj-$(CONFIG_SPI_PHYTIUM_QSPI) += spi-phytium-qspi.o +obj-$(CONFIG_SPI_PHYTIUM) += spi-phytium-dma.o obj-$(CONFIG_SPI_PIC32) += spi-pic32.o obj-$(CONFIG_SPI_PIC32_SQI) += spi-pic32-sqi.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o diff --git a/drivers/spi/spi-phytium-dma.c b/drivers/spi/spi-phytium-dma.c index d6b14e1831228188eccadd9afe90f70e3403c228..4f78d0659ef4f0080ae19a4a977c68bc1f1fd862 100644 --- a/drivers/spi/spi-phytium-dma.c +++ b/drivers/spi/spi-phytium-dma.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "spi-phytium.h" #define RX_BUSY 0 @@ -550,3 +551,5 @@ void phytium_spi_dmaops_set(struct phytium_spi *fts) fts->dma_ops = &phytium_spi_dma_generic_ops; } EXPORT_SYMBOL_GPL(phytium_spi_dmaops_set); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-phytium-qspi.c b/drivers/spi/spi-phytium-qspi.c new file mode 100755 index 0000000000000000000000000000000000000000..e909a8505d8646637a8d244162438310b2b15c58 --- /dev/null +++ b/drivers/spi/spi-phytium-qspi.c @@ -0,0 +1,801 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium Quad SPI controller driver. + * + * Copyright (c) 2022-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define QSPI_FLASH_CAP_REG 0x00 +#define QSPI_FLASH_CAP_NUM_SHIFT 3 +#define QSPI_FLASH_CAP_NUM_MASK (0x3 << QSPI_FLASH_CAP_NUM_SHIFT) +#define QSPI_FLASH_CAP_CAP_SHIFT 0 +#define QSPI_FLASH_CAP_CAP_MASK (0x7 << QSPI_FLASH_CAP_CAP_SHIFT) + +#define QSPI_RD_CFG_REG 0x04 +#define QSPI_RD_CFG_RD_CMD_SHIFT 24 +#define QSPI_RD_CFG_RD_CMD_MASK (0xff << QSPI_RD_CFG_RD_CMD_SHIFT) +#define QSPI_RD_CFG_RD_THROUGH_SHIFT 23 +#define QSPI_RD_CFG_RD_THROUGH_MASK (0x1 << QSPI_RD_CFG_RD_THROUGH_SHIFT) +#define QSPI_RD_CFG_RD_TRANSFER_SHIFT 20 +#define QSPI_RD_CFG_RD_TRANSFER_MASK (0x7 << QSPI_RD_CFG_RD_TRANSFER_SHIFT) +#define QSPI_RD_CFG_RD_ADDR_SEL_SHIFT 19 +#define QSPI_RD_CFG_RD_ADDR_SEL_MASK (0x1 << QSPI_RD_CFG_RD_ADDR_SEL_SHIFT) +#define QSPI_RD_CFG_RD_LATENCY_SHIFT 18 +#define QSPI_RD_CFG_RD_LATENCY_MASK (0x1 << QSPI_RD_CFG_RD_LATENCY_SHIFT) +#define QSPI_RD_CFG_MODE_BYTE_SHIFT 17 +#define QSPI_RD_CFG_MODE_BYTE_MASK (0x1 << QSPI_RD_CFG_MODE_BYTE_SHIFT) +#define QSPI_RD_CFG_CMD_SIGN_SHIFT 9 +#define QSPI_RD_CFG_CMD_SIGN_MASK (0xff << QSPI_RD_CFG_CMD_SIGN_SHIFT) +#define QSPI_RD_CFG_DUMMY_SHIFT 4 +#define QSPI_RD_CFG_DUMMY_MASK (0x1f << QSPI_RD_CFG_DUMMY_SHIFT) +#define QSPI_RD_CFG_D_BUFFER_SHIFT 3 +#define QSPI_RD_CFG_D_BUFFER_MASK (0x1 << QSPI_RD_CFG_D_BUFFER_SHIFT) +#define QSPI_RD_CFG_RD_SCK_SEL_SHIFT 0 +#define QSPI_RD_CFG_RD_SCK_SEL_MASK (0x7 << QSPI_RD_CFG_RD_SCK_SEL_SHIFT) + +#define QSPI_WR_CFG_REG 0x08 +#define QSPI_WR_CFG_WR_CMD_SHIFT 24 +#define QSPI_WR_CFG_WR_CMD_MASK (0xff << QSPI_WR_CFG_WR_CMD_SHIFT) +#define QSPI_WR_CFG_WR_WAIT_SHIFT 9 +#define QSPI_WR_CFG_WR_WAIT_MASK (0x01 << QSPI_WR_CFG_WR_WAIT_SHIFT) +#define QSPI_WR_CFG_WR_THROUGH_SHIFT 8 +#define QSPI_WR_CFG_WR_THROUGH_MASK (0x01 << QSPI_WR_CFG_WR_THROUGH_SHIFT) +#define QSPI_WR_CFG_WR_TRANSFER_SHIFT 5 +#define QSPI_WR_CFG_WR_TRANSFER_MASK (0X7 << QSPI_WR_CFG_WR_TRANSFER_SHIFT) +#define QSPI_WR_CFG_WR_ADDR_SEL_SHIFT 4 +#define QSPI_WR_CFG_WR_ADDR_SEL_MASK (0x1 << QSPI_WR_CFG_WR_ADDR_SEL_SHIFT) +#define QSPI_WR_CFG_WR_MODE_SHIFT 3 +#define QSPI_WR_CFG_WR_MODE_MASK (0x1 << QSPI_WR_CFG_WR_MODE_SHIFT) +#define QSPI_WR_CFG_WR_SCK_SEL_SHIFT 0 +#define QSPI_WR_CFG_WR_SCK_SEL_MASK (0x7 << QSPI_WR_CFG_WR_SCK_SEL_SHIFT) + +#define QSPI_FLUSH_REG 0x0c +#define QSPI_FLUSH_EN (0x1 << 0) + +#define QSPI_CMD_PORT_REG 0x10 +#define QSPI_CMD_PORT_CMD_SHIFT 24 +#define QSPI_CMD_PORT_CMD_MASK (0xff << QSPI_CMD_PORT_CMD_SHIFT) +#define QSPI_CMD_PORT_WAIT_SHIFT 22 +#define QSPI_CMD_PORT_WAIT_MASK (0x1 << QSPI_CMD_PORT_WAIT_SHIFT) +#define QSPI_CMD_PORT_THROUGH_SHIFT 21 +#define QSPI_CMD_PORT_THROUGH_MASK (0x1 << QSPI_CMD_PORT_THROUGH_SHIFT) +#define QSPI_CMD_PORT_CS_SHIFT 19 +#define QSPI_CMD_PORT_CS_MASK (0x3 << QSPI_CMD_PORT_CS_SHIFT) +#define QSPI_CMD_PORT_TRANSFER_SHIFT 16 +#define QSPI_CMD_PORT_TRANSFER_MASK (0x7 << QSPI_CMD_PORT_TRANSFER_SHIFT) +#define QSPI_CMD_PORT_CMD_ADDR_SHIFT 15 +#define QSPI_CMD_PORT_CMD_ADDR_MASK (0x1 << QSPI_CMD_PORT_CMD_ADDR_SHIFT) +#define QSPI_CMD_PORT_LATENCY_SHIFT 14 +#define QSPI_CMD_PORT_LATENCY_MASK (0x1 << QSPI_CMD_PORT_LATENCY_SHIFT) +#define QSPI_CMD_PORT_DATA_XFER_SHIFT 13 +#define QSPI_CMD_PORT_DATA_XFER_MASK (0x1 << QSPI_CMD_PORT_DATA_XFER_SHIFT) +#define QSPI_CMD_PORT_ADDR_SEL_SHIFT 12 +#define QSPI_CMD_PORT_ADDR_SEL_MASK (0x1 << QSPI_CMD_PORT_ADDR_SEL_SHIFT) +#define QSPI_CMD_PORT_DUMMY_SHIFT 7 +#define QSPI_CMD_PORT_DUMMY_MASK (0x1f << QSPI_CMD_PORT_DUMMY_SHIFT) +#define QSPI_CMD_PORT_P_BUFFER_SHIFT 6 +#define QSPI_CMD_PORT_P_BUFFER_MASK (0x1 << QSPI_CMD_PORT_P_BUFFER_SHIFT) +#define QSPI_CMD_PORT_RW_NUM_SHIFT 3 +#define QSPI_CMD_PORT_RW_NUM_MASK (0x7 << QSPI_CMD_PORT_RW_NUM_SHIFT) +#define QSPI_CMD_PORT_SCK_SEL_SHIFT 0 +#define QSPI_CMD_PORT_SCK_SEL_MASK (0x7 << QSPI_CMD_PORT_SCK_SEL_SHIFT) + +#define QSPI_ADDR_PORT_REG 0x14 +#define QSPI_HD_PORT_REG 0x18 +#define QSPI_LD_PORT_REG 0x1c + +#define QSPI_FUN_SET_REG 0x20 +#define QSPI_FUN_SET_HOLD_SHIFT 24 +#define QSPI_FUN_SET_HOLD_MASK (0xff << QSPI_FUN_SET_HOLD_SHIFT) +#define QSPI_FUN_SET_SETUP_SHIFT 16 +#define QSPI_FUN_SET_SETUP_MASK (0xff << QSPI_FUN_SET_SETUP_SHIFT) +#define QSPI_FUN_SET_DELAY_SHIFT 0 +#define QSPI_FUN_SET_DELAY_MASK (0xffff << QSPI_FUN_SET_DELAY_SHIFT) + +#define QSPI_WIP_REG 0x24 +#define QSPI_WIP_W_CMD_SHIFT 24 +#define QSPI_WIP_W_CMD_MASK (0xff << QSPI_WIP_W_CMD_SHIFT) +#define QSPI_WIP_W_TRANSFER_SHIFT 3 +#define QSPI_WIP_W_TRANSFER_MASK (0x3 << QSPI_WIP_W_TRANSFER_SHIFT) +#define QSPI_WIP_W_SCK_SEL_SHIFT 0 +#define QSPI_WIP_W_SCK_SEL_MASK (0x7 << QSPI_WIP_W_SCK_SEL_SHIFT) + +#define QSPI_WP_REG 0x28 +#define QSPI_WP_EN_SHIFT 17 +#define QSPI_WP_EN_MASK (0x1 << QSPI_WP_EN_SHIFT) +#define QSPI_WP_IO2_SHIFT 16 +#define QSPI_WP_IO2_MASK (0x1 << QSPI_WP_IO2_SHIFT) +#define QSPI_WP_HOLD_SHIFT 8 +#define QSPI_WP_HOLD_MASK (0xff << QSPI_WP_HOLD_SHIFT) +#define QSPI_WP_SETUP_SHIFT 0 +#define QSPI_WP_SETUP_MASK (0xff << QSPI_WP_SETUP_SHIFT) + +#define QSPI_MODE_REG 0x2c +#define QSPI_MODE_VALID_SHIFT 8 +#define QSPI_MODE_VALID_MASK (0xff << QSPI_MODE_VALID_SHIFT) +#define QSPI_MODE_SHIFT 0 +#define QSPI_MODE_MASK (0xff << QSPI_MODE_SHIFT) + +#define PHYTIUM_QSPI_MAX_NORCHIP 4 +#define PHYTIUM_QSPI_MAX_MMAP_SZ (SZ_256M * PHYTIUM_QSPI_MAX_NORCHIP) +#define PHYTIUM_QSPI_MAX_XFER_SZ 8 +#define PHYTIUM_QSPI_DEFAULT_SCK_SEL 5 + +#define XFER_PROTO_1_1_1 0x0 +#define XFER_PROTO_1_1_2 0x1 +#define XFER_PROTO_1_1_4 0x2 +#define XFER_PROTO_1_2_2 0x3 +#define XFER_PROTO_1_4_4 0x4 +#define XFER_PROTO_2_2_2 0x5 +#define XFER_PROTO_4_4_4 0x6 + +struct phytium_qspi_flash { + u32 cs; + u32 clk_div; + + void __iomem *base; + resource_size_t size; + struct spi_device *spi; +}; + +struct phytium_qspi { + struct device *dev; + struct spi_controller *ctrl; + + void __iomem *io_base; + void __iomem *mm_base; + resource_size_t mm_size; + resource_size_t used_size; + + struct clk *clk; + u32 clk_rate; + + struct phytium_qspi_flash flash[PHYTIUM_QSPI_MAX_NORCHIP]; + u8 fnum; + bool nodirmap; +}; + +static bool phytium_qspi_check_buswidth(u8 width) +{ + switch (width) { + case 1: + case 2: + case 4: + return 0; + } + + return -ENOTSUPP; +} + +static uint phytium_spi_nor_clac_clk_div(int div) +{ + uint clk_div = 0; + + if (div <= 2) + clk_div = 1; + else if (div <= 4) + clk_div = 2; + else if (div <= 8) + clk_div = 3; + else if (div <= 16) + clk_div = 4; + else if (div <= 32) + clk_div = 5; + else if (div <= 64) + clk_div = 6; + else if (div <= 128) + clk_div = 7; + else + clk_div = 65535; + + return clk_div; +} + +static int phytium_spi_nor_protocol_encode(const struct spi_mem_op *op, u32 *code) +{ + int ret = 0; + + if (op->cmd.buswidth == 1 && + op->addr.buswidth == 1 && + op->data.buswidth == 1) + *code = XFER_PROTO_1_1_1; + else if (op->cmd.buswidth == 1 && + op->addr.buswidth == 1 && + op->data.buswidth == 2) + *code = XFER_PROTO_1_1_2; + else if (op->cmd.buswidth == 1 && + op->addr.buswidth == 1 && + op->data.buswidth == 4) + *code = XFER_PROTO_1_1_4; + else if (op->cmd.buswidth == 1 && + op->addr.buswidth == 2 && + op->data.buswidth == 2) + *code = XFER_PROTO_1_2_2; + else if (op->cmd.buswidth == 1 && + op->addr.buswidth == 4 && + op->data.buswidth == 4) + *code = XFER_PROTO_1_4_4; + else if (op->cmd.buswidth == 2 && + op->addr.buswidth == 2 && + op->data.buswidth == 2) + *code = XFER_PROTO_2_2_2; + else if (op->cmd.buswidth == 4 && + op->addr.buswidth == 4 && + op->data.buswidth == 4) + *code = XFER_PROTO_4_4_4; + else + *code = XFER_PROTO_1_1_1; + + return ret; +} + +static int phytium_qspi_flash_capacity_encode(u32 size, u32 *cap) +{ + int ret = 0; + + switch (size) { + case SZ_4M: + *cap = 0x0; + break; + case SZ_8M: + *cap = 0x1; + break; + case SZ_16M: + *cap = 0x2; + break; + case SZ_32M: + *cap = 0x3; + break; + case SZ_64M: + *cap = 0x4; + break; + case SZ_128M: + *cap = 0x5; + break; + case SZ_256M: + *cap = 0x6; + break; + case SZ_512M: + *cap = 0x7; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int phytium_qspi_write_port(struct phytium_qspi *qspi, + const u8 *buf, const size_t len) +{ + u32 bouncebuf[2] = { 0 }; + + if (len > PHYTIUM_QSPI_MAX_XFER_SZ) { + dev_err(qspi->dev, "WRITE data exceeds 8 bytes.\n"); + return -EINVAL; + } + + memcpy(bouncebuf, buf, len); + + if (len > 4) + writel_relaxed(bouncebuf[1], qspi->io_base + QSPI_HD_PORT_REG); + writel_relaxed(bouncebuf[0], qspi->io_base + QSPI_LD_PORT_REG); + + return 0; +} + +static int phytium_qspi_read_port(struct phytium_qspi *qspi, + u8 *buf, size_t len) +{ + u32 bouncebuf[2] = { 0 }; + + if (len > PHYTIUM_QSPI_MAX_XFER_SZ) { + dev_err(qspi->dev, "READ data exceeds 8 bytes.\n"); + return -EINVAL; + } + + /* Dummy write to LD_PORT register and issue READ ops*/ + writel_relaxed(0, qspi->io_base + QSPI_LD_PORT_REG); + + /* Read data */ + bouncebuf[0] = readl_relaxed(qspi->io_base + QSPI_LD_PORT_REG); + if (len > 4) + bouncebuf[1] = readl_relaxed(qspi->io_base + QSPI_HD_PORT_REG); + + memcpy(buf, bouncebuf, len); + + return 0; +} + +static int phytium_qspi_adjust_op_size(struct spi_mem *mem, + struct spi_mem_op *op) +{ + if (op->data.nbytes > PHYTIUM_QSPI_MAX_XFER_SZ) + op->data.nbytes = PHYTIUM_QSPI_MAX_XFER_SZ; + + return 0; +} + +static bool phytium_qspi_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + int ret; + + ret = phytium_qspi_check_buswidth(op->cmd.buswidth); + + if (op->addr.nbytes) + ret |= phytium_qspi_check_buswidth(op->addr.buswidth); + + if (op->dummy.nbytes) + ret |= phytium_qspi_check_buswidth(op->dummy.buswidth); + + if (op->data.nbytes) + ret |= phytium_qspi_check_buswidth(op->data.buswidth); + + if (ret) + return false; + + /* Max 32 dummy clock cycles supported */ + if (op->dummy.nbytes && + (op->dummy.nbytes * 8 / op->dummy.buswidth > 32)) + return false; + + return spi_mem_default_supports_op(mem, op); +} + +static int phytium_qspi_exec_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct phytium_qspi *qspi = spi_controller_get_devdata(mem->spi->master); + struct phytium_qspi_flash *flash = &qspi->flash[mem->spi->chip_select]; + u32 cmd, transfer; + int ret; + + dev_dbg(qspi->dev, "cmd:%#x mode: %d.%d.%d.%d addr:%#llx len:%#x\n", + op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth, op->addr.val, + op->data.nbytes); + + cmd = op->cmd.opcode << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + ret = phytium_spi_nor_protocol_encode(op, &transfer); + if (ret) { + dev_err(qspi->dev, "Unsupported SPI NOR protocol.\n"); + goto out; + } + cmd |= transfer << QSPI_CMD_PORT_TRANSFER_SHIFT; + + if (op->addr.nbytes) { + cmd |= QSPI_CMD_PORT_CMD_ADDR_MASK; + if (op->addr.nbytes == 4) + cmd |= QSPI_CMD_PORT_ADDR_SEL_MASK; + + /* Write target address to ADDR_PORT register */ + writel_relaxed(op->addr.val, qspi->io_base + QSPI_ADDR_PORT_REG); + } + + if (op->dummy.nbytes) { + cmd |= QSPI_CMD_PORT_LATENCY_MASK; + cmd |= ((op->dummy.nbytes * 8) / op->dummy.buswidth) << + QSPI_CMD_PORT_LATENCY_SHIFT; + } + + if (op->data.nbytes) { + cmd |= QSPI_CMD_PORT_DATA_XFER_MASK; + cmd &= ~QSPI_CMD_PORT_P_BUFFER_MASK; + cmd |= (op->data.nbytes-1) << QSPI_CMD_PORT_RW_NUM_SHIFT; + } + + cmd |= flash->clk_div; + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + + if (op->data.dir == SPI_MEM_DATA_IN) { + ret = phytium_qspi_read_port(qspi, op->data.buf.in, op->data.nbytes); + if (ret) { + dev_err(qspi->dev, "Failed to read data from the port.\n"); + goto out; + } + } else if (op->data.dir == SPI_MEM_DATA_OUT) { + ret = phytium_qspi_write_port(qspi, op->data.buf.out, op->data.nbytes); + if (ret) { + dev_err(qspi->dev, "Failed to write data to the port.\n"); + goto out; + } + } else { + /* Dummy write to LD_PORT register and issue the command */ + writel_relaxed(0, qspi->io_base + QSPI_LD_PORT_REG); + } + +out: + return ret; +} + +static int phytium_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc) +{ + struct spi_device *spi = desc->mem->spi; + struct phytium_qspi *qspi = spi_controller_get_devdata(spi->master); + struct phytium_qspi_flash *flash = &qspi->flash[spi->chip_select]; + struct spi_nor *nor = spi_mem_get_drvdata(desc->mem); + u32 cmd, transfer; + int ret = 0; + + if (!qspi->mm_base || !qspi->mm_size) { + ret = -EOPNOTSUPP; + goto out; + } + + if (!flash->base) { + flash->base = qspi->mm_base + qspi->used_size; + qspi->used_size += nor->mtd.size; + } + + /* Setup RD/WR_CFG register */ + if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) { + cmd = desc->info.op_tmpl.cmd.opcode << QSPI_RD_CFG_RD_CMD_SHIFT; + ret = phytium_spi_nor_protocol_encode(&desc->info.op_tmpl, &transfer); + if (ret) { + dev_err(qspi->dev, "Unsupported SPI NOR protocol.\n"); + goto out; + } + cmd |= transfer << QSPI_RD_CFG_RD_TRANSFER_SHIFT; + + if (desc->info.op_tmpl.addr.nbytes == 4) + cmd |= QSPI_RD_CFG_RD_ADDR_SEL_MASK; + + if (nor->read_dummy) { + cmd |= QSPI_RD_CFG_RD_LATENCY_MASK; + cmd |= (nor->read_dummy - 1) << QSPI_RD_CFG_DUMMY_SHIFT; + } + + cmd |= QSPI_RD_CFG_D_BUFFER_MASK; + cmd |= flash->clk_div & QSPI_RD_CFG_RD_SCK_SEL_MASK; + + writel_relaxed(cmd, qspi->io_base + QSPI_RD_CFG_REG); + + dev_dbg(qspi->dev, "Create read dirmap and setup RD_CFG_REG [%#x].\n", cmd); + } else if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { + cmd = desc->info.op_tmpl.cmd.opcode << QSPI_WR_CFG_WR_CMD_SHIFT; + ret = phytium_spi_nor_protocol_encode(&desc->info.op_tmpl, &transfer); + if (ret) { + dev_err(qspi->dev, "Unsupported SPI NOR protocol.\n"); + goto out; + } + cmd |= transfer << QSPI_WR_CFG_WR_TRANSFER_SHIFT; + + if (desc->info.op_tmpl.addr.nbytes == 4) + cmd |= QSPI_WR_CFG_WR_ADDR_SEL_MASK; + + cmd |= QSPI_WR_CFG_WR_MODE_MASK; + cmd |= flash->clk_div & QSPI_WR_CFG_WR_SCK_SEL_MASK; + + writel_relaxed(cmd, qspi->io_base + QSPI_WR_CFG_REG); + + dev_dbg(qspi->dev, "Create write dirmap and setup WR_CFG_REG [%#x].\n", cmd); + } else { + ret = -EINVAL; + } + +out: + return ret; +} + +static ssize_t phytium_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) +{ + struct spi_device *spi = desc->mem->spi; + struct phytium_qspi *qspi = spi_controller_get_devdata(spi->master); + struct phytium_qspi_flash *flash = &qspi->flash[spi->chip_select]; + + void __iomem *src = flash->base + offs; + u8 *buf_rx = buf; + + memcpy_fromio(buf_rx, src, len); + + return len; +} + +static ssize_t phytium_qspi_dirmap_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf) +{ + struct spi_device *spi = desc->mem->spi; + struct phytium_qspi *qspi = spi_controller_get_devdata(spi->master); + struct phytium_qspi_flash *flash = &qspi->flash[spi->chip_select]; + + void __iomem *dst = flash->base + offs; + void __iomem *addr; + int i; + size_t mask = 0x03; + u_char tmp[4] = {0}; + + if (offs & 0x03) { + dev_err(qspi->dev, "Addr not four-byte aligned!\n"); + return -EINVAL; + } + + for (i = 0; i < len / 4; i++) + writel_relaxed(*(u32 *)(buf + 4 * i), dst + 4 * i); + + if (len & mask) { + addr = dst + (len & ~mask); + memcpy(tmp, buf + (len & ~mask), len & mask); + writel_relaxed(*(u32 *)(tmp), addr); + } + + //write cache data to flash + writel_relaxed(QSPI_FLUSH_EN, qspi->io_base + QSPI_FLUSH_REG); + + return len; +} + +static int phytium_qspi_setup(struct spi_device *spi) +{ + struct spi_controller *ctrl = spi->master; + struct phytium_qspi *qspi = spi_controller_get_devdata(ctrl); + struct phytium_qspi_flash *flash; + uint clk_div; + + if (ctrl->busy) + return -EBUSY; + + flash = &qspi->flash[spi->chip_select]; + + flash->cs = spi->chip_select; + flash->spi = spi; + if (flash->cs >= PHYTIUM_QSPI_MAX_NORCHIP) { + dev_err(qspi->dev, "Flash CS is out of range.\n"); + return -EINVAL; + } + qspi->fnum++; + + + if (spi->max_speed_hz) { + clk_div = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz); + flash->clk_div = phytium_spi_nor_clac_clk_div(clk_div); + if (flash->clk_div == 65535) { + dev_err(qspi->dev, "qspi maximum frequency setting is error.\n"); + return -EINVAL; + } + } else + flash->clk_div = PHYTIUM_QSPI_DEFAULT_SCK_SEL; + + return 0; +} + +static struct spi_controller_mem_ops phytium_qspi_mem_ops = { + .adjust_op_size = phytium_qspi_adjust_op_size, + .supports_op = phytium_qspi_supports_op, + .exec_op = phytium_qspi_exec_op, + .dirmap_create = phytium_qspi_dirmap_create, + .dirmap_read = phytium_qspi_dirmap_read, + .dirmap_write = phytium_qspi_dirmap_write, +}; + +/** + * Direct mapping is supported only when all flashes under the controller + * are of the same size and the mapping address is continuous. For those + * cases which flashes are of different sizes, the driver offered a non-dirmap + * mem_ops with which read/write ops is executed through command port. + */ +static struct spi_controller_mem_ops phytium_qspi_mem_ops_nodirmap = { + .adjust_op_size = phytium_qspi_adjust_op_size, + .supports_op = phytium_qspi_supports_op, + .exec_op = phytium_qspi_exec_op, +}; + +/** + * phytium_qspi_probe - Probe method for the QSPI driver + * @pdev: Pointer to the platform_device structure + * + * This function initializes the driver data structures and the hardware. + * + * Return: 0 on success and error value on failure + */ +static int phytium_qspi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_controller *ctrl; + struct resource *res; + struct phytium_qspi *qspi; + int i, ret; + u32 flash_cap; + struct spi_mem *mem; + struct spi_nor *nor; + + ctrl = spi_alloc_master(dev, sizeof(*qspi)); + if (!ctrl) + return -ENOMEM; + + ctrl->mode_bits = SPI_CPOL | SPI_CPHA | + SPI_RX_DUAL | SPI_RX_QUAD | + SPI_TX_DUAL | SPI_TX_QUAD; + ctrl->setup = phytium_qspi_setup; + ctrl->num_chipselect = PHYTIUM_QSPI_MAX_NORCHIP; + ctrl->dev.of_node = dev->of_node; + + qspi = spi_controller_get_devdata(ctrl); + qspi->ctrl = ctrl; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi"); + qspi->io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->io_base)) { + ret = PTR_ERR(qspi->io_base); + goto probe_master_put; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm"); + qspi->mm_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->mm_base)) { + ret = PTR_ERR(qspi->mm_base); + goto probe_master_put; + } + + qspi->mm_size = resource_size(res); + if (qspi->mm_size > PHYTIUM_QSPI_MAX_MMAP_SZ) { + ret = -EINVAL; + goto probe_master_put; + } + qspi->used_size = 0; + + qspi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(qspi->clk)) { + ret = PTR_ERR(qspi->clk); + goto probe_master_put; + } + + qspi->clk_rate = clk_get_rate(qspi->clk); + if (!qspi->clk_rate) { + ret = -EINVAL; + goto probe_master_put; + } + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + goto probe_master_put; + } + + ret = clk_prepare_enable(qspi->clk); + if (ret) { + dev_err(dev, "Failed to enable PCLK of the controller.\n"); + goto probe_clk_failed; + } + + qspi->nodirmap = device_property_present(dev, "no-direct-mapping"); + ctrl->mem_ops = qspi->nodirmap ? + &phytium_qspi_mem_ops_nodirmap : + &phytium_qspi_mem_ops; + + qspi->dev = dev; + platform_set_drvdata(pdev, qspi); + + ret = devm_spi_register_controller(dev, ctrl); + if (ret) { + dev_err(dev, "failed to register SPI controller: %d\n", ret); + goto probe_setup_failed; + } + + if (!qspi->nodirmap) { + /* + * The controller supports direct mapping access only if all + * flashes are of same size. + */ + + i = 0; + for (i = 0; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) { + if (qspi->flash[i].spi) { + mem = spi_get_drvdata(qspi->flash[i].spi); + if (mem) { + nor = spi_mem_get_drvdata(mem); + if (nor) + qspi->flash[i].size = nor->mtd.size; + } + } + } + + for (i = 1; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) { + if (qspi->flash[i].size != qspi->flash[0].size) { + dev_err(dev, "Flashes are of different sizes.\n"); + ret = -EINVAL; + goto probe_setup_failed; + } + } + + ret = phytium_qspi_flash_capacity_encode(qspi->flash[0].size, + &flash_cap); + if (ret) { + dev_err(dev, "Flash size is invalid.\n"); + goto probe_setup_failed; + } + + flash_cap |= qspi->fnum << QSPI_FLASH_CAP_NUM_SHIFT; + + writel_relaxed(flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + } + + return 0; + +probe_setup_failed: + clk_disable_unprepare(qspi->clk); +probe_clk_failed: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); +probe_master_put: + + return ret; +} + +/** + * phytium_qspi_remove - Remove method for the QSPI driver + * @pdev: Pointer to the platform_device structure + * + * This function is called if a device is physically removed from the system + * or if the driver module is being unloaded. It free all resources allocated + * to the device. + * + * Return: 0 on success and error value on failure + */ +static int phytium_qspi_remove(struct platform_device *pdev) +{ + struct phytium_qspi *qspi = platform_get_drvdata(pdev); + + clk_disable_unprepare(qspi->clk); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static int __maybe_unused phytium_qspi_suspend(struct device *dev) +{ + return pm_runtime_force_suspend(dev); +} + +static int __maybe_unused phytium_qspi_resume(struct device *dev) +{ + return pm_runtime_force_resume(dev); +} + +static const struct dev_pm_ops phytium_qspi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytium_qspi_suspend, + phytium_qspi_resume) +}; + +static const struct of_device_id phytium_qspi_of_match[] = { + { .compatible = "phytium,qspi-nor" }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_qspi_of_match); + +static struct platform_driver phytium_qspi_driver = { + .probe = phytium_qspi_probe, + .remove = phytium_qspi_remove, + .driver = { + .name = "phytium-qspi", + .of_match_table = of_match_ptr(phytium_qspi_of_match), + .pm = &phytium_qspi_pm_ops, + }, +}; +module_platform_driver(phytium_qspi_driver); + +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium Quad SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/phytium-uart.c b/drivers/tty/serial/phytium-uart.c index a68d3a857258238ff139d778fafbe3f397aaee02..084bf9e1f964d7f2e93d5ee5b7978d6c3bb919a8 100644 --- a/drivers/tty/serial/phytium-uart.c +++ b/drivers/tty/serial/phytium-uart.c @@ -754,7 +754,7 @@ static struct phytium_uart_port *uart_ports[UART_NR]; static struct uart_driver phytium_uart = { .owner = THIS_MODULE, .driver_name = DRV_NAME, - .dev_name = "ttyS", + .dev_name = "ttyFTX", .nr = UART_NR, }; diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c index f5792b2cb1d355c72e2bebe22fd692910643a166..00d97248769ff027ef61f44d3f76caa340f3268b 100644 --- a/drivers/usb/phytium/host.c +++ b/drivers/usb/phytium/host.c @@ -1930,9 +1930,9 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = fls(desc.bInterval) - 1; interval = clamp_val(interval, 0, 15); interval = 1 << interval; - if ((1 << interval) != desc.bInterval) - pr_info("rounding to %d microframes, desc %d microframes\n", - 1 << interval, desc.bInterval); + if (interval != desc.bInterval) + pr_debug("rounding to %d microframes, desc %d microframes\n", + interval, desc.bInterval); break; } @@ -1940,7 +1940,7 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = clamp_val(desc.bInterval, 1, 16) - 1; interval = 1 << interval; if (interval != desc.bInterval - 1) - pr_info("rounding to %d %sframes\n", 1 << interval, + pr_debug("rounding to %d %sframes\n", interval, speed == USB_SPEED_FULL ? "" : "micro"); } break; @@ -1948,7 +1948,7 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, if (usb_endpoint_xfer_isoc(&desc)) { interval = clamp_val(desc.bInterval, 1, 16) - 1; if (interval != desc.bInterval - 1) - pr_info("rounding to %d %sframes\n", 1 << interval, + pr_debug("rounding to %d %sframes\n", 1 << interval, speed == USB_SPEED_FULL ? "" : "micro"); interval += 3; break; @@ -1959,7 +1959,7 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = fls(desc.bInterval * 8) - 1; interval = clamp_val(interval, 3, 10); if ((1 << interval) != desc.bInterval * 8) - pr_info("rounding to %d microframes, desc %d microframes\n", + pr_debug("rounding to %d microframes, desc %d microframes\n", 1 << interval, desc.bInterval); } } diff --git a/sound/pci/hda/hda_phytium.c b/sound/pci/hda/hda_phytium.c index 17dcf92d5450ad71647350da639890505cdcb2d0..69bfe699d2358ecf9c75bd58e8417b0b00fd774f 100644 --- a/sound/pci/hda/hda_phytium.c +++ b/sound/pci/hda/hda_phytium.c @@ -253,6 +253,37 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) return 1; /* OK, it's fine */ } +static int hda_ft_dma_configure(struct device *dev) +{ + const struct of_device_id *match_of; + const struct acpi_device_id *match_acpi; + + if (dev->of_node) { + match_of = of_match_device(dev->driver->of_match_table, dev); + if (!match_of) { + dev_err(dev, "Error DT match data is missing\n"); + return -ENODEV; + } + set_dma_ops(dev, NULL); + /* + * Because there is no way to transfer to non-coherent dma in + * of_dma_configure if 'dma-coherent' is described in DT, + * use acpi_dma_configure to alloc dma_ops correctly. + */ + acpi_dma_configure(dev, DEV_DMA_NON_COHERENT); + } else if (has_acpi_companion(dev)) { + match_acpi = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!match_acpi) { + dev_err(dev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + set_dma_ops(dev, NULL); + acpi_dma_configure(dev, DEV_DMA_NON_COHERENT); + } + + return 0; +} + /* The work for pending PCM period updates. */ static void azx_irq_pending_work(struct work_struct *work) { @@ -854,6 +885,10 @@ static int azx_first_init(struct azx *chip) chip->align_buffer_size = 1; } + err = hda_ft_dma_configure(hddev); + if (err < 0) + return err; + /* allow 64bit DMA address if supported by H/W */ if (!(gcap & AZX_GCAP_64OK)) dma_bits = 32; diff --git a/sound/soc/codecs/es8336.c b/sound/soc/codecs/es8336.c index 40519327783123c48fe921f43715d8d2c62e2e22..719b7de0e37bc3c1d3973afce4358c5f01746784 100644 --- a/sound/soc/codecs/es8336.c +++ b/sound/soc/codecs/es8336.c @@ -992,7 +992,7 @@ static int es8336_i2c_probe(struct i2c_client *i2c, } dev_dbg(&i2c->dev, "mic1-src %x", es8336->mic_src); - if (!es8336->spk_ctl_gpio) + if (IS_ERR_OR_NULL(es8336->spk_ctl_gpio)) dev_info(&i2c->dev, "Can not get spk_ctl_gpio\n"); else es8336_enable_spk(es8336, false); @@ -1000,7 +1000,7 @@ static int es8336_i2c_probe(struct i2c_client *i2c, es8336->hp_det_gpio = devm_gpiod_get_index_optional(&i2c->dev, "det", 0, GPIOD_IN); - if (!es8336->hp_det_gpio) { + if (IS_ERR_OR_NULL(es8336->hp_det_gpio)) { dev_info(&i2c->dev, "Can not get hp_det_gpio\n"); } else { INIT_DELAYED_WORK(&es8336->work, hp_work); diff --git a/sound/soc/phytium/pmdk_dp.c b/sound/soc/phytium/pmdk_dp.c index fd0cf41c34b6c2ece621389ba463dc2c16490400..b6ba8a341b5aa93792699e2d3231a288822c3f75 100755 --- a/sound/soc/phytium/pmdk_dp.c +++ b/sound/soc/phytium/pmdk_dp.c @@ -169,7 +169,7 @@ static int pmdk_sound_probe(struct platform_device *pdev) struct pmdk_dp_private *priv; struct snd_soc_dai_link *pmdk_dai; int num_dp = 2; - char dp_mask; + char dp_mask = 0x7; int i,j = 0; card->dev = &pdev->dev;