From a235069cfd456afd9abd188a978d9077e69595bf Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Thu, 3 Apr 2025 11:14:01 +0800 Subject: [PATCH 001/154] iommu/arm-smmu-v3: Change pm interface during system sleep transition Set with NOIRQ flag. Signed-off-by: liutianyu1250 --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 4dbeb03a09..659e031449 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -3691,7 +3691,7 @@ MODULE_DEVICE_TABLE(of, arm_smmu_of_match); #ifdef CONFIG_PM_SLEEP static const struct dev_pm_ops arm_smmu_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(arm_smmu_suspend, + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(arm_smmu_suspend, arm_smmu_resume) }; #define ARM_SMMU_PM_OPS (&arm_smmu_pm_ops) -- Gitee From ea7096d302369764839a395a812a184cbb9562c0 Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Tue, 25 Feb 2025 11:59:27 +0200 Subject: [PATCH 002/154] usb: xhci: Enable the TRB overfetch quirk on VIA VL805 commit c133ec0e5717868c9967fa3df92a55e537b1aead upstream. Raspberry Pi is a major user of those chips and they discovered a bug - when the end of a transfer ring segment is reached, up to four TRBs can be prefetched from the next page even if the segment ends with link TRB and on page boundary (the chip claims to support standard 4KB pages). It also appears that if the prefetched TRBs belong to a different ring whose doorbell is later rung, they may be used without refreshing from system RAM and the endpoint will stay idle if their cycle bit is stale. Other users complain about IOMMU faults on x86 systems, unsurprisingly. Deal with it by using existing quirk which allocates a dummy page after each transfer ring segment. This was seen to resolve both problems. RPi came up with a more efficient solution, shortening each segment by four TRBs, but it complicated the driver and they ditched it for this quirk. Also rename the quirk and add VL805 device ID macro. Signed-off-by: Michal Pecio Link: https://github.com/raspberrypi/linux/issues/4685 Closes: https://bugzilla.kernel.org/show_bug.cgi?id=215906 CC: stable@vger.kernel.org Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20250225095927.2512358-2-mathias.nyman@linux.intel.com [ Michal: merge conflict with white space and an unrelated quirk ] Signed-off-by: Michal Pecio Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 3 ++- drivers/usb/host/xhci-pci.c | 9 ++++++--- drivers/usb/host/xhci.h | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 006e1b15fb..cdf9806c87 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2492,7 +2492,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * and our use of dma addresses in the trb_address_map radix tree needs * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need. */ - if (xhci->quirks & XHCI_ZHAOXIN_TRB_FETCH) + if (xhci->quirks & XHCI_TRB_OVERFETCH) + /* Buggy HC prefetches beyond segment bounds - allocate dummy space at the end */ xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, TRB_SEGMENT_SIZE * 2, TRB_SEGMENT_SIZE * 2, xhci->page_size * 2); else diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 96be5bc219..1f8fe617ae 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -36,6 +36,8 @@ #define PCI_VENDOR_ID_ETRON 0x1b6f #define PCI_DEVICE_ID_EJ168 0x7023 +#define PCI_DEVICE_ID_VIA_VL805 0x3483 + #define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1 @@ -293,8 +295,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == 0x3432) xhci->quirks |= XHCI_BROKEN_STREAMS; - if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) { + if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == PCI_DEVICE_ID_VIA_VL805) { xhci->quirks |= XHCI_LPM_SUPPORT; + xhci->quirks |= XHCI_TRB_OVERFETCH; xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS; } @@ -340,11 +343,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->device == 0x9202) { xhci->quirks |= XHCI_RESET_ON_RESUME; - xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; + xhci->quirks |= XHCI_TRB_OVERFETCH; } if (pdev->device == 0x9203) - xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; + xhci->quirks |= XHCI_TRB_OVERFETCH; } /* xHC spec requires PCI devices to support D3hot and D3cold */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index bb3c362a19..b44596f621 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1894,7 +1894,7 @@ struct xhci_hcd { #define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42) #define XHCI_SUSPEND_RESUME_CLKS BIT_ULL(43) #define XHCI_RESET_TO_DEFAULT BIT_ULL(44) -#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45) +#define XHCI_TRB_OVERFETCH BIT_ULL(45) #define XHCI_ZHAOXIN_HOST BIT_ULL(46) unsigned int num_active_eps; -- Gitee From 5cefcf820758109871da222545dece53fed8543d Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 11 Apr 2025 17:28:59 +0800 Subject: [PATCH 003/154] Revert "drm: fix PMDKI2S can't register when only using DP1" This reverts commit 016825b9b36017108c8605f0f8da91eff70dcf83. --- drivers/gpu/drm/phytium/phytium_dp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/phytium/phytium_dp.c b/drivers/gpu/drm/phytium/phytium_dp.c index cc1e2d2ae7..92ad326d06 100644 --- a/drivers/gpu/drm/phytium/phytium_dp.c +++ b/drivers/gpu/drm/phytium/phytium_dp.c @@ -2425,8 +2425,7 @@ static const struct hdmi_codec_ops phytium_audio_codec_ops = { .hook_plugged_cb = phytium_dp_audio_hook_plugged_cb, }; -static int phytium_dp_audio_codec_init(struct phytium_dp_device *phytium_dp, - const int port) +static int phytium_dp_audio_codec_init(struct phytium_dp_device *phytium_dp) { struct device *dev = phytium_dp->dev->dev; struct hdmi_codec_pdata codec_data = { @@ -2438,9 +2437,12 @@ static int phytium_dp_audio_codec_init(struct phytium_dp_device *phytium_dp, }; phytium_dp->audio_pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, - codec_id + port, + codec_id, &codec_data, sizeof(codec_data)); + if (!PTR_ERR_OR_ZERO(phytium_dp->audio_pdev)) + codec_id += 1; + return PTR_ERR_OR_ZERO(phytium_dp->audio_pdev); } @@ -2449,6 +2451,7 @@ static void phytium_dp_audio_codec_fini(struct phytium_dp_device *phytium_dp) if (!PTR_ERR_OR_ZERO(phytium_dp->audio_pdev)) platform_device_unregister(phytium_dp->audio_pdev); phytium_dp->audio_pdev = NULL; + codec_id -= 1; } static long phytium_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) @@ -2643,7 +2646,7 @@ int phytium_dp_init(struct drm_device *dev, int port) drm_connector_helper_add(&phytium_dp->connector, &phytium_connector_helper_funcs); drm_connector_attach_encoder(&phytium_dp->connector, &phytium_dp->encoder); - ret = phytium_dp_audio_codec_init(phytium_dp, port); + ret = phytium_dp_audio_codec_init(phytium_dp); if (ret) { DRM_ERROR("failed to initialize audio codec\n"); goto failed_connector_init; -- Gitee From 468110aeabfccb3ae0da491824181f07906d35f0 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 11 Apr 2025 17:30:56 +0800 Subject: [PATCH 004/154] Revert "sound: pmdk_dp: add default dp-mask value" This reverts commit ba8eb825e4b892b71ebcf830b50d3a40f4467471. --- sound/soc/phytium/pmdk_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/phytium/pmdk_dp.c b/sound/soc/phytium/pmdk_dp.c index 38c977d66a..d60fb9de43 100755 --- a/sound/soc/phytium/pmdk_dp.c +++ b/sound/soc/phytium/pmdk_dp.c @@ -157,7 +157,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 = 0x7; + char dp_mask; int i,j = 0; card->dev = &pdev->dev; -- Gitee From 9da2db588d8e77da2c2477aa8bde8a68be73a5c6 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 11 Apr 2025 17:39:25 +0800 Subject: [PATCH 005/154] Revert "sound: soc: phytium pmdk_dp add handle dp-mask" This reverts commit ceba6015b1c26b2169565ff23243d6881f279d10. --- sound/soc/phytium/pmdk_dp.c | 43 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/sound/soc/phytium/pmdk_dp.c b/sound/soc/phytium/pmdk_dp.c index d60fb9de43..24fa6e893f 100755 --- a/sound/soc/phytium/pmdk_dp.c +++ b/sound/soc/phytium/pmdk_dp.c @@ -8,8 +8,6 @@ #include #include -#define DAI_CNT(pmdk_dai_) sizeof(pmdk_dai_)/sizeof(struct snd_soc_dai_link) - struct pmdk_dp_private { struct snd_soc_jack jack0; struct snd_soc_jack jack1; @@ -116,27 +114,28 @@ SND_SOC_DAILINK_DEFS(pmdk_dp2_dai, DAILINK_COMP_ARRAY(COMP_CODEC("hdmi-audio-codec.1346918658", "i2s-hifi")), DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy"))); -static struct snd_soc_dai_link pmdk_dai_local[] = { -{ +static struct snd_soc_dai_link pmdk_dai0 = { .name = "Phytium dp0-audio", .stream_name = "Playback", .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp0_init, SND_SOC_DAILINK_REG(pmdk_dp0_dai), -},{ +}; + +static struct snd_soc_dai_link pmdk_dai1 = { .name = "Phytium dp1-audio", .stream_name = "Playback", .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp1_init, SND_SOC_DAILINK_REG(pmdk_dp1_dai), -}, -{ +}; + +static struct snd_soc_dai_link pmdk_dai2 = { .name = "Phytium dp2-audio", .stream_name = "Playback", .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp2_init, SND_SOC_DAILINK_REG(pmdk_dp2_dai), -}, }; static struct snd_soc_card pmdk = { @@ -157,27 +156,27 @@ 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; - int i,j = 0; card->dev = &pdev->dev; - device_property_read_u32(&pdev->dev, "num-dp", &num_dp); - device_property_read_u8(&pdev->dev, "dp-mask", &dp_mask); pmdk_dai = devm_kzalloc(&pdev->dev, num_dp * sizeof(*pmdk_dai), GFP_KERNEL); if (!pmdk_dai) return -ENOMEM; - if (!num_dp || num_dp > DAI_CNT(pmdk_dai_local)) + switch (num_dp) { + case 1: + pmdk_dai[0] = pmdk_dai0; + break; + case 2: + pmdk_dai[0] = pmdk_dai0; + pmdk_dai[1] = pmdk_dai1; + break; + case 3: + pmdk_dai[0] = pmdk_dai0; + pmdk_dai[1] = pmdk_dai1; + pmdk_dai[2] = pmdk_dai2; + break; + default: return -EINVAL; - - for(i = 0; i < num_dp; i++) { - for (; j < DAI_CNT(pmdk_dai_local); j++) { - if (BIT(j) & dp_mask) { - pmdk_dai[i] = pmdk_dai_local[j]; - j++; - break; - } - } } card->dai_link = pmdk_dai; -- Gitee From 2bca970f4657b63fd9fb9cb96d84726929a502e2 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 11 Apr 2025 17:43:05 +0800 Subject: [PATCH 006/154] arm64: dts: reset pe2202 dp_i2s pe2202 only have dp1. if num_dp == 1, still use dai name i2s_dp0 in dp_i2s1 Signed-off-by: liutianyu1250 --- arch/arm64/boot/dts/phytium/pe2202.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/phytium/pe2202.dtsi b/arch/arm64/boot/dts/phytium/pe2202.dtsi index 1478ee40c2..8106a300c4 100644 --- a/arch/arm64/boot/dts/phytium/pe2202.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2202.dtsi @@ -182,3 +182,7 @@ macb3: ethernet@32012000 { status = "disabled"; }; }; + +&i2s_dp1 { + dai-name = "phytium-i2s-dp0"; +}; -- Gitee From ad9e549d26a5077932ef88cb8d2f72c7a8b3d3ce Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 4 Jan 2024 15:54:24 +0800 Subject: [PATCH 007/154] net: phytium: Bugfix mac wrong DMA address access issue Slove the kernel crash problem caused by mac wrong DMA memory access which is caused by IO read out of order. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I24ba24227708ca32d3a14ba2073ee9871b7a7b2e --- drivers/net/ethernet/phytium/phytmac.h | 2 ++ drivers/net/ethernet/phytium/phytmac_main.c | 21 ++++++++++++++------ drivers/net/ethernet/phytium/phytmac_v1.c | 22 ++++++++++++++------- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 97a7526099..b81fbcd18a 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -38,6 +38,7 @@ #define MIN_TX_RING_SIZE 64 #define MIN_RX_RING_SIZE 64 #define DEFAULT_TX_DESC_MIN_FREE 64 +#define DEFAULT_RX_DESC_MIN_FREE 64 #define MEMORY_SIZE 4096 #define MHU_SIZE 0x20 @@ -503,6 +504,7 @@ struct phytmac_hw_if { struct packet_info *packet); void (*init_rx_map)(struct phytmac_queue *queue, u32 index); unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); + unsigned int (*rx_clean)(struct phytmac_queue *queue, u32 cleaned_count); void (*transmit)(struct phytmac_queue *queue); void (*restart)(struct phytmac *pdata); int (*tx_complete)(const struct phytmac_dma_desc *desc); diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 828bf3a832..b41d28d147 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -983,12 +983,17 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) struct phytmac_hw_if *hw_if = pdata->hw_if; unsigned int index, space; struct phytmac_rx_buffer *rx_buf_info; + unsigned int rx_unclean = 0; space = CIRC_SPACE(queue->rx_head, queue->rx_tail, pdata->rx_ring_size); + if (space < DEFAULT_RX_DESC_MIN_FREE) + return; + + index = queue->rx_head & (pdata->rx_ring_size - 1); + while (space > 0) { - index = queue->rx_head & (pdata->rx_ring_size - 1); rx_buf_info = &queue->rx_buffer_info[index]; if (!phytmac_alloc_mapped_page(pdata, rx_buf_info)) @@ -1001,16 +1006,20 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) hw_if->rx_map(queue, index, rx_buf_info->addr + rx_buf_info->page_offset); - queue->rx_head++; - if (queue->rx_head >= pdata->rx_ring_size) - queue->rx_head &= (pdata->rx_ring_size - 1); - + index = (index + 1) & (pdata->rx_ring_size - 1); + rx_unclean++; space--; } queue->rx_next_to_alloc = queue->rx_head; /* make newly descriptor to hardware */ wmb(); + hw_if->rx_clean(queue, rx_unclean); + /* make newly descriptor to hardware */ + wmb(); + queue->rx_head += rx_unclean; + if (queue->rx_head >= pdata->rx_ring_size) + queue->rx_head &= (pdata->rx_ring_size - 1); } static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, @@ -1132,7 +1141,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) if (!hw_if->tx_complete(desc)) break; - /* Process all buffers of the current transmitted frame */ + /* Process all buffers of the current transmitted frame */ for (;; head++) { tx_skb = phytmac_get_tx_skb(queue, head); skb = tx_skb->skb; diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index ccd8fefe1a..c509bf1df8 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -914,14 +914,21 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, addr |= PHYTMAC_BIT(RX_WRAP); desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); - /* Make newly descriptor to hardware */ - dma_wmb(); - desc->desc0 = lower_32_bits(addr); - } else { - desc->desc1 = 0; - /* Make newly descriptor to hardware */ - dma_wmb(); + desc->desc0 = lower_32_bits(addr) | PHYTMAC_BIT(RX_USED); + } + return 0; +} + +static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count) +{ + struct phytmac_dma_desc *desc; + u32 index = queue->rx_head + count - 1; + + while (count) { + desc = phytmac_get_rx_desc(queue, index); desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + index--; + count--; } return 0; @@ -1379,6 +1386,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { .get_rx_pkt_len = phytmac_rx_pkt_len, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, + .rx_clean = phytmac_rx_clean_desc, .rx_checksum = phytmac_rx_checksum, .rx_single_buffer = phytmac_rx_single_buffer, .rx_pkt_start = phytmac_rx_sof, -- Gitee From 9e52cfdfb4ba98fafd0b48f25aa0df5243e62d25 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 31 Jan 2024 11:54:43 +0800 Subject: [PATCH 008/154] net: phytium: Fix autoneg config error issue Modify autoneg config value according to enable or disable and do not change other bits value of PHYTMAC_PCSCTRL register. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: If8e2ef240900fbb5bfc8fcf9e7657a9b5e14ab04 --- drivers/net/ethernet/phytium/Kconfig | 15 --------------- drivers/net/ethernet/phytium/phytmac_pci.c | 8 ++++---- drivers/net/ethernet/phytium/phytmac_v1.c | 10 ++++------ 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig index d3f16d0f79..8b0520f6e7 100644 --- a/drivers/net/ethernet/phytium/Kconfig +++ b/drivers/net/ethernet/phytium/Kconfig @@ -20,7 +20,6 @@ if NET_VENDOR_PHYTIUM config PHYTMAC tristate "Phytium GMAC support" depends on HAS_DMA - #depends on PTP_1588_CLOCK_OPTIONAL select PHYLINK select CRC32 help @@ -56,18 +55,4 @@ config PHYTMAC_PCI To compile this driver as a module, choose M here: the module will be called phytmac_pci. -#config PHYTMAC_1P0 -# bool "Phytmac 1p0 support" -# depends on PHYTMAC -# default y -# help -# To compile this driver as a module, choose M here: the module - -#config PHYTMAC_2P0 -# bool "Phytmac 2p0 support" -# depends on PHYTMAC -# default y -# help -# To compile this driver as a module, choose M here: the module - endif # NET_VENDOR_PHYTIUM diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index cc0cbae284..be1ae2a74c 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -38,10 +38,10 @@ static const u32 fixedlink[][5] = { }; 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]), {}}, + {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 int phytmac_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index c509bf1df8..05a2949116 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -149,16 +149,14 @@ static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) { - u32 ctrl, old_ctrl; + u32 value = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); if (enable) - ctrl |= PHYTMAC_BIT(AUTONEG); + value |= PHYTMAC_BIT(AUTONEG); else - ctrl &= ~PHYTMAC_BIT(AUTONEG); + value &= ~PHYTMAC_BIT(AUTONEG); - old_ctrl = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); - if (old_ctrl != ctrl) - PHYTMAC_WRITE(pdata, PHYTMAC_PCSCTRL, ctrl); + PHYTMAC_WRITE(pdata, PHYTMAC_PCSCTRL, value); return 0; } -- Gitee From f0deba5495591af4753be818cce9d7164847a1ca Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 31 Jan 2024 14:07:16 +0800 Subject: [PATCH 009/154] net: phytmac: Bugfix udp stopped issue in half deplux mode Solve udp stopped problem under the iperf3 test, because port links down in half deplex mode and don't free skb that aren't sent. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ide43eace3a719312aadac8f083dbb2449f1328de --- drivers/net/ethernet/phytium/phytmac_v1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 05a2949116..da014b716e 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -925,6 +925,7 @@ static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count while (count) { desc = phytmac_get_rx_desc(queue, index); desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + dma_wmb(); index--; count--; } -- Gitee From abdb473349b0a6744d3d66a0964d358d89c50567 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 2 Feb 2024 09:11:10 +0800 Subject: [PATCH 010/154] phytnetled: phytium: Add bindings for phytium 10G mac led This patch document the DT bindings for 10G mac led controller. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Id1708caf7cb42d6ef8b0fa3ba155f42ff6a30b07 --- .../devicetree/bindings/leds/phytnet_led.yaml | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/phytnet_led.yaml diff --git a/Documentation/devicetree/bindings/leds/phytnet_led.yaml b/Documentation/devicetree/bindings/leds/phytnet_led.yaml new file mode 100644 index 0000000000..ae0acf9ec5 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/phytnet_led.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/phytnet_led.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium mac led controller + +maintainers: + - LongShixiang + +description: + This modules is used to control mac led. + +properties: + compatible: + const: phytium,net_led + net_dev: + maxItems: 1 + description: Phandler of specified Net device + led-gpios: + minItems: 1 + maxItems: 2 + description: |- + the gpios used for led control based on net_dev condition. + One represents LINK condition, another represents ACT condition. + +required: + - compatible + - net_dev + - led-gpios + +examples: + - | + gpiochip0: gpop_controller{ + ... + } + eth0: ethernet{ + ... + } + phytium_net_led0 { + compatible = "phytium,net_led"; + net_dev = <ð0>; + led-gpios = <&gpiochip0 9 GPIO_ACTIVE_HIGH>, /* link */ + <&gpiochip0 11 GPIO_ACTIVE_HIGH>; /* act */ + }; -- Gitee From 94dd7303acda2e5285d98033f64a9cb4355de7bb Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 12 Feb 2025 16:07:12 +0800 Subject: [PATCH 011/154] phytnetled: phytium: Add link/act led driver for 10G mac That patch adds driver support for phytium 10G mac link/act led control according port link and active state. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I63df12d4b43576de852fbcd106fa29a09c53c0ba --- drivers/char/Kconfig | 2 + drivers/char/Makefile | 1 + drivers/char/phytnetled/Kconfig | 12 + drivers/char/phytnetled/Makefile | 1 + drivers/char/phytnetled/phytnet_led.c | 318 ++++++++++++++++++++++++++ drivers/char/phytnetled/phytnet_led.h | 14 ++ 6 files changed, 348 insertions(+) create mode 100644 drivers/char/phytnetled/Kconfig create mode 100644 drivers/char/phytnetled/Makefile create mode 100644 drivers/char/phytnetled/phytnet_led.c create mode 100644 drivers/char/phytnetled/phytnet_led.h diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index b4e65d1ede..ea0d81b2b0 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -508,4 +508,6 @@ config RANDOM_TRUST_BOOTLOADER believe its RNG facilities may be faulty. This may also be configured at boot time with "random.trust_bootloader=on/off". +source "drivers/char/phytnetled/Kconfig" + endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index ffce287ef4..3ccc78c1d8 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -47,3 +47,4 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o +obj-$(CONFIG_PHYTNET_LED) += phytnetled/ diff --git a/drivers/char/phytnetled/Kconfig b/drivers/char/phytnetled/Kconfig new file mode 100644 index 0000000000..c868ef6f53 --- /dev/null +++ b/drivers/char/phytnetled/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# PCMCIA character device configuration +# +config PHYTNET_LED + tristate "Phytium mac led control module" + depends on PHYTMAC + depends on GPIO_PHYTIUM_PLAT + default m + help + If you have a network (Ethernet) controller of this type and + want to use it control port led say Y or M here. \ No newline at end of file diff --git a/drivers/char/phytnetled/Makefile b/drivers/char/phytnetled/Makefile new file mode 100644 index 0000000000..ea129f5e48 --- /dev/null +++ b/drivers/char/phytnetled/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PHYTNET_LED) += phytnet_led.o diff --git a/drivers/char/phytnetled/phytnet_led.c b/drivers/char/phytnetled/phytnet_led.c new file mode 100644 index 0000000000..0b5fbb7502 --- /dev/null +++ b/drivers/char/phytnetled/phytnet_led.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022-2023 Phytium Technology Co.,Ltd. + * + */ +#include +#include +#include +#include +#include +#include +#include "phytnet_led.h" + +#define DRIVER_NAME "phytnet_led" +#define DRIVER_VERSION "1.0" +#define DRIVER_AUTHOR "LongShixiang " +#define DRIVER_DESC "net device led control module" +#define NET_DEV_PROPNAME "net_dev" +#define LED_OF_NAME "led" +#define CHECK_INTERVAL 125 /* Unit: ms */ +#define NDEV_CHECK_DELAY 30000 /* Unit: 30s */ +#define LED_ON 1 +#define LED_OFF 0 +#define LINK_OFFSET 0 +#define ACT_OFFSET 1 + +#if defined(CONFIG_OF) +static const struct of_device_id phytnet_led_of_ids[] = { + { .compatible = "phytium,net_led"}, + {} +}; + +MODULE_DEVICE_TABLE(of, phytnet_led_of_ids); +#endif /* CONFIG_OF */ + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytnet_acpi_ids[] = { + { .id = "PHYT800C"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, phytnet_acpi_ids); +#else +#define phytnet_acpi_ids NULL +#endif /* CONFIG_ACPI */ + +static void +led_on(struct gpio_desc *gd) +{ + gpiod_set_value(gd, LED_ON); +} + +static void +led_off(struct gpio_desc *gd) +{ + gpiod_set_value(gd, LED_OFF); +} + +static void +led_blink(struct led_data *phytnet_led) +{ + phytnet_led->act_val = !phytnet_led->act_val; + gpiod_set_value(phytnet_led->act, phytnet_led->act_val); +} + +static int +port_is_linkup(struct led_data *phytnet_led) +{ + if (netif_carrier_ok(phytnet_led->ndev)) + return true; + else + return false; +} + +static bool +port_is_act(struct led_data *phytnet_led) +{ + bool ret = false; + + if (phytnet_led->ndev_rx != phytnet_led->ndev->stats.rx_packets) { + phytnet_led->ndev_rx = phytnet_led->ndev->stats.rx_packets; + ret = true; + } + + if (phytnet_led->ndev_tx != phytnet_led->ndev->stats.tx_packets) { + phytnet_led->ndev_tx = phytnet_led->ndev->stats.tx_packets; + ret = true; + } + + return ret; +} + +static void +led_control(struct led_data *phytnet_led) +{ + while (!phytnet_led->led_stop) { + msleep(CHECK_INTERVAL); + + if (!netif_running(phytnet_led->ndev)) { + led_off(phytnet_led->link); + led_off(phytnet_led->act); + continue; + } + + if (port_is_linkup(phytnet_led)) + led_on(phytnet_led->link); + else + led_off(phytnet_led->link); + + if (port_is_act(phytnet_led)) + led_blink(phytnet_led); + else + led_off(phytnet_led->act); + } +} + +static int +of_ndev_init(struct led_data *phytnet_led) +{ + struct device_node *net_node; + + net_node = of_parse_phandle(phytnet_led->pdev->dev.of_node, NET_DEV_PROPNAME, 0); + if (!net_node) { + dev_err(&phytnet_led->pdev->dev, "Failed to get netdev ofnode from device tree\n"); + return -ENODEV; + } + + phytnet_led->ndev = of_find_net_device_by_node(net_node); + + if (!phytnet_led->ndev) { + dev_err(&phytnet_led->pdev->dev, "Failed to get acpi ndev\n"); + return -ENODEV; + } + + dev_info(&phytnet_led->pdev->dev, "Successfully get ndev...\n"); + dev_hold(phytnet_led->ndev); + + return 0; +} + + +static int +acpi_ndev_init(struct led_data *phytnet_led) +{ + int err; + struct net_device *find_ndev; + const char *ndev_acpi_path; + acpi_handle net_handler; + struct acpi_device *adev; + acpi_status status; + struct device *find_dev; + + err = device_property_read_string(&phytnet_led->pdev->dev, + NET_DEV_PROPNAME, &ndev_acpi_path); + if (err) { + dev_err(&phytnet_led->pdev->dev, "Failed to read net_dev property!\n"); + return -ENODEV; + } + + status = acpi_get_handle(NULL, (acpi_string)ndev_acpi_path, &net_handler); + if (ACPI_FAILURE(status)) { + dev_err(&phytnet_led->pdev->dev, "Failed to get acpi handler on path: %s\n", + ndev_acpi_path); + return -ENODEV; + } + + err = acpi_bus_get_device(net_handler, &adev); + if (err) { + dev_err(&phytnet_led->pdev->dev, "Failed to get adev dev\n"); + return -ENODEV; + } + + for_each_netdev(&init_net, find_ndev) { + if (find_ndev->dev.parent != NULL) { + find_dev = find_ndev->dev.parent; + if (&adev->fwnode == find_dev->fwnode) + phytnet_led->ndev = find_ndev; + } + } + + if (!phytnet_led->ndev) { + dev_err(&phytnet_led->pdev->dev, "Failed to get acpi ndev\n"); + return -ENODEV; + } + + dev_info(&phytnet_led->pdev->dev, "Successfully get ndev...\n"); + dev_hold(phytnet_led->ndev); + + return 0; +} + +static int +gpio_init(struct led_data *phytnet_led) +{ + int err; + + phytnet_led->link = gpiod_get_index(&phytnet_led->pdev->dev, LED_OF_NAME, + LINK_OFFSET, GPIOD_OUT_HIGH); + if (IS_ERR(phytnet_led->link)) { + dev_err(&phytnet_led->pdev->dev, "Failed to get link led gpio, err code: %ld\n", + PTR_ERR(phytnet_led->link)); + + return PTR_ERR(phytnet_led->link); + } + + err = gpiod_direction_output(phytnet_led->link, LED_OFF); + if (err) { + dev_err(&phytnet_led->pdev->dev, "Failed to set linkled dir, err code: %ld\n", + PTR_ERR(phytnet_led->link)); + return err; + } + + phytnet_led->act = gpiod_get_index(&phytnet_led->pdev->dev, LED_OF_NAME, + ACT_OFFSET, GPIOD_OUT_HIGH); + if (IS_ERR(phytnet_led->act)) { + dev_err(&phytnet_led->pdev->dev, "Failed to get actled gpio, err code:%d\n", err); + return PTR_ERR(phytnet_led->act); + } + + err = gpiod_direction_output(phytnet_led->act, LED_OFF); + if (err) { + dev_err(&phytnet_led->pdev->dev, "Failed to set act led gpio dir, err:%d\n", err); + return err; + } + + return 0; +} + +static void +led_init_and_control(struct work_struct *work) +{ + int err = -1; + struct led_data *phytnet_led = container_of(work, struct led_data, led_control_work.work); + + if (phytnet_led->pdev->dev.of_node) + err = of_ndev_init(phytnet_led); + else if (has_acpi_companion(&phytnet_led->pdev->dev)) + err = acpi_ndev_init(phytnet_led); + + if (err) { + dev_err(&phytnet_led->pdev->dev, "ndev init wrong\n"); + return; + } + + err = gpio_init(phytnet_led); + if (err) { + dev_err(&phytnet_led->pdev->dev, "gpio init wrong\n"); + return; + } + + led_control(phytnet_led); +} + +static int +net_led_probe(struct platform_device *pdev) +{ + struct led_data *phytnet_led; + + phytnet_led = devm_kzalloc(&pdev->dev, sizeof(struct led_data), GFP_KERNEL); + if (!phytnet_led) + return -ENOMEM; + + platform_set_drvdata(pdev, phytnet_led); + + phytnet_led->act = LED_OFF; + phytnet_led->pdev = pdev; + phytnet_led->led_stop = 0; + + INIT_DELAYED_WORK(&phytnet_led->led_control_work, led_init_and_control); + schedule_delayed_work(&phytnet_led->led_control_work, msecs_to_jiffies(NDEV_CHECK_DELAY)); + + return 0; +} + +static int +net_led_remove(struct platform_device *pdev) +{ + struct led_data *phytnet_led = platform_get_drvdata(pdev); + + phytnet_led->led_stop = 1; + cancel_delayed_work_sync(&phytnet_led->led_control_work); + + if (phytnet_led->ndev) + dev_put(phytnet_led->ndev); + + if (phytnet_led->link) { + led_off(phytnet_led->link); + gpiod_put(phytnet_led->link); + } + + if (phytnet_led->act) { + led_off(phytnet_led->act); + gpiod_put(phytnet_led->act); + } + + if (&pdev->dev) + devm_kfree(&pdev->dev, phytnet_led); + + return 0; +} + +static struct platform_driver net_led_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(phytnet_led_of_ids), + .acpi_match_table = ACPI_PTR(phytnet_acpi_ids), + }, + .probe = net_led_probe, + .remove = net_led_remove, +}; + +module_platform_driver(net_led_driver); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/char/phytnetled/phytnet_led.h b/drivers/char/phytnetled/phytnet_led.h new file mode 100644 index 0000000000..8f2fbadde0 --- /dev/null +++ b/drivers/char/phytnetled/phytnet_led.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2022-2023 Phytium Technology Co.,Ltd. + * + */ +struct led_data { + struct platform_device *pdev; + struct net_device *ndev; + unsigned long ndev_rx, ndev_tx; + struct gpio_desc *link, *act; + struct delayed_work led_control_work; + int led_stop; + int act_val; +}; -- Gitee From 2d5cf318978550e1339d6b57aab3268051e28109 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 26 Feb 2024 15:45:36 +0800 Subject: [PATCH 012/154] net/macb: Slove UDP flow was stopped issue The NIC does not work after sending UDP packets for a period of time, because the NIC links down but socket buffers are not released, which will result in memory leakage. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ifce1d4b29a5ba23323ee0cb65474dad39241cdfa --- drivers/net/ethernet/cadence/macb_main.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index dff2b139c0..3552bd5f9e 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -106,6 +106,8 @@ struct sifive_fu540_macb_mgmt { #define MACB_MDIO_TIMEOUT 1000000 /* in usecs */ +static void macb_tx_unmap(struct macb *bp, struct macb_tx_skb *tx_skb); + /* DMA buffer descriptor might be different size * depends on hardware configuration: * @@ -769,9 +771,11 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct macb *bp = netdev_priv(ndev); + struct macb_tx_skb *tx_skb; struct macb_queue *queue; unsigned int q; u32 ctrl; + int i; if (bp->use_ncsi) ncsi_stop_dev(bp->ndev); @@ -785,6 +789,16 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, ctrl = macb_readl(bp, NCR) & ~(MACB_BIT(RE) | MACB_BIT(TE)) & ~(MACB_BIT(2PT5G)); macb_writel(bp, NCR, ctrl); + /* Tx clean */ + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + for (i = 0; i < bp->tx_ring_size; i++) { + tx_skb = macb_tx_skb(queue, i); + /* free unsent skb buffers */ + if (tx_skb) + macb_tx_unmap(bp, tx_skb); + } + } + netif_tx_stop_all_queues(ndev); } -- Gitee From 2650251b4d867f77843a0647a7d1117d2362fedc Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 31 Jul 2024 11:38:48 +0800 Subject: [PATCH 013/154] net/phytmac: Add support for phytmac v2.0 This patch Adds support for phtymac v2.0,including acpi description, rx tail pointer supporting, power management and other functions. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I6504979ce0ba56fa77df2fb800c4eae9214f7ba3 --- drivers/net/ethernet/phytium/Makefile | 2 +- drivers/net/ethernet/phytium/phytmac.h | 57 +- .../net/ethernet/phytium/phytmac_ethtool.c | 1 + drivers/net/ethernet/phytium/phytmac_main.c | 107 ++-- drivers/net/ethernet/phytium/phytmac_pci.c | 5 +- .../net/ethernet/phytium/phytmac_platform.c | 148 ++++- drivers/net/ethernet/phytium/phytmac_ptp.c | 3 + drivers/net/ethernet/phytium/phytmac_ptp.h | 1 + drivers/net/ethernet/phytium/phytmac_v1.c | 113 +++- drivers/net/ethernet/phytium/phytmac_v1.h | 19 +- drivers/net/ethernet/phytium/phytmac_v2.c | 591 +++++++++++------- drivers/net/ethernet/phytium/phytmac_v2.h | 74 ++- 12 files changed, 784 insertions(+), 337 deletions(-) diff --git a/drivers/net/ethernet/phytium/Makefile b/drivers/net/ethernet/phytium/Makefile index 6e710d4d54..debaed63e4 100644 --- a/drivers/net/ethernet/phytium/Makefile +++ b/drivers/net/ethernet/phytium/Makefile @@ -1,9 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. # # Makefile for the Phytium network device drivers. # -# obj-$(CONFIG_PHYTMAC) += phytmac.o diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index b81fbcd18a..f2ac13a3c1 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -1,4 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #ifndef _PHYTMAC_H #define _PHYTMAC_H @@ -13,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.5" +#define PHYTMAC_DRIVER_VERSION "1.0.28" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -45,6 +47,8 @@ #define PHYTMAC_POWEROFF 1 #define PHYTMAC_POWERON 2 +#define PHYTMAC_PWCTL_GMAC_ID 6 +#define PHYTMAC_PWCTL_DEFAULT_VAL 0 #define PHYTMAC_WOL_MAGIC_PACKET 1 @@ -59,8 +63,12 @@ #define PHYTMAC_CAPS_TAILPTR 0x00000040 #define PHYTMAC_CAPS_START 0x00000080 #define PHYTMAC_CAPS_NO_WOL 0x0000100 -#define PHYTMAC_CAPS_LPI 0x0000400 +#define PHYTMAC_CAPS_PWCTRL 0x0000200 #define PHYTMAC_CAPS_MSG 0x0000800 +#define PHYTMAC_CAPS_RXPTR 0x0001000 + +#define VERSION_V0 0 +#define VERSION_V3 0x3 #define PHYTMAC_TX 0x1 #define PHYTMAC_RX 0x2 @@ -123,6 +131,39 @@ #define PHYTMAC_WAKE_UCAST 0x00000004 #define PHYTMAC_WAKE_MCAST 0x00000008 +enum phytmac_interface { + PHYTMAC_PHY_INTERFACE_MODE_NA, + PHYTMAC_PHY_INTERFACE_MODE_INTERNAL, + PHYTMAC_PHY_INTERFACE_MODE_MII, + PHYTMAC_PHY_INTERFACE_MODE_GMII, + PHYTMAC_PHY_INTERFACE_MODE_SGMII, + PHYTMAC_PHY_INTERFACE_MODE_TBI, + PHYTMAC_PHY_INTERFACE_MODE_REVMII, + PHYTMAC_PHY_INTERFACE_MODE_RMII, + PHYTMAC_PHY_INTERFACE_MODE_RGMII, + PHYTMAC_PHY_INTERFACE_MODE_RGMII_ID, + PHYTMAC_PHY_INTERFACE_MODE_RGMII_RXID, + PHYTMAC_PHY_INTERFACE_MODE_RGMII_TXID, + PHYTMAC_PHY_INTERFACE_MODE_RTBI, + PHYTMAC_PHY_INTERFACE_MODE_SMII, + PHYTMAC_PHY_INTERFACE_MODE_XGMII, + PHYTMAC_PHY_INTERFACE_MODE_MOCA, + PHYTMAC_PHY_INTERFACE_MODE_QSGMII, + PHYTMAC_PHY_INTERFACE_MODE_TRGMII, + PHYTMAC_PHY_INTERFACE_MODE_100BASEX, + PHYTMAC_PHY_INTERFACE_MODE_1000BASEX, + PHYTMAC_PHY_INTERFACE_MODE_2500BASEX, + PHYTMAC_PHY_INTERFACE_MODE_5GBASER, + PHYTMAC_PHY_INTERFACE_MODE_RXAUI, + PHYTMAC_PHY_INTERFACE_MODE_XAUI, + /* 10GBASE-R, XFI, SFI - single lane 10G Serdes */ + PHYTMAC_PHY_INTERFACE_MODE_10GBASER, + PHYTMAC_PHY_INTERFACE_MODE_USXGMII, + /* 10GBASE-KR - with Clause 73 AN */ + PHYTMAC_PHY_INTERFACE_MODE_10GKR, + PHYTMAC_PHY_INTERFACE_MODE_MAX, +}; + struct packet_info { int lso; int desc_cnt; @@ -215,7 +256,6 @@ static const struct phytmac_statistics queue_statistics[] = { struct phytmac_config { struct phytmac_hw_if *hw_if; u32 caps; - u32 tsu_rate; u16 queue_num; }; @@ -368,8 +408,8 @@ struct phytmac_msg { u32 tx_msg_tail; u32 rx_msg_head; u32 rx_msg_tail; - /* Lock to protect msg */ - spinlock_t msg_lock; + /* use msg_mutex to protect msg */ + struct mutex msg_mutex; }; struct ts_ctrl { @@ -402,7 +442,6 @@ struct phytmac { u32 min_tx_length; u32 jumbo_len; u32 wol; - u32 lpi; u32 power_state; struct work_struct restart_task; /* Lock to protect mac config */ @@ -431,6 +470,7 @@ struct phytmac { struct phylink_pcs phylink_pcs; int pause; phy_interface_t phy_interface; + enum phytmac_interface phytmac_v2_interface; int speed; int duplex; int autoneg; @@ -447,6 +487,7 @@ struct phytmac { /* Lock to protect fs */ spinlock_t rx_fs_lock; unsigned int max_rx_fs; + u32 version; }; struct phytmac_hw_if { @@ -506,7 +547,7 @@ struct phytmac_hw_if { unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); unsigned int (*rx_clean)(struct phytmac_queue *queue, u32 cleaned_count); void (*transmit)(struct phytmac_queue *queue); - void (*restart)(struct phytmac *pdata); + void (*update_rx_tail)(struct phytmac_queue *queue); int (*tx_complete)(const struct phytmac_dma_desc *desc); bool (*rx_complete)(const struct phytmac_dma_desc *desc); int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); @@ -516,6 +557,7 @@ struct phytmac_hw_if { bool (*rx_pkt_start)(const struct phytmac_dma_desc *desc); bool (*rx_pkt_end)(const struct phytmac_dma_desc *desc); unsigned int (*zero_rx_desc_addr)(struct phytmac_dma_desc *desc); + unsigned int (*zero_tx_desc)(struct phytmac_dma_desc *desc); void (*clear_rx_desc)(struct phytmac_queue *queue, int begin, int end); void (*clear_tx_desc)(struct phytmac_queue *queue); /* ptp */ @@ -603,6 +645,7 @@ int phytmac_drv_probe(struct phytmac *pdata); int phytmac_drv_remove(struct phytmac *pdata); int phytmac_drv_suspend(struct phytmac *pdata); int phytmac_drv_resume(struct phytmac *pdata); +void phytmac_drv_shutdown(struct phytmac *pdata); struct phytmac *phytmac_alloc_pdata(struct device *dev); void phytmac_free_pdata(struct phytmac *pdata); int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size); diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index d88c928587..b577694f74 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ #include #include diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index b41d28d147..7e96f42b7a 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2,6 +2,8 @@ /* * Phytium Ethernet Controller driver * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -130,10 +132,12 @@ static int phytmac_get_mac_address(struct phytmac *pdata) if (is_valid_ether_addr(addr)) { phytmac_hw_addr_set(pdata->ndev, addr); + ether_addr_copy(pdata->ndev->perm_addr, addr); return 0; } dev_info(pdata->dev, "invalid hw address, using random\n"); eth_hw_addr_random(pdata->ndev); + ether_addr_copy(pdata->ndev->perm_addr, pdata->ndev->dev_addr); return 0; } @@ -1014,12 +1018,7 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) queue->rx_next_to_alloc = queue->rx_head; /* make newly descriptor to hardware */ wmb(); - hw_if->rx_clean(queue, rx_unclean); - /* make newly descriptor to hardware */ - wmb(); - queue->rx_head += rx_unclean; - if (queue->rx_head >= pdata->rx_ring_size) - queue->rx_head &= (pdata->rx_ring_size - 1); + hw_if->update_rx_tail(queue); } static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, @@ -1129,7 +1128,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) unsigned int tail = queue->tx_tail; unsigned int head; - spin_lock(&pdata->lock); + spin_lock(&queue->tx_lock); for (head = queue->tx_head; head != tail && packet_count < budget; ) { struct sk_buff *skb; @@ -1166,7 +1165,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) packet_count++; } - /* Now we can safely release resources */ + /* Now we can safely release resources */ phytmac_tx_unmap(pdata, tx_skb, budget); if (complete) { @@ -1184,7 +1183,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) if (__netif_subqueue_stopped(pdata->ndev, queue_index) && (phytmac_maybe_wake_tx_queue(queue))) netif_wake_subqueue(pdata->ndev, queue_index); - spin_unlock(&pdata->lock); + spin_unlock(&queue->tx_lock); return packet_count; } @@ -1267,6 +1266,7 @@ static inline int phytmac_clear_csum(struct sk_buff *skb) static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) { +#ifdef PHYTMAC_SW_FCS bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) || skb_is_nonlinear(*skb); int padlen = ETH_ZLEN - (*skb)->len; @@ -1276,7 +1276,7 @@ static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) u32 fcs; int i; - if ((ndev->features & NETIF_F_HW_CSUM) || + if (!(ndev->features & NETIF_F_HW_CSUM) || !((*skb)->ip_summed != CHECKSUM_PARTIAL) || skb_shinfo(*skb)->gso_size || phytmac_ptp_one_step(*skb)) return 0; @@ -1313,6 +1313,8 @@ static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) for (i = 0; i < 4; ++i) skb_put_u8(*skb, (fcs >> (i * 8)) & 0xff); +#endif + return 0; } @@ -1362,13 +1364,13 @@ static int phytmac_packet_info(struct phytmac *pdata, desc_cnt += TXD_USE_COUNT(pdata, skb_frag_size(&skb_shinfo(skb)->frags[f])); packet->desc_cnt = desc_cnt; - if ((!(pdata->ndev->features & NETIF_F_HW_CSUM)) && +#ifdef PHYTMAC_SW_FCS + if ((pdata->ndev->features & NETIF_F_HW_CSUM) && skb->ip_summed != CHECKSUM_PARTIAL && !is_lso && !phytmac_ptp_one_step(skb)) packet->nocrc = 1; - else - packet->nocrc = 0; +#endif if (netif_msg_pktdata(pdata)) { netdev_info(pdata->ndev, "packet info: desc_cnt=%d, nocrc=%d,ip_summed=%d\n", @@ -1508,7 +1510,6 @@ static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *nd struct phytmac_queue *queue = &pdata->queues[queue_index]; netdev_tx_t ret = NETDEV_TX_OK; struct packet_info packet; - unsigned long flags; if (phytmac_clear_csum(skb)) { dev_kfree_skb_any(skb); @@ -1529,7 +1530,7 @@ static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *nd if (netif_msg_pktdata(pdata)) phytmac_dump_pkt(pdata, skb, true); - spin_lock_irqsave(&pdata->lock, flags); + spin_lock_bh(&queue->tx_lock); /* Check that there are enough descriptors available */ ret = phytmac_maybe_stop_tx_queue(queue, packet.desc_cnt); if (ret) @@ -1548,7 +1549,7 @@ static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *nd hw_if->transmit(queue); tx_return: - spin_unlock_irqrestore(&pdata->lock, flags); + spin_unlock_bh(&queue->tx_lock); return ret; } @@ -1694,6 +1695,7 @@ static void phytmac_mac_link_down(struct phylink_config *config, unsigned int mo unsigned int q; unsigned long flags; struct phytmac_tx_skb *tx_skb; + struct phytmac_dma_desc *tx_desc = NULL; int i; if (netif_msg_link(pdata)) { @@ -1712,18 +1714,22 @@ static void phytmac_mac_link_down(struct phylink_config *config, unsigned int mo /* Disable Rx and Tx */ hw_if->enable_network(pdata, false, PHYTMAC_RX | PHYTMAC_TX); + spin_unlock_irqrestore(&pdata->lock, flags); /* Tx clean */ for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + spin_lock_bh(&queue->tx_lock); for (i = 0; i < pdata->tx_ring_size; i++) { tx_skb = phytmac_get_tx_skb(queue, i); if (tx_skb) phytmac_tx_unmap(pdata, tx_skb, 0); + + tx_desc = phytmac_get_tx_desc(queue, i); + hw_if->zero_tx_desc(tx_desc); } + spin_unlock_bh(&queue->tx_lock); } - spin_unlock_irqrestore(&pdata->lock, flags); - netif_tx_stop_all_queues(ndev); } @@ -1936,10 +1942,6 @@ static int phytmac_open(struct net_device *ndev) if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "open\n"); - /* phytmac_powerup */ - if (pdata->power_state == PHYTMAC_POWEROFF) - hw_if->poweron(pdata, PHYTMAC_POWERON); - if (hw_if->init_msg_ring) hw_if->init_msg_ring(pdata); @@ -2046,10 +2048,6 @@ static int phytmac_close(struct net_device *ndev) if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) phytmac_ptp_unregister(pdata); - /* phytmac_powerup */ - if (pdata->power_state == PHYTMAC_POWERON) - hw_if->poweron(pdata, PHYTMAC_POWEROFF); - return 0; } @@ -2322,6 +2320,7 @@ int phytmac_drv_probe(struct phytmac *pdata) { struct net_device *ndev = pdata->ndev; struct device *dev = pdata->dev; + struct phytmac_hw_if *hw_if = pdata->hw_if; int ret = 0; if (netif_msg_probe(pdata)) @@ -2345,12 +2344,13 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_out; } - netif_carrier_off(ndev); - ret = register_netdev(ndev); - if (ret) { - dev_err(pdata->dev, "Cannot register net device, aborting.\n"); - goto err_out; - } + if (pdata->power_state == PHYTMAC_POWEROFF) + hw_if->poweron(pdata, PHYTMAC_POWERON); + + if (hw_if->init_msg_ring) + hw_if->init_msg_ring(pdata); + + mutex_init(&pdata->msg_ring.msg_mutex); if (pdata->use_mii && !pdata->mii_bus) { ret = phytmac_mdio_register(pdata); @@ -2372,6 +2372,13 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_phylink_init; } + ret = register_netdev(ndev); + if (ret) { + dev_err(pdata->dev, "Cannot register net device, aborting.\n"); + goto err_phylink_init; + } + netif_carrier_off(ndev); + if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "probe successfully! Phytium %s at 0x%08lx irq %d (%pM)\n", "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); @@ -2386,8 +2393,6 @@ int phytmac_drv_probe(struct phytmac *pdata) if (pdata->mii_bus) mdiobus_free(pdata->mii_bus); - unregister_netdev(ndev); - err_out: return ret; } @@ -2396,12 +2401,15 @@ EXPORT_SYMBOL_GPL(phytmac_drv_probe); int phytmac_drv_remove(struct phytmac *pdata) { struct net_device *ndev = pdata->ndev; + struct phytmac_hw_if *hw_if = pdata->hw_if; if (ndev) { if (pdata->use_ncsi && pdata->ncsidev) ncsi_unregister_dev(pdata->ncsidev); unregister_netdev(ndev); + if (pdata->power_state == PHYTMAC_POWERON) + hw_if->poweron(pdata, PHYTMAC_POWEROFF); if (pdata->use_mii && pdata->mii_bus) { mdiobus_unregister(pdata->mii_bus); @@ -2410,6 +2418,8 @@ int phytmac_drv_remove(struct phytmac *pdata) if (pdata->phylink) phylink_destroy(pdata->phylink); + + mutex_destroy(&pdata->msg_ring.msg_mutex); } return 0; @@ -2435,12 +2445,15 @@ int phytmac_drv_suspend(struct phytmac *pdata) /* napi_disable */ for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + hw_if->clear_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); napi_disable(&queue->tx_napi); napi_disable(&queue->rx_napi); } if (pdata->wol) { hw_if->set_wol(pdata, pdata->wol); + pdata->power_state = PHYTMAC_POWEROFF; } else { rtnl_lock(); phylink_stop(pdata->phylink); @@ -2462,15 +2475,14 @@ int phytmac_drv_resume(struct phytmac *pdata) struct phytmac_hw_if *hw_if = pdata->hw_if; struct ethtool_rx_fs_item *item; - if (!netif_running(pdata->ndev)) - return 0; - - if (pdata->power_state == PHYTMAC_POWEROFF) - hw_if->poweron(pdata, PHYTMAC_POWERON); + hw_if->poweron(pdata, PHYTMAC_POWERON); if (hw_if->init_msg_ring) hw_if->init_msg_ring(pdata); + if (!netif_running(pdata->ndev)) + return 0; + if (pdata->wol) { hw_if->set_wol(pdata, 0); rtnl_lock(); @@ -2533,6 +2545,23 @@ void phytmac_free_pdata(struct phytmac *pdata) } EXPORT_SYMBOL_GPL(phytmac_free_pdata); +void phytmac_drv_shutdown(struct phytmac *pdata) +{ + struct net_device *netdev = pdata->ndev; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + rtnl_lock(); + netif_device_detach(netdev); + + if (netif_running(netdev)) + phytmac_close(netdev); + rtnl_unlock(); + + if (pdata->power_state == PHYTMAC_POWERON) + hw_if->poweron(pdata, PHYTMAC_POWEROFF); +} +EXPORT_SYMBOL_GPL(phytmac_drv_shutdown); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index be1ae2a74c..62766c49d1 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Phytium GMAC PCI wrapper. + * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * + * Author: Wenting Song */ #include @@ -20,7 +24,6 @@ struct phytmac_data { struct phytmac_hw_if *hw_if; u32 caps; - u32 tsu_rate; u16 queue_num; int speed; bool duplex; diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 5033e03ac2..9550971189 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Phytium GMAC Platform wrapper. + * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * + * Author: Wenting Song */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -20,18 +24,17 @@ static const struct phytmac_config phytium_1p0_config = { | PHYTMAC_CAPS_JUMBO | PHYTMAC_CAPS_LSO, .queue_num = 4, - .tsu_rate = 300000000, }; static const struct phytmac_config phytium_2p0_config = { .hw_if = &phytmac_2p0_hw, .caps = PHYTMAC_CAPS_TAILPTR - | PHYTMAC_CAPS_LPI + | PHYTMAC_CAPS_RXPTR + | PHYTMAC_CAPS_PWCTRL | PHYTMAC_CAPS_LSO | PHYTMAC_CAPS_MSG | PHYTMAC_CAPS_JUMBO, .queue_num = 2, - .tsu_rate = 300000000, }; #if defined(CONFIG_OF) @@ -46,7 +49,8 @@ MODULE_DEVICE_TABLE(of, phytmac_dt_ids); #ifdef CONFIG_ACPI static const struct acpi_device_id phytmac_acpi_ids[] = { { .id = "PHYT0046", .driver_data = (kernel_ulong_t)&phytium_1p0_config }, - { } + { .id = "PHYT0056", .driver_data = (kernel_ulong_t)&phytium_2p0_config }, + {} }; MODULE_DEVICE_TABLE(acpi, phytmac_acpi_ids); @@ -54,6 +58,95 @@ MODULE_DEVICE_TABLE(acpi, phytmac_acpi_ids); #define phytmac_acpi_ids NULL #endif +static const char *phytmac_phy_modes(enum phytmac_interface interface) +{ + switch (interface) { + case PHYTMAC_PHY_INTERFACE_MODE_NA: + return ""; + case PHYTMAC_PHY_INTERFACE_MODE_INTERNAL: + return "internal"; + case PHYTMAC_PHY_INTERFACE_MODE_MII: + return "mii"; + case PHYTMAC_PHY_INTERFACE_MODE_GMII: + return "gmii"; + case PHYTMAC_PHY_INTERFACE_MODE_SGMII: + return "sgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_TBI: + return "tbi"; + case PHYTMAC_PHY_INTERFACE_MODE_REVMII: + return "rev-mii"; + case PHYTMAC_PHY_INTERFACE_MODE_RMII: + return "rmii"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII: + return "rgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII_ID: + return "rgmii-id"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII_RXID: + return "rgmii-rxid"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII_TXID: + return "rgmii-txid"; + case PHYTMAC_PHY_INTERFACE_MODE_RTBI: + return "rtbi"; + case PHYTMAC_PHY_INTERFACE_MODE_SMII: + return "smii"; + case PHYTMAC_PHY_INTERFACE_MODE_XGMII: + return "xgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_MOCA: + return "moca"; + case PHYTMAC_PHY_INTERFACE_MODE_QSGMII: + return "qsgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_TRGMII: + return "trgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_100BASEX: + return "100base-x"; + case PHYTMAC_PHY_INTERFACE_MODE_1000BASEX: + return "1000base-x"; + case PHYTMAC_PHY_INTERFACE_MODE_2500BASEX: + return "2500base-x"; + case PHYTMAC_PHY_INTERFACE_MODE_5GBASER: + return "5gbase-r"; + case PHYTMAC_PHY_INTERFACE_MODE_RXAUI: + return "rxaui"; + case PHYTMAC_PHY_INTERFACE_MODE_XAUI: + return "xaui"; + case PHYTMAC_PHY_INTERFACE_MODE_10GBASER: + return "10gbase-r"; + case PHYTMAC_PHY_INTERFACE_MODE_USXGMII: + return "usxgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_10GKR: + return "10gbase-kr"; + default: + return "unknown"; + } +} + +static int phytmac_v2_get_phy_mode(struct platform_device *pdev) +{ + const char *pm; + int err, i; + int phy_interface; + + err = device_property_read_string(&pdev->dev, "phy-mode", &pm); + if (err < 0) + return err; + + phy_interface = PHYTMAC_PHY_INTERFACE_MODE_MAX + 1; + for (i = 0; i < PHYTMAC_PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phytmac_phy_modes(i))) { + phy_interface = i; + dev_notice(&pdev->dev, "Phy mode is %s.\n", pm); + break; + } + } + + if (phy_interface > PHYTMAC_PHY_INTERFACE_MODE_MAX) { + dev_err(&pdev->dev, "Invalid phy mode value: %s!\n", pm); + return -EINVAL; + } + + return phy_interface; +} + static int phytmac_get_phy_mode(struct platform_device *pdev) { const char *pm; @@ -79,6 +172,8 @@ static int phytmac_plat_probe(struct platform_device *pdev) struct phytmac *pdata; int ret, i; u32 queue_num; + const struct of_device_id *match = NULL; + const struct acpi_device_id *match_acpi = NULL; pdata = phytmac_alloc_pdata(&pdev->dev); if (IS_ERR(pdata)) { @@ -91,8 +186,6 @@ static int phytmac_plat_probe(struct platform_device *pdev) pdata->platdev = pdev; if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(phytmac_dt_ids, np); if (match && match->data) { phytmac_config = match->data; @@ -101,11 +194,9 @@ static int phytmac_plat_probe(struct platform_device *pdev) pdata->queues_max_num = phytmac_config->queue_num; } } else if (has_acpi_companion(&pdev->dev)) { - const struct acpi_device_id *match; - - match = acpi_match_device(phytmac_acpi_ids, &pdev->dev); - if (match && match->driver_data) { - phytmac_config = (void *)match->driver_data; + match_acpi = acpi_match_device(phytmac_acpi_ids, &pdev->dev); + if (match_acpi && match_acpi->driver_data) { + phytmac_config = (void *)match_acpi->driver_data; pdata->hw_if = phytmac_config->hw_if; pdata->capacities = phytmac_config->caps; pdata->queues_max_num = phytmac_config->queue_num; @@ -121,6 +212,18 @@ static int phytmac_plat_probe(struct platform_device *pdev) } pdata->ndev->base_addr = regs->start; + if (pdev->dev.of_node && match) { + if (!strcmp(match->compatible, "phytium,gmac-1.0")) + pdata->version = PHYTMAC_READ(pdata, PHYTMAC_VERSION) & 0xff; + else + pdata->version = VERSION_V3; + } else if (has_acpi_companion(&pdev->dev) && match_acpi) { + if (!strcmp(match_acpi->id, "PHYT0046")) + pdata->version = PHYTMAC_READ(pdata, PHYTMAC_VERSION) & 0xff; + else + pdata->version = VERSION_V3; + } + if (pdata->capacities & PHYTMAC_CAPS_MSG) { ++i; regs = platform_get_resource(pdev, IORESOURCE_MEM, i); @@ -133,11 +236,10 @@ static int phytmac_plat_probe(struct platform_device *pdev) } } - if (device_property_read_bool(&pdev->dev, "lpi")) - pdata->capacities |= PHYTMAC_CAPS_LPI; + if (device_property_read_bool(&pdev->dev, "powerctrl")) + pdata->capacities |= PHYTMAC_CAPS_PWCTRL; - if (pdata->capacities & PHYTMAC_CAPS_LPI) { - /* lpi resource */ + if (pdata->version == VERSION_V3 && pdev->dev.of_node) { ++i; regs = platform_get_resource(pdev, IORESOURCE_MEM, i); if (regs) { @@ -180,6 +282,14 @@ static int phytmac_plat_probe(struct platform_device *pdev) else pdata->phy_interface = ret; + if (pdata->version == VERSION_V3) { + ret = phytmac_v2_get_phy_mode(pdev); + if (ret < 0) + pdata->phytmac_v2_interface = PHYTMAC_PHY_INTERFACE_MODE_USXGMII; + else + pdata->phytmac_v2_interface = ret; + } + ret = phytmac_drv_probe(pdata); if (ret) goto err_mem; @@ -211,6 +321,13 @@ static int phytmac_plat_remove(struct platform_device *pdev) return 0; } +static void phytmac_plat_shutdown(struct platform_device *pdev) +{ + struct phytmac *pdata = platform_get_drvdata(pdev); + + phytmac_drv_shutdown(pdata); +} + static int __maybe_unused phytmac_plat_suspend(struct device *dev) { struct phytmac *pdata = dev_get_drvdata(dev); @@ -244,6 +361,7 @@ static struct platform_driver phytmac_driver = { .acpi_match_table = phytmac_acpi_ids, .pm = &phytmac_plat_pm_ops, }, + .shutdown = phytmac_plat_shutdown, }; module_platform_driver(phytmac_driver); diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.c b/drivers/net/ethernet/phytium/phytmac_ptp.c index 26b1b75edb..4803842bd7 100644 --- a/drivers/net/ethernet/phytium/phytmac_ptp.c +++ b/drivers/net/ethernet/phytium/phytmac_ptp.c @@ -2,6 +2,9 @@ /** * 1588 PTP support for Phytium GMAC device. * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * + * Author: Wenting Song */ #include #include diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.h b/drivers/net/ethernet/phytium/phytmac_ptp.h index 0ee385e5fe..930ca9d864 100644 --- a/drivers/net/ethernet/phytium/phytmac_ptp.h +++ b/drivers/net/ethernet/phytium/phytmac_ptp.h @@ -2,6 +2,7 @@ /* * Phytium Ethernet Controller driver * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index da014b716e..15e3cd2f49 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include "phytmac.h" #include "phytmac_v1.h" @@ -211,6 +213,11 @@ static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, else PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_100M); + if (interface == PHY_INTERFACE_MODE_SGMII) { + if (speed == SPEED_2500) + phytmac_enable_autoneg(pdata, 0); + } + return 0; } @@ -289,7 +296,6 @@ static int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) static void phytmac_reset_hw(struct phytmac *pdata) { - struct phytmac_queue *queue; unsigned int q; u32 ctrl; @@ -300,7 +306,7 @@ static void phytmac_reset_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); /* Disable and clear all interrupts and disable queues */ - for (q = 0, queue = pdata->queues; q < pdata->queues_max_num; ++q, ++queue) { + for (q = 0; q < pdata->queues_max_num; ++q) { if (q == 0) { PHYTMAC_WRITE(pdata, PHYTMAC_ID, -1); PHYTMAC_WRITE(pdata, PHYTMAC_IS, -1); @@ -317,7 +323,10 @@ static void phytmac_reset_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), 0); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(q), 0); + + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_RX_TAILPTR(q), 0); } } @@ -352,6 +361,7 @@ static int phytmac_init_hw(struct phytmac *pdata) u32 config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); u32 dmaconfig; u32 nctrlconfig; + u32 ptrconfig = 0; nctrlconfig = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); nctrlconfig |= PHYTMAC_BIT(MPE); @@ -413,7 +423,14 @@ static int phytmac_init_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, dmaconfig); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, PHYTMAC_BIT(TXTAIL_ENABLE)); + ptrconfig |= PHYTMAC_BIT(TXPTR_EN); + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + ptrconfig |= PHYTMAC_BIT(RXPTR_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, ptrconfig); + + if (pdata->capacities & PHYTMAC_CAPS_START) + PHYTMAC_WRITE(pdata, PHYTMAC_TXSTARTSEL, PHYTMAC_BIT(TSTARTSEL_ENABLE)); if (phy_interface_mode_is_8023z(pdata->phy_interface)) phytmac_pcs_software_reset(pdata, 1); @@ -425,8 +442,46 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) { u32 status, data0, data1, rdata1; int ret; + acpi_handle handle; + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status acpi_sts; + unsigned long long rv; + + if (!(pdata->capacities & PHYTMAC_CAPS_PWCTRL)) { + pdata->power_state = on; + return 0; + } + + if (has_acpi_companion(pdata->dev)) { + handle = ACPI_HANDLE(pdata->dev); - if (pdata->capacities & PHYTMAC_CAPS_LPI) { + netdev_info(pdata->ndev, "set gmac power %s\n", + on == PHYTMAC_POWERON ? "on" : "off"); + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = PHYTMAC_PWCTL_GMAC_ID; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + + if (on == PHYTMAC_POWERON) { + acpi_sts = acpi_evaluate_integer(handle, "PPWO", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWO Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power on\n"); + } else { + acpi_sts = acpi_evaluate_integer(handle, "PPWD", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWD Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power off\n"); + } + } else { ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, 1, PHYTMAC_TIMEOUT); if (ret) @@ -459,16 +514,17 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) data0 & PHYTMAC_BIT(DATA0_FREE), 1, PHYTMAC_TIMEOUT); if (ret) - netdev_err(pdata->ndev, "mnh data0 is busy"); + netdev_err(pdata->ndev, "mnh data0 is busy\n"); rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); if (rdata1 == data1) netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); else netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); } + pdata->power_state = on; return 0; @@ -611,6 +667,9 @@ static int phytmac_get_feature_all(struct phytmac *pdata) /* max rx fs */ pdata->max_rx_fs = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT3, SCR2CMP); + if (pdata->version == VERSION_V3) + pdata->capacities |= PHYTMAC_CAPS_RXPTR; + if (netif_msg_hw(pdata)) netdev_info(pdata->ndev, "get feature queue_num=%d, daw=%d, dbw=%d, rx_fs=%d, rx_bd=%d, tx_bd=%d\n", pdata->queues_num, pdata->dma_addr_width, pdata->dma_data_width, @@ -741,7 +800,7 @@ static int phytmac_init_ring_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), upper_32_bits(queue->rx_ring_addr)); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(q), queue->tx_tail); } return 0; @@ -941,32 +1000,33 @@ static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) return 0; } +static unsigned int phytmac_zero_tx_desc(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = 0; + desc->desc1 &= ~PHYTMAC_BIT(TX_USED); + + return 0; +} + static void phytmac_tx_start(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(queue->index), PHYTMAC_BIT(TXTAIL_UPDATE) | queue->tx_tail); - if (pdata->capacities & PHYTMAC_CAPS_START) - PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, - PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); + if (likely(pdata->capacities & PHYTMAC_CAPS_START)) + PHYTMAC_WRITE(pdata, PHYTMAC_TXSTART, PHYTMAC_BIT(TSTART)); } -static void phytmac_restart(struct phytmac *pdata) +static void phytmac_update_rx_tail(struct phytmac_queue *queue) { - int q; - struct phytmac_queue *queue; + struct phytmac *pdata = queue->pdata; - for (q = 0; q < pdata->queues_num; q++) { - queue = &pdata->queues[q]; - if (queue->tx_head != queue->tx_tail) { - PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, - PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); - break; - } - } + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_RX_TAILPTR(queue->index), queue->rx_head); } static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) @@ -1151,7 +1211,7 @@ static void phytmac_clear_tx_desc(struct phytmac_queue *queue) desc->desc1 |= PHYTMAC_BIT(TX_WRAP); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(queue->index), queue->tx_tail); } static void phytmac_get_hw_stats(struct phytmac *pdata) @@ -1379,7 +1439,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { /* tx and rx */ .tx_map = phytmac_tx_map_desc, .transmit = phytmac_tx_start, - .restart = phytmac_restart, + .update_rx_tail = phytmac_update_rx_tail, .tx_complete = phytmac_tx_complete, .rx_complete = phytmac_rx_complete, .get_rx_pkt_len = phytmac_rx_pkt_len, @@ -1393,6 +1453,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { .clear_rx_desc = phytmac_clear_rx_desc, .clear_tx_desc = phytmac_clear_tx_desc, .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, + .zero_tx_desc = phytmac_zero_tx_desc, /* ptp */ .init_ts_hw = phytmac_ptp_init_hw, .set_time = phytmac_set_time, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index 6936cb0906..7a56fdfcd3 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -1,4 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #ifndef _PHYTMAC_V1_H #define _PHYTMAC_V1_H @@ -53,6 +55,9 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_USXSTATUS 0x0A88 #define PHYTMAC_TXBDCTRL 0x04CC #define PHYTMAC_RXBDCTRL 0x04D0 +#define PHYTMAC_TXSTART 0x0E70 +#define PHYTMAC_TXSTARTSEL 0x0E74 +#define PHYTMAC_VERSION 0x0FF0 /* Fdir match registers */ #define PHYTMAC_FDIR(i) (0x0540 + ((i) << 2)) @@ -75,7 +80,8 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_IDR(i) (0x0620 + ((i) << 2)) #define PHYTMAC_IMR(i) (0x0640 + ((i) << 2)) #define PHYTMAC_TAIL_ENABLE (0x0e7c) -#define PHYTMAC_TAILPTR(i) (0x0e80 + ((i) << 2)) +#define PHYTMAC_TX_TAILPTR(i) (0x0e80 + ((i) << 2)) +#define PHYTMAC_RX_TAILPTR(i) (0x0ec0 + ((i) << 2)) #define PHYTMAC_PHY_INT_ENABLE 0x1C88 #define PHYTMAC_PHY_INT_CLEAR 0x1C8C @@ -363,8 +369,11 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_TXTAIL_UPDATE_WIDTH 1 /* Bitfields in TAIL_ENABLE */ -#define PHYTMAC_TXTAIL_ENABLE_INDEX 0 /* Enable tx tail */ -#define PHYTMAC_TXTAIL_ENABLE_WIDTH 1 +#define PHYTMAC_TXPTR_EN_INDEX 0 /* Enable tx tail */ +#define PHYTMAC_TXPTR_EN_WIDTH 1 + +#define PHYTMAC_RXPTR_EN_INDEX 16 /* Enable rx tail */ +#define PHYTMAC_RXPTR_EN_WIDTH 1 /* Bitfields in INT ENABLE */ #define PHYTMAC_WOL_RECEIVE_ENABLE_INDEX 28 /* Enable wol_event_receieve */ @@ -374,6 +383,10 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_WOL_RECEIVE_DISABLE_INDEX 28 /* Disable wol_event_receieve */ #define PHYTMAC_WOL_RECEIVE_DISABLE_WIDTH 1 +/* Bitfields in PHYTMAC_TXSTARTSEL */ +#define PHYTMAC_TSTARTSEL_ENABLE_INDEX 0 /* Enable Tstart_sel */ +#define PHYTMAC_TSTARTSEL_ENABLE_WIDTH 1 + #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index a996e38d86..0035e0c4cf 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #include #include #include @@ -13,17 +15,19 @@ #include #include #include +#include #include "phytmac.h" #include "phytmac_v2.h" -static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, - u16 cmd_subid, void *data, int len, int wait) +static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, + u16 cmd_subid, void *data, int len, int wait) { int index = 0; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; int ret = 0; + mutex_lock(&pdata->msg_ring.msg_mutex); ++pdata->msg_ring.tx_msg_tail; if (pdata->msg_ring.tx_msg_tail > pdata->msg_ring.tx_msg_ring_size) pdata->msg_ring.tx_msg_tail = 1; @@ -32,17 +36,17 @@ static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, wait = 1; memset(&msg, 0, sizeof(msg)); memset(&msg_rx, 0, sizeof(msg_rx)); - msg.module_id = PHYTMAC_MODULE_ID_GMAC; - msg.cmd_id = cmd_id; + msg.cmd_type = cmd_id; msg.cmd_subid = cmd_subid; - msg.flags = PHYTMAC_FLAGS_MSG_NOINT; + msg.status0 = PHYTMAC_FLAGS_MSG_NOINT; if (len) memcpy(&msg.para[0], data, len); if (netif_msg_hw(pdata)) { - netdev_info(pdata->ndev, "tx msg: cmdid=%d, subid=%d, flags=%d, len=%d, tail=%d\n", - msg.cmd_id, msg.cmd_subid, msg.flags, len, pdata->msg_ring.tx_msg_tail); + netdev_info(pdata->ndev, "tx msg: cmdid:%d, subid:%d, status0:%d, len:%d, tail:%d\n", + msg.cmd_type, msg.cmd_subid, msg.status0, len, + pdata->msg_ring.tx_msg_tail); } memcpy(pdata->msg_regs + PHYTMAC_MSG(index), &msg, sizeof(msg)); @@ -51,16 +55,17 @@ static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, if (wait) { memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); - while (!(msg_rx.flags & 0x1)) { + while (!(msg_rx.status0 & PHYTMAC_CMD_PRC_COMPLETED)) { cpu_relax(); memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); } } + mutex_unlock(&pdata->msg_ring.msg_mutex); return ret; } -void phytmac_reset_hw(struct phytmac *pdata) +static void phytmac_v2_reset_hw(struct phytmac *pdata) { int q; u16 cmd_id, cmd_subid; @@ -70,26 +75,27 @@ void phytmac_reset_hw(struct phytmac *pdata) for (q = 0; q < pdata->queues_max_num; ++q) { PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(q), -1); PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(q), -1); - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_RX_PTR(q), 0); } /* reset hw rx/tx enable */ cmd_id = PHYTMAC_MSG_CMD_DEFAULT; cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_HW; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); /* reset tx ring */ memset(&ring, 0, sizeof(ring)); ring.queue_num = pdata->queues_max_num; cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 0); /* reset rx ring */ cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 1); } -static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) +static int phytmac_v2_get_mac_addr(struct phytmac *pdata, u8 *addr) { int index; u16 cmd_id, cmd_subid; @@ -97,7 +103,7 @@ static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_ADDR; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); index = pdata->msg_ring.tx_msg_tail; if (index <= 0) @@ -114,7 +120,7 @@ static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) return 0; } -int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) +static int phytmac_v2_set_mac_addr(struct phytmac *pdata, const u8 *addr) { u16 cmd_id; u16 cmd_subid; @@ -126,46 +132,70 @@ int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) para.addrl = cpu_to_le32(*((u32 *)addr)); para.addrh = cpu_to_le16(*((u16 *)(addr + 4))); - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_init_hw(struct phytmac *pdata) +static int phytmac_v2_pcs_software_reset(struct phytmac *pdata, int reset) +{ + u16 cmd_id; + u16 cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (reset) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PCS_RESET; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PCS_RESET; + + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + return 0; +} + +static int phytmac_v2_init_hw(struct phytmac *pdata) { u16 cmd_id, cmd_subid; struct phytmac_dma_info dma; struct phytmac_eth_info eth; + u8 mdc; - phytmac_set_mac_addr(pdata, pdata->ndev->dev_addr); + if (pdata->mii_bus) { + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + phytmac_v2_set_mac_addr(pdata, pdata->ndev->dev_addr); cmd_id = PHYTMAC_MSG_CMD_SET; if (pdata->capacities & PHYTMAC_CAPS_JUMBO) cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_JUMBO; else cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_1536_FRAMES; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); if (pdata->ndev->flags & IFF_PROMISC) { cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); } if (pdata->ndev->features & NETIF_F_RXCSUM) { cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); } - if (!(pdata->ndev->flags & IFF_BROADCAST)) { + if (pdata->ndev->flags & IFF_BROADCAST) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_BC; + else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_BC; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); - } + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_STRIPCRC; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); memset(&dma, 0, sizeof(dma)); cmd_subid = PHYTMAC_MSG_CMD_SET_DMA; @@ -176,22 +206,31 @@ static int phytmac_init_hw(struct phytmac *pdata) dma.hw_dma_cap |= HW_DMA_CAP_CSUM; if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) dma.hw_dma_cap |= HW_DMA_CAP_PTP; + if (pdata->dma_data_width == PHYTMAC_DBW32) + dma.hw_dma_cap |= HW_DMA_CAP_DDW32; if (pdata->dma_data_width == PHYTMAC_DBW64) dma.hw_dma_cap |= HW_DMA_CAP_DDW64; if (pdata->dma_data_width == PHYTMAC_DBW128) dma.hw_dma_cap |= HW_DMA_CAP_DDW128; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); + + cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; + mdc = PHYTMAC_CLK_DIV96; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 1); memset(ð, 0, sizeof(eth)); cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; eth.index = 0; eth.etype = (uint16_t)ETH_P_IP; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)ð, sizeof(eth), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)ð, sizeof(eth), 1); + + if (phy_interface_mode_is_8023z(pdata->phy_interface)) + phytmac_v2_pcs_software_reset(pdata, 1); return 0; } -static int phytmac_enable_multicast(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_multicast(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -201,11 +240,11 @@ static int phytmac_enable_multicast(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MC; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) +static int phytmac_v2_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) { u16 cmd_id, cmd_subid; struct phytmac_mc_info para; @@ -215,12 +254,12 @@ static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_HASH_MC; para.mc_bottom = (u32)mc_filter[0]; para.mc_top = (u32)mc_filter[1]; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_init_ring_hw(struct phytmac *pdata) +static int phytmac_v2_init_ring_hw(struct phytmac *pdata) { u16 cmd_id, cmd_subid; struct phytmac_ring_info rxring; @@ -239,27 +278,27 @@ static int phytmac_init_ring_hw(struct phytmac *pdata) txring.hw_dma_cap |= HW_DMA_CAP_64B; rxring.hw_dma_cap |= HW_DMA_CAP_64B; for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), queue->tx_head); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(q), queue->tx_head); txring.addr[q] = queue->tx_ring_addr; rxring.addr[q] = queue->rx_ring_addr; } - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&txring), sizeof(txring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&txring), sizeof(txring), 0); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE; rxbuf.queue_num = pdata->queues_num; rxbuf.buffer_size = pdata->rx_buffer_len / 64; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxbuf), sizeof(rxbuf), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxbuf), sizeof(rxbuf), 0); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_RX_RING; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); return 0; } -int phytmac_init_msg_ring(struct phytmac *pdata) +static int phytmac_v2_init_msg_ring(struct phytmac *pdata) { u32 size = 0; @@ -277,7 +316,7 @@ int phytmac_init_msg_ring(struct phytmac *pdata) return 0; } -static int phytmac_get_feature_all(struct phytmac *pdata) +static int phytmac_v2_get_feature_all(struct phytmac *pdata) { u16 cmd_id, cmd_subid; int index; @@ -286,7 +325,7 @@ static int phytmac_get_feature_all(struct phytmac *pdata) memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_CAPS; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); index = pdata->msg_ring.tx_msg_tail; if (index <= 0) @@ -316,25 +355,25 @@ static int phytmac_get_feature_all(struct phytmac *pdata) return 0; } -void phytmac_get_regs(struct phytmac *pdata, u32 *reg_buff) +static void phytmac_v2_get_regs(struct phytmac *pdata, u32 *reg_buff) { u16 cmd_id, cmd_subid; - u16 reg_num; int index; + u8 interface; cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_REG_READ; - reg_num = 16; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)®_num, 2, 1); + cmd_subid = PHYTMAC_MSG_CMD_GET_REGS_FOR_ETHTOOL; + interface = pdata->phytmac_v2_interface; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&interface), sizeof(interface), 1); index = pdata->msg_ring.tx_msg_tail; if (index <= 0) index += pdata->msg_ring.tx_msg_ring_size; - - memcpy(®_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); + memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + READ_REG_NUM_MAX * sizeof(u32)); } -static void phytmac_get_hw_stats(struct phytmac *pdata) +static void phytmac_v2_get_hw_stats(struct phytmac *pdata) { u16 cmd_id, cmd_subid; u8 count; @@ -345,27 +384,20 @@ static void phytmac_get_hw_stats(struct phytmac *pdata) cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - count = 1; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); - - cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - count = 2; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); + /* There are 45 registers in total, read 16 regs at a time, read 13 regs at last time */ + for (i = 1; i <= 3; i++) { + count = i; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); - cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - count = 3; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); - - for (i = 0; i < 3; i++) { - index = pdata->msg_ring.tx_msg_tail + i - 2; + index = pdata->msg_ring.tx_msg_tail; if (index <= 0) index += pdata->msg_ring.tx_msg_ring_size; - memcpy(&stats[i * 16], pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); + + memcpy(&stats[(i - 1) * READ_REG_NUM_MAX], pdata->msg_regs + PHYTMAC_MSG(index) + + MSG_HDR_LEN, sizeof(u32) * READ_REG_NUM_MAX); } - for (i = 0, j = 0; i < 44; i++) { + for (i = 0, j = 0; i < 45; i++) { if (i == 0 || i == 20) { val = (u64)stats[i + 1] << 32 | stats[i]; *p += val; @@ -384,7 +416,7 @@ static void phytmac_get_hw_stats(struct phytmac *pdata) } } -static void phytmac_mdio_idle(struct phytmac *pdata) +static void phytmac_v2_mdio_idle(struct phytmac *pdata) { u32 val; @@ -396,7 +428,7 @@ static void phytmac_mdio_idle(struct phytmac *pdata) } } -static int phytmac_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, int is_c45) +static int phytmac_v2_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, int is_c45) { u16 data; @@ -407,7 +439,7 @@ static int phytmac_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_READ) | PHYTMAC_BITS(PHYADDR, mii_id) @@ -421,14 +453,14 @@ static int phytmac_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, | PHYTMAC_BITS(REGADDR, regnum) | PHYTMAC_BITS(CONST, 2))); } - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return data; } -static int phytmac_mdio_data_write(struct phytmac *pdata, int mii_id, - int regnum, int is_c45, u16 data) +static int phytmac_v2_mdio_data_write(struct phytmac *pdata, int mii_id, + int regnum, int is_c45, u16 data) { if (is_c45) { PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) @@ -437,7 +469,7 @@ static int phytmac_mdio_data_write(struct phytmac *pdata, int mii_id, | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_WRITE) | PHYTMAC_BITS(PHYADDR, mii_id) @@ -452,17 +484,55 @@ static int phytmac_mdio_data_write(struct phytmac *pdata, int mii_id, | PHYTMAC_BITS(VALUE, data) | PHYTMAC_BITS(CONST, 2))); } - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return 0; } -static int phytmac_powerup_hw(struct phytmac *pdata, int on) +static int phytmac_v2_powerup_hw(struct phytmac *pdata, int on) { u32 status, data0, data1, rdata1; int ret; + acpi_handle handle; + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status acpi_sts; + unsigned long long rv; + + if (!(pdata->capacities & PHYTMAC_CAPS_PWCTRL)) { + pdata->power_state = on; + return 0; + } - if (pdata->capacities & PHYTMAC_CAPS_LPI) { + if (has_acpi_companion(pdata->dev)) { + handle = ACPI_HANDLE(pdata->dev); + + netdev_info(pdata->ndev, "set gmac power %s\n", + on == PHYTMAC_POWERON ? "on" : "off"); + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = PHYTMAC_PWCTL_GMAC_ID; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + + if (on == PHYTMAC_POWERON) { + acpi_sts = acpi_evaluate_integer(handle, "PPWO", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWO Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power on\n"); + } else { + acpi_sts = acpi_evaluate_integer(handle, "PPWD", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWD Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power off\n"); + } + } else { ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, 1, PHYTMAC_TIMEOUT); if (ret) @@ -500,10 +570,10 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); if (rdata1 == data1) netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); else netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); } pdata->power_state = on; @@ -511,20 +581,32 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) return 0; } -static int phytmac_set_wake(struct phytmac *pdata, int wake) +static int phytmac_v2_set_wake(struct phytmac *pdata, int wake) { u16 cmd_id, cmd_subid; - u8 wol = (u8)wake; + struct phytmac_wol para; + u32 wol_type = 0; + + if (wake & PHYTMAC_WAKE_MAGIC) + wol_type |= PHYTMAC_BIT(MAGIC); + if (wake & PHYTMAC_WAKE_ARP) + wol_type |= PHYTMAC_BIT(ARP); + if (wake & PHYTMAC_WAKE_UCAST) + wol_type |= PHYTMAC_BIT(UCAST); + if (wake & PHYTMAC_WAKE_MCAST) + wol_type |= PHYTMAC_BIT(MCAST); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_WOL; - - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&wol), 1, 1); + memset(¶, 0, sizeof(para)); + para.wol_type = cpu_to_le32(wol_type); + para.wake = (u8)wake; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_enable_promise(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_promise(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; u8 rxcsum = 0; @@ -538,12 +620,12 @@ static int phytmac_enable_promise(struct phytmac *pdata, int enable) rxcsum = 1; } - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxcsum), 1, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxcsum), 1, 1); return 0; } -static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_rxcsum(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -553,12 +635,12 @@ static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_RXCSUM; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_txcsum(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -568,12 +650,12 @@ static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_TXCSUM; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_mdio(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_mdio(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -583,12 +665,12 @@ static int phytmac_enable_mdio(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MDIO; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_autoneg(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -598,13 +680,13 @@ static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); pdata->autoneg = enable; return 0; } -static int phytmac_enable_pause(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_pause(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -614,12 +696,12 @@ static int phytmac_enable_pause(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PAUSE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) +static int phytmac_v2_enable_network(struct phytmac *pdata, int enable, int rx_tx) { u16 cmd_id, cmd_subid; @@ -629,12 +711,12 @@ static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_NETWORK; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +static int phytmac_v2_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) { struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; struct phytmac_fdir_info fdir; @@ -662,19 +744,19 @@ static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_ fdir.srcport_mask = tp4sp_m->psrc; } - fdir.location = rx_flow->location; - fdir.queue = rx_flow->ring_cookie; + fdir.location = (u8)(rx_flow->location); + fdir.queue = (u8)(rx_flow->ring_cookie); if (fdir.ipsrc_en || fdir.ipdst_en || fdir.port_en) { cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_ADD_FDIR; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); } return 0; } -static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +static int phytmac_v2_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) { struct phytmac_fdir_info fdir; u16 cmd_id, cmd_subid; @@ -682,21 +764,21 @@ static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_ memset(&fdir, 0, sizeof(fdir)); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_DEL_FDIR; - fdir.location = (u8)rx_flow->location; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + fdir.location = (u8)(rx_flow->location); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); return 0; } -static void phytmac_tx_start(struct phytmac_queue *queue) +static void phytmac_v2_tx_start(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(queue->index), queue->tx_tail); queue->tx_xmit_more = 0; } -static u32 phytmac_get_irq_mask(u32 mask) +static u32 phytmac_v2_get_irq_mask(u32 mask) { u32 value = 0; @@ -709,7 +791,7 @@ static u32 phytmac_get_irq_mask(u32 mask) return value; } -static u32 phytmac_get_irq_status(u32 value) +static u32 phytmac_v2_get_irq_status(u32 value) { u32 status = 0; @@ -722,111 +804,124 @@ static u32 phytmac_get_irq_status(u32 value) return status; } -static void phytmac_enable_irq(struct phytmac *pdata, - int queue_index, u32 mask) +static void phytmac_v2_enable_irq(struct phytmac *pdata, + int queue_index, u32 mask) { u32 value; - value = phytmac_get_irq_mask(mask); + value = phytmac_v2_get_irq_mask(mask); PHYTMAC_WRITE(pdata, PHYTMAC_INT_ER(queue_index), value); } -static void phytmac_disable_irq(struct phytmac *pdata, - int queue_index, u32 mask) +static void phytmac_v2_disable_irq(struct phytmac *pdata, + int queue_index, u32 mask) { u32 value; - value = phytmac_get_irq_mask(mask); + value = phytmac_v2_get_irq_mask(mask); PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(queue_index), value); } -static void phytmac_clear_irq(struct phytmac *pdata, - int queue_index, u32 mask) +static void phytmac_v2_clear_irq(struct phytmac *pdata, + int queue_index, u32 mask) { u32 value; - value = phytmac_get_irq_mask(mask); + value = phytmac_v2_get_irq_mask(mask); PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(queue_index), value); } -static unsigned int phytmac_get_irq(struct phytmac *pdata, int queue_index) +static unsigned int phytmac_v2_get_irq(struct phytmac *pdata, int queue_index) { u32 status; u32 value; value = PHYTMAC_READ(pdata, PHYTMAC_INT_SR(queue_index)); - status = phytmac_get_irq_status(value); + status = phytmac_v2_get_irq_status(value); return status; } -static void phytmac_interface_config(struct phytmac *pdata, unsigned int mode, - const struct phylink_link_state *state) +static void phytmac_v2_interface_config(struct phytmac *pdata, unsigned int mode, + const struct phylink_link_state *state) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; + u8 autoneg = 0; + + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + if (mode == MLO_AN_FIXED) + autoneg = 0; + else + autoneg = 1; + } + + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + autoneg = 1; + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + autoneg = 0; memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_SET; - cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_CONFIG; - para.interface = state->interface; - para.autoneg = (mode == MLO_AN_FIXED ? 0 : 1); + cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_MAC_CONFIG; + para.interface = pdata->phytmac_v2_interface; + para.autoneg = autoneg; para.speed = state->speed; para.duplex = state->duplex; pdata->autoneg = para.autoneg; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); } -static int phytmac_interface_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) +static int phytmac_v2_interface_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; + if (interface == PHY_INTERFACE_MODE_SGMII) { + if (speed == SPEED_2500) + pdata->autoneg = 0; + } + memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_LINK_CONFIG; - para.interface = interface; + para.interface = pdata->phytmac_v2_interface; para.duplex = duplex; para.speed = speed; para.autoneg = pdata->autoneg; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_interface_linkdown(struct phytmac *pdata) +static int phytmac_v2_interface_linkdown(struct phytmac *pdata) { return 0; } -static int phytmac_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) +static int phytmac_v2_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) { - struct phytmac_interface_info para; u16 cmd_id, cmd_subid; if (interface == PHY_INTERFACE_MODE_USXGMII || interface == PHY_INTERFACE_MODE_10GBASER) { - memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_PCS_LINK_UP; - para.interface = interface; - para.duplex = duplex; - para.speed = speed; - para.autoneg = 0; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); } return 0; } -static int phytmac_pcs_linkdown(struct phytmac *pdata) +static int phytmac_v2_pcs_linkdown(struct phytmac *pdata) { return 0; } -static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) +static unsigned int phytmac_v2_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) { if (interface == PHY_INTERFACE_MODE_SGMII || interface == PHY_INTERFACE_MODE_1000BASEX || @@ -839,8 +934,8 @@ static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t return 0; } -static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, - u32 tx_tail, struct packet_info *packet) +static unsigned int phytmac_v2_tx_map_desc(struct phytmac_queue *queue, + u32 tx_tail, struct packet_info *packet) { unsigned int i, ctrl; struct phytmac *pdata = queue->pdata; @@ -883,8 +978,8 @@ static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, return 0; } -static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, - u32 index) +static void phytmac_v2_init_rx_map_desc(struct phytmac_queue *queue, + u32 index) { struct phytmac_dma_desc *desc; @@ -896,7 +991,7 @@ static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, desc->desc0 |= PHYTMAC_BIT(RXUSED); } -static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, dma_addr_t addr) +static unsigned int phytmac_v2_rx_map_desc(struct phytmac_queue *queue, u32 index, dma_addr_t addr) { struct phytmac *pdata = queue->pdata; struct phytmac_dma_desc *desc; @@ -909,19 +1004,21 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); /* Make newly descriptor to hardware */ - dma_wmb(); + if (!(pdata->capacities & PHYTMAC_CAPS_RXPTR)) + dma_wmb(); desc->desc0 = lower_32_bits(addr); } else { desc->desc1 = 0; /* make newly descriptor to hardware */ - dma_wmb(); + if (!(pdata->capacities & PHYTMAC_CAPS_RXPTR)) + dma_wmb(); desc->desc0 &= ~PHYTMAC_BIT(RXUSED); } return 0; } -static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) +static unsigned int phytmac_v2_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { desc->desc2 = 0; desc->desc0 = PHYTMAC_BIT(RXUSED); @@ -929,12 +1026,29 @@ static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) return 0; } -static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) +static unsigned int phytmac_v2_zero_tx_desc(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = 0; + desc->desc1 &= ~PHYTMAC_BIT(TXUSED); + + return 0; +} + +static void phytmac_v2_update_rx_tail(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_RX_PTR(queue->index), queue->rx_head); +} + +static int phytmac_v2_tx_complete(const struct phytmac_dma_desc *desc) { return PHYTMAC_GET_BITS(desc->desc1, TXUSED); } -static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_complete(const struct phytmac_dma_desc *desc) { dma_addr_t addr; bool used; @@ -949,7 +1063,7 @@ static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) return false; } -static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) +static int phytmac_v2_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) { if (pdata->capacities & PHYTMAC_CAPS_JUMBO) return desc->desc1 & PHYTMAC_RXJFRMLEN_MASK; @@ -957,7 +1071,7 @@ static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_de return desc->desc1 & PHYTMAC_RXFRMLEN_MASK; } -static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_checksum(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; u32 check = value >> PHYTMAC_RXCSUM_INDEX & 0x3; @@ -965,28 +1079,28 @@ static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) return (check == PHYTMAC_RXCSUM_IP_TCP || check == PHYTMAC_RXCSUM_IP_UDP); } -static bool phytmac_rx_single_buffer(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_single_buffer(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; return ((value & PHYTMAC_BIT(RXSOF)) && (value & PHYTMAC_BIT(RXEOF))); } -static bool phytmac_rx_sof(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_sof(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; return (value & PHYTMAC_BIT(RXSOF)); } -static bool phytmac_rx_eof(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_eof(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; return (value & PHYTMAC_BIT(RXEOF)); } -static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) +static void phytmac_v2_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) { unsigned int frag; unsigned int tmp = end; @@ -1001,7 +1115,7 @@ static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int en } } -static void phytmac_clear_tx_desc(struct phytmac_queue *queue) +static void phytmac_v2_clear_tx_desc(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; struct phytmac_dma_desc *desc = NULL; @@ -1018,10 +1132,10 @@ static void phytmac_clear_tx_desc(struct phytmac_queue *queue) desc->desc1 = PHYTMAC_BIT(TXUSED); } desc->desc1 |= PHYTMAC_BIT(TXWRAP); - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(queue->index), queue->tx_tail); } -static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) +static void phytmac_v2_get_time(struct phytmac *pdata, struct timespec64 *ts) { u32 ns, secl, sech; @@ -1033,7 +1147,7 @@ static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) ts->tv_sec = (((u64)sech << 32) | secl) & TIMER_SEC_MAX_VAL; } -void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) +static void phytmac_v2_set_time(struct phytmac *pdata, time64_t sec, long nsec) { u32 secl, sech; @@ -1046,7 +1160,7 @@ void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_NSEC, nsec); } -void phytmac_clear_time(struct phytmac *pdata) +static void phytmac_v2_clear_time(struct phytmac *pdata) { u32 value; @@ -1064,7 +1178,7 @@ void phytmac_clear_time(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, 0); } -int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) +static int phytmac_v2_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) { u16 cmd_id, cmd_subid; struct phytmac_ts_config para; @@ -1074,12 +1188,12 @@ int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) para.tx_mode = ctrl->tx_control; para.rx_mode = ctrl->rx_control; para.one_step = ctrl->one_step; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) +static int phytmac_v2_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) { u32 value; @@ -1090,17 +1204,17 @@ static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) return 0; } -static void phytmac_ptp_init_hw(struct phytmac *pdata) +static void phytmac_v2_ptp_init_hw(struct phytmac *pdata) { struct timespec64 ts; ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); - phytmac_set_time(pdata, ts.tv_sec, ts.tv_nsec); + phytmac_v2_set_time(pdata, ts.tv_sec, ts.tv_nsec); - phytmac_set_tsincr(pdata, &pdata->ts_incr); + phytmac_v2_set_tsincr(pdata, &pdata->ts_incr); } -static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) +static int phytmac_v2_adjust_fine(struct phytmac *pdata, long ppm, bool negative) { struct ts_incr ts_incr; u32 tmp; @@ -1120,22 +1234,25 @@ static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) & ((1 << PHYTMAC_INCR_NSEC_WIDTH) - 1); ts_incr.sub_ns = adj & ((1 << PHYTMAC_INCR_SNSEC_WIDTH) - 1); - phytmac_set_tsincr(pdata, &ts_incr); + phytmac_v2_set_tsincr(pdata, &ts_incr); return 0; } -int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) +static int phytmac_v2_adjust_time(struct phytmac *pdata, s64 delta, int neg) { u32 adj; if (delta > PHYTMAC_ASEC_MAX) { struct timespec64 now, then; - then = ns_to_timespec64(delta); - phytmac_get_time(pdata, &now); + if (neg) + then = ns_to_timespec64(-delta); + else + then = ns_to_timespec64(delta); + phytmac_v2_get_time(pdata, &now); now = timespec64_add(now, then); - phytmac_set_time(pdata, now.tv_sec, now.tv_nsec); + phytmac_v2_set_time(pdata, now.tv_sec, now.tv_nsec); } else { adj = (neg << PHYTMAC_AADD_INDEX) | delta; PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, adj); @@ -1144,7 +1261,7 @@ int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) return 0; } -static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) +static int phytmac_v2_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) { int ts_valid = 0; @@ -1155,7 +1272,7 @@ static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc return ts_valid; } -static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) +static void phytmac_v2_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) { struct timespec64 ts2; @@ -1163,7 +1280,7 @@ static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct PHYTMAC_GET_BITS(ts_1, TS_SECL); ts->tv_nsec = PHYTMAC_GET_BITS(ts_1, TS_NSEC); - phytmac_get_time(pdata, &ts2); + phytmac_v2_get_time(pdata, &ts2); if (((ts->tv_sec ^ ts2.tv_sec) & (PHYTMAC_TS_SEC_TOP >> 1)) != 0) ts->tv_sec -= PHYTMAC_TS_SEC_TOP; @@ -1171,78 +1288,80 @@ static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct ts->tv_sec += (ts2.tv_sec & (~PHYTMAC_TS_SEC_MASK)); } -static unsigned int phytmac_get_ts_rate(struct phytmac *pdata) +static unsigned int phytmac_v2_get_ts_rate(struct phytmac *pdata) { return 300000000; } struct phytmac_hw_if phytmac_2p0_hw = { - .init_msg_ring = phytmac_init_msg_ring, - .reset_hw = phytmac_reset_hw, - .init_hw = phytmac_init_hw, - .init_ring_hw = phytmac_init_ring_hw, - .get_feature = phytmac_get_feature_all, - .get_regs = phytmac_get_regs, - .get_stats = phytmac_get_hw_stats, - .set_mac_address = phytmac_set_mac_addr, - .get_mac_address = phytmac_get_mac_addr, - .mdio_read = phytmac_mdio_data_read, - .mdio_write = phytmac_mdio_data_write, - .poweron = phytmac_powerup_hw, - .set_wol = phytmac_set_wake, - .enable_promise = phytmac_enable_promise, - .enable_multicast = phytmac_enable_multicast, - .set_hash_table = phytmac_set_mc_hash, - .enable_rx_csum = phytmac_enable_rxcsum, - .enable_tx_csum = phytmac_enable_txcsum, - .enable_mdio_control = phytmac_enable_mdio, - .enable_autoneg = phytmac_enable_autoneg, - .enable_pause = phytmac_enable_pause, - .enable_network = phytmac_enable_network, - .add_fdir_entry = phytmac_add_fdir_entry, - .del_fdir_entry = phytmac_del_fdir_entry, + .init_msg_ring = phytmac_v2_init_msg_ring, + .reset_hw = phytmac_v2_reset_hw, + .init_hw = phytmac_v2_init_hw, + .init_ring_hw = phytmac_v2_init_ring_hw, + .get_feature = phytmac_v2_get_feature_all, + .get_regs = phytmac_v2_get_regs, + .get_stats = phytmac_v2_get_hw_stats, + .set_mac_address = phytmac_v2_set_mac_addr, + .get_mac_address = phytmac_v2_get_mac_addr, + .mdio_read = phytmac_v2_mdio_data_read, + .mdio_write = phytmac_v2_mdio_data_write, + .poweron = phytmac_v2_powerup_hw, + .set_wol = phytmac_v2_set_wake, + .enable_promise = phytmac_v2_enable_promise, + .enable_multicast = phytmac_v2_enable_multicast, + .set_hash_table = phytmac_v2_set_mc_hash, + .enable_rx_csum = phytmac_v2_enable_rxcsum, + .enable_tx_csum = phytmac_v2_enable_txcsum, + .enable_mdio_control = phytmac_v2_enable_mdio, + .enable_autoneg = phytmac_v2_enable_autoneg, + .enable_pause = phytmac_v2_enable_pause, + .enable_network = phytmac_v2_enable_network, + .add_fdir_entry = phytmac_v2_add_fdir_entry, + .del_fdir_entry = phytmac_v2_del_fdir_entry, /* mac config */ - .mac_config = phytmac_interface_config, - .mac_linkup = phytmac_interface_linkup, - .mac_linkdown = phytmac_interface_linkdown, - .pcs_linkup = phytmac_pcs_linkup, - .pcs_linkdown = phytmac_pcs_linkdown, - .get_link = phytmac_pcs_get_link, + .mac_config = phytmac_v2_interface_config, + .mac_linkup = phytmac_v2_interface_linkup, + .mac_linkdown = phytmac_v2_interface_linkdown, + .pcs_linkup = phytmac_v2_pcs_linkup, + .pcs_linkdown = phytmac_v2_pcs_linkdown, + .get_link = phytmac_v2_pcs_get_link, /* irq */ - .enable_irq = phytmac_enable_irq, - .disable_irq = phytmac_disable_irq, - .clear_irq = phytmac_clear_irq, - .get_irq = phytmac_get_irq, + .enable_irq = phytmac_v2_enable_irq, + .disable_irq = phytmac_v2_disable_irq, + .clear_irq = phytmac_v2_clear_irq, + .get_irq = phytmac_v2_get_irq, /* tx and rx */ - .tx_map = phytmac_tx_map_desc, - .transmit = phytmac_tx_start, - .tx_complete = phytmac_tx_complete, - .rx_complete = phytmac_rx_complete, - .get_rx_pkt_len = phytmac_rx_pkt_len, - .init_rx_map = phytmac_init_rx_map_desc, - .rx_map = phytmac_rx_map_desc, - .rx_checksum = phytmac_rx_checksum, - .rx_single_buffer = phytmac_rx_single_buffer, - .rx_pkt_start = phytmac_rx_sof, - .rx_pkt_end = phytmac_rx_eof, - .clear_rx_desc = phytmac_clear_rx_desc, - .clear_tx_desc = phytmac_clear_tx_desc, - .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, + .tx_map = phytmac_v2_tx_map_desc, + .transmit = phytmac_v2_tx_start, + .update_rx_tail = phytmac_v2_update_rx_tail, + .tx_complete = phytmac_v2_tx_complete, + .rx_complete = phytmac_v2_rx_complete, + .get_rx_pkt_len = phytmac_v2_rx_pkt_len, + .init_rx_map = phytmac_v2_init_rx_map_desc, + .rx_map = phytmac_v2_rx_map_desc, + .rx_checksum = phytmac_v2_rx_checksum, + .rx_single_buffer = phytmac_v2_rx_single_buffer, + .rx_pkt_start = phytmac_v2_rx_sof, + .rx_pkt_end = phytmac_v2_rx_eof, + .clear_rx_desc = phytmac_v2_clear_rx_desc, + .clear_tx_desc = phytmac_v2_clear_tx_desc, + .zero_rx_desc_addr = phytmac_v2_zero_rx_desc_addr, + .zero_tx_desc = phytmac_v2_zero_tx_desc, /* ptp */ - .init_ts_hw = phytmac_ptp_init_hw, - .set_time = phytmac_set_time, - .clear_time = phytmac_clear_time, - .get_time = phytmac_get_time, - .set_ts_config = phytmac_set_tsmode, - .set_incr = phytmac_set_tsincr, - .adjust_fine = phytmac_adjust_fine, - .adjust_time = phytmac_adjust_time, - .ts_valid = phytmac_ts_valid, - .get_timestamp = phytmac_get_dma_ts, - .get_ts_rate = phytmac_get_ts_rate, + .init_ts_hw = phytmac_v2_ptp_init_hw, + .set_time = phytmac_v2_set_time, + .clear_time = phytmac_v2_clear_time, + .get_time = phytmac_v2_get_time, + .set_ts_config = phytmac_v2_set_tsmode, + .set_incr = phytmac_v2_set_tsincr, + .adjust_fine = phytmac_v2_adjust_fine, + .adjust_time = phytmac_v2_adjust_time, + .ts_valid = phytmac_v2_ts_valid, + .get_timestamp = phytmac_v2_get_dma_ts, + .get_ts_rate = phytmac_v2_get_ts_rate, }; EXPORT_SYMBOL_GPL(phytmac_2p0_hw); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 92e4806ac2..d2da4acb69 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #ifndef _PHYTMAC_V2_H #define _PHYTMAC_V2_H extern struct phytmac_hw_if phytmac_2p0_hw; +#define PHYTMAC_CMD_PRC_COMPLETED 0x1 #define PHYTMAC_MSG_SRAM_SIZE 4096 -#define MSG_HDR_LEN 8 +#define MSG_HDR_LEN 8 +#define READ_REG_NUM_MAX 16 #define PHYTMAC_TX_MSG_HEAD 0x000 #define PHYTMAC_TX_MSG_TAIL 0x004 @@ -83,7 +87,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define TIMER_SEC_MAX_VAL (((u64)1 << PHYTMAC_TIMER_SEC_WIDTH) - 1) #define TIMER_NSEC_MAX_VAL ((1 << PHYTMAC_TIMER_NSEC_WIDTH) - 1) -#define PHYTMAC_TAIL_PTR(i) (0x0100 + ((i) * 4)) +#define PHYTMAC_TX_PTR(i) (0x0100 + ((i) * 4)) +#define PHYTMAC_RX_PTR(i) (0x0030 + ((i) * 4)) #define PHYTMAC_INT_ER(i) (0x0140 + ((i) * 4)) #define PHYTMAC_INT_DR(i) (0x0180 + ((i) * 4)) #define PHYTMAC_INT_MR(i) (0x01c0 + ((i) * 4)) @@ -200,15 +205,38 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_TS_SEC_MASK 0x3f #define PHYTMAC_TS_SEC_TOP 0x40 +/* WOL register */ +#define PHYTMAC_ARP_IP_INDEX 0 +#define PHYTMAC_ARP_IP_WIDTH 16 +#define PHYTMAC_MAGIC_INDEX 16 +#define PHYTMAC_MAGIC_WIDTH 1 +#define PHYTMAC_ARP_INDEX 17 +#define PHYTMAC_ARP_WIDTH 1 +#define PHYTMAC_UCAST_INDEX 18 +#define PHYTMAC_UCAST_WIDTH 1 +#define PHYTMAC_MCAST_INDEX 19 +#define PHYTMAC_MCAST_WIDTH 1 + #define HW_DMA_CAP_64B 0x1 #define HW_DMA_CAP_CSUM 0x2 #define HW_DMA_CAP_PTP 0x4 -#define HW_DMA_CAP_DDW64 0x8 -#define HW_DMA_CAP_DDW128 0x10 +#define HW_DMA_CAP_DDW32 0x8 +#define HW_DMA_CAP_DDW64 0x10 +#define HW_DMA_CAP_DDW128 0x20 +#define PHYTMAC_DBW32 1 #define PHYTMAC_DBW64 2 #define PHYTMAC_DBW128 4 +#define PHYTMAC_CLK_DIV8 0 +#define PHYTMAC_CLK_DIV16 1 +#define PHYTMAC_CLK_DIV32 2 +#define PHYTMAC_CLK_DIV48 3 +#define PHYTMAC_CLK_DIV64 4 +#define PHYTMAC_CLK_DIV96 5 +#define PHYTMAC_CLK_DIV128 6 +#define PHYTMAC_CLK_DIV224 7 + enum phytmac_msg_cmd_id { PHYTMAC_MSG_CMD_DEFAULT = 0, PHYTMAC_MSG_CMD_SET, @@ -228,7 +256,7 @@ enum phytmac_set_subid { PHYTMAC_MSG_CMD_SET_INIT_RING = 1, PHYTMAC_MSG_CMD_SET_INIT_TX_RING = 2, PHYTMAC_MSG_CMD_SET_INIT_RX_RING = 3, - PHYTMAC_MSG_CMD_SET_MAC_CONFIG = 4, + PHYTMAC_MSG_CMD_SET_INIT_MAC_CONFIG = 4, PHYTMAC_MSG_CMD_SET_ADDR = 5, PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE = 6, PHYTMAC_MSG_CMD_SET_DMA = 7, @@ -269,6 +297,13 @@ enum phytmac_set_subid { PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG = 42, PHYTMAC_MSG_CMD_SET_RX_DATA_OFFSET = 43, PHYTMAC_MSG_CMD_SET_WOL = 44, + PHYTMAC_MSG_CMD_SET_ENABLE_RSC = 45, + PHYTMAC_MSG_CMD_SET_DISABLE_RSC = 46, + PHYTMAC_MSG_CMD_SET_ENABLE_TX_START = 47, + PHYTMAC_MSG_CMD_SET_ENABLE_PCS_RESET = 48, + PHYTMAC_MSG_CMD_SET_DISABLE_PCS_RESET = 49, + PHYTMAC_MSG_CMD_SET_MDC = 50, + PHYTMAC_MSG_CMD_SET_OUTSTANDING = 51, }; enum phytmac_get_subid { @@ -278,6 +313,8 @@ enum phytmac_get_subid { PHYTMAC_MSG_CMD_GET_BD_PREFETCH, PHYTMAC_MSG_CMD_GET_STATS, PHYTMAC_MSG_CMD_GET_REG_READ, + PHYTMAC_MSG_CMD_GET_RX_FLOW, + PHYTMAC_MSG_CMD_GET_REGS_FOR_ETHTOOL, }; struct phytmac_interface_info { @@ -292,6 +329,11 @@ struct phytmac_mc_info { u32 mc_top; } __packed; +struct phytmac_reg_info { + u32 offset; + u16 regnum; +} __packed; + struct phytmac_fdir_info { u32 ip4src; u32 ip4dst; @@ -351,12 +393,26 @@ struct phytmac_feature { u8 max_rx_fs; } __packed; +struct phytmac_wol { + u32 wol_type; + u8 wake; +} __packed; + struct phytmac_msg_info { - u16 module_id; - u16 cmd_id; - u16 cmd_subid; - u16 flags; + u8 reserved; + u8 seq; + u8 cmd_type; + u8 cmd_subid; + u16 len; + u8 status1; + u8 status0; u8 para[64]; } __packed; +struct phytmac_ots_config { + u32 axi_rd; + u32 axi_wr; + u8 queuenum; +} __packed; + #endif -- Gitee From 641e76b0cf5be9671b06ac3000aaf917df53cef9 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 18 Feb 2025 15:22:29 +0800 Subject: [PATCH 014/154] net/phytmac: Modify cmd processing function Change the para size in the msg struct from 64 to 56 and add mutux to avoid race. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I948383a1e074e9ab7f9ec3cfd9292f6624d7230c --- drivers/net/ethernet/phytium/phytmac.h | 8 +- .../net/ethernet/phytium/phytmac_ethtool.c | 4 +- drivers/net/ethernet/phytium/phytmac_v1.c | 6 +- drivers/net/ethernet/phytium/phytmac_v2.c | 142 +++++++++--------- drivers/net/ethernet/phytium/phytmac_v2.h | 24 ++- 5 files changed, 102 insertions(+), 82 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index f2ac13a3c1..62a62a2d25 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.28" +#define PHYTMAC_DRIVER_VERSION "1.0.29" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -73,7 +73,8 @@ #define PHYTMAC_TX 0x1 #define PHYTMAC_RX 0x2 -#define PHYTMAC_GREGS_LEN 16 +#define PHYTMAC_ETHTOOLD_REGS_LEN 64 +#define PHYTMAC_STATIS_REG_NUM 45 #define PHYTMAC_MTU_MIN_SIZE ETH_MIN_MTU @@ -405,7 +406,8 @@ struct phytmac_msg { u32 tx_msg_ring_size; u32 rx_msg_ring_size; u32 tx_msg_head; - u32 tx_msg_tail; + u32 tx_msg_wr_tail; + u32 tx_msg_rd_tail; u32 rx_msg_head; u32 rx_msg_tail; /* use msg_mutex to protect msg */ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index b577694f74..736f07d00e 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -70,7 +70,7 @@ static void phytmac_get_ethtool_strings(struct net_device *ndev, u32 sset, u8 *p static inline int phytmac_get_regs_len(struct net_device *ndev) { - return PHYTMAC_GREGS_LEN; + return PHYTMAC_ETHTOOLD_REGS_LEN; } static void phytmac_get_regs(struct net_device *ndev, @@ -81,7 +81,7 @@ static void phytmac_get_regs(struct net_device *ndev, struct phytmac_hw_if *hw_if = pdata->hw_if; u32 *regs_buff = p; - memset(p, 0, PHYTMAC_GREGS_LEN * sizeof(u32)); + memset(p, 0, PHYTMAC_ETHTOOLD_REGS_LEN); hw_if->get_regs(pdata, regs_buff); } diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 15e3cd2f49..705b486520 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1216,15 +1216,15 @@ static void phytmac_clear_tx_desc(struct phytmac_queue *queue) static void phytmac_get_hw_stats(struct phytmac *pdata) { - u32 stats[45]; + u32 stats[PHYTMAC_STATIS_REG_NUM]; int i, j; u64 val; u64 *p = &pdata->stats.tx_octets; - for (i = 0 ; i < 45; i++) + for (i = 0 ; i < PHYTMAC_STATIS_REG_NUM; i++) stats[i] = PHYTMAC_READ(pdata, PHYTMAC_OCTTX + i * 4); - for (i = 0, j = 0; i < 45; i++) { + for (i = 0, j = 0; i < PHYTMAC_STATIS_REG_NUM; i++) { if (i == 0 || i == 20) { val = (u64)stats[i + 1] << 32 | stats[i]; *p += val; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 0035e0c4cf..b9cb86eaef 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -22,42 +22,62 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, u16 cmd_subid, void *data, int len, int wait) { - int index = 0; + u32 tx_head, tx_tail, ring_size; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; int ret = 0; mutex_lock(&pdata->msg_ring.msg_mutex); - ++pdata->msg_ring.tx_msg_tail; - if (pdata->msg_ring.tx_msg_tail > pdata->msg_ring.tx_msg_ring_size) - pdata->msg_ring.tx_msg_tail = 1; - index = pdata->msg_ring.tx_msg_tail; + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + tx_tail = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_wr_tail); + pdata->msg_ring.tx_msg_rd_tail = tx_tail; + ring_size = pdata->msg_ring.tx_msg_ring_size; + + while ((tx_tail + 1) % ring_size == tx_head) { + netdev_info(pdata->ndev, "Tx msg ring is overrun, tx_tail:0x%x, tx_head:0x%x", + tx_tail, tx_head); + cpu_relax(); + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + } wait = 1; memset(&msg, 0, sizeof(msg)); memset(&msg_rx, 0, sizeof(msg_rx)); msg.cmd_type = cmd_id; msg.cmd_subid = cmd_subid; - msg.status0 = PHYTMAC_FLAGS_MSG_NOINT; - - if (len) + if (len > 0 && len <= PHYTMAC_MSG_PARA_LEN) { memcpy(&msg.para[0], data, len); + } else if (len > PHYTMAC_MSG_PARA_LEN) { + netdev_err(pdata->ndev, "Tx msg para len %d is greater than the max len %d", + len, PHYTMAC_MSG_PARA_LEN); + mutex_unlock(&pdata->msg_ring.msg_mutex); + return -EINVAL; + } if (netif_msg_hw(pdata)) { - netdev_info(pdata->ndev, "tx msg: cmdid:%d, subid:%d, status0:%d, len:%d, tail:%d\n", - msg.cmd_type, msg.cmd_subid, msg.status0, len, - pdata->msg_ring.tx_msg_tail); + netdev_info(pdata->ndev, "Tx msg: cmdid:%d, subid:%d, status0:%d, len:%d, tail:%d", + msg.cmd_type, msg.cmd_subid, msg.status0, len, tx_tail); } - memcpy(pdata->msg_regs + PHYTMAC_MSG(index), &msg, sizeof(msg)); - PHYTMAC_WRITE(pdata, PHYTMAC_TX_MSG_TAIL, - pdata->msg_ring.tx_msg_tail | PHYTMAC_BIT(TX_MSG_INT)); + memcpy(pdata->msg_regs + PHYTMAC_MSG(tx_tail), &msg, sizeof(msg)); + tx_tail = phytmac_v2_tx_ring_wrap(pdata, ++tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_MSG_TAIL, tx_tail | PHYTMAC_BIT(TX_MSG_INT)); + pdata->msg_ring.tx_msg_wr_tail = tx_tail; if (wait) { - memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); - while (!(msg_rx.status0 & PHYTMAC_CMD_PRC_COMPLETED)) { + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + while (tx_head != tx_tail) { cpu_relax(); - memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + } + + memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(pdata->msg_ring.tx_msg_rd_tail), + PHYTMAC_MSG_HDR_LEN); + if (!(msg_rx.status0 & PHYTMAC_CMD_PRC_SUCCESS)) { + netdev_err(pdata->ndev, "Msg process error, cmdid:%d, subid:%d, status0:%d, tail:%d", + msg.cmd_type, msg.cmd_subid, msg.status0, tx_tail); + mutex_unlock(&pdata->msg_ring.msg_mutex); + return -EINVAL; } } @@ -105,10 +125,9 @@ static int phytmac_v2_get_mac_addr(struct phytmac *pdata, u8 *addr) cmd_subid = PHYTMAC_MSG_CMD_GET_ADDR; phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 8); + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, + sizeof(struct phytmac_mac)); addr[0] = para.addrl & 0xff; addr[1] = (para.addrl >> 8) & 0xff; @@ -148,7 +167,7 @@ static int phytmac_v2_pcs_software_reset(struct phytmac *pdata, int reset) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PCS_RESET; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } @@ -216,7 +235,7 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; mdc = PHYTMAC_CLK_DIV96; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 0); memset(ð, 0, sizeof(eth)); cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; @@ -293,25 +312,23 @@ static int phytmac_v2_init_ring_hw(struct phytmac *pdata) cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_RX_RING; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 1); return 0; } static int phytmac_v2_init_msg_ring(struct phytmac *pdata) { - u32 size = 0; - - pdata->msg_ring.tx_msg_tail = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_TAIL) & 0xff; - size = PHYTMAC_READ_BITS(pdata, PHYTMAC_SIZE, TXRING_SIZE); - pdata->msg_ring.tx_msg_ring_size = size; - if (pdata->msg_ring.tx_msg_tail == size) - pdata->msg_ring.tx_msg_tail = 0; + u32 tx_msg_tail; + pdata->msg_ring.tx_msg_ring_size = PHYTMAC_READ_BITS(pdata, PHYTMAC_SIZE, TXRING_SIZE); + tx_msg_tail = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_TAIL) & 0xff; + pdata->msg_ring.tx_msg_wr_tail = phytmac_v2_tx_ring_wrap(pdata, tx_msg_tail); + pdata->msg_ring.tx_msg_rd_tail = pdata->msg_ring.tx_msg_wr_tail; PHYTMAC_WRITE(pdata, PHYTMAC_MSG_IMR, 0xfffffffe); if (netif_msg_hw(pdata)) - netdev_info(pdata->ndev, "mac msg ring: tx_msg_ring_size=%d, tx_msg_tail=%d\n", - size, pdata->msg_ring.tx_msg_tail); + netdev_info(pdata->ndev, "Msg ring size:%d, tx msg tail=%d\n", + pdata->msg_ring.tx_msg_ring_size, tx_msg_tail); return 0; } @@ -326,12 +343,8 @@ static int phytmac_v2_get_feature_all(struct phytmac *pdata) cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_CAPS; phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); - - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - - memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, sizeof(struct phytmac_feature)); pdata->queues_max_num = para.queue_num; @@ -359,45 +372,38 @@ static void phytmac_v2_get_regs(struct phytmac *pdata, u32 *reg_buff) { u16 cmd_id, cmd_subid; int index; - u8 interface; + struct phytmac_ethtool_reg msg; + memset(&msg, 0, sizeof(msg)); cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_REGS_FOR_ETHTOOL; - interface = pdata->phytmac_v2_interface; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&interface), sizeof(interface), 1); - - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, - READ_REG_NUM_MAX * sizeof(u32)); + msg.interface = pdata->phytmac_v2_interface; + /* There are 16 regs in total, read 14 regs at first time, read 2 regs at last time */ + msg.cnt = 0; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&msg), sizeof(msg), 1); + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, + PHYTMAC_MSG_PARA_LEN); + + msg.cnt = 1; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&msg), sizeof(msg), 1); + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(reg_buff + PHYTMAC_MSG_PARA_LEN / sizeof(u32), pdata->msg_regs + + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, + PHYTMAC_ETHTOOLD_REGS_LEN - PHYTMAC_MSG_PARA_LEN); } static void phytmac_v2_get_hw_stats(struct phytmac *pdata) { - u16 cmd_id, cmd_subid; - u8 count; - int i, j, index; - u32 stats[48]; + u32 stats[PHYTMAC_STATIS_REG_NUM]; + int i, j; u64 val; u64 *p = &pdata->stats.tx_octets; - cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - /* There are 45 registers in total, read 16 regs at a time, read 13 regs at last time */ - for (i = 1; i <= 3; i++) { - count = i; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); - - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - - memcpy(&stats[(i - 1) * READ_REG_NUM_MAX], pdata->msg_regs + PHYTMAC_MSG(index) + - MSG_HDR_LEN, sizeof(u32) * READ_REG_NUM_MAX); - } + for (i = 0 ; i < PHYTMAC_STATIS_REG_NUM; i++) + stats[i] = PHYTMAC_READ(pdata, PHYTMAC_OCT_TX + i * 4); - for (i = 0, j = 0; i < 45; i++) { + for (i = 0, j = 0; i < PHYTMAC_STATIS_REG_NUM; i++) { if (i == 0 || i == 20) { val = (u64)stats[i + 1] << 32 | stats[i]; *p += val; @@ -910,7 +916,7 @@ static int phytmac_v2_pcs_linkup(struct phytmac *pdata, phy_interface_t interfac cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_PCS_LINK_UP; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); } return 0; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index d2da4acb69..e7efbb7e4f 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -6,10 +6,11 @@ extern struct phytmac_hw_if phytmac_2p0_hw; -#define PHYTMAC_CMD_PRC_COMPLETED 0x1 +#define PHYTMAC_CMD_PRC_SUCCESS 0x1 #define PHYTMAC_MSG_SRAM_SIZE 4096 -#define MSG_HDR_LEN 8 -#define READ_REG_NUM_MAX 16 +#define PHYTMAC_MSG_HDR_LEN 8 +#define PHYTMAC_MSG_PARA_LEN 56 +#define PHYTMAC_READ_REG_NUM_MAX (PHYTMAC_MSG_PARA_LEN / sizeof(u32)) #define PHYTMAC_TX_MSG_HEAD 0x000 #define PHYTMAC_TX_MSG_TAIL 0x004 @@ -29,7 +30,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_TIMER_SEC 0x0258 #define PHYTMAC_TIMER_NSEC 0x025c #define PHYTMAC_TIMER_ADJUST 0x0260 -#define PHYTMAC_MSG(i) (((i) - 1) * 0x48) +#define PHYTMAC_MSG(i) ((i) * sizeof(struct phytmac_msg_info)) +#define PHYTMAC_OCT_TX 0x400 #define PHYTMAC_MODULE_ID_GMAC 0x60 #define PHYTMAC_FLAGS_MSG_COMP 0x1 @@ -246,7 +248,7 @@ enum phytmac_msg_cmd_id { }; enum phytmac_default_subid { - PHYTMAC_MSG_CMD_DEFAULT_RESET_HW = 0, + PHYTMAC_MSG_CMD_DEFAULT_RESET_HW = 1, PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE, PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE, }; @@ -406,7 +408,7 @@ struct phytmac_msg_info { u16 len; u8 status1; u8 status0; - u8 para[64]; + u8 para[PHYTMAC_MSG_PARA_LEN]; } __packed; struct phytmac_ots_config { @@ -415,4 +417,14 @@ struct phytmac_ots_config { u8 queuenum; } __packed; +struct phytmac_ethtool_reg { + u8 interface; + u8 cnt; +} __packed; + +static inline unsigned int phytmac_v2_tx_ring_wrap(struct phytmac *pdata, unsigned int index) +{ + return index & (pdata->msg_ring.tx_msg_ring_size - 1); +} + #endif -- Gitee From befbb783d3fb7433d71dc7cbe4af8bc54b6be764 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 18 Feb 2025 15:36:30 +0800 Subject: [PATCH 015/154] net/phytmac: Add XDP feature support Add XDP feature support to the phytmac driver. XDP by optimizing the data processing flow provided significant performance improvements for high-performance network application. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I87a7ec11b600d3029b5e447d14d8cc25ae9e936c --- drivers/net/ethernet/phytium/phytmac.h | 46 ++- drivers/net/ethernet/phytium/phytmac_main.c | 437 ++++++++++++++++++-- 2 files changed, 447 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 62a62a2d25..6a7953c05d 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -12,10 +12,11 @@ #include #include #include +#include #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.29" +#define PHYTMAC_DRIVER_VERSION "1.0.30" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -132,6 +133,14 @@ #define PHYTMAC_WAKE_UCAST 0x00000004 #define PHYTMAC_WAKE_MCAST 0x00000008 +/* XDP */ +#define PHYTMAC_XDP_PASS 0 +#define PHYTMAC_XDP_CONSUMED BIT(0) +#define PHYTMAC_XDP_TX BIT(1) +#define PHYTMAC_XDP_REDIR BIT(2) + +#define PHYTMAC_DESC_NEEDED (MAX_SKB_FRAGS + 4) + enum phytmac_interface { PHYTMAC_PHY_INTERFACE_MODE_NA, PHYTMAC_PHY_INTERFACE_MODE_INTERNAL, @@ -336,8 +345,20 @@ struct phytmac_dma_desc { }; #endif +/* TX resources are shared between XDP and netstack + * and we need to tag the buffer type to distinguish them + */ +enum phytmac_tx_buf_type { + PHYTMAC_TYPE_SKB = 0, + PHYTMAC_TYPE_XDP, +}; + struct phytmac_tx_skb { - struct sk_buff *skb; + union { + struct sk_buff *skb; + struct xdp_frame *xdpf; + }; + enum phytmac_tx_buf_type type; dma_addr_t addr; size_t length; bool mapped_as_page; @@ -360,6 +381,7 @@ struct phytmac_queue { struct phytmac *pdata; int irq; int index; + struct bpf_prog *xdp_prog; /* tx queue info */ unsigned int tx_head; @@ -382,6 +404,7 @@ struct phytmac_queue { struct phytmac_rx_buffer *rx_buffer_info; struct napi_struct rx_napi; struct phytmac_queue_stats stats; + struct xdp_rxq_info xdp_rxq; #ifdef CONFIG_PHYTMAC_ENABLE_PTP struct work_struct tx_ts_task; @@ -428,6 +451,7 @@ struct phytmac { struct platform_device *platdev; struct net_device *ndev; struct device *dev; + struct bpf_prog *xdp_prog; struct ncsi_dev *ncsidev; struct fwnode_handle *fwnode; struct phytmac_hw_if *hw_if; @@ -492,6 +516,23 @@ struct phytmac { u32 version; }; +/* phytmac_desc_unused - calculate if we have unused descriptors */ +static inline int phytmac_txdesc_unused(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + if (queue->tx_head > queue->tx_tail) + return queue->tx_head - queue->tx_tail - 1; + + return pdata->tx_ring_size + queue->tx_head - queue->tx_tail - 1; +} + +static inline struct netdev_queue *phytmac_get_txq(const struct phytmac *pdata, + struct phytmac_queue *queue) +{ + return netdev_get_tx_queue(pdata->ndev, queue->index); +} + struct phytmac_hw_if { int (*init_msg_ring)(struct phytmac *pdata); int (*init_hw)(struct phytmac *pdata); @@ -628,6 +669,7 @@ struct phytmac_hw_if { #define PHYTMAC_RX_DMA_ATTR \ (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) #define PHYTMAC_SKB_PAD (NET_SKB_PAD) +#define PHYTMAC_ETH_PKT_HDR_PAD (ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2)) #define PHYTMAC_RXBUFFER_2048 2048 #define PHYTMAC_MAX_FRAME_BUILD_SKB \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 7e96f42b7a..81412ee085 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include "phytmac.h" #include "phytmac_ptp.h" @@ -65,6 +67,16 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); * space in the SRAM (16KB) even when there is. */ +static int phytmac_xdp_xmit_back(struct phytmac *pdata, struct xdp_buff *xdp); + +static inline int phytmac_calc_rx_buf_len(void) +{ +#if (PAGE_SIZE < 8192) + return rounddown(PHYTMAC_MAX_FRAME_BUILD_SKB, RX_BUFFER_MULTIPLE); +#endif + return rounddown(PHYTMAC_RXBUFFER_2048, RX_BUFFER_MULTIPLE); +} + static int phytmac_queue_phyaddr_check(struct phytmac *pdata, dma_addr_t ring_base_addr, int offset) { @@ -84,9 +96,20 @@ static int phytmac_queue_phyaddr_check(struct phytmac *pdata, dma_addr_t ring_ba static int phytmac_change_mtu(struct net_device *ndev, int new_mtu) { + struct phytmac *pdata = netdev_priv(ndev); + int max_frame = new_mtu + PHYTMAC_ETH_PKT_HDR_PAD; + if (netif_running(ndev)) return -EBUSY; + if (pdata->xdp_prog) { + if (max_frame > phytmac_calc_rx_buf_len()) { + netdev_warn(pdata->ndev, + "Requested MTU size is not supported with XDP.\n"); + return -EINVAL; + } + } + if (new_mtu > MAX_MTU) { netdev_info(ndev, "Can not set MTU over %d.\n", MAX_MTU); return -EINVAL; @@ -274,14 +297,6 @@ static struct net_device_stats *phytmac_get_stats(struct net_device *dev) return nstat; } -static inline int phytmac_calc_rx_buf_len(void) -{ -#if (PAGE_SIZE < 8192) - return rounddown(PHYTMAC_MAX_FRAME_BUILD_SKB, RX_BUFFER_MULTIPLE); -#endif - return rounddown(PHYTMAC_RXBUFFER_2048, RX_BUFFER_MULTIPLE); -} - struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, unsigned int index) { @@ -394,6 +409,11 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) if (queue->rx_ring) queue->rx_ring = NULL; + if (queue->xdp_prog) { + queue->xdp_prog = NULL; + xdp_rxq_info_unreg(&queue->xdp_rxq); + } + if (queue->rx_buffer_info) { vfree(queue->rx_buffer_info); queue->rx_buffer_info = NULL; @@ -521,6 +541,20 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) queue->rx_buffer_info = vzalloc(size); if (!queue->rx_buffer_info) goto err; + + memset(&queue->xdp_rxq, 0, sizeof(queue->xdp_rxq)); + WRITE_ONCE(queue->xdp_prog, pdata->xdp_prog); + + /* XDP RX-queue info */ + ret = xdp_rxq_info_reg(&queue->xdp_rxq, queue->pdata->ndev, q); + if (ret < 0) { + netdev_err(pdata->ndev, "Failed to register xdp_rxq index %u\n", q); + goto err; + } + + xdp_rxq_info_unreg_mem_model(&queue->xdp_rxq); + WARN_ON(xdp_rxq_info_reg_mem_model(&queue->xdp_rxq, + MEM_TYPE_PAGE_SHARED, NULL)); } return 0; @@ -841,6 +875,115 @@ static struct sk_buff *phytmac_build_skb(struct phytmac_rx_buffer *rx_buffer, return skb; } +static void phytmac_rx_buffer_flip(struct phytmac_rx_buffer *rx_buffer, unsigned int size) +{ + unsigned int truesize; + +#if (PAGE_SIZE < 8192) + truesize = PHYTMAC_RX_PAGE_SIZE / 2; + rx_buffer->page_offset ^= truesize; +#else + truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + size); + rx_buffer->page_offset += truesize; +#endif +} + +static struct sk_buff *phytmac_run_xdp(struct phytmac *pdata, + struct xdp_buff *xdp) +{ + int err, result = PHYTMAC_XDP_PASS; + struct bpf_prog *xdp_prog; + u32 act; + + rcu_read_lock(); + xdp_prog = READ_ONCE(pdata->xdp_prog); + + if (!xdp_prog) + goto xdp_out; + + prefetchw(xdp->data_hard_start); /* xdp_frame write */ + + act = bpf_prog_run_xdp(xdp_prog, xdp); + switch (act) { + case XDP_PASS: + break; + case XDP_TX: + result = phytmac_xdp_xmit_back(pdata, xdp); + if (result == PHYTMAC_XDP_CONSUMED) + goto out_failure; + break; + case XDP_REDIRECT: + err = xdp_do_redirect(pdata->ndev, xdp, xdp_prog); + if (err) + goto out_failure; + result = PHYTMAC_XDP_REDIR; + break; + default: + bpf_warn_invalid_xdp_action(act); + fallthrough; + case XDP_ABORTED: +out_failure: + trace_xdp_exception(pdata->ndev, xdp_prog, act); + fallthrough; + case XDP_DROP: + result = PHYTMAC_XDP_CONSUMED; + break; + } +xdp_out: + rcu_read_unlock(); + return ERR_PTR(-result); +} + +static struct sk_buff *phytmac_rx_xdp_single(struct phytmac_queue *queue, + struct phytmac_dma_desc *desc, + unsigned int *xdp_xmit) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_rx_buffer *rx_buffer; + struct sk_buff *skb = NULL; + unsigned int len; + struct xdp_buff xdp; + + xdp.rxq = &queue->xdp_rxq; + + len = hw_if->get_rx_pkt_len(pdata, desc); + rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); + + hw_if->zero_rx_desc_addr(desc); + + xdp.data = page_address(rx_buffer->page) + rx_buffer->page_offset; + xdp.data_meta = xdp.data; + xdp.data_hard_start = xdp.data - PHYTMAC_SKB_PAD; + xdp.data_end = xdp.data + len; +#if (PAGE_SIZE < 8192) + xdp.frame_sz = PHYTMAC_RX_PAGE_SIZE / 2; +#else + xdp.frame_sz = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + len); +#endif + skb = phytmac_run_xdp(pdata, &xdp); + + if (IS_ERR(skb)) { + unsigned int xdp_res = -PTR_ERR(skb); + + if (xdp_res & (PHYTMAC_XDP_TX | PHYTMAC_XDP_REDIR)) { + *xdp_xmit |= xdp_res; + phytmac_rx_buffer_flip(rx_buffer, len); + } else { + rx_buffer->pagecnt_bias++; + } + phytmac_put_rx_buffer(queue, rx_buffer); + pdata->ndev->stats.rx_bytes += len; + queue->stats.rx_bytes += len; + } else { + rx_buffer->pagecnt_bias++; + } + + return skb; +} + static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phytmac_dma_desc *desc) { struct phytmac *pdata = queue->pdata; @@ -1028,6 +1171,7 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, struct phytmac_hw_if *hw_if = pdata->hw_if; struct sk_buff *skb; struct phytmac_dma_desc *desc; + unsigned int xdp_xmit = 0; int count = 0; while (count < budget) { @@ -1040,10 +1184,15 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, /* Ensure ctrl is at least as up-to-date as rxused */ dma_rmb(); - if (hw_if->rx_single_buffer(desc)) - skb = phytmac_rx_single(queue, desc); - else + if (hw_if->rx_single_buffer(desc)) { + skb = phytmac_rx_xdp_single(queue, desc, &xdp_xmit); + if (!IS_ERR(skb)) + skb = phytmac_rx_single(queue, desc); + } else { + if (pdata->xdp_prog) + netdev_warn(pdata->ndev, "xdp does not support multiple buffers!!\n"); skb = phytmac_rx_mbuffer(queue); + } if (!skb) { netdev_warn(pdata->ndev, "phytmac rx skb is NULL\n"); @@ -1052,18 +1201,28 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, pdata->ndev->stats.rx_packets++; queue->stats.rx_packets++; - pdata->ndev->stats.rx_bytes += skb->len; - queue->stats.rx_bytes += skb->len; + if (!IS_ERR(skb)) { + pdata->ndev->stats.rx_bytes += skb->len; + queue->stats.rx_bytes += skb->len; + } queue->rx_tail = (queue->rx_tail + 1) & (pdata->rx_ring_size - 1); count++; + if (IS_ERR(skb)) { + skb = NULL; + continue; + } + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) phytmac_ptp_rxstamp(pdata, skb, desc); napi_gro_receive(napi, skb); } + if (xdp_xmit & PHYTMAC_XDP_REDIR) + xdp_do_flush_map(); + phytmac_rx_clean(queue); return count; @@ -1081,8 +1240,13 @@ static void phytmac_tx_unmap(struct phytmac *pdata, struct phytmac_tx_skb *tx_sk tx_skb->addr = 0; } - if (tx_skb->skb) { - napi_consume_skb(tx_skb->skb, budget); + if (tx_skb->type == PHYTMAC_TYPE_XDP) { + if (tx_skb->xdpf) + xdp_return_frame(tx_skb->xdpf); + tx_skb->xdpf = NULL; + } else { + if (tx_skb->skb) + napi_consume_skb(tx_skb->skb, budget); tx_skb->skb = NULL; } } @@ -1116,6 +1280,19 @@ static int phytmac_maybe_wake_tx_queue(struct phytmac_queue *queue) return (space <= (3 * pdata->tx_ring_size / 4)) ? 1 : 0; } +static inline void phytmac_do_ptp_txstamp(struct phytmac_queue *queue, + struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + if (unlikely(skb_shinfo(skb)->tx_flags & + SKBTX_HW_TSTAMP) && + !phytmac_ptp_one_step(skb)) { + phytmac_ptp_txstamp(queue, skb, desc); + } + } +} + static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) { struct phytmac *pdata = queue->pdata; @@ -1143,26 +1320,31 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) /* Process all buffers of the current transmitted frame */ for (;; head++) { tx_skb = phytmac_get_tx_skb(queue, head); - skb = tx_skb->skb; - if (skb) { - complete = 1; - if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { - if (unlikely(skb_shinfo(skb)->tx_flags & - SKBTX_HW_TSTAMP) && - !phytmac_ptp_one_step(skb)) { - phytmac_ptp_txstamp(queue, skb, desc); - } + if (tx_skb->type == PHYTMAC_TYPE_SKB) { + skb = tx_skb->skb; + if (skb) { + complete = 1; + phytmac_do_ptp_txstamp(queue, skb, desc); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "desc %u (data %p) tx complete\n", + head, tx_skb->skb->data); + + pdata->ndev->stats.tx_packets++; + queue->stats.tx_packets++; + pdata->ndev->stats.tx_bytes += tx_skb->skb->len; + queue->stats.tx_bytes += tx_skb->skb->len; + packet_count++; + } + } else if (tx_skb->type == PHYTMAC_TYPE_XDP) { + if (tx_skb->xdpf) { + complete = 1; + pdata->ndev->stats.tx_packets++; + queue->stats.tx_packets++; + pdata->ndev->stats.tx_bytes += tx_skb->xdpf->len; + queue->stats.tx_bytes += tx_skb->xdpf->len; + packet_count++; } - - if (netif_msg_drv(pdata)) - netdev_info(pdata->ndev, "desc %u (data %p) tx complete\n", - head, tx_skb->skb->data); - - pdata->ndev->stats.tx_packets++; - queue->stats.tx_packets++; - pdata->ndev->stats.tx_bytes += tx_skb->skb->len; - queue->stats.tx_bytes += tx_skb->skb->len; - packet_count++; } /* Now we can safely release resources */ @@ -1410,6 +1592,7 @@ static unsigned int phytmac_tx_map(struct phytmac *pdata, /* Save info to properly release resources */ tx_skb->skb = NULL; + tx_skb->type = PHYTMAC_TYPE_SKB; tx_skb->addr = mapping; tx_skb->length = size; tx_skb->mapped_as_page = false; @@ -1438,6 +1621,7 @@ static unsigned int phytmac_tx_map(struct phytmac *pdata, /* Save info to properly release resources */ tx_skb->skb = NULL; + tx_skb->type = PHYTMAC_TYPE_SKB; tx_skb->addr = mapping; tx_skb->length = size; tx_skb->mapped_as_page = true; @@ -1502,6 +1686,59 @@ static inline void phytmac_init_ring(struct phytmac *pdata) hw_if->init_ring_hw(pdata); } +static int phytmac_start_xmit_xdp(struct phytmac *pdata, + struct phytmac_queue *queue, + struct xdp_frame *xdpf) +{ + u32 len; + struct phytmac_tx_skb *tx_buffer; + struct packet_info packet; + dma_addr_t dma; + struct phytmac_hw_if *hw_if = pdata->hw_if; + u16 tx_tail; + + len = xdpf->len; + + memset(&packet, 0, sizeof(struct packet_info)); + + if (unlikely(!phytmac_txdesc_unused(queue))) + return PHYTMAC_XDP_CONSUMED; + + dma = dma_map_single(pdata->dev, xdpf->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, dma)) + return PHYTMAC_XDP_CONSUMED; + + /* record the location of the first descriptor for this packet */ + tx_buffer = phytmac_get_tx_skb(queue, queue->tx_tail); + tx_buffer->mapped_as_page = false; + + /* Temporarily set the tail pointer for the next package */ + tx_tail = queue->tx_tail + 1; + + dma_unmap_len_set(tx_buffer, length, len); + dma_unmap_addr_set(tx_buffer, addr, dma); + tx_buffer->type = PHYTMAC_TYPE_XDP; + tx_buffer->xdpf = xdpf; + + packet.lso = 0; + packet.mss = 0; + packet.nocrc = 0; + + /* Avoid any potential race with xdp_xmit and cleanup */ + smp_wmb(); + + hw_if->tx_map(queue, tx_tail, &packet); + + queue->tx_tail = tx_tail & (pdata->tx_ring_size - 1); + + hw_if->transmit(queue); + + /* Make sure there is space in the ring for the next send. */ + phytmac_maybe_stop_tx_queue(queue, PHYTMAC_DESC_NEEDED); + + return PHYTMAC_XDP_TX; +} + static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct phytmac *pdata = netdev_priv(ndev); @@ -2185,6 +2422,136 @@ int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size) return ret; } +static int phytmac_xdp_setup(struct net_device *dev, struct bpf_prog *prog) +{ + int i, frame_size = dev->mtu + PHYTMAC_ETH_PKT_HDR_PAD; + struct phytmac *pdata = netdev_priv(dev); + struct bpf_prog *old_prog; + bool running = netif_running(dev); + bool need_reset; + + /* verify phytmac rx ring attributes are sufficient for XDP */ + if (frame_size > phytmac_calc_rx_buf_len()) { + netdev_warn(dev, "XDP RX buffer size %d is too small for the frame size %d\n", + phytmac_calc_rx_buf_len(), frame_size); + return -EINVAL; + } + + old_prog = xchg(&pdata->xdp_prog, prog); + need_reset = (!!prog != !!old_prog); + + /* device is up and bpf is added/removed, must setup the RX queues */ + if (need_reset && running) { + phytmac_close(dev); + } else { + for (i = 0; i < pdata->queues_num; i++) + (void)xchg(&pdata->queues[i].xdp_prog, + pdata->xdp_prog); + } + + if (old_prog) + bpf_prog_put(old_prog); + + /* bpf is just replaced, RXQ and MTU are already setup */ + if (!need_reset) + return 0; + + if (running) + phytmac_open(dev); + + return 0; +} + +static int phytmac_xdp(struct net_device *dev, struct netdev_bpf *xdp) +{ + switch (xdp->command) { + case XDP_SETUP_PROG: + return phytmac_xdp_setup(dev, xdp->prog); + default: + return -EINVAL; + } +} + +static struct phytmac_queue *phytmac_xdp_txq_mapping(struct phytmac *pdata) +{ + unsigned int r_idx = smp_processor_id(); + + if (r_idx >= pdata->queues_num) + r_idx = r_idx % pdata->queues_num; + + return &pdata->queues[r_idx]; +} + +static int phytmac_xdp_xmit_back(struct phytmac *pdata, struct xdp_buff *xdp) +{ + struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp); + int cpu = smp_processor_id(); + struct phytmac_queue *queue; + struct netdev_queue *nq; + u32 ret; + + if (unlikely(!xdpf)) + return PHYTMAC_XDP_CONSUMED; + + /* During program transitions its possible adapter->xdp_prog is assigned + * but ring has not been configured yet. In this case simply abort xmit. + */ + queue = pdata->xdp_prog ? phytmac_xdp_txq_mapping(pdata) : NULL; + if (unlikely(!queue)) + return PHYTMAC_XDP_CONSUMED; + + nq = phytmac_get_txq(pdata, queue); + __netif_tx_lock(nq, cpu); + /* Avoid transmit queue timeout since we share it with the slow path */ + nq->trans_start = jiffies; + ret = phytmac_start_xmit_xdp(pdata, queue, xdpf); + __netif_tx_unlock(nq); + + return ret; +} + +static int phytmac_xdp_xmit(struct net_device *dev, int n, + struct xdp_frame **frames, u32 flags) +{ + struct phytmac *pdata = netdev_priv(dev); + int cpu = smp_processor_id(); + struct phytmac_queue *queue; + struct netdev_queue *nq; + int drops = 0; + int i; + + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + /* During program transitions its possible pdata->xdp_prog is assigned + * but ring has not been configured yet. In this case simply abort xmit. + */ + queue = pdata->xdp_prog ? phytmac_xdp_txq_mapping(pdata) : NULL; + if (unlikely(!queue)) + return -ENXIO; + + nq = phytmac_get_txq(pdata, queue); + __netif_tx_lock(nq, cpu); + + /* Avoid transmit queue timeout since we share it with the slow path */ + nq->trans_start = jiffies; + + for (i = 0; i < n; i++) { + struct xdp_frame *xdpf = frames[i]; + int err; + + err = phytmac_start_xmit_xdp(pdata, queue, xdpf); + if (err != PHYTMAC_XDP_TX) { + xdp_return_frame_rx_napi(xdpf); + drops++; + } + } + + __netif_tx_unlock(nq); + + return n - drops; +} + static const struct net_device_ops phytmac_netdev_ops = { .ndo_open = phytmac_open, .ndo_stop = phytmac_close, @@ -2199,6 +2566,8 @@ static const struct net_device_ops phytmac_netdev_ops = { .ndo_features_check = phytmac_features_check, .ndo_vlan_rx_add_vid = ncsi_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = ncsi_vlan_rx_kill_vid, + .ndo_bpf = phytmac_xdp, + .ndo_xdp_xmit = phytmac_xdp_xmit, }; static int phytmac_init(struct phytmac *pdata) -- Gitee From 501cc1f48f8cbc30ea82a9e48a0917f2a4399921 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 18 Feb 2025 15:39:41 +0800 Subject: [PATCH 016/154] net/phytmac: Bugfix set WOL failed issue Before configuring WOL, we need to obtain the packet types that WOL supports. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: If90b1116d0cbb97e29e14f01f5a10fbf0555c3c2 --- drivers/net/ethernet/phytium/Kconfig | 2 +- drivers/net/ethernet/phytium/phytmac.h | 2 +- .../net/ethernet/phytium/phytmac_ethtool.c | 20 ++++++++----------- drivers/net/ethernet/phytium/phytmac_main.c | 2 +- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig index 8b0520f6e7..9e426b4b2a 100644 --- a/drivers/net/ethernet/phytium/Kconfig +++ b/drivers/net/ethernet/phytium/Kconfig @@ -12,7 +12,7 @@ config NET_VENDOR_PHYTIUM Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all the - remaining Cadence network card questions. If you say Y, you will be + remaining Phytium network card questions. If you say Y, you will be asked for your specific card in the following questions. if NET_VENDOR_PHYTIUM diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 6a7953c05d..ac673ddfe1 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.30" +#define PHYTMAC_DRIVER_VERSION "1.0.32" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 736f07d00e..1f0b3c9e4b 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -90,24 +90,20 @@ static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol { struct phytmac *pdata = netdev_priv(ndev); + wol->wolopts = 0; phylink_ethtool_get_wol(pdata->phylink, wol); - if (pdata->wol & PHYTMAC_WAKE_MAGIC) { + wol->supported = WAKE_MAGIC | WAKE_ARP | + WAKE_UCAST | WAKE_MCAST; + + if (pdata->wol & PHYTMAC_WAKE_MAGIC) wol->wolopts |= WAKE_MAGIC; - wol->supported |= WAKE_MAGIC; - } - if (pdata->wol & PHYTMAC_WAKE_ARP) { + if (pdata->wol & PHYTMAC_WAKE_ARP) wol->wolopts |= WAKE_ARP; - wol->supported |= WAKE_ARP; - } - if (pdata->wol & PHYTMAC_WAKE_UCAST) { + if (pdata->wol & PHYTMAC_WAKE_UCAST) wol->wolopts |= WAKE_UCAST; - wol->supported |= WAKE_UCAST; - } - if (pdata->wol & PHYTMAC_WAKE_MCAST) { + if (pdata->wol & PHYTMAC_WAKE_MCAST) wol->wolopts |= WAKE_MCAST; - wol->supported |= WAKE_MCAST; - } } static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 81412ee085..b7fa6bf608 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -62,7 +62,7 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); /* Max length of transmit frame must be a multiple of 8 bytes */ #define PHYTMAC_TX_LEN_ALIGN 8 -/* Limit maximum TX length as per Cadence TSO errata. This is to avoid a +/* Limit maximum TX length as per TSO errata. This is to avoid a * false amba_error in TX path from the DMA assuming there is not enough * space in the SRAM (16KB) even when there is. */ -- Gitee From 69697ebdf372f1cb919d27c8a3060e22dbb18774 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 18 Feb 2025 15:54:44 +0800 Subject: [PATCH 017/154] net/phytmac: Bugfix invalid wait context After the spinlock is called, the mutex should no longer to be invoked, which will lead to unnecessary sleep. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I37a7583dc97c3489658ae4d5ab51845a3a8cfc18 --- drivers/net/ethernet/phytium/phytmac.h | 8 +++----- drivers/net/ethernet/phytium/phytmac_main.c | 6 +----- drivers/net/ethernet/phytium/phytmac_v2.c | 10 ++++------ 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index ac673ddfe1..22b0db2aa3 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.32" +#define PHYTMAC_DRIVER_VERSION "1.0.33" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -433,8 +433,8 @@ struct phytmac_msg { u32 tx_msg_rd_tail; u32 rx_msg_head; u32 rx_msg_tail; - /* use msg_mutex to protect msg */ - struct mutex msg_mutex; + /* use msg_lock to protect msg */ + spinlock_t msg_lock; }; struct ts_ctrl { @@ -472,8 +472,6 @@ struct phytmac { struct work_struct restart_task; /* Lock to protect mac config */ spinlock_t lock; - /* Lock to protect msg tx */ - spinlock_t msg_lock; u32 rx_ring_size; u32 tx_ring_size; u32 dma_data_width; diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index b7fa6bf608..afdc61da56 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2719,8 +2719,6 @@ int phytmac_drv_probe(struct phytmac *pdata) if (hw_if->init_msg_ring) hw_if->init_msg_ring(pdata); - mutex_init(&pdata->msg_ring.msg_mutex); - if (pdata->use_mii && !pdata->mii_bus) { ret = phytmac_mdio_register(pdata); if (ret) { @@ -2787,8 +2785,6 @@ int phytmac_drv_remove(struct phytmac *pdata) if (pdata->phylink) phylink_destroy(pdata->phylink); - - mutex_destroy(&pdata->msg_ring.msg_mutex); } return 0; @@ -2898,7 +2894,7 @@ struct phytmac *phytmac_alloc_pdata(struct device *dev) pdata->dev = dev; spin_lock_init(&pdata->lock); - spin_lock_init(&pdata->msg_lock); + spin_lock_init(&pdata->msg_ring.msg_lock); spin_lock_init(&pdata->ts_clk_lock); pdata->msg_enable = netif_msg_init(debug, PHYTMAC_DEFAULT_MSG_ENABLE); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index b9cb86eaef..06c76f177e 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -27,7 +27,7 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, struct phytmac_msg_info msg_rx; int ret = 0; - mutex_lock(&pdata->msg_ring.msg_mutex); + spin_lock(&pdata->msg_ring.msg_lock); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; tx_tail = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_wr_tail); pdata->msg_ring.tx_msg_rd_tail = tx_tail; @@ -36,7 +36,6 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, while ((tx_tail + 1) % ring_size == tx_head) { netdev_info(pdata->ndev, "Tx msg ring is overrun, tx_tail:0x%x, tx_head:0x%x", tx_tail, tx_head); - cpu_relax(); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; } @@ -50,7 +49,7 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, } else if (len > PHYTMAC_MSG_PARA_LEN) { netdev_err(pdata->ndev, "Tx msg para len %d is greater than the max len %d", len, PHYTMAC_MSG_PARA_LEN); - mutex_unlock(&pdata->msg_ring.msg_mutex); + spin_unlock(&pdata->msg_ring.msg_lock); return -EINVAL; } @@ -67,7 +66,6 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (wait) { tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; while (tx_head != tx_tail) { - cpu_relax(); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; } @@ -76,12 +74,12 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (!(msg_rx.status0 & PHYTMAC_CMD_PRC_SUCCESS)) { netdev_err(pdata->ndev, "Msg process error, cmdid:%d, subid:%d, status0:%d, tail:%d", msg.cmd_type, msg.cmd_subid, msg.status0, tx_tail); - mutex_unlock(&pdata->msg_ring.msg_mutex); + spin_unlock(&pdata->msg_ring.msg_lock); return -EINVAL; } } - mutex_unlock(&pdata->msg_ring.msg_mutex); + spin_unlock(&pdata->msg_ring.msg_lock); return ret; } -- Gitee From 7e41def1d26240a84dbfe73efe6d1f114a1cab35 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 18 Feb 2025 15:55:56 +0800 Subject: [PATCH 018/154] net/phytmac: Change the requested resource type from nRE to nRnE The resource type is changed from nRE to nRnE to avoid interrupt loss at low probability of occurrence. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I306a1f2ea71556d729b842e4e3c69e4b963a41f1 --- drivers/net/ethernet/phytium/phytmac.h | 6 +- drivers/net/ethernet/phytium/phytmac_main.c | 65 +++++++++++++++++++ .../net/ethernet/phytium/phytmac_platform.c | 3 +- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 22b0db2aa3..216975d827 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.33" +#define PHYTMAC_DRIVER_VERSION "1.0.34" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -676,6 +676,10 @@ struct phytmac_hw_if { #define PHYTMAC_RX_PAGE_ORDER 0 #define PHYTMAC_RX_PAGE_SIZE (PAGE_SIZE << PHYTMAC_RX_PAGE_ORDER) +#define phytmac_ioremap_np(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRnE)) + +void __iomem * +phytmac_devm_ioremap_resource_np(struct device *dev, const struct resource *res); struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, unsigned int index); struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index afdc61da56..0ced40c036 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2927,6 +2927,71 @@ void phytmac_drv_shutdown(struct phytmac *pdata) } EXPORT_SYMBOL_GPL(phytmac_drv_shutdown); +static void phytmac_devm_iounmap_np(struct device *dev, void *res) +{ + iounmap(*(void __iomem **)res); +} + +static void __iomem *phytmac_devm_ioremap_np(struct device *dev, resource_size_t offset, + resource_size_t size) +{ + void __iomem **ptr, *addr = NULL; + + ptr = devres_alloc_node(phytmac_devm_iounmap_np, sizeof(*ptr), GFP_KERNEL, + dev_to_node(dev)); + if (!ptr) + return NULL; + + addr = phytmac_ioremap_np(offset, size); + if (addr) { + *ptr = addr; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return addr; +} + +void __iomem * +phytmac_devm_ioremap_resource_np(struct device *dev, const struct resource *res) +{ + resource_size_t size; + void __iomem *dest_ptr; + char *pretty_name; + + if (!res || resource_type(res) != IORESOURCE_MEM) { + dev_err(dev, "invalid resource %pR\n", res); + return IOMEM_ERR_PTR(-EINVAL); + } + + size = resource_size(res); + + if (res->name) + pretty_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", + dev_name(dev), res->name); + else + pretty_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!pretty_name) { + dev_err(dev, "can't generate pretty name for resource %pR\n", res); + return IOMEM_ERR_PTR(-ENOMEM); + } + + if (!devm_request_mem_region(dev, res->start, size, pretty_name)) { + dev_err(dev, "can't request region for resource %pR\n", res); + return IOMEM_ERR_PTR(-EBUSY); + } + + dest_ptr = phytmac_devm_ioremap_np(dev, res->start, size); + if (!dest_ptr) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + devm_release_mem_region(dev, res->start, size); + dest_ptr = IOMEM_ERR_PTR(-ENOMEM); + } + + return dest_ptr; +} +EXPORT_SYMBOL_GPL(phytmac_devm_ioremap_resource_np); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 9550971189..12df94aa67 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -204,7 +204,8 @@ static int phytmac_plat_probe(struct platform_device *pdev) } i = 0; - pdata->mac_regs = devm_platform_get_and_ioremap_resource(pdev, i, ®s); + regs = platform_get_resource(pdev, IORESOURCE_MEM, i); + pdata->mac_regs = phytmac_devm_ioremap_resource_np(&pdev->dev, regs); if (IS_ERR(pdata->mac_regs)) { dev_err(&pdev->dev, "mac_regs ioremap failed\n"); ret = PTR_ERR(pdata->mac_regs); -- Gitee From cfc44a4d28d8b65fc7f409a7fbc02aa4c191dc60 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 18 Feb 2025 15:57:43 +0800 Subject: [PATCH 019/154] net/phytmac: BugFix Memory leak when releasing resource The size of the memory released in the dma_free_coherent is smaller than the size of the memory allocated in the dma_alloc_coherent, which will lead to memory leakage and fragmentation. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Id702707b978721c60db705067a9fab6c96ac5cba --- drivers/net/ethernet/phytium/phytmac.h | 5 +---- drivers/net/ethernet/phytium/phytmac_main.c | 12 ++++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 216975d827..34e4ca070f 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.34" +#define PHYTMAC_DRIVER_VERSION "1.0.35" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -113,9 +113,6 @@ #define PHYTMAC_MSG_READ(_pdata, _reg) \ __raw_readl((_pdata)->mac_regs + (_reg)) -#define PHYTMAC_WRITE(_pdata, _reg, _val) \ - __raw_writel((_val), (_pdata)->mac_regs + (_reg)) - #define LSO_UFO 1 #define LSO_TSO 2 diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 0ced40c036..0a0862ffb6 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -364,6 +364,7 @@ static int phytmac_free_tx_resource(struct phytmac *pdata) struct phytmac_dma_desc *tx_ring_base = NULL; dma_addr_t tx_ring_base_addr; unsigned int q; + int tx_offset; int size; queue = pdata->queues; @@ -381,8 +382,9 @@ static int phytmac_free_tx_resource(struct phytmac *pdata) } if (tx_ring_base) { - size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + - RING_ADDR_INTERVAL); + tx_offset = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + tx_offset = ALIGN(tx_offset, 4096); + size = pdata->queues_num * tx_offset; dma_free_coherent(pdata->dev, size, tx_ring_base, tx_ring_base_addr); } @@ -395,6 +397,7 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) struct phytmac_dma_desc *rx_ring_base = NULL; dma_addr_t rx_ring_base_addr; unsigned int q; + int rx_offset; int size; queue = pdata->queues; @@ -421,8 +424,9 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) } if (rx_ring_base) { - size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + - RING_ADDR_INTERVAL); + rx_offset = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + rx_offset = ALIGN(rx_offset, 4096); + size = pdata->queues_num * rx_offset; dma_free_coherent(pdata->dev, size, rx_ring_base, rx_ring_base_addr); } -- Gitee From 32cef4a41687aa33eaffe87dcf451c049a9c334d Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 23 Dec 2024 14:34:57 +0800 Subject: [PATCH 020/154] net/macb: Supported S4 suspend and resume function The MPE bit needs to be enabled after resume. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I14d4c1b8c2e472fc55b1d497ded883f298f3b8fc --- drivers/net/ethernet/cadence/macb_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 3552bd5f9e..57c3665078 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -3047,6 +3047,10 @@ static void macb_init_hw(struct macb *bp) macb_reset_hw(bp); macb_set_hwaddr(bp); + config = macb_readl(bp, NCR); + config |= MACB_BIT(MPE); + macb_writel(bp, NCR, config); + config = macb_mdc_clk_div(bp); config |= MACB_BF(RBOF, NET_IP_ALIGN); /* Make eth data aligned */ config |= MACB_BIT(DRFCS); /* Discard Rx FCS */ -- Gitee From 45c3faeff2b9961a1488167a2a5074f4756bd662 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 23 Dec 2024 14:35:52 +0800 Subject: [PATCH 021/154] net/macb: Bugfix kernel panic when unloading driver module When the network port is linked, the clk_disable_unprepare function will be called twice, one of them is in the unregister_netdev, and the double clk_disable will cause a kernel panic. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: If540c664080d24472e4f3741f510f919d7586d09 --- drivers/net/ethernet/cadence/macb_main.c | 30 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 57c3665078..5e3d397ed3 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -5449,11 +5449,16 @@ 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 (__clk_is_enabled(bp->tx_clk)) + clk_disable_unprepare(bp->tx_clk); + if (__clk_is_enabled(bp->hclk)) + clk_disable_unprepare(bp->hclk); + if (__clk_is_enabled(bp->pclk)) + clk_disable_unprepare(bp->pclk); + if (__clk_is_enabled(bp->rx_clk)) + clk_disable_unprepare(bp->rx_clk); + if (__clk_is_enabled(bp->tsu_clk)) + clk_disable_unprepare(bp->tsu_clk); pm_runtime_set_suspended(&pdev->dev); } phylink_destroy(bp->phylink); @@ -5633,12 +5638,17 @@ static int __maybe_unused macb_runtime_suspend(struct device *dev) struct macb *bp = netdev_priv(netdev); if (!(device_may_wakeup(dev))) { - clk_disable_unprepare(bp->tx_clk); - clk_disable_unprepare(bp->hclk); - clk_disable_unprepare(bp->pclk); - clk_disable_unprepare(bp->rx_clk); + if (__clk_is_enabled(bp->tx_clk)) + clk_disable_unprepare(bp->tx_clk); + if (__clk_is_enabled(bp->hclk)) + clk_disable_unprepare(bp->hclk); + if (__clk_is_enabled(bp->pclk)) + clk_disable_unprepare(bp->pclk); + if (__clk_is_enabled(bp->rx_clk)) + clk_disable_unprepare(bp->rx_clk); } - clk_disable_unprepare(bp->tsu_clk); + if (__clk_is_enabled(bp->tsu_clk)) + clk_disable_unprepare(bp->tsu_clk); return 0; } -- Gitee From c6f2f32b9589da7d2bdaae4d6ce497a729b8b9d3 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 23 Dec 2024 14:37:46 +0800 Subject: [PATCH 022/154] net/macb: Add 2.5G speed support for MACB driver Adapt to 2.5G speed for phytium MACB driver, including 2.5G electrical ports and 2.5G optical ports. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I26cdab7019de093e4f29e97f90567e8f54375392 --- drivers/net/ethernet/cadence/macb_main.c | 27 +++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 5e3d397ed3..2749fc7807 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -838,7 +838,21 @@ static void phytium_gem1p0_sel_clk(struct macb *bp, int speed) gem_writel(bp, RX_CLK_SEL3_0, 0x0); /*0x1c78*/ gem_writel(bp, RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { - if (speed == SPEED_1000) { + if (speed == SPEED_2500) { + gem_writel(bp, DIV_SEL0_LN, 0x1); /*0x1c08*/ + gem_writel(bp, DIV_SEL1_LN, 0x2); /*0x1c0c*/ + gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ + gem_writel(bp, TX_CLK_SEL0, 0x0); /*0x1c20*/ + gem_writel(bp, TX_CLK_SEL1, 0x1); /*0x1c24*/ + gem_writel(bp, TX_CLK_SEL2, 0x1); /*0x1c28*/ + gem_writel(bp, TX_CLK_SEL3, 0x1); /*0x1c2c*/ + gem_writel(bp, RX_CLK_SEL0, 0x1); /*0x1c30*/ + gem_writel(bp, RX_CLK_SEL1, 0x0); /*0x1c34*/ + gem_writel(bp, TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + gem_writel(bp, TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + gem_writel(bp, RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + gem_writel(bp, RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ + } else if (speed == SPEED_1000) { gem_writel(bp, SRC_SEL_LN, 0x1); /*0x1c04*/ gem_writel(bp, DIV_SEL0_LN, 0x4); /*0x1c08*/ gem_writel(bp, DIV_SEL1_LN, 0x8); /*0x1c0c*/ @@ -1010,10 +1024,16 @@ static void macb_mac_link_up(struct phylink_config *config, if (speed == SPEED_2500) { u32 network_ctrl; + u32 pcsctrl, old_pcsctrl; network_ctrl = macb_readl(bp, NCR); network_ctrl |= MACB_BIT(2PT5G); macb_writel(bp, NCR, network_ctrl); + + old_pcsctrl = gem_readl(bp, PCSCNTRL); + pcsctrl = old_pcsctrl & ~GEM_BIT(PCSAUTONEG); + if (old_pcsctrl != pcsctrl) + gem_writel(bp, PCSCNTRL, pcsctrl); } if (bp->phy_interface == PHY_INTERFACE_MODE_10GBASER || @@ -1149,10 +1169,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; -- Gitee From bbfceee59565af4354205b2c504bcbe6c500c6fb Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 23 Dec 2024 14:41:07 +0800 Subject: [PATCH 023/154] net/macb: Change the minimum rx ring size to 128 If the budget is equal to 64, gem_rx can exit after receiving a maximum of budget packets. If the ring size is changed to 64, a descriptor that has not been refilled is considered to be a valid descriptor to be received, and the error "inconsistent Rx descriptor chain" is reported. So we need to limit the minimum rx ring size to be greater than 64. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ia0953a7dce53ccd7261255357268ab3c6f6d6263 --- drivers/net/ethernet/cadence/macb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 2749fc7807..cd2dc3005b 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -53,7 +53,7 @@ struct sifive_fu540_macb_mgmt { #define RX_BUFFER_MULTIPLE 64 /* bytes */ #define DEFAULT_RX_RING_SIZE 512 /* must be power of 2 */ -#define MIN_RX_RING_SIZE 64 +#define MIN_RX_RING_SIZE 128 #define MAX_RX_RING_SIZE 8192 #define RX_RING_BYTES(bp) (macb_dma_desc_get_size(bp) \ * (bp)->rx_ring_size) -- Gitee From 5b208f0925ed0450909795aa57a68a9924ca3fc5 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 23 Dec 2024 14:43:18 +0800 Subject: [PATCH 024/154] net/macb: Fixed the CPU stall issue in S4 test In the S4 test, it is possible that macb_mac_link_down concurrency with macb_tx_interrupt caused an endless loop, which in turn caused the CPU to stall. So, it is necessary to clear the tx_used bit in macb_mac_link_down to break the condition for the macb_tx_interrupt to enter the loop. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I948478f5ec343d5646db32b93b1a0531616b3f6f --- drivers/net/ethernet/cadence/macb_main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index cd2dc3005b..8a820c88f0 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -107,6 +107,8 @@ struct sifive_fu540_macb_mgmt { #define MACB_MDIO_TIMEOUT 1000000 /* in usecs */ static void macb_tx_unmap(struct macb *bp, struct macb_tx_skb *tx_skb); +static void macb_set_addr(struct macb *bp, struct macb_dma_desc *desc, + dma_addr_t addr); /* DMA buffer descriptor might be different size * depends on hardware configuration: @@ -773,6 +775,7 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, struct macb *bp = netdev_priv(ndev); struct macb_tx_skb *tx_skb; struct macb_queue *queue; + struct macb_dma_desc *tx_desc = NULL; unsigned int q; u32 ctrl; int i; @@ -790,14 +793,20 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, macb_writel(bp, NCR, ctrl); /* Tx clean */ + spin_lock(&bp->lock); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { for (i = 0; i < bp->tx_ring_size; i++) { tx_skb = macb_tx_skb(queue, i); /* free unsent skb buffers */ if (tx_skb) macb_tx_unmap(bp, tx_skb); + + tx_desc = macb_tx_desc(queue, i); + macb_set_addr(bp, tx_desc, 0); + tx_desc->ctrl &= ~MACB_BIT(TX_USED); } } + spin_unlock(&bp->lock); netif_tx_stop_all_queues(ndev); } -- Gitee From 04c1d40d1338338da3cefd0e4c5a056cd61e8b67 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 23 Dec 2024 14:55:01 +0800 Subject: [PATCH 025/154] net/macb: Bugfix MACB driver logical condition error PHY_INTERFACE_MODE_2500BASEX was incorrectly used as a condition expression, causing it to be evaluated as true. This patch explicitly compares state->interface with the two mode values, ensuring the condition logic is correct. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ib397f9eab626eddd1287fcc98767f3809f4175e5 --- drivers/net/ethernet/cadence/macb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 8a820c88f0..2c639b5546 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -753,7 +753,7 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode, * otherwise writes will not take effect. */ if (macb_is_gem(bp) && (state->interface == PHY_INTERFACE_MODE_SGMII || - PHY_INTERFACE_MODE_2500BASEX)) { + state->interface == PHY_INTERFACE_MODE_2500BASEX)) { u32 pcsctrl, old_pcsctrl; old_pcsctrl = gem_readl(bp, PCSCNTRL); -- Gitee From e0ebe3d18ca5c3c22108c725b0c12f4b56c37e16 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 13 Sep 2023 10:35:33 +0800 Subject: [PATCH 026/154] macb: net: Add ACPI support for macb driver Add ACPI support for phytium mac v1.0 driver, which currently is used for pe220x platforms. Signed-off-by: Li wencheng Signed-off-by: Wang Yinfeng Change-Id: I2e2fc7f64eb225e72dce2150bcf82a0b5320068c --- drivers/net/ethernet/cadence/macb_main.c | 88 +++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 2c639b5546..d0aaa43c18 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4293,6 +4293,75 @@ static void macb_probe_queues(void __iomem *mem, *num_queues = hweight32(*queue_mask); } +#ifdef CONFIG_ACPI +static int macb_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; + + pdata = dev_get_platdata(&pdev->dev); + if (pdata) { + *pclk = pdata->pclk; + *hclk = pdata->hclk; + } else { + *pclk = NULL; + *hclk = NULL; + } + + *tx_clk = NULL; + *rx_clk = NULL; + *tsu_clk = NULL; + + 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 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) @@ -5150,6 +5219,21 @@ static const struct macb_config phytium_gem1p0_config = { .sel_clk_hw = phytium_gem1p0_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 = macb_clk_acpi_init, + .init = macb_init, + .jumbo_max_len = 10240, + .sel_clk_hw = phytium_gem1p0_sel_clk, +}; +#endif + static const struct macb_config phytium_gem2p0_config = { .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO | @@ -5189,7 +5273,7 @@ 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 }, { } }; @@ -5242,7 +5326,7 @@ 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; bool native_io; -- Gitee From 5273a2b7bb417e420f72b8540b07fdb91787856a Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 20 Feb 2025 14:07:00 +0800 Subject: [PATCH 027/154] net/phytmac: Limit the number of retries to avoid deadlock It is need to limit retries that messages are processed to avoid deadlock, when RV has no response. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I6f298eac13fcfe10db1a5c9d54c245e2c21e6254 --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 20 ++++++++++++++++++-- drivers/net/ethernet/phytium/phytmac_v2.h | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 34e4ca070f..4b09a5a608 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.35" +#define PHYTMAC_DRIVER_VERSION "1.0.36" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 06c76f177e..46b10b3006 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -25,6 +25,7 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, u32 tx_head, tx_tail, ring_size; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; + u32 retry = 0; int ret = 0; spin_lock(&pdata->msg_ring.msg_lock); @@ -34,11 +35,19 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, ring_size = pdata->msg_ring.tx_msg_ring_size; while ((tx_tail + 1) % ring_size == tx_head) { - netdev_info(pdata->ndev, "Tx msg ring is overrun, tx_tail:0x%x, tx_head:0x%x", - tx_tail, tx_head); + udelay(1); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + retry++; + if (retry >= PHYTMAC_RETRY_TIMES) { + netdev_err(pdata->ndev, + "Time out waiting for Tx msg ring free, tx_tail:0x%x, tx_head:0x%x", + tx_tail, tx_head); + spin_unlock(&pdata->msg_ring.msg_lock); + return -EINVAL; + } } + retry = 0; wait = 1; memset(&msg, 0, sizeof(msg)); memset(&msg_rx, 0, sizeof(msg_rx)); @@ -66,7 +75,14 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (wait) { tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; while (tx_head != tx_tail) { + udelay(1); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + retry++; + if (retry >= PHYTMAC_RETRY_TIMES) { + netdev_err(pdata->ndev, "Msg process time out!"); + spin_unlock(&pdata->msg_ring.msg_lock); + return -EINVAL; + } } memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(pdata->msg_ring.tx_msg_rd_tail), diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index e7efbb7e4f..486be9a618 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -239,6 +239,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_CLK_DIV128 6 #define PHYTMAC_CLK_DIV224 7 +#define PHYTMAC_RETRY_TIMES 50000 + enum phytmac_msg_cmd_id { PHYTMAC_MSG_CMD_DEFAULT = 0, PHYTMAC_MSG_CMD_SET, -- Gitee From 2ca7bec431ccb3822913688a5d48dd7e456019f6 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Fri, 21 Feb 2025 09:26:29 +0800 Subject: [PATCH 028/154] dt-bindings: i2s: Add bindings for I2S controllor This patch documents the DT binding for the Phytium I2S controller. Mainline: Open-Source Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: I6ba64feba250c68b465b27ef6771baf652dac91b --- .../bindings/sound/phytium,i2s.yaml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/phytium,i2s.yaml diff --git a/Documentation/devicetree/bindings/sound/phytium,i2s.yaml b/Documentation/devicetree/bindings/sound/phytium,i2s.yaml new file mode 100644 index 0000000000..689e0fd0ab --- /dev/null +++ b/Documentation/devicetree/bindings/sound/phytium,i2s.yaml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +* Phytium I2S controller + +The I2S bus (Inter-IC sound bus) is a serial link for digital +audio data transfer between devices in the system. + +Required properties: + + +- compatible: should be "phytium,i2s" +- reg: It contains two register region: + - first is for physical base address and length of I2S controller. + - second is for physical base address and length of DMA_BDL controller. +- interrupts: should contain the DMA_BDL interrupt. +- clocks: phandle to clock provider with the clock number in the second cell +- dai-name: it will set dai's name used in driver. + +Example for pe2204 I2S controller: + +i2s@28009000 { + compatible = "phytium,i2s"; + reg = <0x0 0x28009000 0x0 0x1000>, <0x0 0x28005000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_600mhz>; + dai-name = "phytium-i2s-lsd"; +}; -- Gitee From 868d5e17381a486252695889260817662683d0dd Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Wed, 18 Dec 2024 08:21:55 +0000 Subject: [PATCH 029/154] codec: Phytium: Handle mute of different directions Without this patch,ADC mute will be called instead of DAC mute when recording ends. So,this patch correctly handles mute of ADC and DAC. Playbacking ends --- DAC mute. Recording ends --- ADC mute. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: Ibab80e32412fd0e5f7035e6861d69e326ee4b608 --- sound/soc/codecs/es8336.c | 22 ++++++++++++---------- sound/soc/codecs/es8388.c | 11 ++++++++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/es8336.c b/sound/soc/codecs/es8336.c index 719b7de0e3..0a202fd6f7 100644 --- a/sound/soc/codecs/es8336.c +++ b/sound/soc/codecs/es8336.c @@ -37,6 +37,8 @@ #include #include "es8336.h" +#define ES8336_MUTE (1 << 5) + static struct snd_soc_component *es8336_component; static const struct reg_default es8336_reg_defaults[] = { @@ -679,19 +681,19 @@ static int es8336_mute(struct snd_soc_dai *dai, int mute, int direction) es8336->muted = mute; - if (mute) { - es8336_enable_spk(es8336, false); - msleep(100); - snd_soc_component_write(component, ES8336_DAC_SET1_REG30, 0x20); - } - - snd_soc_component_write(component, ES8336_DAC_SET1_REG30, 0x00); - msleep(130); - if (!es8336->hp_inserted) es8336_enable_spk(es8336, true); + else + es8336_enable_spk(es8336, false); - return 0; + if (direction) + return snd_soc_component_update_bits(dai->component, + ES8336_ADC_MUTE_REG26, ES8336_MUTE, + mute ? ES8336_MUTE : 0); + else + return snd_soc_component_update_bits(dai->component, + ES8336_DAC_SET1_REG30, ES8336_MUTE, + mute ? ES8336_MUTE : 0); } static int es8336_set_bias_level(struct snd_soc_component *component, diff --git a/sound/soc/codecs/es8388.c b/sound/soc/codecs/es8388.c index fbb3f8399c..fc0e585bc2 100644 --- a/sound/soc/codecs/es8388.c +++ b/sound/soc/codecs/es8388.c @@ -426,9 +426,14 @@ static const struct snd_soc_dapm_route es8388_dapm_routes[] = { static int es8388_mute(struct snd_soc_dai *dai, int mute, int direction) { - return snd_soc_component_update_bits(dai->component, ES8388_DACCONTROL3, - ES8388_DACCONTROL3_DACMUTE, - mute ? ES8388_DACCONTROL3_DACMUTE : 0); + if (direction) + return snd_soc_component_update_bits(dai->component, ES8388_ADCCONTROL7, + ES8388_ADCCONTROL7_ADC_MUTE, + mute ? ES8388_ADCCONTROL7_ADC_MUTE : 0); + else + return snd_soc_component_update_bits(dai->component, ES8388_DACCONTROL3, + ES8388_DACCONTROL3_DACMUTE, + mute ? ES8388_DACCONTROL3_DACMUTE : 0); } static int es8388_startup(struct snd_pcm_substream *substream, -- Gitee From 47c21b2198636c5de2a05e95504e1160457d0510 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Wed, 18 Dec 2024 08:23:52 +0000 Subject: [PATCH 030/154] codec: Phytium: This patch makes ES8336 module reload smoothly It solves the problem of accessing NULL pointer when reloaded. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: If05097f9fc38f48a71a19511ce0668e85e6aeb75 --- sound/soc/codecs/es8336.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8336.c b/sound/soc/codecs/es8336.c index 0a202fd6f7..251fc8b03c 100644 --- a/sound/soc/codecs/es8336.c +++ b/sound/soc/codecs/es8336.c @@ -1022,7 +1022,7 @@ static int es8336_i2c_probe(struct i2c_client *i2c, msecs_to_jiffies(es8336->debounce_time)); } - ret = snd_soc_register_component(&i2c->dev, + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_es8336, &es8336_dai, 1); @@ -1031,7 +1031,6 @@ static int es8336_i2c_probe(struct i2c_client *i2c, static int es8336_i2c_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); return 0; } -- Gitee From 5043f17a06a93a8829b14572feaf878c4f18f7ae Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Wed, 18 Dec 2024 08:25:36 +0000 Subject: [PATCH 031/154] codec: Phytium: Indicate fallthrough clearly to avoid warnings Without this patch, it will report some warnings that cause the code to fail to push. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: I92600ca152bfe431a8f3cce632b7545da871b8be --- sound/soc/codecs/es8388.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/es8388.c b/sound/soc/codecs/es8388.c index fc0e585bc2..84ec8be86b 100644 --- a/sound/soc/codecs/es8388.c +++ b/sound/soc/codecs/es8388.c @@ -542,6 +542,7 @@ static int es8388_set_sysclk(struct snd_soc_dai *codec_dai, break; case 22579200: mclkdiv2 = 1; + fallthrough; /* fallthru */ case 11289600: es8388->sysclk_constraints = &constraints_11289; @@ -549,6 +550,7 @@ static int es8388_set_sysclk(struct snd_soc_dai *codec_dai, break; case 24576000: mclkdiv2 = 1; + fallthrough; /* fallthru */ case 12288000: es8388->sysclk_constraints = &constraints_12288; -- Gitee From 15ea6edb2a64f0d0433eac284060a9edd08a5297 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Wed, 18 Dec 2024 08:44:16 +0000 Subject: [PATCH 032/154] i2s: phytium: Add support for headphone detect Using i2s-gpio for headphone detect and adding the delayed work to handle the events. Mainline: Open-Source Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: Ifca076549d7bafb6748f3da87359759b7cb7ef70 --- sound/soc/phytium/local.h | 33 ++++++++ sound/soc/phytium/phytium_i2s.c | 136 +++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 3 deletions(-) diff --git a/sound/soc/phytium/local.h b/sound/soc/phytium/local.h index 6ca8e7416c..a6c02d6c85 100644 --- a/sound/soc/phytium/local.h +++ b/sound/soc/phytium/local.h @@ -85,6 +85,35 @@ /****************/ +#define I2S_HEADPHONE_ENABLE 1 +#define I2S_HEADPHONE_DISABLE 0 + +#define I2S_GPIO(x) BIT(x) +#define I2S_GPIO_BASE 0xD00 + +/* I2S GPIO registers */ +#define I2S_GPIO_SWPORTA_DR 0x00 /* WR Port A Output Data Register */ + #define I2S_HEADPHONE_FRONT (!I2S_GPIO(1)) + #define I2S_HEADPHONE_REAR I2S_GPIO(1) +#define I2S_GPIO_SWPORTA_DDR 0x04 /* WR Port A Data Direction Register */ + #define I2S_GPIO_INPUT(x) (!I2S_GPIO(x)) + #define I2S_GPIO_OUTPUT(x) I2S_GPIO(x) +#define I2S_GPIO_EXT_PORTA 0x08 /* RO Port A Input Data Register */ +#define I2S_GPIO_SWPORTB_DR 0x0c /* WR Port B Output Data Register */ +#define I2S_GPIO_SWPORTB_DDR 0x10 /* WR Port B Data Direction Register */ +#define I2S_GPIO_EXT_PORTB 0x14 /* RO Port B Input Data Register */ + +#define I2S_GPIO_INTEN 0x18 /* WR Port A Interrput Enable Register */ +#define I2S_GPIO_INTMASK 0x1c /* WR Port A Interrupt Mask Register */ +#define I2S_GPIO_INTTYPE_LEVEL 0x20 /* WR Port A Interrupt Level Register */ + #define I2S_GPIO_LEVEL(x) (!I2S_GPIO(x)) + #define I2S_GPIO_EDGE(x) I2S_GPIO(x) +#define I2S_GPIO_INT_POLARITY 0x24 /* WR Port A Interrupt Polarity Register */ + #define I2S_GPIO_DOWN(x) (!I2S_GPIO(x)) + #define I2S_GPIO_UP(x) I2S_GPIO(x) +#define I2S_GPIO_INTSTATUS 0x28 /* RO Port A Interrupt Status Register */ +#define I2S_GPIO_DEBOUNCE 0x34 /* WR Debounce Enable Register */ +#define I2S_GPIO_PORTA_EOI 0x38 /* WO Port A Clear Interrupt Register */ /* max number of fragments - we may use more if allocating more pages for BDL */ #define BDL_SIZE 4096 @@ -290,7 +319,10 @@ struct i2s_phytium { u32 paddr; void __iomem *regs; void __iomem *regs_db; + void __iomem *regs_gpio; int irq_id; + int gpio_irq_id; + bool detect; /* for pending irqs */ struct work_struct irq_pending_work; @@ -298,6 +330,7 @@ struct i2s_phytium { /* sync probing */ struct completion probe_wait; struct work_struct probe_work; + struct delayed_work i2s_gpio_work; /* extra flags */ unsigned int pcie:1; diff --git a/sound/soc/phytium/phytium_i2s.c b/sound/soc/phytium/phytium_i2s.c index 42c7d4118c..5893ff79d8 100755 --- a/sound/soc/phytium/phytium_i2s.c +++ b/sound/soc/phytium/phytium_i2s.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "local.h" @@ -46,11 +47,25 @@ #define EIGHT_CHANNEL_SUPPORT 8 /* up to 7.1 */ struct pdata_px210_mfd { - struct device *dev; + struct device *dev; char *name; int clk_base; }; +static struct snd_soc_jack hs_jack; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "FrontIn", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Front", + .mask = SND_JACK_HEADPHONE, + }, +}; + static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val) { writel(val, io_base + reg); @@ -157,6 +172,72 @@ irqreturn_t azx_i2s_interrupt(int irq, void *dev_id) return IRQ_RETVAL(handled); } +int azx_i2s_enable_gpio(struct i2s_phytium *i2s) +{ + u32 val; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_SWPORTA_DDR, + (I2S_GPIO_INPUT(0) | I2S_GPIO_OUTPUT(1))); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_SWPORTA_DDR); + if (val != (I2S_GPIO_INPUT(0) | I2S_GPIO_OUTPUT(1))) + goto enable_err; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INTMASK, !I2S_GPIO(0)); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INTMASK); + if (val != !I2S_GPIO(0)) + goto enable_err; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INTTYPE_LEVEL, I2S_GPIO_EDGE(0)); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INTTYPE_LEVEL); + if (val != I2S_GPIO_EDGE(0)) + goto enable_err; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INT_POLARITY, I2S_GPIO_DOWN(0)); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INT_POLARITY); + if (val != I2S_GPIO_DOWN(0)) + goto enable_err; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INTEN, I2S_GPIO(0)); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INTEN); + if (val != I2S_GPIO(0)) + goto enable_err; + + return 0; +enable_err: + return -EBUSY; +} + +static void i2s_gpio_jack_work(struct work_struct *work) +{ + struct i2s_phytium *i2s = container_of(work, struct i2s_phytium, i2s_gpio_work.work); + u32 val; + + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INT_POLARITY); + if (val == 1) { + snd_soc_jack_report(&hs_jack, I2S_HEADPHONE_DISABLE, SND_JACK_HEADSET); + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_SWPORTA_DR, I2S_HEADPHONE_REAR); + } else { + snd_soc_jack_report(&hs_jack, I2S_HEADPHONE_ENABLE, SND_JACK_HEADSET); + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_SWPORTA_DR, I2S_HEADPHONE_FRONT); + } + val = ~val; + val &= 0x1; + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INT_POLARITY, val); +} + +irqreturn_t azx_i2s_gpio_interrupt(int irq, void *dev_id) +{ + struct azx *chip = dev_id; + struct i2s_phytium *i2s = container_of(chip, struct i2s_phytium, chip); + bool handled = true; + + queue_delayed_work(system_power_efficient_wq, &i2s->i2s_gpio_work, + msecs_to_jiffies(100)); + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_PORTA_EOI, I2S_GPIO(0)); + return IRQ_RETVAL(handled); +} + static int azx_acquire_irq(struct azx *chip, int do_disconnect) { struct i2sc_bus *bus = azx_bus(chip); @@ -171,6 +252,22 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) return err; } + if (i2s->detect) { + err = azx_i2s_enable_gpio(i2s); + if (err < 0) { + dev_err(i2s->dev, "failed to enable gpio irq\n"); + return err; + } + + err = devm_request_irq(i2s->dev, i2s->gpio_irq_id, azx_i2s_gpio_interrupt, + IRQF_SHARED, + "phytium i2s gpio", chip); + if (err < 0) { + dev_err(i2s->dev, "failed to request gpio irq\n"); + return err; + } + } + bus->irq = i2s->irq_id; return 0; @@ -378,6 +475,10 @@ static int phytium_i2s_resume(struct snd_soc_component *component) } i2s_write_reg(dev->regs, CLK_CFG0, dev->cfg); i2s_write_reg(dev->regs, CLK_CFG1, 0xf); + + if (dev->detect) + azx_i2s_enable_gpio(dev); + return 0; } @@ -386,6 +487,21 @@ static int phytium_i2s_resume(struct snd_soc_component *component) #define phytium_i2s_resume NULL #endif +static int phytium_i2s_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_card *card = component->card; + int ret; + + ret = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET, + &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); + if (ret < 0) { + dev_err(component->dev, "Cannot create jack\n"); + return ret; + } + return 0; +} + static struct snd_soc_dai_driver phytium_i2s_dai = { .playback = { .stream_name = "i2s-Playback", @@ -971,6 +1087,7 @@ static const struct snd_soc_component_driver phytium_i2s_component = { .pcm_construct = phytium_pcm_new, .suspend = phytium_i2s_suspend, .resume = phytium_i2s_resume, + .probe = phytium_i2s_component_probe, .open = phytium_pcm_open, .close = phytium_pcm_close, @@ -1279,6 +1396,8 @@ static int i2s_phytium_create(struct platform_device *pdev, } INIT_WORK(&i2s->probe_work, azx_probe_work); + if (i2s->detect) + INIT_DELAYED_WORK(&i2s->i2s_gpio_work, i2s_gpio_jack_work); *rchip = chip; return 0; } @@ -1339,17 +1458,28 @@ static int phytium_i2s_probe(struct platform_device *pdev) i2s->paddr = res->start; i2s->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2s->regs)) + return PTR_ERR(i2s->regs); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); i2s->regs_db = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(i2s->regs)) - return PTR_ERR(i2s->regs); + if (IS_ERR(i2s->regs_db)) + return PTR_ERR(i2s->regs_db); i2s->irq_id = platform_get_irq(pdev, 0); if (i2s->irq_id < 0) return i2s->irq_id; + i2s->gpio_irq_id = platform_get_irq(pdev, 1); + i2s->detect = true; + + if (i2s->gpio_irq_id < 0) + i2s->detect = false; + else + i2s->regs_gpio = i2s->regs + I2S_GPIO_BASE; + i2s->i2s_reg_comp1 = I2S_COMP_PARAM_1; i2s->i2s_reg_comp2 = I2S_COMP_PARAM_2; -- Gitee From b4192fa4304c451914c2e7fee36fab9f0e8b4b17 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Wed, 13 Nov 2024 10:28:04 +0000 Subject: [PATCH 033/154] i2s: phytium: Add clear and mask interrupt operations Adding actions to clear and mask interrupts before the interrupt handler is registered for the i2s driver. Mainline: Open-Source Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: If8923517b4b8e0f8b580fb75b39ffef5bbf09963 --- sound/soc/phytium/phytium_i2s.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/sound/soc/phytium/phytium_i2s.c b/sound/soc/phytium/phytium_i2s.c index 5893ff79d8..c6e94b6b9d 100755 --- a/sound/soc/phytium/phytium_i2s.c +++ b/sound/soc/phytium/phytium_i2s.c @@ -243,6 +243,15 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) struct i2sc_bus *bus = azx_bus(chip); struct i2s_phytium *i2s = container_of(chip, struct i2s_phytium, chip); int err; + u32 status; + + status = i2s_read_reg(i2s->regs_db, DMA_STS); + if (status & 0x1) + i2s_write_reg(i2s->regs_db, DMA_STS, 0x1); + if (status & 0x100) + i2s_write_reg(i2s->regs_db, DMA_STS, 0x100); + + i2s_write_reg(i2s->regs_db, DMA_MASK_INT, 0x0); err = devm_request_irq(i2s->dev, i2s->irq_id, azx_i2s_interrupt, IRQF_SHARED, "phytium i2s", chip); @@ -253,11 +262,8 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) } if (i2s->detect) { - err = azx_i2s_enable_gpio(i2s); - if (err < 0) { - dev_err(i2s->dev, "failed to enable gpio irq\n"); - return err; - } + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_PORTA_EOI, I2S_GPIO(0)); + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INTMASK, 0x1); err = devm_request_irq(i2s->dev, i2s->gpio_irq_id, azx_i2s_gpio_interrupt, IRQF_SHARED, @@ -266,6 +272,13 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) dev_err(i2s->dev, "failed to request gpio irq\n"); return err; } + + err = azx_i2s_enable_gpio(i2s); + if (err < 0) { + dev_err(i2s->dev, "failed to enable gpio irq\n"); + return err; + } + } bus->irq = i2s->irq_id; -- Gitee From 66da9ec21df610b14f4d5c1cd7e8196b3e690946 Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Tue, 23 Jul 2024 17:17:37 +0800 Subject: [PATCH 034/154] i2s: phytium: Add I2S driver version information This patch add driver version information, which will be used to synchronize and manage driver patches in the future. Mainline: Open-Source Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I0b35df60f5b518d0439bab009e6d204413059825 --- sound/soc/codecs/es8336.c | 2 ++ sound/soc/codecs/es8388.c | 3 +++ sound/soc/phytium/phytium_i2s.c | 2 ++ 3 files changed, 7 insertions(+) diff --git a/sound/soc/codecs/es8336.c b/sound/soc/codecs/es8336.c index 251fc8b03c..396949277c 100644 --- a/sound/soc/codecs/es8336.c +++ b/sound/soc/codecs/es8336.c @@ -37,6 +37,7 @@ #include #include "es8336.h" +#define ES8336_V1_VERSION "1.0.0" #define ES8336_MUTE (1 << 5) static struct snd_soc_component *es8336_component; @@ -1080,3 +1081,4 @@ static struct i2c_driver es8336_i2c_driver = { module_i2c_driver(es8336_i2c_driver); MODULE_DESCRIPTION("ASoC es8336 driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(ES8336_V1_VERSION); diff --git a/sound/soc/codecs/es8388.c b/sound/soc/codecs/es8388.c index 84ec8be86b..fc271d086d 100644 --- a/sound/soc/codecs/es8388.c +++ b/sound/soc/codecs/es8388.c @@ -28,6 +28,8 @@ #include #include +#define ES8388_V1_VERSION "1.0.0" + static const unsigned int rates_12288[] = { 8000, 12000, 16000, 24000, 32000, 48000, 96000, }; @@ -825,3 +827,4 @@ module_i2c_driver(es8388_i2c_driver); MODULE_DESCRIPTION("ASoC ES8388 driver"); MODULE_AUTHOR("Yiqun Zhang "); MODULE_LICENSE("GPL"); +MODULE_VERSION(ES8388_V1_VERSION); diff --git a/sound/soc/phytium/phytium_i2s.c b/sound/soc/phytium/phytium_i2s.c index c6e94b6b9d..c77eeb5443 100755 --- a/sound/soc/phytium/phytium_i2s.c +++ b/sound/soc/phytium/phytium_i2s.c @@ -31,6 +31,7 @@ #include #include "local.h" +#define PHYTIUM_I2S_V1_VERSION "1.0.0" #define NUM_CAPTURE 1 #define NUM_PLAYBACK 1 @@ -1586,3 +1587,4 @@ module_platform_driver(phytium_i2s_driver); MODULE_DESCRIPTION("Phytium I2S Driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zhang Yiqun "); +MODULE_VERSION(PHYTIUM_I2S_V1_VERSION); -- Gitee From d237eec81bd2d5a4bbf84f9cc283510bac8022f7 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Thu, 25 Jul 2024 02:25:15 +0000 Subject: [PATCH 035/154] ASoC: phytium: Add machine driver version information This patch add driver version information, which will be used to synchronize and manage driver patches in the future. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: I909280ac26f0f41c1b18803715877adf861fcd79 --- sound/soc/phytium/pmdk_es8336.c | 3 +++ sound/soc/phytium/pmdk_es8388.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/sound/soc/phytium/pmdk_es8336.c b/sound/soc/phytium/pmdk_es8336.c index 77d2e272c9..1008533b28 100644 --- a/sound/soc/phytium/pmdk_es8336.c +++ b/sound/soc/phytium/pmdk_es8336.c @@ -8,6 +8,8 @@ #include #include +#define PMDK_ES8336_VERSION "1.0.0" + /* PMDK widgets */ static const struct snd_soc_dapm_widget pmdk_es8336_dapm_widgets[] = { SND_SOC_DAPM_HP("HP", NULL), @@ -94,3 +96,4 @@ module_platform_driver(pmdk_sound_driver); MODULE_AUTHOR("Zhang Yiqun "); MODULE_DESCRIPTION("ALSA SoC PMDK ES8336"); MODULE_LICENSE("GPL"); +MODULE_VERSION(PMDK_ES8336_VERSION); diff --git a/sound/soc/phytium/pmdk_es8388.c b/sound/soc/phytium/pmdk_es8388.c index 57e17313c9..1da82995f4 100644 --- a/sound/soc/phytium/pmdk_es8388.c +++ b/sound/soc/phytium/pmdk_es8388.c @@ -9,6 +9,8 @@ #include #include +#define PMDK_ES8388_VERSION "1.0.0" + static struct snd_soc_jack hs_jack; /* Headset jack detection DAPM pins */ @@ -167,3 +169,4 @@ module_platform_driver(pmdk_sound_driver); MODULE_AUTHOR("Zhang Yiqun"); MODULE_DESCRIPTION("ALSA SoC PMDK ES8388"); MODULE_LICENSE("GPL"); +MODULE_VERSION(PMDK_ES8388_VERSION); -- Gitee From 65f26a681315a71d270b99d450a460d2f16472de Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Fri, 1 Nov 2024 02:43:32 +0000 Subject: [PATCH 036/154] codec: phytium: Bugfix issue after S4 recovery Add operations to restore codec register after resuming from S4, which makes the codes work normally. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: Ic551ae20b299a5549f74ca80a67a467f47d04e49 --- sound/soc/codecs/es8336.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/es8336.c b/sound/soc/codecs/es8336.c index 396949277c..c97b5cd826 100644 --- a/sound/soc/codecs/es8336.c +++ b/sound/soc/codecs/es8336.c @@ -839,31 +839,14 @@ static int es8336_suspend(struct snd_soc_component *component) static int es8336_resume(struct snd_soc_component *component) { - struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + struct regmap *regmap = dev_get_regmap(component->dev, NULL); int ret; - es8336_reset(component); /* UPDATED BY DAVID,15-3-5 */ - ret = snd_soc_component_read(component, ES8336_CLKMGR_ADCDIV2_REG05); - if (!ret) { - es8336_init_regs(component); - snd_soc_component_write(component, ES8336_GPIO_SEL_REG4D, 0x02); - /* max debance time, enable interrupt, low active */ - snd_soc_component_write(component, ES8336_GPIO_DEBUNCE_INT_REG4E, 0xf3); - /* es8336_set_bias_level(component, SND_SOC_BIAS_OFF); */ - snd_soc_component_write(component, ES8336_CPHP_OUTEN_REG17, 0x00); - snd_soc_component_write(component, ES8336_DAC_PDN_REG2F, 0x11); - snd_soc_component_write(component, ES8336_CPHP_LDOCTL_REG1B, 0x03); - snd_soc_component_write(component, ES8336_CPHP_PDN2_REG1A, 0x22); - snd_soc_component_write(component, ES8336_CPHP_PDN1_REG19, 0x06); - snd_soc_component_write(component, ES8336_HPMIX_SWITCH_REG14, 0x00); - snd_soc_component_write(component, ES8336_HPMIX_PDN_REG15, 0x33); - snd_soc_component_write(component, ES8336_HPMIX_VOL_REG16, 0x00); - if (!es8336->hp_inserted) - snd_soc_component_write(component, ES8336_SYS_PDN_REG0D, 0x3F); - snd_soc_component_write(component, ES8336_SYS_LP1_REG0E, 0xFF); - snd_soc_component_write(component, ES8336_SYS_LP2_REG0F, 0xFF); - snd_soc_component_write(component, ES8336_CLKMGR_CLKSW_REG01, 0xF3); - snd_soc_component_update_bits(component, ES8336_ADC_PDN_LINSEL_REG22, 0xC0, 0xC0); + regcache_mark_dirty(regmap); + ret = regcache_sync(regmap); + if (ret) { + dev_err(component->dev, "unable to sync regcache\n"); + return ret; } return 0; } @@ -944,6 +927,8 @@ const struct regmap_config es8336_regmap_config = { .cache_type = REGCACHE_RBTREE, .reg_defaults = es8336_reg_defaults, .num_reg_defaults = ARRAY_SIZE(es8336_reg_defaults), + .use_single_read = true, + .use_single_write = true, }; static const struct snd_soc_component_driver soc_component_dev_es8336 = { -- Gitee From be753f7eb71f66a238280e77175318d48b1232aa Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Mon, 24 Feb 2025 16:50:50 +0800 Subject: [PATCH 037/154] dt-bindings: i2s: Add bindings for I2S-v2.0 controllor This patch documents the DT binding for the Phytium I2S-v2.0 controller. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: Ief6821e18ae23845faf08cc47f71ea0af1bafb75 --- .../bindings/sound/phytium,i2s-2.0.yaml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/phytium,i2s-2.0.yaml diff --git a/Documentation/devicetree/bindings/sound/phytium,i2s-2.0.yaml b/Documentation/devicetree/bindings/sound/phytium,i2s-2.0.yaml new file mode 100644 index 0000000000..6dfd5c85d7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/phytium,i2s-2.0.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/phytium,virt-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium I2S V2 Controller + +maintainers: + - Dai Jingtao + +properties: + compatible: + enum: + - phytium,i2s-2.0 + + reg: + items: + - first is for physical base address and length of I2S regfile. + - second is for sharemem to send command. + - third is for physical base address and length of DMA_BDL controller. + + interrupts: + maxItems: 2 + + clocks: + items: + - description: Bus Clock + + clock-names: + items: + - const: i2s_clk + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + + +examples: + - | + i2s_v2: i2s@27010000 { + compatible = "phytium,i2s-2.0"; + reg = <0x0 0x27010000 0x0 0x800>, + <0x0 0x26fe3000 0x0 0x100>, + <0x0 0x18000000 0x0 0x1000>; + interrupts = , + ; + clocks = <&sysclk_1200mhz>; + clock-names = "i2s_clk"; + }; -- Gitee From ea0b5099ff137ebea19180cd4816f5c4d20531cf Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Thu, 9 May 2024 11:58:05 +0800 Subject: [PATCH 038/154] i2s: phytium: Add support for phytium i2s-v2 driver This patch adds support for phytium i2s-v2 controller. Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I57d9a72f4d9b7925810ce13b62eca39001c96a27 --- sound/soc/phytium/Kconfig | 20 +- sound/soc/phytium/Makefile | 6 + sound/soc/phytium/phytium-i2s-v2.c | 1159 ++++++++++++++++++++++++ sound/soc/phytium/phytium-i2s-v2.h | 192 ++++ sound/soc/phytium/phytium-machine-v2.c | 103 +++ 5 files changed, 1479 insertions(+), 1 deletion(-) create mode 100644 sound/soc/phytium/phytium-i2s-v2.c create mode 100644 sound/soc/phytium/phytium-i2s-v2.h create mode 100644 sound/soc/phytium/phytium-machine-v2.c diff --git a/sound/soc/phytium/Kconfig b/sound/soc/phytium/Kconfig index 74e3735b9f..43f6b9007e 100755 --- a/sound/soc/phytium/Kconfig +++ b/sound/soc/phytium/Kconfig @@ -1,3 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +comment "Phytium I2S Driver and Phytium I2S V2 Driver are generally not used together" + depends on SND_SOC_PHYTIUM_I2S && SND_SOC_PHYTIUM_I2S_V2 + config SND_SOC_PHYTIUM_I2S tristate "Phytium I2S Device Driver" help @@ -29,9 +33,23 @@ config SND_PMDK_MA1026 Say Y if you want to add Phytium machine support for MA1026 codecs. +config SND_SOC_PHYTIUM_I2S_V2 + tristate "Phytium I2S V2 Device Driver" + help + Say Y or M if you want to add support for I2S v2 driver for + Phytium I2S device . The device supports 2 channels each + for play and record. + +config SND_SOC_PHYTIUM_MACHINE_V2 + tristate "Phytium Machine V2 Driver" + depends on SND_SOC_PHYTIUM_I2S_V2 + help + Say Y or M if you want to add Phytium machine v2 support for + codecs. + config SND_PMDK_DP tristate "Phytium machine support with DP" - depends on I2C && SND_SOC_PHYTIUM_I2S + depends on SND_SOC_PHYTIUM_I2S || SND_SOC_PHYTIUM_I2S_V2 select SND_SOC_HDMI_CODEC help Say Y if you want to add Phytium machine support for diff --git a/sound/soc/phytium/Makefile b/sound/soc/phytium/Makefile index cc6d8d2961..bb21477eae 100755 --- a/sound/soc/phytium/Makefile +++ b/sound/soc/phytium/Makefile @@ -14,3 +14,9 @@ obj-$(CONFIG_SND_PMDK_MA1026) += snd-soc-pmdk-ma1026.o snd-soc-pmdk-dp-objs :=pmdk_dp.o obj-$(CONFIG_SND_PMDK_DP) += snd-soc-pmdk-dp.o + +snd-soc-phytium-i2s-v2-objs := phytium-i2s-v2.o +obj-$(CONFIG_SND_SOC_PHYTIUM_I2S_V2) += snd-soc-phytium-i2s-v2.o + +snd-soc-phytium-machine-v2-objs := phytium-machine-v2.o +obj-$(CONFIG_SND_SOC_PHYTIUM_MACHINE_V2) += snd-soc-phytium-machine-v2.o diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c new file mode 100644 index 0000000000..165a325d54 --- /dev/null +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -0,0 +1,1159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2S ASoC driver + * + * Copyright (C) 2024, 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 "phytium-i2s-v2.h" + +#define PHYT_I2S_V2_VERSION "1.0.0" + +static struct snd_soc_jack hs_jack; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Front", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_pcm_hardware phytium_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 8000, + .rate_max = 48000, + .formats = (SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 2, + .channels_max = 4, + .buffer_bytes_max = 4096*16, + .period_bytes_min = 1024, + .period_bytes_max = 4096*4, + .periods_min = 2, + .periods_max = 16, +}; + +static int phyt_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + size_t size = phytium_pcm_hardware.buffer_bytes_max; + int ret = 0; + + snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + priv->dev, size, size); + + if (ret < 0) { + dev_err(priv->dev, "can't alloc preallocate buffer\n"); + goto failed_preallocate_buffer; + } + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, priv->dev, BDL_SIZE, + &priv->pcm_config[0].bdl_dmab); + if (ret < 0) { + dev_err(priv->dev, "can't alloc bdl0 buffer\n"); + goto failed_alloc_bdl0; + } + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, priv->dev, BDL_SIZE, + &priv->pcm_config[1].bdl_dmab); + if (ret < 0) { + dev_err(priv->dev, "can't alloc bdl1 buffer\n"); + goto failed_alloc_bdl1; + } + return 0; + +failed_alloc_bdl1: + snd_dma_free_pages(&priv->pcm_config[0].bdl_dmab); +failed_alloc_bdl0: + snd_pcm_lib_preallocate_free_for_all(rtd->pcm); +failed_preallocate_buffer: + return ret; +} + +static void phyt_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + snd_dma_free_pages(&priv->pcm_config[1].bdl_dmab); + snd_dma_free_pages(&priv->pcm_config[0].bdl_dmab); + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int phyt_pcm_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_card *card = component->card; + int ret; + + ret = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET, + &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); + if (ret < 0) { + dev_err(component->dev, "Cannot create jack\n"); + return ret; + } + return 0; +} + +static int phyt_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_soc_set_runtime_hwparams(substream, &phytium_pcm_hardware); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); + runtime->private_data = priv; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + priv->substream_playback = substream; + else + priv->substream_capture = substream; + + return 0; +} + +static int phyt_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + priv->substream_playback = NULL; + else + priv->substream_capture = NULL; + + return 0; +} + +static int phyt_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + int ret; + uint32_t format_val; + + switch (params_format(hw_params)) { + case SNDRV_PCM_FORMAT_S16_LE: + format_val = BYTE_2; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + format_val = BYTE_4; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + format_val = BYTE_4; + break; + + default: + dev_err(priv->dev, "phytium-i2s: unsupported PCM fmt"); + return -EINVAL; + } + + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + priv->pcm_config[DIRECTION_PLAYBACK].format_val = format_val; + else + priv->pcm_config[DIRECTION_CAPTURE].format_val = format_val; + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + if (ret < 0) + return ret; + + return 0; +} + +static void phyt_bdl_entry_setup(dma_addr_t addr, uint32_t **pbdl, + uint32_t period_bytes, uint32_t with_ioc) +{ + uint32_t *bdl = *pbdl; + uint32_t chunk; + + while (period_bytes > 0) { + bdl[0] = cpu_to_le32((u32)addr); + bdl[1] = cpu_to_le32(upper_32_bits(addr)); + if (period_bytes > BDL_BUFFER_MAX_SIZE) + chunk = BDL_BUFFER_MAX_SIZE; + else + chunk = period_bytes; + + bdl[2] = cpu_to_le32(chunk); + period_bytes -= chunk; + bdl[3] = (period_bytes || !with_ioc) ? 0 : cpu_to_le32(0x01); + bdl += 4; + } + + *pbdl = bdl; +} + + +static int phyt_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + struct snd_pcm_runtime *runtime = substream->runtime; + struct phytium_pcm_config *pcm_config; + int periods = 0, i, offset = 0; + uint32_t *bdl = NULL; + struct snd_dma_buffer *data_dmab = NULL; + int direction; + uint32_t frags = 0; + uint32_t config = 0, format_val; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + priv->pcm_config[DIRECTION_PLAYBACK].buffer_size = + snd_pcm_lib_buffer_bytes(substream); + priv->pcm_config[DIRECTION_PLAYBACK].period_bytes = + snd_pcm_lib_period_bytes(substream); + priv->pcm_config[DIRECTION_PLAYBACK].no_period_wakeup = + runtime->no_period_wakeup; + direction = DIRECTION_PLAYBACK; + format_val = priv->pcm_config[DIRECTION_PLAYBACK].format_val; + } else { + priv->pcm_config[DIRECTION_CAPTURE].buffer_size = + snd_pcm_lib_buffer_bytes(substream); + priv->pcm_config[DIRECTION_CAPTURE].period_bytes = + snd_pcm_lib_period_bytes(substream); + priv->pcm_config[DIRECTION_CAPTURE].no_period_wakeup = + runtime->no_period_wakeup; + direction = DIRECTION_CAPTURE; + format_val = priv->pcm_config[DIRECTION_CAPTURE].format_val; + } + + pcm_config = &priv->pcm_config[direction]; + + periods = pcm_config->buffer_size / pcm_config->period_bytes; + bdl = (uint32_t *)pcm_config->bdl_dmab.area; + data_dmab = snd_pcm_get_dma_buf(substream); + for (i = 0; i < periods; i++) { + phyt_bdl_entry_setup(data_dmab->addr + offset, &bdl, + pcm_config->period_bytes, + !pcm_config->no_period_wakeup); + offset += pcm_config->period_bytes; + frags += DIV_ROUND_UP(pcm_config->period_bytes, BDL_BUFFER_MAX_SIZE); + } + + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHAL_CONFG0, CHANNEL_0_1_ENABLE); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_MASK_INT, CHANNEL_0_1_INT_MASK); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_BDLPL(direction), + (uint32_t)(pcm_config->bdl_dmab.addr)); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_BDLPU(direction), + upper_32_bits(pcm_config->bdl_dmab.addr)); + if (direction == DIRECTION_PLAYBACK) + config = PLAYBACK_ADDRESS_OFFSET; + else + config = CAPTRUE_ADDRESS_OFFSET; + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_DEV_ADDR(direction), + E2000_LSD_I2S_BASE + config); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_LVI(direction), frags - 1); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_CBL(direction), + pcm_config->buffer_size); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_DSIZE(direction), + D_SIZE(format_val, direction)); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_DLENGTH(direction), D_LENGTH); + + return 0; +} + +static int phyt_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + int direction; + u32 mask; + int cnt = 10; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + direction = DIRECTION_PLAYBACK; + else + direction = DIRECTION_CAPTURE; + + mask = readl(priv->dma_reg_base + PHYTIUM_DMA_MASK_INT); + mask &= ~BIT(direction); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_MASK_INT, mask); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_CTL(direction), 0); + while (cnt--) { + if (readl(priv->dma_reg_base + PHYTIUM_DMA_CHALX_CTL(direction)) == 0) + break; + udelay(200); + } + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_CTL(direction), 2); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_CTL(direction), 0); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_STS, DMA_TX_DONE); + else + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_STS, DMA_RX_DONE); + + return snd_pcm_lib_free_pages(substream); +} + +static int phyt_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + bool start = false; + int direction = 0, value = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + start = true; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + start = false; + break; + default: + return -EINVAL; + } + + direction = ((substream->stream == + SNDRV_PCM_STREAM_PLAYBACK) ? DIRECTION_PLAYBACK:DIRECTION_CAPTURE); + + if (start) + value = (substream->stream == + SNDRV_PCM_STREAM_PLAYBACK) ? PLAYBACK_START:CAPTURE_START; + else + value = CTL_STOP; + + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_CTL(direction), value); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CTL, DMA_ENABLE); + + return 0; +} + +static snd_pcm_uframes_t phyt_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + uint32_t pos = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pos = readl(priv->dma_reg_base + PHYTIUM_DMA_LPIB(DIRECTION_PLAYBACK)); + else + pos = readl(priv->dma_reg_base + PHYTIUM_DMA_LPIB(DIRECTION_CAPTURE)); + + return bytes_to_frames(substream->runtime, pos); +} + +int phyt_i2s_msg_set_cmd(struct phytium_i2s *priv, struct phyti2s_cmd *msg) +{ + struct phyti2s_cmd *ans_msg; + int timeout = 10, ret = 0; + + mutex_lock(&priv->sharemem_mutex); + memcpy(priv->sharemem_base, msg, sizeof(struct phyti2s_cmd)); + + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_AP2RV_INT_STATE, SEND_INTR); + + ans_msg = priv->sharemem_base; + + while ((ans_msg->complete == PHYTI2S_COMPLETE_NONE + || ans_msg->complete == PHYTI2S_COMPLETE_GOING) + && timeout) { + if (preempt_count() != 0) + udelay(500); + else + usleep_range(500, 1000); + timeout--; + } + + if (timeout == 0) { + dev_err(priv->dev, "wait cmd reply timeout\n"); + ret = -EBUSY; + } + + if (ans_msg->complete == PHYTI2S_COMPLETE_ERROR) { + dev_err(priv->dev, "handle cmd failed\n"); + ret = -EINVAL; + } + + if (ans_msg->complete == PHYTI2S_COMPLETE_ID_NOT_SUPPORTED) { + dev_err(priv->dev, "cmd not support!\n"); + ret = -EINVAL; + } + + if (ans_msg->complete == PHYTI2S_COMPLETE_SUBID_NOT_SUPPORTED) { + dev_err(priv->dev, "cmd subid not support!\n"); + ret = -EINVAL; + } + + if (ans_msg->complete == PHYTI2S_COMPLETE_INVALID_PARAMETERS) { + dev_err(priv->dev, "cmd params not support!\n"); + ret = -EINVAL; + } + mutex_unlock(&priv->sharemem_mutex); + return ret; +} + +static int phyt_i2s_enable_gpio(struct phytium_i2s *priv) +{ + struct phyti2s_cmd *msg = priv->msg; + struct gpio_i2s_data *data = &msg->cmd_para.gpio_i2s_data; + int ret = 0; + + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_GPIO; + msg->complete = 0; + data->enable = 1; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) + dev_err(priv->dev, "PHYTI2S_MSG_CMD_SET_GPIO enable failed: %d\n", ret); + + return ret; +} + +static int phyt_i2s_disable_gpioint(struct phytium_i2s *priv) +{ + struct phyti2s_cmd *msg = priv->msg; + struct gpio_i2s_data *data = &msg->cmd_para.gpio_i2s_data; + int ret = 0; + + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_GPIO; + msg->complete = 0; + data->enable = 0; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) + dev_err(priv->dev, "PHYTIUM_MSG_CMD_SET_GPIO disable failed: %d\n", ret); + + return ret; +} + +static int phyt_pcm_suspend(struct snd_soc_component *component) +{ + return 0; +} + +static int phyt_pcm_resume(struct snd_soc_component *component) +{ + struct phytium_i2s *priv = snd_soc_component_get_drvdata(component); + struct snd_soc_dai *dai; + struct phyti2s_cmd *msg = priv->msg; + struct set_mode_data *data = &msg->cmd_para.set_mode_data; + int ret = 0; + + for_each_component_dais(component, dai) { + if (snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK)) { + data->direction = DIRECTION_PLAYBACK; + data->data_width = priv->data_width; + data->sample_rate = priv->sample_rate; + data->chan_nr = priv->chan_nr; + data->clk_base = priv->clk_base; + data->enable = 1; + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_MODE; + msg->complete = 0; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) { + dev_err(priv->dev, "phytium-i2s: resume failed: %d\n", ret); + ret = -EINVAL; + goto error; + } + } + if (snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) { + data->direction = DIRECTION_CAPTURE; + data->data_width = priv->data_width; + data->sample_rate = priv->sample_rate; + data->chan_nr = priv->chan_nr; + data->clk_base = priv->clk_base; + data->enable = 1; + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_MODE; + msg->complete = 0; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) { + dev_err(priv->dev, "phytium-i2s: resume failed: %d\n", ret); + ret = -EINVAL; + goto error; + } + } + } + phyt_i2s_enable_gpio(priv); +error: + return ret; +} + +static const struct snd_soc_component_driver phytium_i2s_component = { + .name = "phytium-i2s", + .pcm_construct = phyt_pcm_new, + .pcm_destruct = phyt_pcm_free, + .suspend = phyt_pcm_suspend, + .resume = phyt_pcm_resume, + .probe = phyt_pcm_component_probe, + .open = phyt_pcm_open, + .close = phyt_pcm_close, + .hw_params = phyt_pcm_hw_params, + .prepare = phyt_pcm_prepare, + .hw_free = phyt_pcm_hw_free, + .trigger = phyt_pcm_trigger, + .pointer = phyt_pcm_pointer, + .non_legacy_dai_naming = 0, +}; + +static int phyt_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(dai); + struct phyti2s_cmd *msg = priv->msg; + struct set_mode_data *data = &msg->cmd_para.set_mode_data; + int ret = 0; + + memset(msg, 0, sizeof(struct phyti2s_cmd)); + + data->direction = ((substream->stream == + SNDRV_PCM_STREAM_PLAYBACK) ? DIRECTION_PLAYBACK:DIRECTION_CAPTURE); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + data->data_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + data->data_width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + data->data_width = 32; + break; + default: + dev_err(priv->dev, "phytium-i2s: unsupported dai fmt"); + ret = -EINVAL; + goto error; + } + + data->sample_rate = params_rate(params); + data->chan_nr = params_channels(params); + data->enable = 1; + data->clk_base = priv->clk_base; + priv->data_width = data->data_width; + priv->sample_rate = data->sample_rate; + priv->chan_nr = data->chan_nr; + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_MODE; + msg->complete = 0; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) { + dev_err(priv->dev, "phytium-i2s: PHYTI2S_MSG_CMD_SET_MODE failed: %d\n", ret); + ret = -EINVAL; + } + +error: + return ret; +} + +static void i2s_interrupt_playback_stop_work(struct work_struct *work) +{ + struct phytium_i2s *priv = container_of(work, struct phytium_i2s, + i2s_playback_stop_work.work); + struct phyti2s_cmd *msg; + struct trigger_i2s_data *data; + int ret = 0; + + msg = kmalloc(sizeof(struct phyti2s_cmd), GFP_KERNEL); + if (!msg) + return; + data = &msg->cmd_para.trigger_i2s_data; + + data->direction = DIRECTION_PLAYBACK; + data->start = 0; + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_TRIGGER; + msg->complete = 0; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) + dev_err(priv->dev, "PHYTI2S_MSG_CMD_SET_MODE stop playback failed: %d\n", ret); + kfree(msg); +} + +static void i2s_interrupt_capture_stop_work(struct work_struct *work) +{ + struct phytium_i2s *priv = container_of(work, struct phytium_i2s, + i2s_capture_stop_work.work); + struct phyti2s_cmd *msg; + struct trigger_i2s_data *data; + int ret = 0; + + msg = kmalloc(sizeof(struct phyti2s_cmd), GFP_KERNEL); + if (!msg) + return; + data = &msg->cmd_para.trigger_i2s_data; + + data->direction = DIRECTION_CAPTURE; + data->start = 0; + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_TRIGGER; + msg->complete = 0; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) + dev_err(priv->dev, "PHYTI2S_MSG_CMD_SET_MODE stop capture failed: %d\n", ret); + kfree(msg); +} + +static int phyt_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(dai); + struct phyti2s_cmd *msg = priv->msg; + struct trigger_i2s_data *data = &msg->cmd_para.trigger_i2s_data; + bool start = false; + int ret = 0; + + memset(msg, 0, sizeof(struct phyti2s_cmd)); + + data->direction = ((substream->stream == + SNDRV_PCM_STREAM_PLAYBACK) ? DIRECTION_PLAYBACK:DIRECTION_CAPTURE); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + priv->running += 1; + start = true; + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + priv->running -= 1; + // Use delay work to avoid waiting too long in interrupt. + if (priv->interrupt) { + if (data->direction == SNDRV_PCM_STREAM_PLAYBACK) + queue_delayed_work(system_power_efficient_wq, + &priv->i2s_playback_stop_work, + usecs_to_jiffies(200)); + else + queue_delayed_work(system_power_efficient_wq, + &priv->i2s_capture_stop_work, + usecs_to_jiffies(200)); + return ret; + } + start = false; + break; + default: + ret = -EINVAL; + break; + } + + if (!start && priv->running) + goto error; + + data->start = start ? 1:0; + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_TRIGGER; + msg->complete = 0; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) { + dev_err(priv->dev, "phytium-i2s: PHYTI2S_MSG_CMD_SET_TRIGGER failed: %d\n", ret); + ret = -EINVAL; + } + +error: + return ret; +} + +static int phyt_i2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct phytium_i2s *priv = snd_soc_dai_get_drvdata(dai); + struct phyti2s_cmd *msg = priv->msg; + struct set_mode_data *data = &msg->cmd_para.set_mode_data; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + flush_delayed_work(&priv->i2s_playback_stop_work); + else + flush_delayed_work(&priv->i2s_capture_stop_work); + if (priv->running > 0) + return 0; + + memset(msg, 0, sizeof(struct phyti2s_cmd)); + + data->direction = ((substream->stream == + SNDRV_PCM_STREAM_PLAYBACK) ? DIRECTION_PLAYBACK:DIRECTION_CAPTURE); + data->enable = 0; + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_MODE; + msg->complete = 0; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) { + dev_err(priv->dev, "phytium-i2s: SET_MODE disable failed: %d\n", ret); + ret = -EINVAL; + } + + return ret; +} + +static const struct snd_soc_dai_ops phytium_i2s_dai_ops = { + .hw_params = phyt_i2s_hw_params, + .trigger = phyt_i2s_trigger, + .hw_free = phyt_i2s_hw_free, +}; + +static void phyt_i2s_gpio_jack_work(struct work_struct *work) +{ + struct phytium_i2s *priv = container_of(work, struct phytium_i2s, + phyt_i2s_gpio_work.work); + struct phyti2s_cmd *msg; + struct gpio_i2s_data *data; + int ret = 0; + + if (priv->insert == 1) { + snd_soc_jack_report(&hs_jack, HEADPHONE_DISABLE, SND_JACK_HEADSET); + priv->insert = 0; + } else { + snd_soc_jack_report(&hs_jack, HEADPHONE_ENABLE, SND_JACK_HEADSET); + priv->insert = 1; + } + + msg = kmalloc(sizeof(struct phyti2s_cmd), GFP_KERNEL); + if (!msg) + return; + data = &msg->cmd_para.gpio_i2s_data; + + msg->id = PHYTIUM_I2S_LSD_ID; + msg->cmd_id = PHYTI2S_MSG_CMD_SET; + msg->cmd_subid = PHYTI2S_MSG_CMD_SET_GPIO; + msg->complete = 0; + data->enable = -1; + data->insert = priv->insert; + ret = phyt_i2s_msg_set_cmd(priv, msg); + if (ret) + dev_err(priv->dev, "PHYTI2S_MSG_CMD_SET_GPIO report jack failed: %d\n", ret); + kfree(msg); +} + +static irqreturn_t phyt_i2s_gpio_interrupt(int irq, void *dev_id) +{ + struct phytium_i2s *priv = dev_id; + + queue_delayed_work(system_power_efficient_wq, &priv->phyt_i2s_gpio_work, + msecs_to_jiffies(100)); + + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_GPIO_PORTA_EOI, BIT(0)); + return IRQ_HANDLED; +} + +static irqreturn_t phyt_i2s_interrupt(int irq, void *dev_id) +{ + struct phytium_i2s *priv = dev_id; + uint32_t status; + int ret = IRQ_NONE; + + priv->interrupt = 1; + status = readl(priv->dma_reg_base + PHYTIUM_DMA_STS); + + if (status & DMA_TX_DONE) { + snd_pcm_period_elapsed(priv->substream_playback); + writel(DMA_TX_DONE, priv->dma_reg_base + PHYTIUM_DMA_STS); + ret = IRQ_HANDLED; + } + + if (status & DMA_RX_DONE) { + snd_pcm_period_elapsed(priv->substream_capture); + writel(DMA_RX_DONE, priv->dma_reg_base + PHYTIUM_DMA_STS); + ret = IRQ_HANDLED; + } + + priv->interrupt = 0; + + return ret; +} + +static int phyt_configure_dai_driver(struct phytium_i2s *priv, + struct snd_soc_dai_driver *dai_driver) +{ + dai_driver->playback.stream_name = "i2s-Playback"; + dai_driver->playback.channels_min = MIN_CHANNEL_NUM; + dai_driver->playback.channels_max = 4; + dai_driver->playback.rates = SNDRV_PCM_RATE_8000_192000; + dai_driver->playback.formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE; + dai_driver->capture.stream_name = "i2s-Capture"; + dai_driver->capture.channels_min = 2; + dai_driver->capture.channels_max = 4; + dai_driver->capture.rates = SNDRV_PCM_RATE_8000_192000; + dai_driver->capture.formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE; + dai_driver->symmetric_rates = 1; + dai_driver->ops = &phytium_i2s_dai_ops; + + return 0; +} + +static void phyt_i2s_heartbeat(struct phytium_i2s *priv) +{ + u32 debug_reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG, + debug_reg | HEARTBEAT); +} + +static void phyt_i2s_enable_heartbeat(struct phytium_i2s *priv) +{ + u32 debug_reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + + priv->heart_enable = true; + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG, + debug_reg | HEART_ENABLE); +} + +static void phyt_i2s_disable_heartbeat(struct phytium_i2s *priv) +{ + u32 debug_reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + + priv->heart_enable = false; + debug_reg &= ~HEART_ENABLE; + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG, debug_reg); +} + +static void phyt_i2s_enable_debug(struct phytium_i2s *priv) +{ + u32 debug_reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG, + debug_reg | DEBUG_ENABLE); +} + +static void phyt_i2s_disable_debug(struct phytium_i2s *priv) +{ + u32 debug_reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + + debug_reg &= ~DEBUG_ENABLE; + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG, debug_reg); +} + +static void phyt_i2s_timer_handler(struct timer_list *timer) +{ + struct phytium_i2s *priv = from_timer(priv, timer, timer); + + if (priv->heart_enable) + phyt_i2s_heartbeat(priv); + + mod_timer(&priv->timer, jiffies + msecs_to_jiffies(2000)); +} + +static ssize_t phyt_i2s_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct phytium_i2s *priv = dev_get_drvdata(dev); + u32 debug_reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + + return sprintf(buf, "%x\n", debug_reg); +} + +static ssize_t phyt_i2s_debug_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct phytium_i2s *priv = dev_get_drvdata(dev); + char *p; + char *token; + u8 loc, dis_en; + int ret; + long value; + + dev_info(dev, "echo debug(1)/alive(0) enable(1)/disable(0) > debug\n"); + + p = kmalloc(size, GFP_KERNEL); + if (p == NULL) + return -EINVAL; + strscpy(p, buf, sizeof(p)); + + token = strsep(&p, " "); + if (!token) { + ret = -EINVAL; + goto error; + } + + ret = kstrtol(token, 0, &value); + if (ret) + goto error; + loc = (u8)value; + + token = strsep(&p, " "); + if (!token) { + ret = -EINVAL; + goto error; + } + + ret = kstrtol(token, 0, &value); + if (ret) + goto error; + dis_en = value; + + if (loc == 1) { + if (dis_en) + phyt_i2s_enable_debug(priv); + else + phyt_i2s_disable_debug(priv); + } else if (loc == 0) { + if (dis_en) + phyt_i2s_enable_heartbeat(priv); + else + phyt_i2s_disable_heartbeat(priv); + } + + kfree(p); + return size; +error: + kfree(p); + return ret; +} + +static DEVICE_ATTR_RW(phyt_i2s_debug); + +static struct attribute *phyt_i2s_device_attrs[] = { + &dev_attr_phyt_i2s_debug.attr, + NULL, +}; + +static const struct attribute_group phyt_i2s_device_group = { + .attrs = phyt_i2s_device_attrs, +}; + +static int phyt_i2s_probe(struct platform_device *pdev) +{ + struct phytium_i2s *priv; + struct snd_soc_dai_driver *dai_driver; + struct resource *res; + int ret, irq, gpio_irq; + struct clk *clk; + struct fwnode_handle *np; + uint32_t status; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto failed_alloc_phytium_i2s; + } + + priv->msg = devm_kzalloc(&pdev->dev, sizeof(struct phyti2s_cmd), GFP_KERNEL); + if (!priv->msg) { + ret = -ENOMEM; + goto failed_alloc_phytium_i2s; + } + + dev_set_drvdata(&pdev->dev, priv); + priv->dev = &pdev->dev; + + dai_driver = devm_kzalloc(&pdev->dev, sizeof(*dai_driver), GFP_KERNEL); + if (!dai_driver) { + ret = -ENOMEM; + goto failed_alloc_dai_driver; + } + + phyt_configure_dai_driver(priv, dai_driver); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regfile_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->regfile_base)) { + dev_err(&pdev->dev, "failed to ioremap resource0\n"); + ret = PTR_ERR(priv->regfile_base); + goto failed_ioremap_res0; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->sharemem_base = devm_ioremap_wc(&pdev->dev, res->start, resource_size(res)); + if (IS_ERR(priv->sharemem_base)) { + dev_err(&pdev->dev, "failed to ioremap resource1\n"); + ret = PTR_ERR(priv->sharemem_base); + goto failed_ioremap_res1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + priv->dma_reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->dma_reg_base)) { + dev_err(&pdev->dev, "failed to ioremap resource2\n"); + ret = PTR_ERR(priv->dma_reg_base); + goto failed_ioremap_res2; + } + + status = readl(priv->dma_reg_base + PHYTIUM_DMA_STS); + if (status & DMA_TX_DONE) + writel(DMA_TX_DONE, priv->dma_reg_base + PHYTIUM_DMA_STS); + if (status & DMA_RX_DONE) + writel(DMA_RX_DONE, priv->dma_reg_base + PHYTIUM_DMA_STS); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_MASK_INT, 0x0); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, irq, phyt_i2s_interrupt, IRQF_SHARED, + pdev->name, priv); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq\n"); + goto failed_request_irq; + } + + gpio_irq = platform_get_irq(pdev, 1); + if (gpio_irq > 0) { + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_GPIO_PORTA_EOI, BIT(0)); + ret = phyt_i2s_disable_gpioint(priv); + if (ret < 0) { + dev_err(&pdev->dev, "failed to disable gpioint\n"); + goto failed_disable_gpioint; + } + priv->insert = 0; + ret = devm_request_irq(&pdev->dev, gpio_irq, phyt_i2s_gpio_interrupt, + IRQF_SHARED, pdev->name, priv); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request gpio irq\n"); + goto failed_request_irq; + } + ret = phyt_i2s_enable_gpio(priv); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable gpio\n"); + goto failed_enable_gpio; + } + INIT_DELAYED_WORK(&priv->phyt_i2s_gpio_work, phyt_i2s_gpio_jack_work); + } + + if (pdev->dev.of_node) { + device_property_read_string(&pdev->dev, "dai-name", &dai_driver->name); + clk = devm_clk_get(&pdev->dev, NULL); + priv->clk_base = clk_get_rate(clk); + } else if (has_acpi_companion(&pdev->dev)) { + np = dev_fwnode(&(pdev->dev)); + ret = fwnode_property_read_string(np, "dai-name", &dai_driver->name); + if (ret < 0) { + dev_err(&pdev->dev, "missing dai-name property from acpi\n"); + goto failed_get_dai_name; + } + ret = fwnode_property_read_u32(np, "i2s_clk", &priv->clk_base); + if (ret < 0) { + dev_err(&pdev->dev, "missing i2s_clk property from acpi\n"); + goto failed_get_dai_name; + } + } + + ret = devm_snd_soc_register_component(&pdev->dev, &phytium_i2s_component, + dai_driver, 1); + if (ret != 0) { + dev_err(&pdev->dev, "not able to register dai\n"); + goto failed_register_com; + } + + INIT_DELAYED_WORK(&priv->i2s_playback_stop_work, i2s_interrupt_playback_stop_work); + INIT_DELAYED_WORK(&priv->i2s_capture_stop_work, i2s_interrupt_capture_stop_work); + mutex_init(&priv->sharemem_mutex); + + if (sysfs_create_group(&priv->dev->kobj, &phyt_i2s_device_group)) + dev_warn(&pdev->dev, "failed create sysfs\n"); + + phyt_i2s_disable_heartbeat(priv); + phyt_i2s_disable_debug(priv); + priv->timer.expires = jiffies + msecs_to_jiffies(5000); + timer_setup(&priv->timer, phyt_i2s_timer_handler, 0); + add_timer(&priv->timer); + + return 0; +failed_register_com: +failed_get_dai_name: +failed_request_irq: +failed_disable_gpioint: +failed_enable_gpio: +failed_ioremap_res2: +failed_ioremap_res1: +failed_ioremap_res0: +failed_alloc_dai_driver: +failed_alloc_phytium_i2s: + return ret; +} + +static int phyt_i2s_remove(struct platform_device *pdev) +{ + struct phytium_i2s *priv = dev_get_drvdata(&pdev->dev); + + sysfs_remove_group(&priv->dev->kobj, &phyt_i2s_device_group); + del_timer(&priv->timer); + return 0; +} + +static const struct of_device_id phyt_i2s_of_match[] = { + { .compatible = "phytium,i2s-2.0", }, + { }, +}; +MODULE_DEVICE_TABLE(of, phyt_i2s_of_match); + +static const struct acpi_device_id phyt_i2s_acpi_match[] = { + { "PHYT0062", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, phyt_i2s_acpi_match); + +static struct platform_driver phyt_i2s_driver = { + .probe = phyt_i2s_probe, + .remove = phyt_i2s_remove, + .driver = { + .name = "phytium-i2s-v2", + .of_match_table = of_match_ptr(phyt_i2s_of_match), + .acpi_match_table = phyt_i2s_acpi_match, + }, +}; + +module_platform_driver(phyt_i2s_driver); + +MODULE_DESCRIPTION("Phytium I2S V2 Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yang Xun "); +MODULE_VERSION(PHYT_I2S_V2_VERSION); diff --git a/sound/soc/phytium/phytium-i2s-v2.h b/sound/soc/phytium/phytium-i2s-v2.h new file mode 100644 index 0000000000..12122b9945 --- /dev/null +++ b/sound/soc/phytium/phytium-i2s-v2.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Phytium I2S ASoC driver + * + * Copyright (C) 2024, Phytium Technology Co., Ltd. + * + */ + +#ifndef __PHYTIUM_VIRT_I2S_H +#define __PHYTIUM_VIRT_I2S_H + +#include +#include + +struct phytium_pcm_config { + uint32_t buffer_size; + uint32_t period_bytes; + uint32_t frags; + uint32_t format_val; + unsigned int no_period_wakeup: 1; + struct snd_dma_buffer bdl_dmab; +}; + +struct phytium_i2s { + void __iomem *regfile_base; + void __iomem *sharemem_base; + void __iomem *dma_reg_base; + struct device *dev; + struct device *parent; + + struct snd_pcm_substream *substream_playback; + struct snd_pcm_substream *substream_capture; + u32 clk_base; + struct phytium_pcm_config pcm_config[2]; + struct delayed_work i2s_playback_stop_work; + struct delayed_work i2s_capture_stop_work; + struct delayed_work phyt_i2s_gpio_work; + struct phyti2s_cmd *msg; + int interrupt; + int running; + struct timer_list timer; + bool heart_enable; + uint32_t chan_nr; + uint32_t data_width; + uint32_t sample_rate; + int insert; + struct mutex sharemem_mutex; +}; + +struct pdata_pd230x_mfd { + struct device *dev; + char *name; + int clk_base; +}; + +#define MAX_CHANNEL_NUM 8 +#define MIN_CHANNEL_NUM 2 + +#define BDL_SIZE 4096 +#define BDL_BUFFER_MAX_SIZE 0x100000 + +#define DIRECTION_PLAYBACK 0 +#define DIRECTION_CAPTURE 1 + +#define HEADPHONE_DISABLE 0 +#define HEADPHONE_ENABLE 1 + +/* *******************msg*****************************/ +#define PHYTIUM_I2S_LSD_ID 0x00 + +enum phyti2s_msg_cmd_id { + PHYTI2S_MSG_CMD_DEFAULT = 0, + PHYTI2S_MSG_CMD_SET, + PHYTI2S_MSG_CMD_GET, + PHYTI2S_MSG_CMD_DATA, + PHYTI2S_MSG_CMD_REPORT, + PHYTI2S_SYS_PROTOCOL = 0x10, +}; + +enum phyti2s_set_subid { + PHYTI2S_MSG_CMD_SET_MODE = 0, + PHYTI2S_MSG_CMD_SET_TRIGGER, + PHYTI2S_MSG_CMD_SET_GPIO, +}; + +enum phyti2s_complete { + PHYTI2S_COMPLETE_NONE = 0, + PHYTI2S_COMPLETE_DONE, + PHYTI2S_COMPLETE_GOING, + PHYTI2S_COMPLETE_ERROR = 0x10, + PHYTI2S_COMPLETE_ID_NOT_SUPPORTED, + PHYTI2S_COMPLETE_SUBID_NOT_SUPPORTED, + PHYTI2S_COMPLETE_INVALID_PARAMETERS, +}; + +struct set_mode_data { + int direction; + uint32_t chan_nr; + uint32_t data_width; + uint32_t sample_rate; + uint32_t enable; + uint32_t clk_base; +}; + +struct trigger_i2s_data { + int direction; + uint32_t start; +}; + +struct gpio_i2s_data { + int enable; + int insert; +}; + +struct phyti2s_cmd { + uint16_t id; + uint8_t cmd_id; + uint8_t cmd_subid; + uint16_t len; + uint16_t complete; + union { + uint8_t para[56]; + struct set_mode_data set_mode_data; + struct trigger_i2s_data trigger_i2s_data; + struct gpio_i2s_data gpio_i2s_data; + } cmd_para; +}; +/* *******************msg end ****************************/ + + +/*********************register ***************************/ +/* regfile */ +#define PHYTIUM_REGFILE_TX_HEAD 0x00 + #define TX_HEAD_INTR (1 << 16) +#define PHYTIUM_REGFILE_TX_TAIL 0X04 + +#define PHYTIUM_REGFILE_AP2RV_INT_MASK 0x20 +#define PHYTIUM_REGFILE_AP2RV_INT_STATE 0x24 + #define SEND_INTR (1 << 4) +#define PHYTIUM_REGFILE_GPIO_PORTA_EOI 0x30 +#define PHYTIUM_REGFILE_DEBUG 0x58 + #define DEBUG_ENABLE (1 << 0) + #define HEART_ENABLE (1 << 1) + #define HEARTBEAT (1 << 2) + +/* DMA register */ +#define PHYTIUM_DMA_CTL 0x0000 + #define DMA_ENABLE 0x1 +#define PHYTIUM_DMA_CHAL_CONFG0 0x0004 + #define CHANNEL_0_1_ENABLE 0x8180 +#define PHYTIUM_DMA_STS 0x0008 + #define DMA_TX_DONE 0x1 + #define DMA_RX_DONE 0x100 +#define PHYTIUM_DMA_MASK_INT 0x000c + #define CHANNEL_0_1_INT_MASK 0x80000003 +#define PHYTIUM_DMA_BDLPU(x) (0x40 * x + 0x0040) +#define PHYTIUM_DMA_BDLPL(x) (0x40 * x + 0x0044) +#define PHYTIUM_DMA_CHALX_DEV_ADDR(x) (0x40 * x + 0x0048) + #define E2000_LSD_I2S_BASE 0x28009000 + #define PLAYBACK_ADDRESS_OFFSET 0x1c8 + #define CAPTRUE_ADDRESS_OFFSET 0x1c0 +#define PHYTIUM_DMA_CHALX_LVI(x) (0x40 * x + 0x004c) +#define PHYTIUM_DMA_LPIB(x) (0x40 * x + 0x0050) +#define PHYTIUM_DMA_CHALX_CBL(x) (0x40 * x + 0x0054) +#define PHYTIUM_DMA_CHALX_CTL(x) (0x40 * x + 0x0058) + #define PLAYBACK_START 0x1 + #define CAPTURE_START 0x5 + #define CTL_STOP 0x0 +#define PHYTIUM_DMA_CHALX_DSIZE(x) (0x40 * x + 0x0064) + #define BYTE_4 0x0 + #define BYTE_2 0x2 + #define D_SIZE(byte_mode, dir) (byte_mode << (dir*2)) +#define PHYTIUM_DMA_CHALX_DLENGTH(x) (0x40 * x + 0x0068) + #define D_LENGTH 0x0 +/*********************register end ***************************/ + + +static inline unsigned int +phyt_readl_reg(void *base, uint32_t offset) +{ + unsigned int data; + + data = readl(base + offset); + return data; +} + +static inline void +phyt_writel_reg(void *base, uint32_t offset, uint32_t data) +{ + writel(data, base + offset); +} +#endif diff --git a/sound/soc/phytium/phytium-machine-v2.c b/sound/soc/phytium/phytium-machine-v2.c new file mode 100644 index 0000000000..cf29c368b2 --- /dev/null +++ b/sound/soc/phytium/phytium-machine-v2.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Phytium Techonology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#define PHYT_MACHINE_V2_VERSION "1.0.0" + +/* PMDK widgets */ +static const struct snd_soc_dapm_widget phyt_machine_dapm_widgets[] = { + SND_SOC_DAPM_HP("Front", NULL), + SND_SOC_DAPM_HP("Rear", NULL), + + SND_SOC_DAPM_MIC("FrontIn", NULL), + SND_SOC_DAPM_MIC("RearIn", NULL), +}; + +/* PMDK control */ +static const struct snd_kcontrol_new phyt_machine_controls[] = { + SOC_DAPM_PIN_SWITCH("Front"), + SOC_DAPM_PIN_SWITCH("Rear"), + SOC_DAPM_PIN_SWITCH("FrontIn"), + SOC_DAPM_PIN_SWITCH("RearIn"), +}; + +/* PMDK connections */ +static const struct snd_soc_dapm_route phyt_machine_audio_map[] = { + {"INPUT1", NULL, "FrontIn"}, + {"INPUT2", NULL, "RearIn"}, + {"Front", NULL, "OUTPUT1"}, + {"Rear", NULL, "OUTPUT2"}, +}; + +#define PMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBS_CFS) + +SND_SOC_DAILINK_DEFS(phyt_machine, + DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-v2")), + DAILINK_COMP_ARRAY(COMP_CODEC("PHYT1002:00", "phytium-hifi-v2")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy"))); + +static struct snd_soc_dai_link phyt_machine_dai[] = { + { + .name = "PHYTIUM HIFI V2", + .stream_name = "PHYTIUM HIFT V2", + .dai_fmt = PMDK_DAI_FMT, + SND_SOC_DAILINK_REG(phyt_machine), + }, +}; + +static struct snd_soc_card phyt_machine_card = { + .name = "PHYT-MIE-V2", + .owner = THIS_MODULE, + .dai_link = phyt_machine_dai, + .num_links = ARRAY_SIZE(phyt_machine_dai), + + .dapm_widgets = phyt_machine_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(phyt_machine_dapm_widgets), + .controls = phyt_machine_controls, + .num_controls = ARRAY_SIZE(phyt_machine_controls), + .dapm_routes = phyt_machine_audio_map, + .num_dapm_routes = ARRAY_SIZE(phyt_machine_audio_map), +}; + +static int phyt_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &phyt_machine_card; + struct device *dev = &pdev->dev; + + card->dev = dev; + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static const struct acpi_device_id phyt_machine_match[] = { + { "PHYT1001", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, phyt_machine_match); + +static struct platform_driver phyt_machine_driver = { + .probe = phyt_machine_probe, + .driver = { + .name = "phyt_machine", + .acpi_match_table = phyt_machine_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, +}; + +module_platform_driver(phyt_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC Phytium Machine V2"); +MODULE_AUTHOR("Yang Xun "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(PHYT_MACHINE_V2_VERSION); -- Gitee From 1bdbd6ecd304caa82fa2e9ed9fb4a42bd34475e3 Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Thu, 9 May 2024 16:56:05 +0800 Subject: [PATCH 039/154] dt-bindings: codec: Add bindings for phytium-codec-v2.0 This patch documents the DT binding for the Phytium codec-v2.0 Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I6915cc55c3dba752ff98b04e0731dacbf740af0b --- .../bindings/sound/phytium,codec-2.0.yaml | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/phytium,codec-2.0.yaml diff --git a/Documentation/devicetree/bindings/sound/phytium,codec-2.0.yaml b/Documentation/devicetree/bindings/sound/phytium,codec-2.0.yaml new file mode 100644 index 0000000000..06b075b21b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/phytium,codec-2.0.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/phytium,codec-2.0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium Codec V2 Controller + +maintainers: + - Dai Jingtao + - Zhou Zheng + +properties: + compatible: + enum: + - phytium,codec-2.0 + + reg: + items: + - first is for physical base address and length of regfile. + - second is for sharemem to send command. + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +examples: + - | + codec_v2: codec@27010800 { + compatible = "phytium,codec-2.0"; + #sound-dai-cells = <0x0>; + reg = <0x0 0x27010800 0x0 0x800>, + < 0x0 0x26fe3100 0x0 0x100>; + interrupts = ; + status = "disabled"; + }; -- Gitee From c3a74e58c4c308d433eb7f64f17d65fc8768793f Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Thu, 9 May 2024 17:15:16 +0800 Subject: [PATCH 040/154] i2s:phytium: Add support for phytium codec-v2 driver This patch adds support for phytium codec-v2 controller. Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I93db58c897bf7b494cfa4c9ec35644d0d35db27e --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 5 + sound/soc/codecs/phytium-codec-v2.c | 739 ++++++++++++++++++++++++++++ sound/soc/codecs/phytium-codec-v2.h | 140 ++++++ 4 files changed, 889 insertions(+) create mode 100644 sound/soc/codecs/phytium-codec-v2.c create mode 100644 sound/soc/codecs/phytium-codec-v2.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 365deb65c1..20bbe1d313 100755 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1037,6 +1037,11 @@ config SND_SOC_PCM512x_SPI select SND_SOC_PCM512x select REGMAP_SPI +config SND_SOC_PHYTIUM_CODEC_V2 + tristate "Phytium Codec V2 driver" + help + Select Y if you want to add Phytium codec V2 driver. + config SND_SOC_RK3328 tristate "Rockchip RK3328 audio CODEC" select REGMAP_MMIO diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ff15be1667..5200ef8751 100755 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -158,6 +158,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o +snd-soc-phytium-codec-v2-objs := phytium-codec-v2.o snd-soc-rk3328-objs := rk3328_codec.o snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o @@ -300,6 +301,8 @@ snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-wsa881x-objs := wsa881x.o snd-soc-zl38060-objs := zl38060.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o +snd-soc-phytium-codec-v2-objs := phytium-codec-v2.o + # Amp snd-soc-max9877-objs := max9877.o snd-soc-max98504-objs := max98504.o @@ -470,6 +473,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o +obj-$(CONFIG_SND_SOC_PHYTIUM_CODEC_V2) += snd-soc-phytium-codec-v2.o obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o @@ -613,6 +617,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o +obj-$(CONFIG_SND_SOC_PHYTIUM_CODEC_V2) += snd-soc-phytium-codec-v2.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/phytium-codec-v2.c b/sound/soc/codecs/phytium-codec-v2.c new file mode 100644 index 0000000000..3a3e063e26 --- /dev/null +++ b/sound/soc/codecs/phytium-codec-v2.c @@ -0,0 +1,739 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium CODEC ALSA SoC Audio driver + * + * Copyright (C) 2024, 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 "phytium-codec-v2.h" + +#define PHYT_CODEC_V2_VERSION "1.0.0" +#define PHYTIUM_RATES (SNDRV_PCM_RATE_192000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_8000_48000) +#define PHYTIUM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_kcontrol_new phyt_snd_controls[] = { + SOC_SINGLE("PCM Volume", PHYTIUM_CODEC_PLAYBACKE_VOL, 0, 0xc0, 0), + SOC_SINGLE("Playback Volume1", PHYTIUM_CODEC_PLAYBACKE_OUT1_VOL, 0, 0x24, 0), + SOC_SINGLE("Playback Volume2", PHYTIUM_CODEC_PLAYBACKE_OUT2_VOL, 0, 0x24, 0), + + SOC_SINGLE("Capture Digital Volume", PHYTIUM_CODEC_CAPTURE_VOL, 0, 0xc0, 0), + SOC_SINGLE("Mic PGA Volume", PHYTIUM_CODEC_CAPTURE_IN1_VOL, 0, 8, 0), +}; + +/* + * DAPM Controls + */ + +/* Input Mux */ +static const char * const phyt_mux_sel[] = { + "Line 1", "Line 2"}; + +static const struct soc_enum phyt_mux_enum = + SOC_ENUM_SINGLE(PHYTIUM_CODEC_INMUX_SEL, 0, + ARRAY_SIZE(phyt_mux_sel), + phyt_mux_sel); + +static const struct snd_kcontrol_new phyt_pga_controls = + SOC_DAPM_ENUM("Route", phyt_mux_enum); + +/* dapm widgets */ +static const struct snd_soc_dapm_widget phyt_dapm_widgets[] = { + /* Input Signal */ + SND_SOC_DAPM_INPUT("INPUT1"), + SND_SOC_DAPM_INPUT("INPUT2"), + + /* Input Mux */ + SND_SOC_DAPM_MUX("Input Mux", PHYTIUM_CODEC_INMUX_ENABLE, 0, 0, &phyt_pga_controls), + + /* Input ADC */ + SND_SOC_DAPM_ADC("Input ADC", "Capture", PHYTIUM_CODEC_ADC_ENABLE, 0, 0), + + /* Output DAC */ + SND_SOC_DAPM_DAC("Output DAC", "Playback", PHYTIUM_CODEC_DAC_ENABLE, 0, 0), + + /* Output Signal */ + SND_SOC_DAPM_OUTPUT("OUTPUT1"), + SND_SOC_DAPM_OUTPUT("OUTPUT2"), +}; + +static const struct snd_soc_dapm_route phyt_dapm_routes[] = { + { "Input Mux", "Line 1", "INPUT1"}, + { "Input Mux", "Line 2", "INPUT2"}, + + { "Input ADC", NULL, "Input Mux"}, + + { "OUTPUT1", NULL, "Output DAC"}, + { "OUTPUT2", NULL, "Output DAC"}, +}; + +static void phyt_codec_show_status(uint8_t status) +{ + switch (status) { + case 0: + pr_err("success\n"); + break; + case 2: + pr_err("device busy\n"); + break; + case 3: + pr_err("read/write error\n"); + break; + case 4: + pr_err("no device\n"); + break; + default: + pr_err("unknown error: %d\n", status); + break; + } +} + +int phyt_codec_msg_set_cmd(struct phytium_codec *priv) +{ + struct phytcodec_cmd *ans_msg; + int timeout = 5000, ret = 0; + + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_AP2RV_INT_STATE, SEND_INTR); + + ans_msg = priv->sharemem_base; + + while (timeout && (ans_msg->complete == PHYTCODEC_COMPLETE_NOT_READY + || ans_msg->complete == PHYTCODEC_COMPLETE_GOING)) { + udelay(200); + timeout--; + } + + if (timeout == 0) { + dev_err(priv->dev, "failed to receive msg, timeout\n"); + ret = -EINVAL; + } else if (ans_msg->complete >= PHYTCODEC_COMPLETE_GENERIC_ERROR) { + dev_err(priv->dev, "receive msg; generic_error, error code:%d\n", + ans_msg->complete); + ret = -EINVAL; + } else if (ans_msg->complete == PHYTCODEC_COMPLETE_SUCCESS) { + dev_dbg(priv->dev, "receive msg successfully\n"); + } + + if (ans_msg->complete != PHYTCODEC_COMPLETE_SUCCESS) + phyt_codec_show_status(ans_msg->status); + return ret; +} + +static int phyt_cmd(struct snd_soc_component *component, + unsigned int cmd) +{ + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + int ret = 0; + + msg->reserved = 0; + msg->seq = 0; + msg->cmd_id = PHYTCODEC_MSG_CMD_SET; + msg->cmd_subid = cmd; + msg->complete = 0; + ret = phyt_codec_msg_set_cmd(priv); + if (ret < 0) { + dev_err(priv->dev, "set cmd_subid 0x%x failed\n", cmd); + ret = -EINVAL; + goto error; + } +error: + return ret; +} + +static int phyt_pm_cmd(struct snd_soc_component *component, + unsigned int cmd) +{ + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + uint16_t total_regs_len; + uint8_t *regs; + int ret = 0; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); + + msg->reserved = 0; + msg->seq = 0; + msg->cmd_id = PHYTCODEC_MSG_CMD_SET; + msg->cmd_subid = cmd; + msg->complete = 0; + msg->cmd_para.phytcodec_reg.cnt = 0; + if (cmd == PHYTCODEC_MSG_CMD_SET_RESUME) + memcpy(msg->cmd_para.phytcodec_reg.regs, priv->regs, REG_SH_LEN); + ret = phyt_codec_msg_set_cmd(priv); + if (ret < 0) { + dev_err(priv->dev, "set cmd_subid 0x%x failed\n", cmd); + ret = -EINVAL; + goto error; + } + total_regs_len = msg->cmd_para.phytcodec_reg.total_regs_len; + + if (cmd == PHYTCODEC_MSG_CMD_SET_SUSPEND) { + regs = kmalloc(total_regs_len, GFP_KERNEL); + priv->regs = regs; + while (total_regs_len > REG_SH_LEN * msg->cmd_para.phytcodec_reg.cnt) { + memcpy(regs, msg->cmd_para.phytcodec_reg.regs, REG_SH_LEN); + regs += REG_SH_LEN; + msg->complete = 0; + ret = phyt_codec_msg_set_cmd(priv); + if (ret < 0) { + dev_err(priv->dev, "set cmd_subid 0x%x failed\n", cmd); + ret = -EINVAL; + goto error; + } + } + memcpy(regs, msg->cmd_para.phytcodec_reg.regs, + total_regs_len - REG_SH_LEN * (msg->cmd_para.phytcodec_reg.cnt - 1)); + } else if (cmd == PHYTCODEC_MSG_CMD_SET_RESUME) { + regs = priv->regs; + while (total_regs_len > REG_SH_LEN * msg->cmd_para.phytcodec_reg.cnt) { + regs += REG_SH_LEN; + memcpy(msg->cmd_para.phytcodec_reg.regs, regs, REG_SH_LEN); + msg->complete = 0; + ret = phyt_codec_msg_set_cmd(priv); + if (ret < 0) { + dev_err(priv->dev, "set cmd_subid 0x%x failed\n", cmd); + ret = -EINVAL; + goto error; + } + } + kfree(priv->regs); + } +error: + return ret; +} + +static int phyt_show_registers(struct phytium_codec *priv) +{ + struct phytcodec_cmd *msg = priv->sharemem_base; + int ret = 0, i; + + msg->reserved = 0; + msg->seq = 0; + msg->cmd_id = PHYTCODEC_MSG_CMD_GET; + msg->cmd_subid = 0; + msg->complete = 0; + ret = phyt_codec_msg_set_cmd(priv); + if (ret < 0) { + dev_err(priv->dev, "failed to get codec registers\n"); + ret = -EINVAL; + goto error; + } else { + dev_dbg(priv->dev, "show codec registers\n"); + for (i = 0; i < msg->len && i < 56; i++) { + dev_dbg(priv->dev, "%d ", msg->cmd_para.para[i]); + if (i % 16 == 0) + dev_dbg(priv->dev, "\n"); + } + } +error: + return ret; +} + +static int phyt_probe(struct snd_soc_component *component) +{ + return phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_PROBE); +} + +static void phyt_remove(struct snd_soc_component *component) +{ + phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_REMOVE); +} + +static int phyt_suspend(struct snd_soc_component *component) +{ + return phyt_pm_cmd(component, PHYTCODEC_MSG_CMD_SET_SUSPEND); +} + +static int phyt_resume(struct snd_soc_component *component) +{ + return phyt_pm_cmd(component, PHYTCODEC_MSG_CMD_SET_RESUME); +} + +static int phyt_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int ret; + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); + msg->cmd_para.para[0] = priv->channels/2; + + switch (level) { + case SND_SOC_BIAS_ON: + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_ON); + break; + + case SND_SOC_BIAS_PREPARE: + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_PREPARE); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_STANDBY); + else + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_STANDBY); + break; + + case SND_SOC_BIAS_OFF: + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_OFF); + break; + } + + return ret; +} + +static const struct snd_soc_component_driver phyt_component_driver = { + .probe = phyt_probe, + .remove = phyt_remove, + .suspend = phyt_suspend, + .resume = phyt_resume, + .set_bias_level = phyt_set_bias_level, + .controls = phyt_snd_controls, + .num_controls = ARRAY_SIZE(phyt_snd_controls), + .dapm_widgets = phyt_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(phyt_dapm_widgets), + .dapm_routes = phyt_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(phyt_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int phyt_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + int ret; + struct snd_soc_component *component = dai->component; + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); + msg->cmd_para.para[0] = (uint8_t)direction; + if (mute) + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_MUTE); + else + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_UNMUTE); + + return ret; +} + +static int phyt_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + struct snd_soc_component *component = dai->component; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_STARTUP); + else + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_STARTUP_RC); + + return ret; +} + +static void phyt_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + struct snd_soc_component *component = dai->component; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_SHUTDOWN); + else + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_SHUTDOWN_RC); +} + +static int phyt_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int wl, ret = 0; + struct snd_soc_component *component = dai->component; + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + + priv->channels = params_channels(params); + switch (params_width(params)) { + case 16: + wl = 3; + break; + case 18: + wl = 2; + break; + case 20: + wl = 1; + break; + case 24: + wl = 0; + break; + case 32: + wl = 4; + break; + default: + ret = -EINVAL; + goto error; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_write(component, PHYTIUM_CODEC_HW_PARAM, wl); + else + snd_soc_component_write(component, PHYTIUM_CODEC_HW_PARAM_RC, wl); + +error: + return ret; +} + +static int phyt_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + int ret; + struct snd_soc_component *component = codec_dai->component; + + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) + return -EINVAL; + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) + return -EINVAL; + + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) + return -EINVAL; + + ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_DAI_FMT); + + return ret; +} + +static const struct snd_soc_dai_ops phyt_dai_ops = { + .startup = phyt_startup, + .shutdown = phyt_shutdown, + .hw_params = phyt_hw_params, + .mute_stream = phyt_mute, + .set_fmt = phyt_set_dai_fmt, +}; + +static struct snd_soc_dai_driver phyt_dai = { + .name = "phytium-hifi-v2", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = PHYTIUM_RATES, + .formats = PHYTIUM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = PHYTIUM_RATES, + .formats = PHYTIUM_FORMATS, + }, + .ops = &phyt_dai_ops, + .symmetric_rates = 1, +}; + +static const struct regmap_config phyt_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = REG_MAX, + .cache_type = REGCACHE_NONE, +}; + +void phyt_enable_debug(struct phytium_codec *priv) +{ + u32 reg; + + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); + phyt_writel_reg(priv->regfile_base, + PHYTIUM_CODEC_DEBUG, reg | PHYTIUM_CODEC_DEBUG_ENABLE); +} + +void phyt_disable_debug(struct phytium_codec *priv) +{ + u32 reg; + + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); + reg &= ~PHYTIUM_CODEC_DEBUG_ENABLE; + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG, reg); +} + +void phyt_enable_alive(struct phytium_codec *priv) +{ + u32 reg; + + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); + phyt_writel_reg(priv->regfile_base, + PHYTIUM_CODEC_DEBUG, reg | PHYTIUM_CODEC_ALIVE_ENABLE); +} + +void phyt_disable_alive(struct phytium_codec *priv) +{ + u32 reg; + + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); + reg &= ~PHYTIUM_CODEC_ALIVE_ENABLE; + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG, reg); +} + +void phyt_heartbeat(struct phytium_codec *priv) +{ + u32 reg; + + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); + phyt_writel_reg(priv->regfile_base, + PHYTIUM_CODEC_DEBUG, reg | PHYTIUM_CODEC_HEARTBIT_VAL); +} + +static void phyt_timer_handle(struct timer_list *t) +{ + struct phytium_codec *priv = from_timer(priv, t, timer); + + if (priv->alive_enabled && priv->heartbeat) + priv->heartbeat(priv); + + mod_timer(&priv->timer, jiffies + msecs_to_jiffies(2000)); +} + +static ssize_t debug_show(struct device *dev, struct device_attribute *da, char *buf) +{ + struct phytium_codec *priv = dev_get_drvdata(dev); + ssize_t ret; + u32 reg; + + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); + ret = sprintf(buf, "%x\n", reg); + + return ret; +} + +static ssize_t debug_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t size) +{ + struct phytium_codec *priv = dev_get_drvdata(dev); + u8 loc, dis_en, status = 0; + char *p; + char *token; + long value; + u32 reg; + + dev_info(dev, "first number is debug/alive/register, the second number is disable/enable"); + dev_info(dev, "echo 2 1 > debug, print all codec register"); + + p = kmalloc(size, GFP_KERNEL); + strscpy(p, buf, sizeof(p)); + token = strsep(&p, " "); + if (!token) + return -EINVAL; + status = kstrtol(token, 0, &value); + if (status) + return status; + loc = (u8)value; + + token = strsep(&p, " "); + if (!token) + return -EINVAL; + status = kstrtol(token, 0, &value); + if (status) + return status; + dis_en = value; + + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); + if (loc == 1) { + if (dis_en == 1) { + priv->alive_enabled = true; + reg |= BIT(loc); + } else if (dis_en == 0) { + priv->alive_enabled = false; + reg &= ~BIT(loc); + } + } else if (loc == 0) { + if (dis_en == 1) { + priv->debug_enabled = true; + reg |= BIT(loc); + } else if (dis_en == 0) { + priv->debug_enabled = false; + reg &= ~BIT(loc); + } + } else if (loc == 2) + if (dis_en == 1) + phyt_show_registers(priv); + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG, reg); + kfree(p); + + return size; +} + +static DEVICE_ATTR_RW(debug); + +static struct attribute *phyt_codec_device_attr[] = { + &dev_attr_debug.attr, + NULL, +}; + +static const struct attribute_group phyt_codec_device_group = { + .attrs = phyt_codec_device_attr, +}; + +static int phyt_get_channels(struct phytium_codec *priv) +{ + struct phytcodec_cmd *msg = priv->sharemem_base; + int ret = 0; + uint8_t channels; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); + msg->reserved = 0; + msg->seq = 0; + msg->cmd_id = PHYTCODEC_MSG_CMD_GET; + msg->cmd_subid = PHYTCODEC_MSG_CMD_GET_CHANNELS; + msg->complete = 0; + + ret = phyt_codec_msg_set_cmd(priv); + if (ret < 0) { + dev_err(priv->dev, "failed to get codec channels\n"); + return -EINVAL; + } + channels = msg->cmd_para.para[0] * 2; + return channels; +} + +static int phyt_codec_probe(struct platform_device *pdev) +{ + struct phytium_codec *priv; + struct resource *res; + int ret; + struct device *dev; + + dev = &pdev->dev; + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "failed to alloc struct phytium_codec\n"); + ret = -ENOMEM; + goto failed_alloc_phytium_codec; + } + + dev_set_drvdata(dev, priv); + priv->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regfile_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->regfile_base)) { + dev_err(&pdev->dev, "failed to ioremap resource0\n"); + ret = PTR_ERR(priv->regfile_base); + goto failed_ioremap_res0; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->sharemem_base = devm_ioremap_wc(&pdev->dev, res->start, resource_size(res)); + if (IS_ERR(priv->sharemem_base)) { + dev_err(&pdev->dev, "failed to ioremap resource1\n"); + ret = PTR_ERR(priv->sharemem_base); + goto failed_ioremap_res1; + } + + priv->regmap = devm_regmap_init_mmio(dev, priv->regfile_base, + &phyt_codec_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(dev, "failed to init regmap\n"); + ret = PTR_ERR(priv->regmap); + goto failed_regmap_init; + } + + phyt_disable_debug(priv); + phyt_disable_alive(priv); + priv->debug_enabled = false; + priv->alive_enabled = false; + priv->heartbeat = phyt_heartbeat; + priv->timer.expires = jiffies + msecs_to_jiffies(10000); + timer_setup(&priv->timer, phyt_timer_handle, 0); + add_timer(&priv->timer); + + if (sysfs_create_group(&pdev->dev.kobj, &phyt_codec_device_group)) + dev_warn(dev, "failed to create sysfs\n"); + + phyt_dai.playback.channels_max = phyt_get_channels(priv); + phyt_dai.capture.channels_max = phyt_dai.playback.channels_max; + + ret = devm_snd_soc_register_component(dev, &phyt_component_driver, + &phyt_dai, 1); + if (ret != 0) { + dev_err(dev, "not able to register codec dai\n"); + goto failed_register_com; + } + + return 0; +failed_register_com: +failed_regmap_init: +failed_ioremap_res1: +failed_ioremap_res0: +failed_alloc_phytium_codec: + return ret; +} + +static int phyt_codec_remove(struct platform_device *pdev) +{ + struct phytium_codec *priv = dev_get_drvdata(&pdev->dev); + + sysfs_remove_group(&pdev->dev.kobj, &phyt_codec_device_group); + del_timer(&priv->timer); + return 0; +} + +static const struct of_device_id phyt_codec_of_match[] = { + { .compatible = "phytium,codec-2.0", }, + { }, +}; +MODULE_DEVICE_TABLE(of, phyt_codec_of_match); + +static const struct acpi_device_id phyt_codec_acpi_match[] = { + { "PHYT1002", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, phyt_codec_acpi_match); + +static struct platform_driver phyt_codec_driver = { + .probe = phyt_codec_probe, + .remove = phyt_codec_remove, + .driver = { + .name = "phytium-codec-v2", + .of_match_table = of_match_ptr(phyt_codec_of_match), + .acpi_match_table = phyt_codec_acpi_match, + }, +}; + +module_platform_driver(phyt_codec_driver); +MODULE_DESCRIPTION("Phytium CODEC V2 Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yang Xun "); +MODULE_VERSION(PHYT_CODEC_V2_VERSION); diff --git a/sound/soc/codecs/phytium-codec-v2.h b/sound/soc/codecs/phytium-codec-v2.h new file mode 100644 index 0000000000..2dff1a28ee --- /dev/null +++ b/sound/soc/codecs/phytium-codec-v2.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Phytium CODEC ALSA SoC Audio driver + * + * Copyright (C) 2024, Phytium Technology Co., Ltd. + * + */ + +#ifndef __PHYTIUM_CODEC_V2_H +#define __PHYTIUM_CODEC_V2_H + +#include +#include + +/****************register *****************/ +#define PHYTIUM_CODEC_AP2RV_INT_MASK 0x20 +#define PHYTIUM_CODEC_AP2RV_INT_STATE 0x24 + #define SEND_INTR (1 << 4) + +#define PHYTIUM_CODEC_DEBUG 0x58 + #define PHYTIUM_CODEC_DEBUG_ENABLE (1 << 0) + #define PHYTIUM_CODEC_ALIVE_ENABLE (1 << 1) + #define PHYTIUM_CODEC_HEARTBIT_VAL (1 << 2) + +#define PHYTIUM_CODEC_PLAYBACKE_VOL 0X500 +#define PHYTIUM_CODEC_PLAYBACKE_OUT1_VOL 0X504 +#define PHYTIUM_CODEC_PLAYBACKE_OUT2_VOL 0X508 +#define PHYTIUM_CODEC_CAPTURE_VOL 0X50c +#define PHYTIUM_CODEC_CAPTURE_IN1_VOL 0X510 +#define PHYTIUM_CODEC_HW_PARAM 0X514 +#define PHYTIUM_CODEC_HW_PARAM_RC 0X518 +#define PHYTIUM_CODEC_INMUX_ENABLE 0X51c +#define PHYTIUM_CODEC_INMUX_SEL 0X520 +#define PHYTIUM_CODEC_ADC_ENABLE 0X524 +#define PHYTIUM_CODEC_DAC_ENABLE 0x528 +#define PHYTIUM_CODEC_INT_STATUS 0x560 + #define PIPE_NUM 11 + +#define REG_MAX 0x52c +#define REG_SH_LEN 52 + +/****************register end *****************/ + +#define PHYTIUM_CODEC_LSD_ID 0x701 + +enum phytcodec_msg_cmd_id { + PHYTCODEC_MSG_CMD_DEFAULT = 0, + PHYTCODEC_MSG_CMD_SET, + PHYTCODEC_MSG_CMD_GET, + PHYTCODEC_MSG_CMD_DATA, + PHYTCODEC_MSG_CMD_REPORT, + PHYTCODEC_MSG_PROTOCOL = 0x10, +}; + +enum phytcodec_get_subid { + PHYTCODEC_MSG_CMD_GET_CHANNELS = 0, +}; + +enum phytcodec_set_subid { + PHYTCODEC_MSG_CMD_SET_PROBE = 0, + PHYTCODEC_MSG_CMD_SET_REMOVE, + PHYTCODEC_MSG_CMD_SET_SUSPEND, + PHYTCODEC_MSG_CMD_SET_RESUME, + PHYTCODEC_MSG_CMD_SET_STARTUP, + PHYTCODEC_MSG_CMD_SET_STARTUP_RC, + PHYTCODEC_MSG_CMD_SET_MUTE, + PHYTCODEC_MSG_CMD_SET_UNMUTE, + PHYTCODEC_MSG_CMD_SET_DAI_FMT, + PHYTCODEC_MSG_CMD_SET_BIAS_ON, + PHYTCODEC_MSG_CMD_SET_BIAS_OFF, + PHYTCODEC_MSG_CMD_SET_BIAS_PREPARE, + PHYTCODEC_MSG_CMD_SET_BIAS_STANDBY, + PHYTCODEC_MSG_CMD_SET_SHUTDOWN, + PHYTCODEC_MSG_CMD_SET_SHUTDOWN_RC, +}; + +enum phytcodec_complete { + PHYTCODEC_COMPLETE_NOT_READY = 0, + PHYTCODEC_COMPLETE_SUCCESS, + PHYTCODEC_COMPLETE_GOING, + PHYTCODEC_COMPLETE_GENERIC_ERROR = 0x10, + PHYTCODEC_COMPLETE_TYPE_NOT_SUPPORTED, + PHYTCODEC_COMPLETE_CMD_NOT_SUPPORTED, + PHYTCODEC_COMPLETE_INVALID_PARAMETERS, +}; + +struct phytcodec_reg { + uint16_t total_regs_len; + uint8_t one_reg_len; + uint8_t cnt; + uint8_t regs[52]; +}; + +struct phytcodec_cmd { + uint8_t reserved; + uint8_t seq; + uint8_t cmd_id; + uint8_t cmd_subid; + uint16_t len; + uint8_t status; + uint8_t complete; + union { + uint8_t para[56]; + struct phytcodec_reg phytcodec_reg; + } cmd_para; +}; + +struct phytium_codec { + void __iomem *regfile_base; + void __iomem *sharemem_base; + struct device *dev; + uint8_t *regs; + uint8_t channels; + + struct regmap *regmap; + struct completion comp; + bool deemph; + + bool debug_enabled; + bool alive_enabled; + struct timer_list timer; + void (*heartbeat)(struct phytium_codec *priv); +}; + +static inline unsigned int +phyt_readl_reg(void *base, uint32_t offset) +{ + unsigned int data; + + data = readl(base + offset); + + return data; +} + +static inline void +phyt_writel_reg(void *base, uint32_t offset, uint32_t data) +{ + writel(data, base + offset); +} +#endif -- Gitee From dc8c7a373bd6679a1d6587b3779a92bae30bab14 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Tue, 14 Jan 2025 02:24:11 +0000 Subject: [PATCH 041/154] i2s-v2: phytium: Increase the waiting time The origin waiting duration is at a critical value, which has a probability of causing a waiting timeout error. Therefore, it is necessary to increase the waiting time. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng sharemem_mutex); memcpy(priv->sharemem_base, msg, sizeof(struct phyti2s_cmd)); -- Gitee From 43d7ce19714a44849e6b6d0c0a6d840ceade0ff8 Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Tue, 14 Jan 2025 10:10:33 +0800 Subject: [PATCH 042/154] i2s: phytium: Eliminate some of the misinfomation There are two irqs in i2s, the second of which is used for headphone detection. But dp-i2s will not use it. This patch change the method to get irq_id, to avoid error message "IRQ index 1 not found" when loading dp-i2s driver. Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: I87f1de5c9aee9ae1077d86eab7ae015502c48755 --- sound/soc/phytium/phytium-i2s-v2.c | 11 ++++++++--- sound/soc/phytium/phytium_i2s.c | 6 +++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c index 2bd56f4e57..ab260b95c9 100644 --- a/sound/soc/phytium/phytium-i2s-v2.c +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -119,12 +119,16 @@ static void phyt_pcm_free(struct snd_soc_component *component, static int phyt_pcm_component_probe(struct snd_soc_component *component) { + struct phytium_i2s *priv = snd_soc_component_get_drvdata(component); struct snd_soc_card *card = component->card; int ret; + if (priv->insert < 0) + return 0; + ret = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET, - &hs_jack, hs_jack_pins, - ARRAY_SIZE(hs_jack_pins)); + &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret < 0) { dev_err(component->dev, "Cannot create jack\n"); return ret; @@ -1045,7 +1049,8 @@ static int phyt_i2s_probe(struct platform_device *pdev) goto failed_request_irq; } - gpio_irq = platform_get_irq(pdev, 1); + gpio_irq = platform_get_irq_optional(pdev, 1); + priv->insert = -1; if (gpio_irq > 0) { phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_GPIO_PORTA_EOI, BIT(0)); ret = phyt_i2s_disable_gpioint(priv); diff --git a/sound/soc/phytium/phytium_i2s.c b/sound/soc/phytium/phytium_i2s.c index c77eeb5443..c6015f5081 100755 --- a/sound/soc/phytium/phytium_i2s.c +++ b/sound/soc/phytium/phytium_i2s.c @@ -503,9 +503,13 @@ static int phytium_i2s_resume(struct snd_soc_component *component) static int phytium_i2s_component_probe(struct snd_soc_component *component) { + struct i2s_phytium *dev = snd_soc_component_get_drvdata(component); struct snd_soc_card *card = component->card; int ret; + if (!dev->detect) + return 0; + ret = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET, &hs_jack, hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); @@ -1486,7 +1490,7 @@ static int phytium_i2s_probe(struct platform_device *pdev) if (i2s->irq_id < 0) return i2s->irq_id; - i2s->gpio_irq_id = platform_get_irq(pdev, 1); + i2s->gpio_irq_id = platform_get_irq_optional(pdev, 1); i2s->detect = true; if (i2s->gpio_irq_id < 0) -- Gitee From c6871aaeb4e05c055d1bbaeaf164850b58a69b1d Mon Sep 17 00:00:00 2001 From: Chen Zhenhua Date: Thu, 9 Jan 2025 16:13:46 +0800 Subject: [PATCH 043/154] usb: phytium: Add Copyright information This patch add Copyright (c) 2022, Phytium Technology, Co., Ltd. Mainline: Open-Source Signed-off-by: Chen Zhenhua Signed-off-by: Wang Yinfeng Change-Id: I2e83f1bf810773f45f3aef74d1edc3e8771b7e61 --- drivers/usb/phytium/core.c | 4 ++++ drivers/usb/phytium/core.h | 4 ++++ drivers/usb/phytium/dma.c | 4 ++++ drivers/usb/phytium/dma.h | 4 ++++ drivers/usb/phytium/gadget.c | 4 ++++ drivers/usb/phytium/gadget.h | 4 ++++ drivers/usb/phytium/host.c | 4 ++++ drivers/usb/phytium/host_api.h | 4 ++++ drivers/usb/phytium/hw-regs.h | 5 +++++ drivers/usb/phytium/pci.c | 4 ++++ drivers/usb/phytium/platform.c | 4 ++++ 11 files changed, 45 insertions(+) diff --git a/drivers/usb/phytium/core.c b/drivers/usb/phytium/core.c index c0182c0770..ed2d1d661c 100644 --- a/drivers/usb/phytium/core.c +++ b/drivers/usb/phytium/core.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include "core.h" int phytium_core_reset(struct phytium_cusb *config, bool skip_wait) diff --git a/drivers/usb/phytium/core.h b/drivers/usb/phytium/core.h index f563672cca..0237476bea 100644 --- a/drivers/usb/phytium/core.h +++ b/drivers/usb/phytium/core.h @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __PHYTIUM_CORE_H__ #define __PHYTIUM_CORE_H__ diff --git a/drivers/usb/phytium/dma.c b/drivers/usb/phytium/dma.c index 2e0d6df3eb..55742bf591 100644 --- a/drivers/usb/phytium/dma.c +++ b/drivers/usb/phytium/dma.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include #include "core.h" diff --git a/drivers/usb/phytium/dma.h b/drivers/usb/phytium/dma.h index 073a078c12..ed2fd40690 100644 --- a/drivers/usb/phytium/dma.h +++ b/drivers/usb/phytium/dma.h @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __PHYTIUM_DMA_H__ #define __PHYTIUM_DMA_H__ diff --git a/drivers/usb/phytium/gadget.c b/drivers/usb/phytium/gadget.c index afcb846c8d..0c07819b97 100644 --- a/drivers/usb/phytium/gadget.c +++ b/drivers/usb/phytium/gadget.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include "gadget.h" #include "dma.h" diff --git a/drivers/usb/phytium/gadget.h b/drivers/usb/phytium/gadget.h index d87b55ade7..6d7bd4b328 100644 --- a/drivers/usb/phytium/gadget.h +++ b/drivers/usb/phytium/gadget.h @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __PHYTIUM_GADGET_H_ #define __PHYTIUM_GADGET_H_ diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c index f0da060cf0..90bab6b05d 100644 --- a/drivers/usb/phytium/host.c +++ b/drivers/usb/phytium/host.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include #include diff --git a/drivers/usb/phytium/host_api.h b/drivers/usb/phytium/host_api.h index b99d2b4980..6003954eb1 100644 --- a/drivers/usb/phytium/host_api.h +++ b/drivers/usb/phytium/host_api.h @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __PHYTIUM_HOST_API_H_ #define __PHYTIUM_HOST_API_H_ diff --git a/drivers/usb/phytium/hw-regs.h b/drivers/usb/phytium/hw-regs.h index 8da9f8e9b9..8200ac3cf7 100644 --- a/drivers/usb/phytium/hw-regs.h +++ b/drivers/usb/phytium/hw-regs.h @@ -1,4 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __LINUX_PHYTIUM_HW_REGS #define __LINUX_PHYTIUM_HW_REGS diff --git a/drivers/usb/phytium/pci.c b/drivers/usb/phytium/pci.c index 964fd67bfc..72adf1aa39 100644 --- a/drivers/usb/phytium/pci.c +++ b/drivers/usb/phytium/pci.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include #include diff --git a/drivers/usb/phytium/platform.c b/drivers/usb/phytium/platform.c index bcf5859a4d..5d3ec8793d 100644 --- a/drivers/usb/phytium/platform.c +++ b/drivers/usb/phytium/platform.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include //#include -- Gitee From 9784ac48c67e0d782c0ca0b9ee555d67ad825d20 Mon Sep 17 00:00:00 2001 From: Chen Zhenhua Date: Tue, 12 Dec 2023 12:45:21 +0800 Subject: [PATCH 044/154] usb: phytium: Fix USB driver compilation warning issue In the switch statement, if fallthrough is required, it needs to be clearly indicated, otherwise compilation will report a warning or error. Mainline: Open-Source Signed-off-by: Chen Zhenhua Signed-off-by: Wang Yinfeng Change-Id: Ifa7e922b4ac7158580e16debe07e505e38187e92 --- drivers/usb/phytium/host.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c index 90bab6b05d..4406c00de9 100644 --- a/drivers/usb/phytium/host.c +++ b/drivers/usb/phytium/host.c @@ -2083,6 +2083,7 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, int spee speed == USB_SPEED_FULL ? "" : "micro"); break; } + fallthrough; /* fall through */ case USB_SPEED_LOW: if (usb_endpoint_xfer_int(&desc) || usb_endpoint_xfer_isoc(&desc)) { -- Gitee From 6fd040aeea178afabb21f4c846f2855d55b2a2a6 Mon Sep 17 00:00:00 2001 From: Chen Zhenhua Date: Sun, 28 Apr 2024 13:57:35 +0800 Subject: [PATCH 045/154] usb: phytium: Eliminate warning information Eliminate warnings by using checkpatch.pl script to checkout code on different kernel versions. Mainline: Open-Source Signed-off-by: Chen Zhenhua Signed-off-by: Wang Yinfeng Change-Id: Id2d5141db8b34eaa2a4d23ca1bb6c5c265de0169 --- drivers/usb/phytium/dma.c | 6 +++--- drivers/usb/phytium/dma.h | 2 +- drivers/usb/phytium/host.c | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/usb/phytium/dma.c b/drivers/usb/phytium/dma.c index 55742bf591..b61b02c1dc 100644 --- a/drivers/usb/phytium/dma.c +++ b/drivers/usb/phytium/dma.c @@ -420,7 +420,7 @@ static int32_t phytium_dma_channelRelease(struct DMA_CONTROLLER *priv, struct DM } } - channel->status = DMA_STATUS_UNKNOW; + channel->status = DMA_STATUS_UNKNOWN; return 0; } @@ -650,7 +650,7 @@ static enum DMA_Status phytium_dma_getChannelStatus(struct DMA_CONTROLLER *priv, uint32_t ep_cmd, ep_sts; if (!priv || !channel) - return DMA_STATUS_UNKNOW; + return DMA_STATUS_UNKNOWN; if (channel->status >= DMA_STATUS_BUSY) { phytium_write32(&priv->regs->ep_sel, channel->isDirTx | channel->hwUsbEppNum); @@ -696,7 +696,7 @@ static int32_t phytium_dma_channelAbort(struct DMA_CONTROLLER *priv, struct DMA_ } } } - if (channel->status != DMA_STATUS_UNKNOW) + if (channel->status != DMA_STATUS_UNKNOWN) channel->status = DMA_STATUS_FREE; return 0; diff --git a/drivers/usb/phytium/dma.h b/drivers/usb/phytium/dma.h index ed2fd40690..3d58e06bc1 100644 --- a/drivers/usb/phytium/dma.h +++ b/drivers/usb/phytium/dma.h @@ -65,7 +65,7 @@ struct DMA_Trb { }; enum DMA_Status { - DMA_STATUS_UNKNOW, + DMA_STATUS_UNKNOWN, DMA_STATUS_FREE, DMA_STATUS_ABORT, DMA_STATUS_BUSY, diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c index 4406c00de9..9f6c5fab9c 100644 --- a/drivers/usb/phytium/host.c +++ b/drivers/usb/phytium/host.c @@ -15,7 +15,7 @@ #define DRV_NAME "phytium_usb" -#define HOST_GENERIC_EP_CONTROLL 0x00 +#define HOST_GENERIC_EP_CONTROL 0x00 #define HOST_GENERIC_EP_ISOC 0x01 #define HOST_GENERIC_EP_BULK 0x02 #define HOST_GENERIC_EP_INT 0x03 @@ -799,7 +799,7 @@ static void hostStartReq(struct HOST_CTRL *priv, struct HOST_REQ *req) case USB_ENDPOINT_XFER_CONTROL: usbReq = getNextReq(hostEp); - priv->in[HOST_GENERIC_EP_CONTROLL].scheduledUsbHEp = hostEp; + priv->in[HOST_GENERIC_EP_CONTROL].scheduledUsbHEp = hostEp; priv->ep0State = HOST_EP0_STAGE_SETUP; hostEpPriv->currentHwEp = hostEpPriv->genericHwEp; hostEpPriv->genericHwEp->scheduledUsbHEp = hostEp; @@ -984,7 +984,7 @@ static int32_t hostEp0Irq(struct HOST_CTRL *priv, uint8_t isIn) if (!priv) return ret; - hwEp = isIn ? &priv->in[HOST_GENERIC_EP_CONTROLL] : &priv->out[HOST_GENERIC_EP_CONTROLL]; + hwEp = isIn ? &priv->in[HOST_GENERIC_EP_CONTROL] : &priv->out[HOST_GENERIC_EP_CONTROL]; hostEp = hwEp->scheduledUsbHEp; usbHEpPriv = (struct HOST_EP_PRIV *)hostEp->hcPriv; @@ -1020,13 +1020,13 @@ static int32_t hostEp0Irq(struct HOST_CTRL *priv, uint8_t isIn) switch (priv->ep0State) { case HOST_EP0_STAGE_IN: pr_debug("Ep0 Data IN\n"); - usbHEpPriv->currentHwEp = &priv->out[HOST_GENERIC_EP_CONTROLL]; + usbHEpPriv->currentHwEp = &priv->out[HOST_GENERIC_EP_CONTROL]; usbReq->actualLength = length; priv->ep0State = HOST_EP0_STAGE_STATUSOUT; break; case HOST_EP0_STAGE_OUT: pr_debug("Ep0 Data OUT\n"); - usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROLL]; + usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROL]; usbReq->actualLength = length; priv->ep0State = HOST_EP0_STAGE_STATUSIN; break; @@ -1035,12 +1035,12 @@ static int32_t hostEp0Irq(struct HOST_CTRL *priv, uint8_t isIn) if (!usbReq->setup->wLength) { pr_debug("EP0_STAGE_STATUSIN\n"); priv->ep0State = HOST_EP0_STAGE_STATUSIN; - usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROLL]; + usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROL]; break; } else if (usbReq->setup->bRequestType & USB_DIR_IN) { pr_debug("EP0_STAGE_STAGE_IN\n"); priv->ep0State = HOST_EP0_STAGE_IN; - usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROLL]; + usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROL]; nextStage = 1; break; } -- Gitee From 9521517c3fcab7d7c8163ed164f6cfb4d91e3e72 Mon Sep 17 00:00:00 2001 From: Chen Zhenhua Date: Sun, 28 Apr 2024 14:11:13 +0800 Subject: [PATCH 046/154] usb: phytium: Fix the incorrect return value in hostEp0Irq function The patch fixes the issue of incorrect return value in the hostEp0Irq function, Sometimes it is necessary to return a value of 1, but always return 0 can cause USB communication error. Mainline: Open-Source Signed-off-by: Chen Zhenhua Signed-off-by: Wang Yinfeng Change-Id: I4e522ab201d396a41355c6059a0a4ed5aaf92e9f --- drivers/usb/phytium/host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c index 9f6c5fab9c..888770dcf3 100644 --- a/drivers/usb/phytium/host.c +++ b/drivers/usb/phytium/host.c @@ -1070,7 +1070,7 @@ static int32_t hostEp0Irq(struct HOST_CTRL *priv, uint8_t isIn) scheduleNextTransfer(priv, usbReq, hwEp); } - return 0; + return ret; } static void updateTimeIntTransfer(struct list_head *head, struct HOST_EP_PRIV *lastFinished) -- Gitee From 629d4559b7fceffa38b0f604b94b3780503469bd Mon Sep 17 00:00:00 2001 From: Chen Zhenhua Date: Sun, 28 Apr 2024 14:32:16 +0800 Subject: [PATCH 047/154] usb: phytium: Disable PCI platform driver by default This patch is applicable to the PCI platform, and by default, the configuration of this patch is turned off. Mainline: NA Signed-off-by: Chen Zhenhua Signed-off-by: Wang Yinfeng Change-Id: I4e94be217022bdb66bc7e3ec59a4e12f7bc69bb8 --- drivers/usb/phytium/Kconfig | 11 +++++++++++ drivers/usb/phytium/Makefile | 2 ++ 2 files changed, 13 insertions(+) diff --git a/drivers/usb/phytium/Kconfig b/drivers/usb/phytium/Kconfig index f131a3f886..2cb04f672c 100644 --- a/drivers/usb/phytium/Kconfig +++ b/drivers/usb/phytium/Kconfig @@ -6,3 +6,14 @@ config USB_PHYTIUM If you choose to build this driver is a dynamically linked modules, the module will be called phytium-usb.ko + +config USB_PHYTIUM_PCI + tristate "PHYTIUM PCI USB Support" + default n + depends on USB + depends on USB_GADGET + help + Say Y or M here if your system has a OTG USB Controller based on PHYTIUM SOC. + + If you choose to build this driver is a dynamically linked modules, the module will + be called phytium-usb-pci.ko diff --git a/drivers/usb/phytium/Makefile b/drivers/usb/phytium/Makefile index 05d422d4a5..e176c334cb 100644 --- a/drivers/usb/phytium/Makefile +++ b/drivers/usb/phytium/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_USB_PHYTIUM) += phytium-usb.o +obj-$(CONFIG_USB_PHYTIUM_PCI) += phytium-usb-pci.o phytium-usb-y := core.o dma.o platform.o host.o gadget.o +phytium-usb-pci-y := core.o dma.o pci.o host.o gadget.o -- Gitee From 69df78a62af491c39699ff2c2a7cbded7a715782 Mon Sep 17 00:00:00 2001 From: Chen Zhenhua Date: Tue, 18 Feb 2025 09:09:04 +0800 Subject: [PATCH 048/154] xhci: Enable Phytium SOC to support S1 wake-up function This patch makes Phytium SOC to support S1 wake-up function. Mainline: Open-Source Signed-off-by: Chen Zhenhua Signed-off-by: Wang Yinfeng Change-Id: I9b6c1db276a5b0d7411e12845fdbe579fb940521 --- drivers/usb/host/xhci-plat.c | 8 ++++---- drivers/usb/host/xhci-ring.c | 6 ++++++ drivers/usb/host/xhci.c | 18 ++++++++++++++++++ drivers/usb/host/xhci.h | 3 +++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index c8db7a9f30..4ac40bb05b 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -115,6 +115,10 @@ static int xhci_plat_start(struct usb_hcd *hcd) return xhci_run(hcd); } +static const struct xhci_plat_priv xhci_plat_phytium_pe220x = { + .quirks = XHCI_RESET_ON_RESUME | XHCI_S1_SUSPEND_WAKEUP, +}; + #ifdef CONFIG_OF static const struct xhci_plat_priv xhci_plat_marvell_armada = { .init_quirk = xhci_mvebu_mbus_init_quirk, @@ -137,10 +141,6 @@ static const struct xhci_plat_priv xhci_plat_brcm = { .quirks = XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS, }; -static const struct xhci_plat_priv xhci_plat_phytium_pe220x = { - .quirks = XHCI_RESET_ON_RESUME, -}; - static const struct of_device_id usb_xhci_of_match[] = { { .compatible = "generic-xhci", diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index eb70f07e36..2a0895bc3e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2927,6 +2927,12 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) goto out; } + if ((status & STS_WAKEUP) && (xhci->quirks & XHCI_S1_SUSPEND_WAKEUP)) { + status |= STS_WAKEUP; + writel(status, &xhci->op_regs->status); + ret = IRQ_HANDLED; + } + if (!(status & STS_EINT)) goto out; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index b069fe3f8a..edbbe320f5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1078,6 +1078,17 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) return -ETIMEDOUT; } } + + if (xhci->quirks & XHCI_S1_SUSPEND_WAKEUP) { + if (device_may_wakeup(xhci_to_hcd(xhci)->self.controller) && do_wakeup) { + if (enable_irq_wake(hcd->irq)) + xhci_err(xhci, "failed to enable irq wakes\n"); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + if (xhci->shared_hcd) + set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); + } + } + spin_unlock_irq(&xhci->lock); /* @@ -1127,6 +1138,13 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) time_before(jiffies, xhci->usb3_rhub.bus_state.next_statechange)) msleep(100); + if (xhci->quirks & XHCI_S1_SUSPEND_WAKEUP) { + if (device_may_wakeup(xhci_to_hcd(xhci)->self.controller)) { + if (disable_irq_wake(hcd->irq)) + xhci_err(xhci, "failed to disable irq wakes\n"); + } + } + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b44596f621..f3a82513ee 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -239,6 +239,8 @@ struct xhci_op_regs { /* USBSTS - USB status - status bitmasks */ /* HC not running - set to 1 when run/stop bit is cleared. */ #define STS_HALT XHCI_STS_HALT +/* event wakeup interrupt */ +#define STS_WAKEUP (1 << 1) /* serious error, e.g. PCI parity error. The HC will clear the run/stop bit. */ #define STS_FATAL (1 << 2) /* event interrupt - clear this prior to clearing any IP flags in IR set*/ @@ -1896,6 +1898,7 @@ struct xhci_hcd { #define XHCI_RESET_TO_DEFAULT BIT_ULL(44) #define XHCI_TRB_OVERFETCH BIT_ULL(45) #define XHCI_ZHAOXIN_HOST BIT_ULL(46) +#define XHCI_S1_SUSPEND_WAKEUP BIT_ULL(47) unsigned int num_active_eps; unsigned int limit_active_eps; -- Gitee From 981bc204c23bb8f1a2b96e7e1fad4e493ad28ffe Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Wed, 26 Feb 2025 14:12:28 +0800 Subject: [PATCH 049/154] hda: phytium: Re-write function for dma_ops alloc This patch fixes the problem when "dma-coherent" is described in DT on pd1904 and pd2008 SoCs. The old used API for ACPI may not meet the demand, we re-write the dma_ops_alloc function. Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I6c7cab3cfca36415b930685891d1475bb4f29c93 --- sound/pci/hda/hda_phytium.c | 43 ++++++++----------------------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/sound/pci/hda/hda_phytium.c b/sound/pci/hda/hda_phytium.c index aead6f079e..749a0f9077 100644 --- a/sound/pci/hda/hda_phytium.c +++ b/sound/pci/hda/hda_phytium.c @@ -253,37 +253,6 @@ 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) { @@ -885,9 +854,15 @@ static int azx_first_init(struct azx *chip) chip->align_buffer_size = 1; } - err = hda_ft_dma_configure(hddev); - if (err < 0) - return err; + if (has_acpi_companion(hddev)) { + match = acpi_match_device(hddev->driver->acpi_match_table, hddev); + if (!match) { + dev_err(hddev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + set_dma_ops(hddev, NULL); + acpi_dma_configure(hddev, DEV_DMA_NON_COHERENT); + } /* allow 64bit DMA address if supported by H/W */ if (!(gcap & AZX_GCAP_64OK)) -- Gitee From 24de472bc1d3cbd0123e3fcb6be7e4e7eb775d96 Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Sat, 12 Oct 2024 14:58:28 +0800 Subject: [PATCH 050/154] hda: phytium: Add stream enable when resume If system suspends when playing audio, HDA stream will be stopped and can not restart after resume, which will cause input/output error. Add stream enable to avoid input/output error. Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I3c0feb6a4b3ec8196d3929132a5c7eb6d415ebe7 --- sound/pci/hda/hda_phytium.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/hda_phytium.c b/sound/pci/hda/hda_phytium.c index 749a0f9077..b59f518ec8 100644 --- a/sound/pci/hda/hda_phytium.c +++ b/sound/pci/hda/hda_phytium.c @@ -493,6 +493,7 @@ static int azx_resume(struct device *dev) } azx_dev = get_azx_dev(substream); + snd_hdac_stream_start(azx_stream(azx_dev), true); hda->substream = NULL; } -- Gitee From d7518d182b6464f826dd58bac7847aa586c7fd70 Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Fri, 27 Dec 2024 14:07:38 +0800 Subject: [PATCH 051/154] hda: phytium: Add link reset in resume process Link reset was done in BIOS before, which may cause pop when resumed from S3. To avoid pop, reset link in resume process. Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I8b43503a8a2b30e572d1c231d0dacffb975a091f --- sound/pci/hda/hda_phytium.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_phytium.c b/sound/pci/hda/hda_phytium.c index b59f518ec8..fb579f6824 100644 --- a/sound/pci/hda/hda_phytium.c +++ b/sound/pci/hda/hda_phytium.c @@ -477,7 +477,7 @@ static int azx_resume(struct device *dev) snd_hdac_bus_exit_link_reset(bus); usleep_range(1000, 1200); - azx_init_chip(chip, 0); + azx_init_chip(chip, 1); snd_power_change_state(card, SNDRV_CTL_POWER_D0); -- Gitee From e4f3e62434c7de0040c29b706c52faf95ed16c29 Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Tue, 21 Jan 2025 18:21:21 +0800 Subject: [PATCH 052/154] hda: phytium: Add parameter to enable automatic power-saving Add power_save parameter to enable automatic power saving mode. The value of power_save is timeout value of automatic power-saving (in second, 0 is disable). Default value is 1. Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: Id2cb7ca499cbddaa0ce8d240b188a8143eed0539 --- sound/pci/hda/hda_phytium.c | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_phytium.c b/sound/pci/hda/hda_phytium.c index fb579f6824..7a0f3f731c 100644 --- a/sound/pci/hda/hda_phytium.c +++ b/sound/pci/hda/hda_phytium.c @@ -65,6 +65,11 @@ static int probe_only[SNDRV_CARDS]; static int jackpoll_ms[SNDRV_CARDS]; static int single_cmd = -1; static int enable_msi = -1; +#ifdef CONFIG_PM +static int power_save = 1; +#else +#define power_save 0 +#endif #ifdef CONFIG_SND_HDA_INPUT_BEEP static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = CONFIG_SND_HDA_INPUT_BEEP_MODE}; @@ -97,7 +102,17 @@ module_param_array(beep_mode, bool, NULL, 0444); MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode (0=off, 1=on) (default=1)."); #endif -#define power_save 0 +#ifdef CONFIG_PM +static int param_set_xint(const char *val, const struct kernel_param *kp); +static const struct kernel_param_ops param_ops_xint = { + .set = param_set_xint, + .get = param_get_int, +}; +#define param_check_xint param_check_int + +module_param(power_save, xint, 0644); +MODULE_PARM_DESC(power_save, "Automatic power-saving timeout (in second, 0 = disable)."); +#endif /* CONFIG_PM */ static int align_buffer_size = -1; module_param(align_buffer_size, bint, 0644); @@ -415,6 +430,28 @@ static void azx_del_card_list(struct azx *chip) mutex_unlock(&card_list_lock); } +/* trigger power-save check at writing parameter */ +static int param_set_xint(const char *val, const struct kernel_param *kp) +{ + struct hda_ft *hda; + struct azx *chip; + int prev = power_save; + int ret = param_set_int(val, kp); + + if (ret || prev == power_save) + return ret; + + mutex_lock(&card_list_lock); + list_for_each_entry(hda, &card_list, list) { + chip = &hda->chip; + if (!hda->probe_continued || chip->disabled) + continue; + snd_hda_set_power_save(&chip->bus, power_save * 1000); + } + mutex_unlock(&card_list_lock); + return 0; +} + #else #define azx_add_card_list(chip) /* NOP */ #define azx_del_card_list(chip) /* NOP */ -- Gitee From 8137937696eb118c840fe2e46c2c1c748cf1823f Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Tue, 21 Jan 2025 18:41:40 +0800 Subject: [PATCH 053/154] hda: phytium: Add runtime status inquiry node Add runtime status inquiry node. Use "cat runtime_status" to inquire the runtime status of codec. D0 is working, and D3 is in IDLE. Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I188ad8541d35fcb1ccb74544f4f3cb78c4e89ef1 --- sound/pci/hda/hda_phytium.c | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/sound/pci/hda/hda_phytium.c b/sound/pci/hda/hda_phytium.c index 7a0f3f731c..1fe49515cc 100644 --- a/sound/pci/hda/hda_phytium.c +++ b/sound/pci/hda/hda_phytium.c @@ -855,6 +855,7 @@ static int azx_first_init(struct azx *chip) unsigned int dma_bits = 64; struct resource *res; + const struct acpi_device_id *match; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hda->regs = devm_ioremap_resource(hddev, res); @@ -968,6 +969,51 @@ static const struct hda_controller_ops axi_hda_ops = { .link_power = azx_ft_link_power, }; +static ssize_t runtime_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct hdac_bus *bus = azx_bus(chip); + unsigned int cmd = 0x001f0500; + unsigned int res = -1; + char *status; + + dev_info(dev, "Inquire codec status!\n"); + mutex_lock(&bus->cmd_mutex); + snd_hdac_bus_send_cmd(bus, cmd); + snd_hdac_bus_get_response(bus, 0, &res); + mutex_unlock(&bus->cmd_mutex); + + switch (res & 0x3) { + case 0x0: + status = "D0"; + break; + case 0x1: + status = "D1"; + break; + case 0x2: + status = "D2"; + break; + case 0x3: + status = "D3"; + break; + } + + return sprintf(buf, "%s\n", status); +} + +static DEVICE_ATTR_RO(runtime_status); + +static struct attribute *runtime_status_attrs[] = { + &dev_attr_runtime_status.attr, + NULL, +}; + +static const struct attribute_group hda_ft_runtime_status_group = { + .attrs = runtime_status_attrs, +}; + static DECLARE_BITMAP(probed_devs, SNDRV_CARDS); static int hda_ft_probe(struct platform_device *pdev) @@ -1009,11 +1055,18 @@ static int hda_ft_probe(struct platform_device *pdev) if (schedule_probe) schedule_work(&hda->probe_work); + if (sysfs_create_group(&hda->dev->kobj, &hda_ft_runtime_status_group)) { + dev_warn(&pdev->dev, "failed create sysfs\n"); + goto err_sysfs; + } + set_bit(dev, probed_devs); if (chip->disabled) complete_all(&hda->probe_wait); return 0; +err_sysfs: + sysfs_remove_group(&hda->dev->kobj, &hda_ft_runtime_status_group); out_free: snd_card_free(card); return err; @@ -1084,6 +1137,7 @@ static int hda_ft_remove(struct platform_device *pdev) chip = card->private_data; hda = container_of(chip, struct hda_ft, chip); cancel_work_sync(&hda->probe_work); + sysfs_remove_group(&hda->dev->kobj, &hda_ft_runtime_status_group); clear_bit(chip->dev_index, probed_devs); snd_card_free(card); -- Gitee From 75936f5a7bcd6bc61e02434a2048fe591afabb73 Mon Sep 17 00:00:00 2001 From: YuDa Date: Wed, 24 Jul 2024 02:37:59 +0000 Subject: [PATCH 054/154] drm/phytium: Hand audio plugged event based on edid audio capability In order to handle the audio plugged event, we should judge from the edid audio capability. Signed-off-by: Wang Hao Signed-off-by: YuDa Signed-off-by: Wang Yinfeng Change-Id: I0e6e94c7e567a87b131f4ba7197e6a21486105d5 --- drivers/gpu/drm/phytium/phytium_dp.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/phytium/phytium_dp.c b/drivers/gpu/drm/phytium/phytium_dp.c index 92ad326d06..92550f750b 100644 --- a/drivers/gpu/drm/phytium/phytium_dp.c +++ b/drivers/gpu/drm/phytium/phytium_dp.c @@ -1735,6 +1735,7 @@ static int phytium_dp_long_pulse(struct drm_connector *connector, bool hpd_raw_s enum drm_connector_status status = connector->status; bool video_enable = false; uint32_t index = 0; + struct edid *edid = NULL; if (phytium_dp->is_edp) status = connector_status_connected; @@ -1768,6 +1769,15 @@ static int phytium_dp_long_pulse(struct drm_connector *connector, bool hpd_raw_s mdelay(2); phytium_dp_hw_enable_video(phytium_dp); } + + edid = drm_get_edid(connector, &phytium_dp->aux.ddc); + + if (edid && drm_edid_is_valid(edid)) + phytium_dp->has_audio = drm_detect_monitor_audio(edid); + else + phytium_dp->has_audio = false; + + kfree(edid); } out: -- Gitee From 7bddb2cbac11e2c384afaee479c076f0e4ca6b7d Mon Sep 17 00:00:00 2001 From: YuDa Date: Wed, 24 Jul 2024 02:45:40 +0000 Subject: [PATCH 055/154] drm/phytium: Add new function callback interface Add the new callback interface for drm driver to free object. Mainline: Open-Source Signed-off-by: JiangYing Signed-off-by: YuDa Signed-off-by: Wang Yinfeng Change-Id: I49606b60323862bf1fe8154255ea1f0d3faff593 --- drivers/gpu/drm/phytium/phytium_display_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/phytium/phytium_display_drv.c b/drivers/gpu/drm/phytium/phytium_display_drv.c index 3f0084610f..9cb88830df 100644 --- a/drivers/gpu/drm/phytium/phytium_display_drv.c +++ b/drivers/gpu/drm/phytium/phytium_display_drv.c @@ -345,6 +345,7 @@ struct drm_driver phytium_display_drm_driver = { .irq_uninstall = phytium_irq_uninstall, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_free_object_unlocked = phytium_gem_free_object, .gem_prime_export = drm_gem_prime_export, .gem_prime_import = drm_gem_prime_import, .gem_prime_import_sg_table = phytium_gem_prime_import_sg_table, -- Gitee From be9552f2ad4275cd2127fec6a60968ae0d95b4e2 Mon Sep 17 00:00:00 2001 From: YuDa Date: Thu, 26 Sep 2024 07:01:44 +0000 Subject: [PATCH 056/154] drm/phytium: Change the maximum resolution supported by pe220x Change the maximum resolution supported by pe220x from 3840x2160 to 1920x1080. Mainline: Open-Source Signed-off-by: YuDa Signed-off-by: Wang Yinfeng Change-Id: I695d55daedfe7d4f96963b3caac7a0347a96f435 --- drivers/gpu/drm/phytium/pe220x_dc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/phytium/pe220x_dc.h b/drivers/gpu/drm/phytium/pe220x_dc.h index 1fd0db3b8d..8902d11348 100644 --- a/drivers/gpu/drm/phytium/pe220x_dc.h +++ b/drivers/gpu/drm/phytium/pe220x_dc.h @@ -9,8 +9,8 @@ #define __PE220X_DC_H__ #define PE220X_DC_PIX_CLOCK_MAX (594000) -#define PE220X_DC_HDISPLAY_MAX 3840 -#define PE220X_DC_VDISPLAY_MAX 2160 +#define PE220X_DC_HDISPLAY_MAX 1920 +#define PE220X_DC_VDISPLAY_MAX 1080 #define PE220X_DC_ADDRESS_MASK 0x7f extern void pe220x_dc_hw_vram_init(struct phytium_display_private *priv, -- Gitee From 3046c76f0e0d2bce46766d61da9007fa7b05d7c5 Mon Sep 17 00:00:00 2001 From: Lai Xueyu Date: Thu, 16 Nov 2023 10:41:03 +0800 Subject: [PATCH 057/154] mmc: phytium: Add software write protection function Add software write protection function for SD card driver. Mainline: NA Signed-off-by: Lai Xueyu Signed-off-by: Wang Yinfeng Change-Id: I80bcd725f16cf25ada23056c7019bb42e0523070 --- drivers/mmc/host/phytium-mci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/mmc/host/phytium-mci.c b/drivers/mmc/host/phytium-mci.c index febc7ed92e..2b04dbdf18 100644 --- a/drivers/mmc/host/phytium-mci.c +++ b/drivers/mmc/host/phytium-mci.c @@ -62,6 +62,7 @@ static void phytium_mci_init_adma_table(struct phytium_mci_host *host, struct phytium_mci_dma *dma); static void phytium_mci_init_hw(struct phytium_mci_host *host); static int phytium_mci_get_cd(struct mmc_host *mmc); +static int phytium_mci_get_ro(struct mmc_host *mmc); static int phytium_mci_err_irq(struct phytium_mci_host *host, u32 dmac_events, u32 events); static void sdr_set_bits(void __iomem *reg, u32 bs) @@ -1414,6 +1415,20 @@ static int phytium_mci_get_cd(struct mmc_host *mmc) return 1; } +static int phytium_mci_get_ro(struct mmc_host *mmc) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + u32 status; + + status = readl(host->base + MCI_CARD_WRTPRT); + + dev_dbg(host->dev, "mci_get_ro status %d\n", status); + if ((status & 0x1) == 0x1) + return 1; + + return 0; +} + static int phytium_mci_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios) { struct phytium_mci_host *host = mmc_priv(mmc); @@ -1507,6 +1522,7 @@ static struct mmc_host_ops phytium_mci_ops = { .request = phytium_mci_ops_request, .set_ios = phytium_mci_ops_set_ios, .get_cd = phytium_mci_get_cd, + .get_ro = phytium_mci_get_ro, .enable_sdio_irq = phytium_mci_enable_sdio_irq, .ack_sdio_irq = phytium_mci_ack_sdio_irq, .card_busy = phytium_mci_card_busy, -- Gitee From 4db26ebc236301584083b3116671a16c2061f888 Mon Sep 17 00:00:00 2001 From: Lai Xueyu Date: Thu, 16 Nov 2023 10:57:51 +0800 Subject: [PATCH 058/154] mmc: phytium: Add MCI_CLK_DIVIDER function Add MCI_CLK_DIVIDER function to optimize phase settings. Mainline: Open-Source Signed-off-by: Lai Xueyu Signed-off-by: Wang Yinfeng Change-Id: Ie3704fbcf62ea27eb0fc5173d5dd31449dcb62f3 --- drivers/mmc/host/phytium-mci.c | 8 ++++++-- drivers/mmc/host/phytium-mci.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/phytium-mci.c b/drivers/mmc/host/phytium-mci.c index 2b04dbdf18..45166c1da4 100644 --- a/drivers/mmc/host/phytium-mci.c +++ b/drivers/mmc/host/phytium-mci.c @@ -227,8 +227,12 @@ static void phytium_mci_set_clk(struct phytium_mci_host *host, struct mmc_ios *i } } - dev_dbg(host->dev, "UHS_REG_EXT ext: %x, CLKDIV: %x\n", - readl(host->base + MCI_UHS_REG_EXT), readl(host->base + MCI_CLKDIV)); + if (div >= 2) + writel(((2 * (div & 0xff)) & 0xffff), host->base + MCI_CLK_DIVIDER); + + dev_dbg(host->dev, "UHS_REG_EXT ext: %x, CLKDIV: %x MCI_CLK_DIVIDER %x %x\n", + readl(host->base + MCI_UHS_REG_EXT), readl(host->base + MCI_CLKDIV), + readl(host->base + MCI_CLK_DIVIDER), ((2 * (div & 0xff)) & 0xffff)); sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); diff --git a/drivers/mmc/host/phytium-mci.h b/drivers/mmc/host/phytium-mci.h index 90f51b9ee1..e2d6d0b3a8 100644 --- a/drivers/mmc/host/phytium-mci.h +++ b/drivers/mmc/host/phytium-mci.h @@ -104,6 +104,7 @@ #define MCI_UHS_REG_EXT 0x108 /* the UHS register extension */ #define MCI_EMMC_DDR_REG 0x10C /* the EMMC DDR reg */ #define MCI_ENABLE_SHIFT 0x110 /* the enable phase shift reg */ +#define MCI_CLK_DIVIDER 0x114 /* CLK DIVIDER */ #define MCI_DATA 0x200 /* the data FIFO access */ /* Command register defines */ -- Gitee From 7bdbc91c44b486e74fe7a75bf4f998ca98522ee7 Mon Sep 17 00:00:00 2001 From: Lai Xueyu Date: Wed, 31 Jan 2024 11:18:14 +0800 Subject: [PATCH 059/154] mmc: phytium: Add emmc DDR feature support The patch adds support for emmc DDR feature. The newly added dual edge mode includes MMC_DDR52 and MMC_HS400 modes, which have twice the speed of the corresponding single edge mode. Mainline: Open-Source Signed-off-by: Lai Xueyu Signed-off-by: Wang Yinfeng Change-Id: I04cc5641b3b0001b8734fc4f8bfa06dca8787ca3 --- drivers/mmc/host/phytium-mci.c | 23 ++++++++++++++++++++--- drivers/mmc/host/phytium-mci.h | 4 +++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/phytium-mci.c b/drivers/mmc/host/phytium-mci.c index 45166c1da4..8b77868445 100644 --- a/drivers/mmc/host/phytium-mci.c +++ b/drivers/mmc/host/phytium-mci.c @@ -44,7 +44,7 @@ static const u32 data_ints_mask = MCI_INT_MASK_DTO | MCI_INT_MASK_DCRC | MCI_INT MCI_INT_MASK_SBE_BCI; static const u32 cmd_err_ints_mask = MCI_INT_MASK_RTO | MCI_INT_MASK_RCRC | MCI_INT_MASK_RE | MCI_INT_MASK_DCRC | MCI_INT_MASK_DRTO | - MCI_MASKED_INTS_SBE_BCI; + MCI_MASKED_INTS_SBE_BCI | MCI_MASKED_INTS_EBE; static const u32 dmac_ints_mask = MCI_DMAC_INT_ENA_FBE | MCI_DMAC_INT_ENA_DU | MCI_DMAC_INT_ENA_NIS | MCI_DMAC_INT_ENA_AIS; @@ -179,7 +179,7 @@ static void phytium_mci_set_clk(struct phytium_mci_host *host, struct mmc_ios *i host->clk_rate, ios->clock); if (ios->clock >= 25000000) - tmp_ext_reg = 0x202; + tmp_ext_reg = 0x102; else if (ios->clock == 400000) tmp_ext_reg = 0x502; else @@ -227,6 +227,17 @@ static void phytium_mci_set_clk(struct phytium_mci_host *host, struct mmc_ios *i } } + if (!(readl(host->base + MCI_CLKDIV) & 0xff00) && + (ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_MMC_HS400 || + ios->timing == MMC_TIMING_UHS_DDR50)) { + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_CRC_SERIAL_DATA); + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_DRV_SHIFT_EN); + } else { + sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_CRC_SERIAL_DATA); + sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_DRV_SHIFT_EN); + } + if (div >= 2) writel(((2 * (div & 0xff)) & 0xffff), host->base + MCI_CLK_DIVIDER); @@ -1265,6 +1276,7 @@ static void phytium_mci_init_hw(struct phytium_mci_host *host) sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); sdr_set_bits(host->base + MCI_UHS_REG_EXT, MCI_EXT_CLK_ENABLE); sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT); + sdr_clr_bits(host->base + MCI_EMMC_DDR_REG, MCI_EMMC_DDR_CYCLE); phytium_mci_reset_hw(host); @@ -1367,8 +1379,13 @@ static void phytium_mci_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct phytium_mci_host *host = mmc_priv(mmc); - if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50) + if (ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_MMC_HS400 || + ios->timing == MMC_TIMING_UHS_DDR50) { sdr_set_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_DDR); + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_START_BIT_MODE); + sdr_clr_bits(host->base + MCI_EMMC_DDR_REG, MCI_EMMC_DDR_CYCLE); + } else sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_DDR); diff --git a/drivers/mmc/host/phytium-mci.h b/drivers/mmc/host/phytium-mci.h index e2d6d0b3a8..9d3e86a4d5 100644 --- a/drivers/mmc/host/phytium-mci.h +++ b/drivers/mmc/host/phytium-mci.h @@ -133,12 +133,14 @@ #define MCI_CNTRL_CONTROLLER_RESET (0x1 << 0) /* RW */ #define MCI_CNTRL_FIFO_RESET (0x1 << 1) /* RW */ #define MCI_CNTRL_DMA_RESET (0x1 << 2) /* RW */ -#define MCI_CNTRL_RES (0x1 << 3) /* */ +#define MCI_CNTRL_DRV_SHIFT_EN (0x1 << 3) /* */ #define MCI_CNTRL_INT_ENABLE (0x1 << 4) /* RW */ #define MCI_CNTRL_DMA_ENABLE (0x1 << 5) /* RW */ #define MCI_CNTRL_READ_WAIT (0x1 << 6) /* RW */ #define MCI_CNTRL_SEND_IRQ_RESPONSE (0x1 << 7) /* RW */ #define MCI_CNTRL_ABORT_READ_DATA (0x1 << 8) /* RW */ +#define MCI_CNTRL_START_BIT_MODE (0x1 << 9) /* RW */ +#define MCI_CNTRL_CRC_SERIAL_DATA (0x1 << 10) /* RW */ #define MCI_CNTRL_ENDIAN (0x1 << 11) /* RW */ //#define MCI_CNTRL_CARD_VOLTAGE_A (0xF << 16) /* RW */ //#define MCI_CNTRL_CARD_VOLTAGE_B (0xF << 20) /* RW */ -- Gitee From 4ca6e7548f0d01adad974cfeb75ba77d45dc6e17 Mon Sep 17 00:00:00 2001 From: Lai Xueyu Date: Fri, 28 Feb 2025 15:52:11 +0800 Subject: [PATCH 060/154] dt-bindings: mmc: phytium: Add support for phytium mmc v2 driver This patch documents the DT bindings for Phytium mmc v2 controller. Mainline: Open-Source Signed-off-by: Lai Xueyu Signed-off-by: Wang Yinfeng Change-Id: I8fab2ab4d5115cdb9b62dc671a82c05f46a37966 --- .../bindings/mmc/phytium,mci_v2.yaml | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/phytium,mci_v2.yaml diff --git a/Documentation/devicetree/bindings/mmc/phytium,mci_v2.yaml b/Documentation/devicetree/bindings/mmc/phytium,mci_v2.yaml new file mode 100644 index 0000000000..26ae78ffa1 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/phytium,mci_v2.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/phytium,mci_v2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium Multimedia Card Interface controller + +description: | + The highspeed MMC host V2 controller on Phytium SoCs provides an interface + for MMC, SD and SDIO types of memory cards. + +maintainers: + - Lai Xueyu + +allOf: + - $ref: "mmc-controller.yaml" + +properties: + compatible: + const: phytium,mci_2.0 + + reg: + maxItems: 2 + description: mmc controller regfile base registers. + description: mmc controller share memory base registers. + + interrupts: + maxItems: 1 + description: mmc controller v2 interrupt. + + clocks: + maxItems: 1 + description: phandles to input clocks. + + clock-names: + items: + - const: phytium_mci_clk + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +examples: + - | + mmc0_v2: mmc@27018000 { + compatible = "phytium,mci_2.0"; + reg = <0x0 0x27018000 0x0 0x1000>, + <0x0 0x26fea000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_1200mhz>; + clock-names = "phytium_mci_clk"; + status = "disabled"; + }; + + &mmc0 { + bus-width = <4>; + max-frequency = <50000000>; + cap-sdio-irq; + cap-sd-highspeed; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + no-mmc; + status = "ok"; + }; -- Gitee From 0023ee054814ce3d08767af52ca30b2646012bef Mon Sep 17 00:00:00 2001 From: Lai Xueyu Date: Mon, 6 Jan 2025 15:27:45 +0800 Subject: [PATCH 061/154] mmc: phytium: Add phytium mmc v2 driver This patch add support for phytium mmc v2 controller driver. Mainline: Open-Source Signed-off-by: Lai Xueyu Signed-off-by: Wang Yinfeng Change-Id: I13dcf303268f234a82c0c91945fccf62d0c26b17 --- drivers/mmc/host/Kconfig | 10 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/phytium-mci-plat-v2.c | 261 ++++++ drivers/mmc/host/phytium-mci-v2.c | 1077 ++++++++++++++++++++++++ drivers/mmc/host/phytium-mci-v2.h | 314 +++++++ 5 files changed, 1663 insertions(+) create mode 100644 drivers/mmc/host/phytium-mci-plat-v2.c create mode 100644 drivers/mmc/host/phytium-mci-v2.c create mode 100644 drivers/mmc/host/phytium-mci-v2.h diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index ee28b6247d..2ca15f5a00 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -1139,3 +1139,13 @@ config MMC_PHYTIUM_MCI_PLTFM If you have a controller with this interface, say Y or M here. If unsure, say N. + +config MMC_PHYTIUM_MCI_PLTFM_V2 + tristate "Phytium V2 MultiMedia Card Interface support" + depends on ARCH_PHYTIUM && OF + default y if ARCH_PHYTIUM + help + This selects support for the V2 MultiMedia Card Interface on Phytium SoCs. + If you have a controller with this interface, say Y or M here. + + If unsure, say N. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 4a46cf0830..858742962c 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -116,3 +116,4 @@ sdhci-xenon-driver-y += sdhci-xenon.o sdhci-xenon-phy.o obj-$(CONFIG_MMC_PHYTIUM_MCI_PCI) += phytium-mci-pci.o phytium-mci.o obj-$(CONFIG_MMC_PHYTIUM_MCI_PLTFM) += phytium-mci-plat.o phytium-mci.o +obj-$(CONFIG_MMC_PHYTIUM_MCI_PLTFM_V2) += phytium-mci-plat-v2.o phytium-mci-v2.o diff --git a/drivers/mmc/host/phytium-mci-plat-v2.c b/drivers/mmc/host/phytium-mci-plat-v2.c new file mode 100644 index 0000000000..3ad2768880 --- /dev/null +++ b/drivers/mmc/host/phytium-mci-plat-v2.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Phytium Multimedia Card Interface PCI driver + * + * Copyright (C) 2024 Phytium Technology Co.,Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "phytium-mci-v2.h" + +static u32 mci_caps = MMC_CAP_CMD23; + +#if defined CONFIG_PM && defined CONFIG_PM_SLEEP + +static const struct dev_pm_ops phytium_mci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phyt_mci_suspend, + phyt_mci_resume) + SET_RUNTIME_PM_OPS(phyt_mci_runtime_suspend, + phyt_mci_runtime_resume, NULL) +}; +#else +#define phytium_mci_dev_pm_ops NULL +#endif + +static ssize_t debug_enable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + return sprintf(buf, "%d\n", host->debug_enable); +} + +static ssize_t debug_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + struct phytium_mci_host *host = mmc_priv(mmc); + int enable; + int ret; + + ret = sscanf(buf, "%d\n", &enable); + if (ret == 0) { + ret = -EINVAL; + return ret; + } + + host->debug_enable = enable; + + phytium_mci_set_debug_enable(host, host->debug_enable); + + return count; +} + +static ssize_t alive_enable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + return sprintf(buf, "%d\n", host->alive_enable); +} + +static ssize_t alive_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + struct phytium_mci_host *host = mmc_priv(mmc); + int enable; + int ret; + + ret = sscanf(buf, "%d\n", &enable); + if (ret == 0) { + ret = -EINVAL; + return ret; + } + + host->alive_enable = enable; + + phytium_mci_set_alive_enable(host, host->alive_enable); + + return count; +} + +static DEVICE_ATTR_RW(debug_enable); +static DEVICE_ATTR_RW(alive_enable); + +static int phytium_mci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct phytium_mci_host *host; + struct resource *res; + const struct acpi_device_id *match; + struct device *dev = &pdev->dev; + int ret; + + mmc = mmc_alloc_host(sizeof(struct phytium_mci_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_mci_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); + } 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; + } + host->clk_rate = 1200000000; + } + + host->is_use_dma = 1; + host->is_device_x100 = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->regf_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->regf_base)) { + ret = PTR_ERR(host->regf_base); + goto host_free; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + 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, 0); + + if (host->irq < 0) { + ret = -EINVAL; + goto host_free; + } + host->irq_flags = IRQF_SHARED; + dev_dbg(&pdev->dev, "%s %d:irq:%d\n", __func__, __LINE__, host->irq); + host->dev = &pdev->dev; + host->caps = mci_caps; + host->mmc = mmc; + ret = phyt_mci_common_probe(host); + if (ret == MCI_REALEASE_MEM) { + ret = -ENOMEM; + goto release_mem; + } else if (ret) { + goto release; + } + + ret = device_create_file(&mmc->class_dev, + &dev_attr_debug_enable); + if (ret < 0) + goto debug_enable_free; + + ret = device_create_file(&mmc->class_dev, + &dev_attr_alive_enable); + if (ret < 0) + goto alive_enable_free; + + platform_set_drvdata(pdev, mmc); + dev_info(&pdev->dev, "%s %d: probe phytium mci successful.\n", __func__, __LINE__); + return 0; + +alive_enable_free: + device_remove_file(&mmc->class_dev, &dev_attr_alive_enable); +debug_enable_free: + device_remove_file(&mmc->class_dev, &dev_attr_debug_enable); +release: + phyt_mci_deinit_hw(host); +release_mem: + if (host->dma.adma_table) { + dma_free_coherent(&pdev->dev, + MAX_BD_NUM * sizeof(struct phytium_adma2_64_desc), + host->dma.adma_table, host->dma.adma_addr); + } +host_free: + mmc_free_host(mmc); + return ret; +} + +static int phytium_mci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct phytium_mci_host *host; + + mmc = platform_get_drvdata(pdev); + if (!mmc) { + dev_info(&pdev->dev, "%s %d: mmc is null.\n", __func__, __LINE__); + return -1; + } + host = mmc_priv(mmc); + if (!host) { + dev_info(&pdev->dev, "%s %d: host is null.\n", __func__, __LINE__); + mmc_remove_host(mmc); + mmc_free_host(mmc); + return -1; + } + del_timer(&host->alive_timer); + del_timer(&host->hotplug_timer); + mmc_remove_host(host->mmc); + + if (host->dma.adma_table) { + dma_free_coherent(&pdev->dev, + MAX_BD_NUM * sizeof(struct phytium_adma2_64_desc), + host->dma.adma_table, host->dma.adma_addr); + } + phyt_mci_deinit_hw(host); + mmc_free_host(mmc); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id phytium_mci_of_ids[] = { + { .compatible = "phytium,mci_2.0", }, + {} +}; + +MODULE_DEVICE_TABLE(of, phytium_mci_of_ids); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_mci_acpi_ids[] = { + { .id = "PHYT0061" }, + { } +}; + +MODULE_DEVICE_TABLE(acpi, phytium_mci_acpi_ids); +#else +#define phytium_mci_acpi_ids NULL +#endif + +static struct platform_driver phytium_mci_driver = { + .probe = phytium_mci_probe, + .remove = phytium_mci_remove, + .driver = { + .name = "phytium-mci-platform-2.0", + .of_match_table = phytium_mci_of_ids, + .acpi_match_table = phytium_mci_acpi_ids, + .pm = &phytium_mci_dev_pm_ops, + }, +}; + +module_platform_driver(phytium_mci_driver); + +MODULE_DESCRIPTION("Phytium Multimedia Card Interface driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lai Xueyu "); +MODULE_VERSION(PHYTIUM_MMC_V2_DRIVER_VERSION); diff --git a/drivers/mmc/host/phytium-mci-v2.c b/drivers/mmc/host/phytium-mci-v2.c new file mode 100644 index 0000000000..2480906828 --- /dev/null +++ b/drivers/mmc/host/phytium-mci-v2.c @@ -0,0 +1,1077 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Phytium Multimedia Card Interface + * + * Copyright (C) 2024 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 +#include +#include +#include +#include +#include "phytium-mci-v2.h" + +struct init_data_t *init_data; +struct start_command_data_t *start_command_data; +struct start_data_data_t *start_data_data; +struct set_ios_data_t *set_ios_data; +struct cmd_next_data_t *cmd_next_data; +struct err_irq_data_t *err_irq_data; + +static struct phytium_mci_host *shost; + +static const u32 rv2ap_int_mask = MMC_RXRING_TAIL_INT_MASK; + +static const u32 cmd_err_ints_mask = MCI_INT_MASK_RTO | MCI_INT_MASK_RCRC | MCI_INT_MASK_RE | + MCI_INT_MASK_DCRC | MCI_INT_MASK_DRTO | + MCI_MASKED_INTS_SBE_BCI; + +static int ap_phytium_mci_cmd_next(struct phyt_msg_info *rxmsg); +static int ap_phytium_mci_data_xfer_next(struct phyt_msg_info *rxmsg); +static void phytium_mci_adma_reset(struct phytium_mci_host *host); +static void phytium_mci_init(struct phytium_mci_host *host); +static void phytium_mci_init_adma_table(struct phytium_mci_host *host, + struct phytium_mci_dma *dma); +static void phytium_mci_init_hw(struct phytium_mci_host *host); +static int phytium_mci_get_cd(struct mmc_host *mmc); +static int phytium_mci_get_ro(struct mmc_host *mmc); +static void phytium_mci_start_command(struct phytium_mci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd); +static void +phytium_mci_start_data(struct phytium_mci_host *host, struct mmc_request *mrq, + struct mmc_command *cmd, struct mmc_data *data); +static void __phytium_mci_enable_sdio_irq(struct phytium_mci_host *host, int enable); +static int phytium_check_msg(void); +static int phytium_mci_card_busy(struct mmc_host *mmc); +struct phyt_msg_info *shmem[RING_MAX]; +struct phyt_msg_info *rvshmem[RING_MAX_RV]; + +static void phytium_mci_set_int_mask(struct phytium_mci_host *host) +{ + u32 int_mask; + + int_mask = ~rv2ap_int_mask; + + writel(int_mask, host->regf_base + MMC_RV2AP_INT_MASK); + dev_info(host->dev, "set int mask %x\n", int_mask); +} + +static void phytium_mci_prepare_data(struct phytium_mci_host *host, + struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (!(data->host_cookie & MCI_PREPARE_FLAG)) { + data->host_cookie |= MCI_PREPARE_FLAG; + data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data)); + } +} + +static void phytium_mci_unprepare_data(struct phytium_mci_host *host, + struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (data->host_cookie & MCI_ASYNC_FLAG) + return; + + if (data->host_cookie & MCI_PREPARE_FLAG) { + dma_unmap_sg(host->dev, data->sg, data->sg_len, mmc_get_dma_dir(data)); + data->host_cookie &= ~MCI_PREPARE_FLAG; + } +} + +static inline u32 +phytium_mci_cmd_find_resp(struct phytium_mci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 resp; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_R1: + case MMC_RSP_R1B: + resp = 0x5; + break; + + case MMC_RSP_R2: + resp = 0x7; + break; + + case MMC_RSP_R3: + resp = 0x1; + break; + + case MMC_RSP_NONE: + default: + resp = 0x0; + break; + } + + return resp; +} + +static inline +u32 phytium_mci_cmd_prepare_raw_cmd(struct phytium_mci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 opcode = cmd->opcode; + u32 resp = phytium_mci_cmd_find_resp(host, mrq, cmd); + u32 rawcmd = ((opcode & 0x3f) | ((resp & 0x7) << 6)); + + if (opcode == MMC_GO_INACTIVE_STATE || + (opcode == SD_IO_RW_DIRECT && ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT)) + rawcmd |= (0x1 << 14); + else if (opcode == SD_SWITCH_VOLTAGE) + rawcmd |= (0x1 << 28); + + if (test_and_clear_bit(MCI_CARD_NEED_INIT, &host->flags)) + rawcmd |= (0x1 << 15); + + if (cmd->data) { + struct mmc_data *data = cmd->data; + + rawcmd |= (0x1 << 9); + + if (data->flags & MMC_DATA_WRITE) + rawcmd |= (0x1 << 10); + } + + if (host->use_hold) + rawcmd |= (0x1 << 29); + + return (rawcmd | (0x1 << 31)); +} + +static inline void +phytium_mci_adma_write_desc(struct phytium_mci_host *host, + struct phytium_adma2_64_desc *desc, + dma_addr_t addr, u32 len, u32 attribute) +{ + desc->attribute = attribute; + desc->len = len; + desc->addr_lo = lower_32_bits(addr); + desc->addr_hi = upper_32_bits(addr); + dev_dbg(host->dev, "%s %d:addr_lo:0x%x ddr_hi:0x%x\n", __func__, + __LINE__, desc->addr_lo, desc->addr_hi); + + if ((attribute == 0x80000004) || (attribute == 0x8000000c)) { + desc->desc_lo = 0; + desc->desc_hi = 0; + } +} + +static void +phytium_mci_data_sg_write_2_admc_table(struct phytium_mci_host *host, struct mmc_data *data) +{ + struct phytium_adma2_64_desc *desc; + u32 dma_len, i; + dma_addr_t dma_address; + struct scatterlist *sg; + + phytium_mci_init_adma_table(host, &host->dma); + + desc = host->dma.adma_table; + for_each_sg(data->sg, sg, data->sg_count, i) { + dma_address = sg_dma_address(sg); + dma_len = sg_dma_len(sg); + + if (i == 0) { + if (sg_is_last(sg) || (data->sg_count == 1 && dma_len == SD_BLOCK_SIZE)) + phytium_mci_adma_write_desc(host, desc, dma_address, + dma_len, 0x8000000c); + else + phytium_mci_adma_write_desc(host, desc, dma_address, + dma_len, 0x8000001a); + } else if (sg_is_last(sg)) { + phytium_mci_adma_write_desc(host, desc, dma_address, + dma_len, 0x80000004); + } else { + phytium_mci_adma_write_desc(host, desc, dma_address, + dma_len, 0x80000012); + } + + desc++; + } +} + +static void phytium_mci_track_cmd_data(struct phytium_mci_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_mci_request_done(struct phytium_mci_host *host, struct mmc_request *mrq) +{ + phytium_mci_track_cmd_data(host, mrq->cmd, mrq->data); + + if (mrq->data) + phytium_mci_unprepare_data(host, mrq); + + mmc_request_done(host->mmc, mrq); +} + +static void phytium_mci_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + + host->error = 0; + host->mrq = NULL; + WARN_ON(host->mrq); + host->mrq = mrq; + + dev_dbg(host->dev, "%s %d: cmd:%d arg:0x%x\n", __func__, __LINE__, + mrq->cmd->opcode, mrq->cmd->arg); + + if (mrq->sbc) { + phytium_mci_start_command(host, mrq, mrq->sbc); + return; + } + if (mrq->data) { + phytium_mci_prepare_data(host, mrq); + + if ((mrq->data->sg->length >= 512) && host->is_use_dma && + ((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) || + (mrq->cmd->opcode == SD_IO_RW_EXTENDED))) + + host->adtc_type = BLOCK_RW_ADTC; + else + host->adtc_type = COMMOM_ADTC; + + phytium_mci_start_data(host, mrq, mrq->cmd, mrq->data); + return; + } + phytium_mci_start_command(host, mrq, mrq->cmd); +} + +static void phytium_mci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data) + return; + + phytium_mci_prepare_data(host, mrq); + data->host_cookie |= MCI_ASYNC_FLAG; +} + +static void phytium_mci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, + int err) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data) + return; + + if (data->host_cookie & MCI_ASYNC_FLAG) { + data->host_cookie &= ~MCI_ASYNC_FLAG; + phytium_mci_unprepare_data(host, mrq); + } +} + +static void phytium_mci_data_read_without_dma(struct phytium_mci_host *host, + struct mmc_data *data) +{ + u32 length, i, data_val, dma_len, tmp = 0; + u32 *virt_addr; + unsigned long flags; + struct scatterlist *sg; + + length = data->blocks * data->blksz; + + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) { + spin_lock_irqsave(&host->lock, flags); + if (data->host_cookie & MCI_ASYNC_FLAG) { + tmp = MCI_ASYNC_FLAG; + phytium_mci_post_req(host->mmc, data->mrq, 0); + } else { + phytium_mci_unprepare_data(host, data->mrq); + } + + for_each_sg(data->sg, sg, data->sg_count, i) { + dma_len = sg_dma_len(sg); + virt_addr = sg_virt(data->sg); + + for (i = 0; i < (dma_len / 4); i++) { + data_val = readl(host->regf_base + MCI_DATA); + memcpy(virt_addr, &data_val, 4); + ++virt_addr; + } + } + + if (tmp & MCI_ASYNC_FLAG) + phytium_mci_pre_req(host->mmc, data->mrq); + else + phytium_mci_prepare_data(host, data->mrq); + + spin_unlock_irqrestore(&host->lock, flags); + } + data->bytes_xfered = length; +} + +static irqreturn_t phytium_mci_irq(int irq, void *dev_id) +{ + struct phytium_mci_host *host = (struct phytium_mci_host *) dev_id; + u32 rv2ap_event; + + rv2ap_event = readl(host->regf_base + MMC_RV2AP_INT); + if (rv2ap_event & MMC_TXRING_HEAD_INT) { + rv2ap_event &= ~MMC_TXRING_HEAD_INT; + pr_debug("MCIAP %s MMC_TXRING_HEAD_INT %x\n", __func__, rv2ap_event); + writel(rv2ap_event, host->regf_base + MMC_RV2AP_INT); + } + if (rv2ap_event & MMC_RXRING_TAIL_INT) { + rv2ap_event &= ~MMC_RXRING_TAIL_INT; + pr_debug("MCIAP %s MMC_RXRING_TAIL_INT %x\n", __func__, rv2ap_event); + writel(rv2ap_event, host->regf_base + MMC_RV2AP_INT); + phytium_check_msg(); + } + return IRQ_HANDLED; +} + +static void phytium_mci_init_adma_table(struct phytium_mci_host *host, + struct phytium_mci_dma *dma) +{ + struct phytium_adma2_64_desc *adma_table = dma->adma_table; + dma_addr_t dma_addr; + int i; + + memset(adma_table, 0, sizeof(struct phytium_adma2_64_desc) * MAX_BD_NUM); + + for (i = 0; i < (MAX_BD_NUM - 1); i++) { + dma_addr = dma->adma_addr + sizeof(*adma_table) * (i + 1); + adma_table[i].desc_lo = lower_32_bits(dma_addr); + adma_table[i].desc_hi = upper_32_bits(dma_addr); + adma_table[i].attribute = 0; + adma_table[i].NON1 = 0; + adma_table[i].len = 0; + adma_table[i].NON2 = 0; + } + + phytium_mci_adma_reset(host); +} + +static void phytium_mci_ack_sdio_irq(struct mmc_host *mmc) +{ + unsigned long flags; + struct phytium_mci_host *host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + __phytium_mci_enable_sdio_irq(host, 1); + spin_unlock_irqrestore(&host->lock, flags); +} + +#ifdef CONFIG_PM_SLEEP +int phyt_mci_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + phyt_mci_deinit_hw(host); + return 0; +} +EXPORT_SYMBOL(phyt_mci_suspend); + +int phyt_mci_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + phytium_mci_set_int_mask(host); + phytium_mci_init(host); + phytium_mci_init_hw(host); + return 0; +} +EXPORT_SYMBOL(phyt_mci_resume); + +#endif + +#ifdef CONFIG_PM +int phyt_mci_runtime_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + phyt_mci_deinit_hw(host); + return 0; +} +EXPORT_SYMBOL(phyt_mci_runtime_suspend); + +int phyt_mci_runtime_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + phytium_mci_set_int_mask(host); + phytium_mci_init(host); + phytium_mci_init_hw(host); + return 0; +} +EXPORT_SYMBOL(phyt_mci_runtime_resume); + +#endif + +static int phytium_ap_to_rv(struct phyt_msg_info *msg) +{ + int i; + int ret = 0; + int tx_t, tx_h, p; + + tx_t = readl(shost->regf_base + MMC_TX_TAIL) & 0xffff; + tx_h = readl(shost->regf_base + MMC_TX_HEAD) & 0xffff; + + if ((tx_t + 1) % RING_MAX == tx_h) { + pr_err("mci ring buff full\n"); + return -1; + } + + p = tx_t; + + tx_t = (tx_t + 1) % RING_MAX; + + shmem[p] = (struct phyt_msg_info *)(*shmem + p); + pr_debug("MCIAP shmem[%d] %x\n", p, (unsigned int)(long)shmem[p]); + pr_debug("MCIAP MSG CMD:%d SCMD:%d\n", msg->cmd_type, msg->cmd_subid); + + /*write msg to shmem*/ + memcpy(shmem[p], msg, sizeof(struct phyt_msg_info)); + + /*update tx tail pointer*/ + writel(tx_t | MMC_TX_TAIL_INT, shost->regf_base + MMC_TX_TAIL); + + /*wait and check status*/ + for (i = 0; i < COMPLETE_TIMEOUT; i++) { + dsb(sy); + if (shmem[p]->status0 == GOING || shmem[p]->status0 == NOT_READY) { + pr_debug("MCIAP [%d] not complete %x\r\n", p, shmem[p]->status0); + } else if (shmem[p]->status0 == SUCCESS) { + ret = shmem[p]->status1; + pr_debug("MCIAP [%d] complete and return %d\r\n", p, ret); + /*clear status*/ + shmem[p]->status0 = 0; + shmem[p]->status1 = 0; + break; + } else if (shmem[p]->status0 >= GENERIC_ERROR) { + pr_debug("MCIAP [%d] status error %x\r\n", p, shmem[p]->status0); + ret = -1; + /*clear status*/ + shmem[p]->status0 = 0; + shmem[p]->status1 = 0; + break; + } + udelay(40); + } + pr_debug("MCIAP %s end [%d] times:%d\n", __func__, p, i); + return ret; +} + +static void phytium_mci_init(struct phytium_mci_host *host) +{ + host->msg.cmd_type = PHYTMMC_MSG_CMD_SET; + host->msg.cmd_subid = MCI_INIT; + + pr_debug("MCIAP host->mmc->caps %x host->clk_rate %ld\n", + host->mmc->caps, host->clk_rate); + + init_data = (struct init_data_t *)host->msg.data; + init_data->caps = host->mmc->caps; + init_data->clk_rate = host->clk_rate; + + phytium_ap_to_rv(&host->msg); +} + +static void phytium_mci_init_hw(struct phytium_mci_host *host) +{ + host->msg.cmd_type = PHYTMMC_MSG_CMD_SET; + host->msg.cmd_subid = MCI_INIT_HW; + + phytium_ap_to_rv(&host->msg); + dev_info(host->dev, "init hardware done!"); +} + +void phyt_mci_deinit_hw(struct phytium_mci_host *host) +{ + host->msg.cmd_type = PHYTMMC_MSG_CMD_SET; + host->msg.cmd_subid = MCI_DEINIT_HW; + + phytium_ap_to_rv(&host->msg); +} +EXPORT_SYMBOL_GPL(phyt_mci_deinit_hw); + +static void phytium_mci_start_command(struct phytium_mci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 rawcmd; + + host->cmd = cmd; + host->mrq = mrq; + rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd); + + pr_debug("MCIAP arg%x flags%x opcode%d rawcmd%x\n", + host->cmd->arg, host->cmd->flags, host->cmd->opcode, rawcmd); + host->msg.cmd_type = PHYTMMC_MSG_CMD_SET; + host->msg.cmd_subid = MCI_START_CMD; + + start_command_data = (struct start_command_data_t *)host->msg.data; + start_command_data->cmd_arg = cmd->arg; + start_command_data->cmd_flags = cmd->flags; + start_command_data->cmd_opcode = cmd->opcode; + start_command_data->rawcmd = rawcmd; + + phytium_ap_to_rv(&host->msg); +} + +static void +phytium_mci_start_data(struct phytium_mci_host *host, struct mmc_request *mrq, + struct mmc_command *cmd, struct mmc_data *data) +{ + u32 rawcmd; + + host->cmd = cmd; + cmd->error = 0; + host->mrq = mrq; + host->data = data; + rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd); + phytium_mci_data_sg_write_2_admc_table(host, data); + + host->msg.cmd_type = PHYTMMC_MSG_CMD_SET; + host->msg.cmd_subid = MCI_START_DATA; + + start_data_data = (struct start_data_data_t *)host->msg.data; + start_data_data->data_flags = data->flags; + start_data_data->adtc_type = host->adtc_type; + start_data_data->adma_addr = host->dma.adma_addr; + start_data_data->mrq_data_blksz = mrq->data->blksz; + start_data_data->mrq_data_blocks = mrq->data->blocks; + start_data_data->cmd_arg = cmd->arg; + start_data_data->rawcmd = rawcmd; + + phytium_ap_to_rv(&host->msg); +} + +static void phytium_mci_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + + host->msg.cmd_type = PHYTMMC_MSG_CMD_SET; + host->msg.cmd_subid = MCI_OPS_SET_IOS; + + set_ios_data = (struct set_ios_data_t *)host->msg.data; + set_ios_data->ios_clock = ios->clock; + set_ios_data->ios_timing = ios->timing; + set_ios_data->ios_bus_width = ios->bus_width; + set_ios_data->ios_power_mode = ios->power_mode; + + phytium_ap_to_rv(&host->msg); + + if (ios->power_mode == MMC_POWER_UP) + set_bit(MCI_CARD_NEED_INIT, &host->flags); +} + +static void __phytium_mci_enable_sdio_irq(struct phytium_mci_host *host, int enable) +{ + host->msg.cmd_type = PHYTMMC_MSG_CMD_SET; + host->msg.cmd_subid = MCI_SDIO_IRQ_EN; + + host->msg.data[0] = enable & 0xFF; + + phytium_ap_to_rv(&host->msg); +} + +static void phytium_mci_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + + __phytium_mci_enable_sdio_irq(host, enable); +} + +static int phytium_mci_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + int ret = 0; + + host->msg.cmd_type = PHYTMMC_MSG_CMD_SET; + host->msg.cmd_subid = MCI_OPS_SWITCH_VOLT; + + host->msg.data[0] = ios->signal_voltage & 0xFF; + + ret = phytium_ap_to_rv(&host->msg); + + pr_debug("MCIAP %s %d\n", __func__, ret); + + return ret; +} + +static void phytium_mci_hw_reset(struct mmc_host *mmc) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + + host->msg.cmd_type = PHYTMMC_MSG_CMD_DEFAULT; + host->msg.cmd_subid = MCI_HW_RESET; + + phytium_ap_to_rv(&host->msg); +} + +static void phytium_mci_adma_reset(struct phytium_mci_host *host) +{ + host->msg.cmd_type = PHYTMMC_MSG_CMD_DEFAULT; + host->msg.cmd_subid = MCI_ADMA_RESET; + + phytium_ap_to_rv(&host->msg); +} + +static int phytium_mci_get_cd(struct mmc_host *mmc) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + u32 status; + + if (mmc->caps & MMC_CAP_NONREMOVABLE) + return 1; + + status = readl(host->regf_base + MCI_CARD_DETECT); + + pr_debug("MCIAP get cd %d\n", status); + + if ((status & 0x1) == 0x1) + return 0; + + return 1; +} + +static int phytium_mci_card_busy(struct mmc_host *mmc) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + int ret; + + host->msg.cmd_type = PHYTMMC_MSG_CMD_GET; + host->msg.cmd_subid = MCI_GET_CARD_BUSY; + + ret = phytium_ap_to_rv(&host->msg); + + pr_debug("MCIAP card busy %d\n", ret); + return ret; +} + +static int phytium_mci_get_ro(struct mmc_host *mmc) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + int ret; + + host->msg.cmd_type = PHYTMMC_MSG_CMD_GET; + host->msg.cmd_subid = MCI_GET_RO; + + ret = phytium_ap_to_rv(&host->msg); + + dev_dbg(host->dev, "MCIAP ro %d\n", ret); + + return ret; +} + +static int ap_phytium_mci_cmd_next(struct phyt_msg_info *rxmsg) +{ + struct phytium_mci_host *host; + u32 events; + + host = shost; + + if (!host->cmd) + return 1; + + cmd_next_data = (struct cmd_next_data_t *)rxmsg->data; + events = cmd_next_data->events; + host->cmd->resp[0] = cmd_next_data->response0; + host->cmd->resp[1] = cmd_next_data->response1; + host->cmd->resp[2] = cmd_next_data->response2; + host->cmd->resp[3] = cmd_next_data->response3; + + if (!(events & (MCI_RAW_INTS_CMD | MCI_INT_MASK_HTO))) { + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && (events & MCI_RAW_INTS_RTO) + && readl(host->regf_base + MCI_CARD_DETECT)) { + host->cmd->error = -ENOMEDIUM; + host->cmd->resp[0] = 0; + } else if (events & MCI_RAW_INTS_RTO || + (host->cmd->opcode != MMC_SEND_TUNING_BLOCK && + host->cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)) { + host->cmd->error = -ETIMEDOUT; + } else if (events & MCI_RAW_INTS_RCRC) { + host->cmd->error = -EILSEQ; + } else { + host->cmd->error = -ETIMEDOUT; + } + } + + if ((host->cmd->error && !(host->cmd->opcode == MMC_SEND_TUNING_BLOCK || + host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)) || + (host->mrq->sbc && host->mrq->sbc->error)) { + phytium_mci_request_done(host, host->mrq); + } else if (host->cmd == host->mrq->sbc) { + if ((host->mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) || + (host->mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) || + (host->mrq->cmd->opcode == MMC_READ_SINGLE_BLOCK) || + (host->mrq->cmd->opcode == MMC_WRITE_BLOCK)) { + dev_dbg(host->dev, "%s %d:sbc done and next cmd :%d length:%d\n", + __func__, __LINE__, host->mrq->cmd->opcode, + host->mrq->data->sg->length); + phytium_mci_prepare_data(host, host->mrq); + if (host->is_use_dma) + host->adtc_type = BLOCK_RW_ADTC; + else + host->adtc_type = COMMOM_ADTC; + phytium_mci_start_data(host, host->mrq, host->mrq->cmd, host->mrq->data); + } else { + dev_err(host->dev, "%s %d:ERROR: cmd %d followers the SBC\n", + __func__, __LINE__, host->cmd->opcode); + } + } else if (!host->cmd->data) { + phytium_mci_request_done(host, host->mrq); + } + return 1; +} + +static int ap_phytium_mci_data_xfer_next(struct phyt_msg_info *rxmsg) +{ + struct phytium_mci_host *host; + u32 events; + struct mmc_request *mrq; + struct mmc_data *data; + + host = shost; + mrq = host->mrq; + data = host->data; + + events = ((uint32_t *)(rxmsg->data))[0]; + + if (events & MCI_RAW_INTS_DTO) { + if (host->adtc_type == COMMOM_ADTC && + (mrq->cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) + phytium_mci_data_read_without_dma(host, data); + else + data->bytes_xfered = data->blocks * data->blksz; + } else { + data->bytes_xfered = 0; + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) + && readl(host->regf_base + MCI_CARD_DETECT) + && (events & cmd_err_ints_mask)) { + data->error = -ENOMEDIUM; + data->mrq->cmd->error = -ENOMEDIUM; + } else if (events & (MCI_RAW_INTS_DCRC | MCI_RAW_INTS_EBE | + MCI_RAW_INTS_SBE_BCI)) { + data->error = -EILSEQ; + } else { + data->error = -ETIMEDOUT; + } + } + + if (mmc_op_multi(mrq->cmd->opcode) && mrq->stop && + (data->error || !mrq->sbc)) { + phytium_mci_start_command(host, mrq, mrq->stop); + } else { + phytium_mci_request_done(host, mrq); + } + return 1; +} + +static int ap_phytium_mci_card_detect_irq(struct phyt_msg_info *rxmsg) +{ + u8 cd; + struct phytium_mci_host *host; + + host = shost; + cd = rxmsg->data[0]; + + pr_debug("MCIAP card_detect_irq %d\n", cd); + if (cd) { + if (host->mmc->card) { + cancel_delayed_work(&host->mmc->detect); + mmc_detect_change(host->mmc, msecs_to_jiffies(0)); + } + } else { + cancel_delayed_work(&host->mmc->detect); + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + } + return 1; +} + +static int ap_phytium_mci_err_irq(struct phyt_msg_info *rxmsg) +{ + u32 raw_ints; + u32 dmac_status; + u32 ints_mask; + u32 dmac_mask; + u32 opcode; + + err_irq_data = (struct err_irq_data_t *)rxmsg->data; + raw_ints = err_irq_data->raw_ints; + ints_mask = err_irq_data->ints_mask; + dmac_status = err_irq_data->dmac_status; + dmac_mask = err_irq_data->dmac_mask; + opcode = err_irq_data->opcode; + + pr_err("MCIAP ERR raw_ints:%x ints_mask:%x dmac_status:%x dmac_mask:%x cmd:%d\n", + raw_ints, ints_mask, dmac_status, dmac_mask, opcode); + + return 1; +} + +static int phytium_check_msg(void) +{ + int rx_t, rx_h; + int complete; + struct phyt_msg_info rxmsg; + int p = 0; + + rx_t = readl(shost->regf_base + MMC_RX_TAIL) & 0xffff; + rx_h = readl(shost->regf_base + MMC_RX_HEAD) & 0xffff; + + while (rx_t != rx_h) { + pr_debug("MCIAP %s rx_t:%d rx_h:%d\n", __func__, rx_t, rx_h); + + p = rx_h; + rx_h = (rx_h + 1) % RING_MAX_RV; + + writel(rx_h, shost->regf_base + MMC_RX_HEAD); + rvshmem[p] = (struct phyt_msg_info *)(*rvshmem + p); + pr_debug("MCIAP %s %d %x\n", __func__, p, (unsigned int)(long)rvshmem[p]); + + /*read msg from shmem*/ + memcpy(&rxmsg, rvshmem[p], sizeof(struct phyt_msg_info)); + /*read msg from shmem*/ + + /*execute cmd*/ + switch (rxmsg.cmd_type) { + case PHYTMMC_MSG_CMD_REPORT: + switch (rxmsg.cmd_subid) { + case MCI_CMD_NEXT: + complete = ap_phytium_mci_cmd_next(&rxmsg); + break; + case MCI_DATA_NEXT: + complete = ap_phytium_mci_data_xfer_next(&rxmsg); + break; + case MCI_CD_IRQ: + complete = ap_phytium_mci_card_detect_irq(&rxmsg); + break; + case MCI_ERR_IRQ: + complete = ap_phytium_mci_err_irq(&rxmsg); + break; + default: + pr_debug("MCIAP invalid sub cmd\r\n"); + break; + } + break; + default: + pr_debug("MCIAP invalid cmd\r\n"); + break; + } + + /*write complete*/ + rvshmem[p]->status0 = complete; + } + return 1; +} + +int phytium_mci_set_debug_enable(struct phytium_mci_host *host, bool enable) +{ + static bool debug_enable; + u32 debug_reg; + + if (enable == debug_enable) + return 0; + + debug_enable = enable; + + debug_reg = readl(host->regf_base + MCI_DEBUG); + pr_debug("MCIAP %s debug_reg %x\n", __func__, debug_reg); + + if (!debug_enable && (debug_reg & MCI_DEBUG_ENABLE)) { + debug_reg &= ~MCI_DEBUG_ENABLE; + writel(debug_reg, host->regf_base + MCI_DEBUG); + } else if (debug_enable && !(debug_reg & MCI_DEBUG_ENABLE)) { + debug_reg |= MCI_DEBUG_ENABLE; + writel(debug_reg, host->regf_base + MCI_DEBUG); + } + + return 1; +} +EXPORT_SYMBOL(phytium_mci_set_debug_enable); + +int phytium_mci_set_alive_enable(struct phytium_mci_host *host, bool enable) +{ + static bool alive_enable; + u32 debug_reg; + + if (enable == alive_enable) + return 0; + + alive_enable = enable; + + debug_reg = readl(host->regf_base + MCI_DEBUG); + pr_debug("MCIAP %s debug_reg %x\n", __func__, debug_reg); + + if (!alive_enable && (debug_reg & MCI_ALIVE_ENABLE)) { + debug_reg &= ~MCI_ALIVE_ENABLE; + writel(debug_reg, host->regf_base + MCI_DEBUG); + del_timer(&host->alive_timer); + } else if (alive_enable && !(debug_reg & MCI_ALIVE_ENABLE)) { + debug_reg |= MCI_ALIVE_ENABLE | MCI_ALIVE; + writel(debug_reg, host->regf_base + MCI_DEBUG); + add_timer(&host->alive_timer); + } + + return 1; +} +EXPORT_SYMBOL(phytium_mci_set_alive_enable); + +static void alive_timer_func(struct timer_list *t) +{ + struct phytium_mci_host *host; + u32 debug_reg; + + host = from_timer(host, t, alive_timer); + if (!host) + return; + + debug_reg = readl(host->regf_base + MCI_DEBUG); + pr_debug("MCIAP %s debug_reg %x\n", __func__, debug_reg); + debug_reg |= MCI_ALIVE; + writel(debug_reg, host->regf_base + MCI_DEBUG); + mod_timer(&host->alive_timer, jiffies + msecs_to_jiffies(5000)); +} + +static struct mmc_host_ops phytium_mci_ops = { + .post_req = phytium_mci_post_req, + .pre_req = phytium_mci_pre_req, + .request = phytium_mci_ops_request, + .set_ios = phytium_mci_ops_set_ios, + .get_cd = phytium_mci_get_cd, + .get_ro = phytium_mci_get_ro, + .enable_sdio_irq = phytium_mci_enable_sdio_irq, + .ack_sdio_irq = phytium_mci_ack_sdio_irq, + .card_busy = phytium_mci_card_busy, + .start_signal_voltage_switch = phytium_mci_ops_switch_volt, + .hw_reset = phytium_mci_hw_reset, +}; + +int phyt_mci_common_probe(struct phytium_mci_host *host) +{ + struct mmc_host *mmc = host->mmc; + struct device *dev = host->dev; + int ret; + + shost = host; + + dev_info(host->dev, "phytium_mci_common_probe start\n"); + + *shmem = host->base; + *rvshmem = (struct phyt_msg_info *)(*shmem + RING_MAX); + + dma_set_mask(dev, DMA_BIT_MASK(64)); + dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); + + host->alive_timer.expires = jiffies + msecs_to_jiffies(5000); + timer_setup(&host->alive_timer, alive_timer_func, 0); + + mmc->f_min = MCI_F_MIN; + if (!mmc->f_max) + mmc->f_max = MCI_F_MAX; + + mmc->ops = &phytium_mci_ops; + mmc->ocr_avail_sdio = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->ocr_avail_sd = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->ocr_avail_mmc = MMC_VDD_165_195; + mmc->ocr_avail = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps |= host->caps; + + if (mmc->caps & MMC_CAP_SDIO_IRQ) { + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + dev_dbg(host->dev, "%s %d: MMC_CAP_SDIO_IRQ\n", __func__, __LINE__); + } + mmc->caps2 |= host->caps2; + + phytium_mci_set_int_mask(host); + + phytium_mci_init(host); + + phytium_mci_init_hw(host); + + if (host->is_use_dma) { + /* MMC core transfer sizes tunable parameters */ + mmc->max_segs = MAX_BD_NUM; + mmc->max_seg_size = 4 * 1024; + mmc->max_blk_size = 512; + mmc->max_req_size = 512 * 1024; + mmc->max_blk_count = mmc->max_req_size / 512; + host->dma.adma_table = dma_alloc_coherent(host->dev, + MAX_BD_NUM * + sizeof(struct phytium_adma2_64_desc), + &host->dma.adma_addr, GFP_KERNEL); + if (!host->dma.adma_table) + return MCI_REALEASE_MEM; + + host->dma.desc_sz = ADMA2_64_DESC_SZ; + phytium_mci_init_adma_table(host, &host->dma); + } else { + mmc->max_segs = MAX_BD_NUM; + mmc->max_seg_size = 4 * 1024; + mmc->max_blk_size = 512; + mmc->max_req_size = 4 * 512; + mmc->max_blk_count = mmc->max_req_size / 512; + } + + spin_lock_init(&host->lock); + + ret = devm_request_irq(host->dev, host->irq, phytium_mci_irq, + host->irq_flags, "phytium-mci", host); + if (ret) + return ret; + + ret = mmc_add_host(mmc); + if (ret) { + dev_err(host->dev, "%s %d: mmc add host!\n", __func__, __LINE__); + return ret; + } + return 0; +} +EXPORT_SYMBOL(phyt_mci_common_probe); + +MODULE_DESCRIPTION("Phytium Multimedia Card Interface driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lai Xueyu "); +MODULE_VERSION(PHYTIUM_MMC_V2_DRIVER_VERSION); diff --git a/drivers/mmc/host/phytium-mci-v2.h b/drivers/mmc/host/phytium-mci-v2.h new file mode 100644 index 0000000000..f02f9db331 --- /dev/null +++ b/drivers/mmc/host/phytium-mci-v2.h @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for Phytium Multimedia Card Interface + * + * Copyright (C) 2024 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_MCI_H +#define __PHYTIUM_MCI_H + +#include +#include +#include +#include +#include +#include +#include + +/*------------------------------------------------------*/ +/* Common Definition */ +/*------------------------------------------------------*/ +#define PHYTIUM_MMC_V2_DRIVER_VERSION "1.1.0" + +#define SD_BLOCK_SIZE 512 +#define MAX_BD_NUM 128 +#define MCI_CLK 1200000000 +#define MCI_REALEASE_MEM 0x1 + +#define MCI_PREPARE_FLAG (0x1 << 0) +#define MCI_ASYNC_FLAG (0x1 << 1) +#define MCI_MMAP_FLAG (0x1 << 2) + +#define MCI_F_MIN 400000 +#define MCI_F_MAX 50000000 + +/* MCI_RAW_INTS mask */ +#define MCI_RAW_INTS_CD (0x1 << 0) /* W1C */ +#define MCI_RAW_INTS_RE (0x1 << 1) /* W1C */ +#define MCI_RAW_INTS_CMD (0x1 << 2) /* W1C */ +#define MCI_RAW_INTS_DTO (0x1 << 3) /* W1C */ +#define MCI_RAW_INTS_TXDR (0x1 << 4) /* W1C */ +#define MCI_RAW_INTS_RXDR (0x1 << 5) /* W1C */ +#define MCI_RAW_INTS_RCRC (0x1 << 6) /* W1C */ +#define MCI_RAW_INTS_DCRC (0x1 << 7) /* W1C */ +#define MCI_RAW_INTS_RTO (0x1 << 8) /* W1C */ +#define MCI_RAW_INTS_DRTO (0x1 << 9) /* W1C */ +#define MCI_RAW_INTS_HTO (0x1 << 10) /* W1C */ +#define MCI_RAW_INTS_FRUN (0x1 << 11) /* W1C */ +#define MCI_RAW_INTS_HLE (0x1 << 12) /* W1C */ +#define MCI_RAW_INTS_SBE_BCI (0x1 << 13) /* W1C */ +#define MCI_RAW_INTS_ACD (0x1 << 14) /* W1C */ +#define MCI_RAW_INTS_EBE (0x1 << 15) /* W1C */ +#define MCI_RAW_INTS_SDIO (0x1 << 16) /* W1C */ + +/* MCI_MASKED_INTS mask */ +#define MCI_MASKED_INTS_CD (0x1 << 0) /* RO */ +#define MCI_MASKED_INTS_RE (0x1 << 1) /* RO */ +#define MCI_MASKED_INTS_CMD (0x1 << 2) /* RO */ +#define MCI_MASKED_INTS_DTO (0x1 << 3) /* RO */ +#define MCI_MASKED_INTS_TXDR (0x1 << 4) /* RO */ +#define MCI_MASKED_INTS_RXDR (0x1 << 5) /* RO */ +#define MCI_MASKED_INTS_RCRC (0x1 << 6) /* RO */ +#define MCI_MASKED_INTS_DCRC (0x1 << 7) /* RO */ +#define MCI_MASKED_INTS_RTO (0x1 << 8) /* RO */ +#define MCI_MASKED_INTS_DRTO (0x1 << 9) /* RO */ +#define MCI_MASKED_INTS_HTO (0x1 << 10) /* RO */ +#define MCI_MASKED_INTS_FRUN (0x1 << 11) /* RO */ +#define MCI_MASKED_INTS_HLE (0x1 << 12) /* RO */ +#define MCI_MASKED_INTS_SBE_BCI (0x1 << 13) /* RO */ +#define MCI_MASKED_INTS_ACD (0x1 << 14) /* RO */ +#define MCI_MASKED_INTS_EBE (0x1 << 15) /* RO */ +#define MCI_MASKED_INTS_SDIO (0x1 << 16) /* RO */ + +/* MCI_INT_MASK mask */ +#define MCI_INT_MASK_CD (0x1 << 0) /* RW */ +#define MCI_INT_MASK_RE (0x1 << 1) /* RW */ +#define MCI_INT_MASK_CMD (0x1 << 2) /* RW */ +#define MCI_INT_MASK_DTO (0x1 << 3) /* RW */ +#define MCI_INT_MASK_TXDR (0x1 << 4) /* RW */ +#define MCI_INT_MASK_RXDR (0x1 << 5) /* RW */ +#define MCI_INT_MASK_RCRC (0x1 << 6) /* RW */ +#define MCI_INT_MASK_DCRC (0x1 << 7) /* RW */ +#define MCI_INT_MASK_RTO (0x1 << 8) /* RW */ +#define MCI_INT_MASK_DRTO (0x1 << 9) /* RW */ +#define MCI_INT_MASK_HTO (0x1 << 10) /* RW */ +#define MCI_INT_MASK_FRUN (0x1 << 11) /* RW */ +#define MCI_INT_MASK_HLE (0x1 << 12) /* RW */ +#define MCI_INT_MASK_SBE_BCI (0x1 << 13) /* RW */ +#define MCI_INT_MASK_ACD (0x1 << 14) /* RW */ +#define MCI_INT_MASK_EBE (0x1 << 15) /* RW */ +#define MCI_INT_MASK_SDIO (0x1 << 16) /* RW */ + +#define MCI_CARD_DETECT 0x30 /* the card detect reg */ +#define MCI_DATA 0x34 /* the data FIFO access */ + +#define MCI_DEBUG 0x58 /* debug function */ + +/* MCI_DEBUG mask */ +#define MCI_DEBUG_ENABLE (0x1 << 0) /* RW */ +#define MCI_ALIVE_ENABLE (0x1 << 1) /* RW */ +#define MCI_ALIVE (0x1 << 2) /* RW */ +#define MCI_HAVE_LOG (0x1 << 3) /* RW */ +#define MCI_SIZE (0xf << 4) /* RW */ +#define MCI_ADDR (0x3fffff << 8) /* RW */ + +#define AP_TO_RV_MAX_DATA 64 +#define MCI_HW_RESET 0x00 +#define MCI_ADMA_RESET 0x01 +#define MCI_INIT 0x00 +#define MCI_INIT_HW 0x01 +#define MCI_DEINIT_HW 0x02 +#define MCI_START_CMD 0x03 +#define MCI_START_DATA 0x04 +#define MCI_OPS_SET_IOS 0x05 +#define MCI_SDIO_IRQ_EN 0x06 +#define MCI_OPS_SWITCH_VOLT 0x07 +#define MCI_GET_CD 0x00 +#define MCI_GET_CARD_BUSY 0x01 +#define MCI_GET_STATUS 0x02 +#define MCI_GET_RO 0x03 +#define MCI_CD_IRQ 0x00 +#define MCI_ERR_IRQ 0x01 +#define MCI_CMD_NEXT 0x02 +#define MCI_DATA_NEXT 0x03 + +#define RING_MAX 20 +#define RING_MAX_RV 10 + +#define COMPLETE_TIMEOUT 200 + +#define MMC_TX_HEAD 0x0 +#define MMC_TX_TAIL 0x4 +#define MMC_RX_HEAD 0x8 +#define MMC_RX_TAIL 0xc +#define MMC_TX_TAIL_INT (0x1 << 16) /* RW */ + +#define MMC_RV2AP_INT_MASK 0x028 +#define MMC_TXRING_HEAD_INT_MASK 0x1 /* RW */ +#define MMC_RXRING_TAIL_INT_MASK (0x1 << 1) /* RW */ + +#define MMC_RV2AP_INT 0x02c +#define MMC_TXRING_HEAD_INT 0x1 /* RW */ +#define MMC_RXRING_TAIL_INT (0x1 << 1) /* RW */ +/*--------------------------------------*/ +/* Structure Type */ +/*--------------------------------------*/ +/* Maximum segments assuming a 512KiB maximum requisition */ +/* size and a minimum4KiB page size. */ +#define MCI_MAX_SEGS 128 +/* ADMA2 64-bit DMA descriptor size */ +#define ADMA2_64_DESC_SZ 32 + +/* Each descriptor can transfer up to 4KB of data in chained mode */ +/*ADMA2 64-bit descriptor.*/ +struct phytium_adma2_64_desc { + u32 attribute; +#define IDMAC_DES0_DIC BIT(1) +#define IDMAC_DES0_LD BIT(2) +#define IDMAC_DES0_FD BIT(3) +#define IDMAC_DES0_CH BIT(4) +#define IDMAC_DES0_ER BIT(5) +#define IDMAC_DES0_CES BIT(30) +#define IDMAC_DES0_OWN BIT(31) + u32 NON1; + u32 len; + u32 NON2; + u32 addr_lo; /* Lower 32-bits of Buffer Address Pointer 1*/ + u32 addr_hi; /* Upper 32-bits of Buffer Address Pointer 1*/ + u32 desc_lo; /* Lower 32-bits of Next Descriptor Address */ + u32 desc_hi; /* Upper 32-bits of Next Descriptor Address */ +} __packed __aligned(4); + +struct phytium_mci_dma { + struct scatterlist *sg; /* I/O scatter list */ + /* ADMA descriptor table, pointer to adma_table array */ + struct phytium_adma2_64_desc *adma_table; + /* Mapped ADMA descr. table, the physical address of adma_table array */ + dma_addr_t adma_addr; + unsigned int desc_sz; /* ADMA descriptor size */ +}; + +enum adtc_t { + COMMOM_ADTC = 0, + BLOCK_RW_ADTC = 1 +}; + +enum phytmmc_msg_cmd_id { + PHYTMMC_MSG_CMD_DEFAULT = 0, + PHYTMMC_MSG_CMD_SET, + PHYTMMC_MSG_CMD_GET, + PHYTMMC_MSG_CMD_DATA, + PHYTMMC_MSG_CMD_REPORT +}; + +struct phyt_msg_info { + u8 reserved; + u8 seq; + u8 cmd_type; + u8 cmd_subid; + u16 len; + u8 status1; + u8 status0; +#define NOT_READY 0x0 +#define SUCCESS 0x1 +#define GOING 0x2 +#define GENERIC_ERROR 0x10 +#define TYPE_NOT_SUPPORTED 0x11 +#define CMD_NOT_SUPPORTED 0x12 +#define INVALID_PARAMETERS 0x13 + u8 data[56]; +}; + +struct phytium_mci_host { + struct device *dev; + struct mmc_host *mmc; + u32 caps; + u32 caps2; + spinlock_t lock; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + int error; + void __iomem *base; /* host base address */ + void __iomem *regf_base; /* regfile base address */ + void *adma_table1; + dma_addr_t adma_addr1; + struct phytium_mci_dma dma_rx; /* dma channel */ + struct phytium_mci_dma dma_tx; /* dma channel */ + struct phytium_mci_dma dma; /* dma channel */ + u64 dma_mask; + bool vqmmc_enabled; + u32 *sg_virt_addr; + enum adtc_t adtc_type; /* 0:common adtc cmd; 1:block r/w adtc cmd;*/ + struct timer_list hotplug_timer; + struct delayed_work req_timeout; + int irq; /* host interrupt */ + u32 current_rca; /*the current rca value*/ + u32 current_ios_clk; + u32 is_use_dma; + u32 is_device_x100; + struct clk *src_clk; /* phytium_mci source clock */ + unsigned long clk_rate; + unsigned long clk_div; + unsigned long irq_flags; + unsigned long flags; +#define MCI_CARD_NEED_INIT 1 + bool cmd_cs; /*volt switch cmd cs status*/ + bool use_hold; /*use hold*/ + bool clk_set; /*clock set*/ + s32 clk_smpl_drv_25m; + s32 clk_smpl_drv_50m; + s32 clk_smpl_drv_66m; + s32 clk_smpl_drv_100m; + struct phyt_msg_info msg; + struct phyt_msg_info rxmsg; + bool debug_enable; + bool alive_enable; + struct timer_list alive_timer; +}; + +int phyt_mci_common_probe(struct phytium_mci_host *host); +void phyt_mci_deinit_hw(struct phytium_mci_host *host); +int phyt_mci_runtime_suspend(struct device *dev); +int phyt_mci_runtime_resume(struct device *dev); +int phyt_mci_resume(struct device *dev); +int phyt_mci_suspend(struct device *dev); +int phytium_mci_set_debug_enable(struct phytium_mci_host *host, bool enable); +int phytium_mci_set_alive_enable(struct phytium_mci_host *host, bool enable); + +struct init_data_t { + uint32_t caps; + uint32_t clk_rate; +}; + +struct start_command_data_t { + uint32_t cmd_arg; + uint32_t cmd_flags; + uint32_t cmd_opcode; + uint32_t rawcmd; +}; + +struct start_data_data_t { + uint32_t data_flags; + uint32_t adtc_type; + uint64_t adma_addr; + uint32_t mrq_data_blksz; + uint32_t mrq_data_blocks; + uint32_t cmd_arg; + uint32_t rawcmd; +}; + +struct set_ios_data_t { + uint32_t ios_clock; + uint8_t ios_timing; + uint8_t ios_bus_width; + uint8_t ios_power_mode; +}; + +struct cmd_next_data_t { + uint32_t events; + uint32_t response0; + uint32_t response1; + uint32_t response2; + uint32_t response3; +}; + +struct err_irq_data_t { + uint32_t raw_ints; + uint32_t ints_mask; + uint32_t dmac_status; + uint32_t dmac_mask; + uint32_t opcode; +}; +#endif /* __PHYTIUM_MCI_HW_H */ -- Gitee From abc0adf579e0ae16aed169402ca792285b5cd710 Mon Sep 17 00:00:00 2001 From: Lai Xueyu Date: Thu, 21 Nov 2024 10:07:39 +0800 Subject: [PATCH 062/154] mmc: phytium: Fixed SD voltage switch issue Due to the switching time of some voltage switching circuits being much longer than 10ms, we add a delay in waiting for the completion of voltage switching before turning on the clock in the voltage switching logic during the initialization phase of the card. Mainline: Open-Source Signed-off-by: Lai Xueyu Signed-off-by: Wang Yinfeng Change-Id: Ic7a584cedbab742c12f01b2ce9f6c03135a21460 --- drivers/mmc/host/phytium-mci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/phytium-mci.c b/drivers/mmc/host/phytium-mci.c index 8b77868445..4bcdbfae27 100644 --- a/drivers/mmc/host/phytium-mci.c +++ b/drivers/mmc/host/phytium-mci.c @@ -245,6 +245,9 @@ static void phytium_mci_set_clk(struct phytium_mci_host *host, struct mmc_ios *i readl(host->base + MCI_UHS_REG_EXT), readl(host->base + MCI_CLKDIV), readl(host->base + MCI_CLK_DIVIDER), ((2 * (div & 0xff)) & 0xffff)); + if (cur_cmd_index == SD_SWITCH_VOLTAGE) + msleep(40); + sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); if (cur_cmd_index == SD_SWITCH_VOLTAGE) -- Gitee From ddf67d6fc84a414c5f536eae59d76086652703a9 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Mon, 10 Feb 2025 17:34:50 +0800 Subject: [PATCH 063/154] can: phytium: Modify the sampling points of the register The calculation formula for can sampling points is (ponits = (SS + PTS + PBS1) / (SS + PTS + PBS1 + PBS2)), In the registers, the values of PBS1 and PBS2 are stored reversed,so the result of points is wrong. This patch fixes this bug. Mainline: Open-Source Signed-off-by: Wu Jinyong Signed-off-by: Wang Yinfeng Change-Id: Icc9f190ce484fa93c4d04c640d0addd11c74a39f --- drivers/net/can/phytium/phytium_can.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/can/phytium/phytium_can.c b/drivers/net/can/phytium/phytium_can.c index 1b2859ac10..3a2de319c1 100644 --- a/drivers/net/can/phytium/phytium_can.c +++ b/drivers/net/can/phytium/phytium_can.c @@ -818,18 +818,18 @@ static int phytium_can_set_bittiming(struct net_device *dev) /* Setting Time Segment 1 in BTR Register */ btr |= (bt->prop_seg - 1) << 2; - btr |= (bt->phase_seg1 - 1) << 5; + btr |= (bt->phase_seg1 - 1) << 8; /* Setting Time Segment 2 in BTR Register */ - btr |= (bt->phase_seg2 - 1) << 8; + btr |= (bt->phase_seg2 - 1) << 5; /* Setting Synchronous jump width in BTR Register */ btr |= (bt->sjw - 1); dbtr = (dbt->brp - 1) << 16; dbtr |= (dbt->prop_seg - 1) << 2; - dbtr |= (dbt->phase_seg1 - 1) << 5; - dbtr |= (dbt->phase_seg2 - 1) << 8; + dbtr |= (dbt->phase_seg1 - 1) << 8; + dbtr |= (dbt->phase_seg2 - 1) << 5; dbtr |= (dbt->sjw - 1); if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { -- Gitee From 88c7e942fbf9d18a3134b14010e0d164db31131c Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Fri, 14 Feb 2025 17:57:23 +0800 Subject: [PATCH 064/154] dt-bindings: Add bindings for Phytium I3C controller This patch documents the DT binding for the Phytium I3C controller. Mainline: NA Signed-off-by: Wu Jinyong Signed-off-by: Wang Yinfeng Change-Id: I6ac244de4483d67111e0724515fb7cbbc1d7238c --- .../bindings/i3c/phytium,i3c-master.yaml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/i3c/phytium,i3c-master.yaml diff --git a/Documentation/devicetree/bindings/i3c/phytium,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/phytium,i3c-master.yaml new file mode 100644 index 0000000000..0e004735fd --- /dev/null +++ b/Documentation/devicetree/bindings/i3c/phytium,i3c-master.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +* Phytium I3C controller + +This I3C controller is for Phytium Soc. + +Required properties: +- compatible: Shall be "phytium,cdns-i3c-master" +- clocks: Shall reference the pclk and sysclk +- clock-names: Shall contain "pclk" and "sysclk" +- interrupts: The interrupt line connected to this I3C master +- reg: I3C master registers +- #address-cells: Shall be set to 1 +- #size-cells: Shall be set to 0 +- i2c-scl-hz: I2C CLK frequency +- i3c-scl-hz: I3C CLK frequency + +Example: + + i3c-master@28045000 { + compatible = "phytium,cdns-i3c-master"; + reg = <0x0 0x28045000 0x0 0x1000>; + interrupts = ; + clocks = <&coreclock>, <&i3csysclock>; + clock-names = "pclk", "sysclk"; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-hz = <400000>; + i3c-scl-hz = <1000000>; + + nunchuk: nunchuk@52 { + compatible = "nintendo,nunchuk"; + reg = <0x52 0x0 0x10>; + }; + }; -- Gitee From 267b91d04f846c1a1c126e4f4545f9942e16b216 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Fri, 28 Feb 2025 17:03:11 +0800 Subject: [PATCH 065/154] dt-bindings:reset: Add bindings for phytium reset controller. This patch documents the DT binding for the Phytium reset driver. Mainline: NA Signed-off-by: Wu Jinyong Signed-off-by: Wang yinfeng Change-Id: Ic32d441db5c02156926108d3bd6ae998a89c88c3 --- .../bindings/reset/phytium_reset.yaml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Documentation/devicetree/bindings/reset/phytium_reset.yaml diff --git a/Documentation/devicetree/bindings/reset/phytium_reset.yaml b/Documentation/devicetree/bindings/reset/phytium_reset.yaml new file mode 100644 index 0000000000..82171da5f5 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/phytium_reset.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +title: Phytium Reset Controller +====================================== + +This binding describes a reset-controller for Phytium SoCs. + +Required properties: +- compatible: + Usage: required + Value type: + Definition: must be: + "phytium,reset" + +- reg: + Usage: required + Value type: + Definition: must specify the base address and size of the register + space. + +- #reset-cells: + Usage: required + Value type: + Definition: must be 1; cell entry represents the reset index. + +Example: + +reset: reset@2807e000 { + compatible = "phytium,reset"; + reg = <0x0 0x2807e000 0x0 0x10>; + #reset-cells = <1>; +}; + +Specifying reset lines connected to IP modules +============================================== + +Device nodes that need access to reset lines should +specify them as a reset phandle in their corresponding node. + +Example: + +i2c0: i2c@28011000 { + ... + + resets = <&reset 0>; + + ... +}; -- Gitee From 14cd7e866576fbbe02f9d78c16335ef7b03d4086 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Tue, 18 Feb 2025 09:03:28 +0800 Subject: [PATCH 066/154] reset:phytium: Add the driver for reset controller. This patch adds the driver for phytium reset controller. Mainline: NA Signed-off-by: Wu Jinyong Signed-off-by: Wang yinfeng Change-Id: I3b254df51e2bfb9e84c90ca877c9f961072ccd65 --- drivers/reset/Kconfig | 8 +++ drivers/reset/Makefile | 1 + drivers/reset/reset-phytium.c | 127 ++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 drivers/reset/reset-phytium.c diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 147543ad30..99a3ebd0b8 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -221,6 +221,14 @@ config RESET_TI_SYSCON you wish to use the reset framework for such memory-mapped devices, say Y here. Otherwise, say N. +config RESET_PHYTIUM + bool "PHYTIUM Reset Driver" + default ARCH_PHYTIUM + help + This enables the reset driver for phytium SoCs. If you wish + to use the reset framework for such memory-mapped devices, + say Y here. Otherwise, say N. + config RESET_UNIPHIER tristate "Reset controller driver for UniPhier SoCs" depends on ARCH_UNIPHIER || COMPILE_TEST diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 16947610cc..7f134d6e80 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o +obj-$(CONFIG_RESET_PHYTIUM) += reset-phytium.o obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o diff --git a/drivers/reset/reset-phytium.c b/drivers/reset/reset-phytium.c new file mode 100644 index 0000000000..552b3d2481 --- /dev/null +++ b/drivers/reset/reset-phytium.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium Reset Driver. + * + * Copyright (c) 2024, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHYTIUM_RESET_MAX_CNTS (19) +#define PHYTIUM_I2C_RESET_ID0 (3) +#define PHYTIUM_RESET_OFFSET_0 (0) +#define PHYTIUM_RESET_OFFSET_1 (4) + +struct reset_phytium_dev { + void __iomem *base; + struct device *dev; + struct reset_controller_dev rcdev; +}; + +struct reset_reg_info { + u32 reg_offset; + u32 data; +}; +static struct reset_reg_info reset_mng[PHYTIUM_RESET_MAX_CNTS] = { + {PHYTIUM_RESET_OFFSET_0, (u32)BIT(28)}, + {PHYTIUM_RESET_OFFSET_0, (u32)BIT(29)}, + {PHYTIUM_RESET_OFFSET_0, (u32)BIT(30)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(16)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(17)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(18)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(19)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(20)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(21)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(22)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(23)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(24)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(25)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(26)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(27)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(28)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(29)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(30)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(31)} + }; + +static inline struct reset_phytium_dev * +to_reset_phytium_data(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct reset_phytium_dev, rcdev); +} + +static int reset_phytium_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_phytium_dev *rdev = to_reset_phytium_data(rcdev); + u32 reg_val, reset_reg_val; + + if (id >= PHYTIUM_RESET_MAX_CNTS) { + dev_err(rdev->dev, "The reset id is out of range %ld\n", id); + return -EINVAL; + } + + reg_val = readl_relaxed(rdev->base + reset_mng[id].reg_offset); + reset_reg_val = reg_val; + reg_val &= ~reset_mng[id].data; + writel_relaxed(reg_val, rdev->base + reset_mng[id].reg_offset); + writel_relaxed(reset_reg_val, rdev->base + reset_mng[id].reg_offset); + + return 0; +} + +const struct reset_control_ops reset_phytium_ops = { + .reset = reset_phytium_reset, +}; +EXPORT_SYMBOL_GPL(reset_phytium_ops); + +static const struct of_device_id reset_phytium_dt_ids[] = { + { .compatible = "phytium,reset", }, + { /* sentinel */ }, +}; + +static int reset_phytium_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct reset_phytium_dev *rdev; + struct resource *res; + + rdev = devm_kzalloc(dev, sizeof(*rdev), GFP_KERNEL); + if (!rdev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rdev->base = devm_ioremap_resource(dev, res); + if (IS_ERR(rdev->base)) + return PTR_ERR(rdev->base); + + rdev->rcdev.owner = THIS_MODULE; + rdev->rcdev.nr_resets = PHYTIUM_RESET_MAX_CNTS; + rdev->rcdev.ops = &reset_phytium_ops; + rdev->rcdev.of_node = dev->of_node; + rdev->dev = &pdev->dev; + + return devm_reset_controller_register(dev, &rdev->rcdev); +} + +static struct platform_driver reset_phytium_driver = { + .probe = reset_phytium_probe, + .driver = { + .name = "phytium-reset", + .of_match_table = reset_phytium_dt_ids, + }, +}; +builtin_platform_driver(reset_phytium_driver); + +MODULE_AUTHOR("Wu Jinyong "); +MODULE_DESCRIPTION("Phytium reset"); +MODULE_LICENSE("GPL"); -- Gitee From 0f8f9ba0ca05b47a4c3af8d7f65da7a6fa19a4c9 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Tue, 18 Feb 2025 09:12:41 +0800 Subject: [PATCH 067/154] dt-bindings: i2c-2.0: Add phytium i2c-2.0 yaml This patch adds the dt-bindings of phytium i2c_v2 controller. Mainline: NA Signed-off-by: Wu Jinyong Signed-off-by: Wang Yinfeng Change-Id: I60f3ba63f74744cf0e36aacdd78fda2bd91c0709 --- .../bindings/i2c/phytium,i2c-2.0.yaml | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/phytium,i2c-2.0.yaml diff --git a/Documentation/devicetree/bindings/i2c/phytium,i2c-2.0.yaml b/Documentation/devicetree/bindings/i2c/phytium,i2c-2.0.yaml new file mode 100644 index 0000000000..e02036e337 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/phytium,i2c-2.0.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- + +title: Phytium I2C Controller + +maintainers: + - WuJinyong + +properties: + compatible: + - description: Phytium I2C controller + const: phytium,i2c-2.0 + + reg: + minItems: 1 + items: + - description: Offset and length of the memory mapped registers + - description: Offset and length of the memory mapped share memory address + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + items: + - description: I2C controller reference clock source + + clock-frequency: + description: Desired I2C bus clock frequency in Hz + enum: [100000, 400000, 1000000, 3400000] + default: 400000 + + i2c-sda-hold-time-ns: + description: | + The property should contain the SDA hold time in nanoseconds. This option + is only supported in hardware blocks version 1.11a or newer or on + Microsemi SoCs. + + i2c-scl-falling-time-ns: + description: | + The property should contain the SCL falling time in nanoseconds. + This value is used to compute the tLOW period. + default: 300 + + i2c-sda-falling-time-ns: + description: | + The property should contain the SDA falling time in nanoseconds. + This value is used to compute the tHIGH period. + default: 300 + +unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +examples: + - | + i2c@0x2701a000 { + compatible = "phytium,i2c-2.0"; + reg = <0x0 0x2701a000 0x0 0x1000 0x0 0x2701e000 0x0 0x200>; + interrupts = ; + clock-frequency = <400000>; + }; + - | + i2c@2701a000 { + compatible = "phytium,i2c-2.0"; + reg = <0x0 0x2701a000 0x0 0x1000 0x0 0x2701e000 0x0 0x200>; + interrupts = ; + clock-frequency = <400000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <300>; + i2c-scl-falling-time-ns = <300>; + }; + - | + i2c@2701a000 { + compatible = "phytium,i2c-2.0"; + reg = <0x0 0x2701a000 0x0 0x1000 0x0 0x2701e000 0x0 0x200>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <400000>; + clocks = <&i2cclk>; + interrupts = ; + + eeprom@64 { + compatible = "atmel,24c02"; + reg = <0x64>; + }; + }; +... -- Gitee From bbb0a510b3177ce152cef7f417c4af365f3ef4c0 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Tue, 18 Feb 2025 09:27:01 +0800 Subject: [PATCH 068/154] i2c-2.0: phytium: Add phytium i2c_v2 driver This patch adds driver support for phytium i2c_v2 controller. Mainline: Open-Source Signed-off-by: Wu Jinyong Signed-off-by: Wang Yinfeng Change-Id: Id0ac34384e2c2ed3bef1e5127a12399415aeaa6d --- drivers/i2c/busses/Kconfig | 2 + drivers/i2c/busses/Makefile | 2 +- drivers/i2c/busses/phytium_i2c_v2/Kconfig | 16 + drivers/i2c/busses/phytium_i2c_v2/Makefile | 7 + .../busses/phytium_i2c_v2/i2c-phyt-common.c | 512 +++++++++++++++ .../i2c/busses/phytium_i2c_v2/i2c-phyt-core.h | 512 +++++++++++++++ .../busses/phytium_i2c_v2/i2c-phyt-master.c | 581 ++++++++++++++++++ .../busses/phytium_i2c_v2/i2c-phyt-platform.c | 525 ++++++++++++++++ .../busses/phytium_i2c_v2/i2c-phyt-slave.c | 283 +++++++++ 9 files changed, 2439 insertions(+), 1 deletion(-) create mode 100644 drivers/i2c/busses/phytium_i2c_v2/Kconfig create mode 100644 drivers/i2c/busses/phytium_i2c_v2/Makefile create mode 100644 drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c create mode 100644 drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h create mode 100644 drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c create mode 100644 drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-platform.c create mode 100644 drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-slave.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index c2bdd7b92e..76b3fcf2d1 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1234,6 +1234,8 @@ config I2C_PHYTIUM_PLATFORM This driver can also be built as a module. If so, the module will be called i2c-phytium-platform. +source "drivers/i2c/busses/phytium_i2c_v2/Kconfig" + comment "External I2C/SMBus adapter drivers" config I2C_DIOLAN_U2C diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 78372fb658..c7818ae750 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -152,5 +152,5 @@ obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_FSI) += i2c-fsi.o - +obj-$(CONFIG_I2C_V2_PLATFORM) += phytium_i2c_v2/ ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/phytium_i2c_v2/Kconfig b/drivers/i2c/busses/phytium_i2c_v2/Kconfig new file mode 100644 index 0000000000..c08fd8f9f0 --- /dev/null +++ b/drivers/i2c/busses/phytium_i2c_v2/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Sensor device configuration +# + +config I2C_V2_PLATFORM + tristate "Phytium I2C_V2 Platform" + depends on (ACPI && COMMON_CLK) || !ACPI + select I2C_SMBUS + + help + If you say yes to this option, support will be included for the + Phytium I2C adapter. Only master mode is supported. + + This driver can also be built as a module.If so,the module + will be called i2c-phytium-v2-platform. diff --git a/drivers/i2c/busses/phytium_i2c_v2/Makefile b/drivers/i2c/busses/phytium_i2c_v2/Makefile new file mode 100644 index 0000000000..74161298d7 --- /dev/null +++ b/drivers/i2c/busses/phytium_i2c_v2/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the i2c bus drivers. +# + +obj-$(CONFIG_I2C_V2_PLATFORM) += i2c-phytium-v2-platform.o +i2c-phytium-v2-platform-objs := i2c-phyt-platform.o i2c-phyt-common.o i2c-phyt-master.o i2c-phyt-slave.o diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c new file mode 100644 index 0000000000..023b3c0e78 --- /dev/null +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c @@ -0,0 +1,512 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium I2C adapter driver. + * + * Copyright (C) 2023-2024, Phytium Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phyt-core.h" + +#define FT_LOG_LINE_MAX_LEN 400 +#define FT_LOG_MAX_SIZE 8192 + +static void i2c_phyt_send_msg(struct i2c_phyt_dev *dev, + struct phyt_msg_info *set_msg, bool complete_flag); + +static char *i2c_ft_abort_sources[] = { + [FT_I2C_TIMEOUT] = "I2C cmd not acknowledged", + [FT_I2C_CNT_ERR] = "I2C timings count error", + [FT_I2C_TX_ABRT] = "Tx abort", + [FT_I2C_INT_ERR] = "Interrupt error", + [FT_I2C_BLOCK_SIZE] = "smbus block error", + [FT_I2C_INVALID_ADDR] = "slave address invalid", + [FT_I2C_CHECK_STATUS_ERR] = "Uncomplete status", +}; + +u32 i2c_phyt_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) +{ + if (cond) + return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset; + else + return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000 - 3 + offset; +} + +u32 i2c_phyt_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) +{ + return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset; +} + +unsigned long i2c_phyt_clk_rate(struct i2c_phyt_dev *dev) +{ + if (WARN_ON_ONCE(!dev->get_clk_rate_khz)) + return 0; + + return dev->get_clk_rate_khz(dev); +} + +int i2c_phyt_prepare_clk(struct i2c_phyt_dev *dev, bool prepare) +{ + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + + if (prepare) + return clk_prepare_enable(dev->clk); + + clk_disable_unprepare(dev->clk); + + return 0; +} + +void i2c_phyt_handle_tx_abort(struct i2c_phyt_dev *dev) +{ + unsigned long abort_source = dev->abort_source; + int i; + + for_each_set_bit(i, &abort_source, ARRAY_SIZE(i2c_ft_abort_sources)) + dev_info(dev->dev, "%s: %s\n", __func__, i2c_ft_abort_sources[i]); +} + +u32 i2c_phyt_func(struct i2c_adapter *adapter) +{ + struct i2c_phyt_dev *dev = i2c_get_adapdata(adapter); + + return dev->functionality; +} + +void i2c_phyt_common_watchdog(struct i2c_phyt_dev *dev) +{ + u32 reg; + + reg = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_DEBUG); + + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_DEBUG, + reg | FT_I2C_REGFILE_HEARTBIT_VAL); +} + +void i2c_phyt_enable_alive(struct i2c_phyt_dev *dev) +{ + u32 reg; + + reg = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_DEBUG); + + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_DEBUG, + reg | FT_I2C_REGFILE_HEARTBIT_VAL); + + reg = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_DEBUG); + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_DEBUG, + reg | FT_I2C_LOG_ADDR_LOG_ALIVE); +} + +void i2c_phyt_disable_alive(struct i2c_phyt_dev *dev) +{ + u32 reg; + + reg = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_DEBUG); + reg &= ~FT_I2C_LOG_ADDR_LOG_ALIVE; + + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_DEBUG, reg); +} + +void i2c_phyt_enable_debug(struct i2c_phyt_dev *dev) +{ + u32 reg; + + reg = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_DEBUG); + + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_DEBUG, + reg | FT_I2C_LOG_ADDR_LOG_DEBUG); +} + +void i2c_phyt_disable_debug(struct i2c_phyt_dev *dev) +{ + u32 reg; + + reg = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_DEBUG); + reg &= ~FT_I2C_LOG_ADDR_LOG_DEBUG; + + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_DEBUG, reg); +} + + +void i2c_phyt_show_log(struct i2c_phyt_dev *dev) +{ + u32 i, reg, len; + u8 *plog; + + if (!dev->log_addr) + return; + + /*set lock*/ + reg = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_DEBUG); + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_DEBUG, + reg | FT_I2C_LOG_ADDR_LOCK_VAL); + + plog = dev->log_addr; + if (reg & FT_I2C_LOG_ADDR_LOG_FLAG) { + len = strnlen((char *)dev->log_addr, FT_LOG_MAX_SIZE); + dev_info(dev->dev, "log len :%d,addr: 0x%llx,size:%d\n", len, (u64)dev->log_addr, + dev->log_size); + if (len > FT_LOG_LINE_MAX_LEN) { + for (i = 0; i < len; i += FT_LOG_LINE_MAX_LEN) + dev_info(dev->dev, "(log)%.*s\n", FT_LOG_LINE_MAX_LEN, &plog[i]); + } else { + dev_info(dev->dev, "(log)%.*s\n", FT_LOG_LINE_MAX_LEN, &plog[0]); + } + + for (i = 0; i < dev->log_size; i++) + plog[i] = 0; + } + /*clear log flag*/ + reg &= ~FT_I2C_LOG_ADDR_LOG_FLAG; + /*unset lock*/ + reg &= ~FT_I2C_LOG_ADDR_LOCK_VAL; + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_DEBUG, reg); +} + +int i2c_phyt_malloc_log_mem(struct i2c_phyt_dev *dev) +{ + u32 reg; + u64 phy_addr; + + reg = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_DEBUG); + phy_addr = ((reg & FT_I2C_LOG_ADDR_MASK) >> FT_I2C_LOG_ADDR_LOW_SHIFT) << + FT_I2C_LOG_ADDR_SHIFT; + dev->log_size = ((reg & FT_I2C_LOG_SIZE_MASK) >> FT_I2C_LOG_SIZE_LOW_SHIFT) * 1024; + + dev->log_addr = devm_ioremap(dev->dev, phy_addr, dev->log_size); + + if (IS_ERR(dev->log_addr)) { + dev_err(dev->dev, "log_addr is err\n"); + return -ENOMEM; + } + + return 0; +} + +void i2c_phyt_common_regfile_disable_int(struct i2c_phyt_dev *dev) +{ + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_RV2AP_INTR_MASK, + FT_I2C_REGFILE_DISABLE_INTR_VAL); +} + +void i2c_phyt_common_regfile_enable_int(struct i2c_phyt_dev *dev) +{ + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_RV2AP_INTR_MASK, + FT_I2C_REGFILE_ENABLE_INTR_VAL); +} + +void i2c_phyt_common_regfile_clear_rv2ap_int(struct i2c_phyt_dev *dev, + u32 stat) +{ + /*For Desktop type,this opt is useful*/ + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_RV2AP_INTR_STAT, + stat & ~FT_I2C_RV2AP_INTR_BIT4); + /*For Service type,this opt is useful*/ + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_RV2AP_INTR_CLEAR, + stat | FT_I2C_RV2AP_INTR_BIT4); +} + +void i2c_phyt_common_set_cmd(struct i2c_phyt_dev *dev, + struct phyt_msg_info *i2c_mng_msg, u8 cmd, u8 sub_cmd) +{ + i2c_mng_msg->head.cmd_type = cmd; + i2c_mng_msg->head.cmd_subid = sub_cmd; + i2c_mng_msg->head.status0 = FT_I2C_MSG_COMPLETE_UNKNOWN; +} + +void i2c_phyt_set_cmd8(struct i2c_phyt_dev *dev, + u8 sub_cmd, u8 data) +{ + struct phyt_msg_info i2c_mng_msg; + + memset(&i2c_mng_msg, 0, sizeof(i2c_mng_msg)); + + i2c_phyt_common_set_cmd(dev, &i2c_mng_msg, PHYTI2C_MSG_CMD_SET, sub_cmd); + i2c_mng_msg.data[0] = data; + dev->total_shmem_len = FT_I2C_MSG_CMDDATA_SIZE; + + i2c_phyt_send_msg(dev, &i2c_mng_msg, true); +} + +void i2c_phyt_set_cmd16(struct i2c_phyt_dev *dev, + u8 sub_cmd, u16 data) +{ + struct phyt_msg_info i2c_mng_msg; + u16 *ctrl = (u16 *)&i2c_mng_msg.data[0]; + + memset(&i2c_mng_msg, 0, sizeof(i2c_mng_msg)); + + i2c_phyt_common_set_cmd(dev, &i2c_mng_msg, PHYTI2C_MSG_CMD_SET, sub_cmd); + *ctrl = data; + dev->total_shmem_len = FT_I2C_MSG_CMDDATA_SIZE; + + i2c_phyt_send_msg(dev, &i2c_mng_msg, true); +} + +void i2c_phyt_set_cmd32(struct i2c_phyt_dev *dev, + u8 sub_cmd, u32 data) +{ + struct phyt_msg_info i2c_mng_msg; + u32 *cmd_data = (u32 *)&i2c_mng_msg.data[0]; + + memset(&i2c_mng_msg, 0, sizeof(i2c_mng_msg)); + + i2c_phyt_common_set_cmd(dev, &i2c_mng_msg, PHYTI2C_MSG_CMD_SET, sub_cmd); + *cmd_data = data; + dev->total_shmem_len = FT_I2C_MSG_CMDDATA_SIZE; + + i2c_phyt_send_msg(dev, &i2c_mng_msg, true); +} + +void i2c_phyt_data_cmd8_array(struct i2c_phyt_dev *dev, + u8 sub_cmd, u8 *data, int len) +{ + struct phyt_msg_info i2c_mng_msg; + + memset(&i2c_mng_msg, 0, sizeof(i2c_mng_msg)); + + /*set cmd*/ + i2c_phyt_common_set_cmd(dev, &i2c_mng_msg, PHYTI2C_MSG_CMD_DATA, sub_cmd); + /*set data*/ + memcpy(&i2c_mng_msg.data[0], &data[0], len); + /*set len*/ + dev->total_shmem_len = FT_I2C_MSG_CMDDATA_SIZE; + + i2c_phyt_send_msg(dev, &i2c_mng_msg, false); +} + +void i2c_phyt_default_cfg(struct i2c_phyt_dev *dev, + struct i2c_ft_default_cfg_msg *buf) +{ + struct phyt_msg_info i2c_mng_msg; + + memset(&i2c_mng_msg, 0, sizeof(i2c_mng_msg)); + + i2c_phyt_common_set_cmd(dev, &i2c_mng_msg, PHYTI2C_MSG_CMD_DEFAULT, + PHYTI2C_MSG_CMD_DEFAULT_RESUME); + memcpy(&i2c_mng_msg.data[0], (char *)buf, sizeof(struct i2c_ft_default_cfg_msg)); + dev->total_shmem_len = FT_I2C_MSG_CMDDATA_SIZE; + + i2c_phyt_send_msg(dev, &i2c_mng_msg, true); +} + +void i2c_phyt_disable_int(struct i2c_phyt_dev *dev) +{ + i2c_phyt_set_cmd32(dev, PHYTI2C_MSG_CMD_SET_INTERRUPT, 0); +} + +void i2c_phyt_trig_rv_intr(struct i2c_phyt_dev *dev) +{ + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_AP2RV_INTR_STATE, + FT_I2C_REGFILE_AP2RV_SET_INTR_VAL); +} + +static void i2c_phyt_send_msg(struct i2c_phyt_dev *dev, + struct phyt_msg_info *set_msg, bool complete_flag) +{ + u64 *p_dest_msg = (u64 *)dev->tx_shmem_addr; + u64 *p_src_msg = (u64 *)set_msg; + int i, len; + + if (complete_flag) { + reinit_completion(&dev->cmd_complete); + dev->complete_flag = true; + dev->mng.is_need_check = false; + } + len = DIV_ROUND_UP(dev->total_shmem_len, 8); + + for (i = 0; i < len; i++) + p_dest_msg[i] = p_src_msg[i]; + /*For config cmd,no use tail and head,set this value to zero*/ + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_TAIL, 0); + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_HEAD, 0); + dev->mng.tx_cmd_cnt = 1; + dev->mng.cur_cmd_cnt = 0; + i2c_phyt_trig_rv_intr(dev); +} + +void i2c_phyt_notify_rv(struct i2c_phyt_dev *dev, + bool need_check) +{ + reinit_completion(&dev->cmd_complete); + dev->complete_flag = true; + + dev->mng.is_need_check = need_check; + i2c_phyt_trig_rv_intr(dev); +} + +void i2c_phyt_set_suspend(struct i2c_phyt_dev *dev) +{ + i2c_phyt_set_cmd32(dev, PHYTI2C_MSG_CMD_SET_SUSPEND, 0); + + if (i2c_phyt_check_result(dev)) + dev_warn(dev->dev, "fail to set suspend\n"); +} + +void i2c_phyt_set_sda_hold(struct i2c_phyt_dev *dev, u32 data) +{ + i2c_phyt_set_cmd32(dev, PHYTI2C_MSG_CMD_SET_SDA_HOLD, data); +} + +int i2c_phyt_check_status(struct i2c_phyt_dev *dev, + struct phyt_msg_info *msg) +{ + int result = FT_I2C_CHECK_STATUS_ERR; + + dev->abort_source = 1 << FT_I2C_CHECK_STATUS_ERR; + /* RV Has update the result,but not sure the result is OK*/ + if (msg->head.status0 != FT_I2C_MSG_COMPLETE_UNKNOWN) { + if (!dev->mng.is_need_check || + ((msg->head.status0 == FT_I2C_MSG_COMPLETE_OK) && + (msg->head.status1 == FT_I2C_SUCCESS))) { + dev->abort_source = 0; + result = FT_I2C_SUCCESS; + } else { + i2c_phyt_show_log(dev); + dev->abort_source = 1 << msg->head.status1; + } + } + + return result; +} + +int i2c_phyt_check_result(struct i2c_phyt_dev *dev) +{ + if (!wait_for_completion_timeout(&dev->cmd_complete, + dev->adapter.timeout)) { + dev_err(dev->dev, "check timed out\n"); + i2c_phyt_show_log(dev); + dev->abort_source = BIT(FT_I2C_TIMEOUT); + return -EINVAL; + } + if (!dev->abort_source) + return 0; + return -EINVAL; +} + +void i2c_phyt_disable(struct i2c_phyt_dev *dev) +{ + i2c_phyt_set_module_en(dev, FT_I2C_ADAPTER_MODULE_OFF); + + i2c_phyt_check_result(dev); +} + +void i2c_phyt_set_module_en(struct i2c_phyt_dev *dev, u8 data) +{ + i2c_phyt_set_cmd8(dev, PHYTI2C_MSG_CMD_SET_MODULE_EN, data); +} + +void i2c_phyt_set_int_tl(struct i2c_phyt_dev *dev, + u8 tx_threshold, u8 rx_threshold) +{ + struct phyt_msg_info i2c_mng_msg; + struct i2c_phyt_fifo_threshold *ctrl = + (struct i2c_phyt_fifo_threshold *)&i2c_mng_msg.data[0]; + + memset(&i2c_mng_msg, 0, sizeof(i2c_mng_msg)); + + i2c_phyt_common_set_cmd(dev, &i2c_mng_msg, PHYTI2C_MSG_CMD_SET, + PHYTI2C_MSG_CMD_SET_INT_TL); + + ctrl->tx_fifo_threshold = tx_threshold; + ctrl->rx_fifo_threshold = rx_threshold; + + dev->total_shmem_len = FT_I2C_MSG_CMDDATA_SIZE; + + i2c_phyt_send_msg(dev, &i2c_mng_msg, true); +} + +void i2c_phyt_set_int_interrupt(struct i2c_phyt_dev *dev, + u32 is_enable, u32 intr_mask) +{ + struct phyt_msg_info i2c_mng_msg; + u32 *data = (u32 *)i2c_mng_msg.data; + + memset(&i2c_mng_msg, 0, sizeof(i2c_mng_msg)); + + i2c_phyt_common_set_cmd(dev, &i2c_mng_msg, PHYTI2C_MSG_CMD_SET, + PHYTI2C_MSG_CMD_SET_INTERRUPT); + + data[0] = is_enable; + data[1] = intr_mask; + dev->total_shmem_len = FT_I2C_MSG_CMDDATA_SIZE; + + i2c_phyt_send_msg(dev, &i2c_mng_msg, true); +} + +static int i2c_phyt_report_cmd_handle(struct i2c_phyt_dev *dev, + struct phyt_msg_info *rx_msg, u32 head, u32 tail) +{ + u16 sub_cmd = rx_msg->head.cmd_subid; + int ret = -EINVAL; + + switch (sub_cmd) { + case PHYTI2C_MSG_CMD_SLAVE_EVENT: + ret = i2c_phyt_slave_event_process(dev, rx_msg, head, tail); + break; + case PHYTI2C_MSG_CMD_SMBALERT_IN_N: + ret = i2c_phyt_master_smbus_alert_process(dev); + break; + default: + break; + } + + return ret; +} + +void i2c_phyt_slave_isr_handle(struct i2c_phyt_dev *dev) +{ + struct phyt_msg_info *rx_msg = (struct phyt_msg_info *)dev->rx_shmem_addr; + struct phyt_msg_info msg_buf; + u32 head, tail; + + tail = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_TAIL); + head = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_HEAD); + + /*There is only one rx_msg*/ + memcpy(&msg_buf, &rx_msg[0], sizeof(msg_buf)); + + if (msg_buf.head.cmd_type == PHYTI2C_MSG_CMD_REPORT) + i2c_phyt_report_cmd_handle(dev, &msg_buf, head, tail); +} + +static void i2c_phyt_set_complete(struct i2c_phyt_dev *dev) +{ + struct phyt_msg_info *set_msg = (struct phyt_msg_info *)dev->rx_shmem_addr; + struct phyt_msg_info tmp_msg; + + /*There is only one rx_msg*/ + memcpy(&tmp_msg, &set_msg[0], sizeof(struct phyt_msg_info)); + tmp_msg.head.status0 = FT_I2C_MSG_COMPLETE_OK; + tmp_msg.head.cmd_type = 0xff; + tmp_msg.head.cmd_subid = 0xff; + memcpy(&set_msg[0], &tmp_msg, sizeof(struct phyt_msg_info)); +} + +void i2c_phyt_master_isr_handle(struct i2c_phyt_dev *dev) +{ + struct phyt_msg_info *rx_msg = (struct phyt_msg_info *)dev->rx_shmem_addr; + struct phyt_msg_info msg_buf; + + memcpy(&msg_buf, &rx_msg[0], sizeof(struct phyt_msg_info)); + i2c_phyt_set_complete(dev); + + if (msg_buf.head.cmd_type == PHYTI2C_MSG_CMD_REPORT) + i2c_phyt_report_cmd_handle(dev, &msg_buf, 0, 0); +} + +MODULE_AUTHOR("Wu Jinyong "); +MODULE_DESCRIPTION("Phytium I2C bus adapter core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h new file mode 100644 index 0000000000..dbe540b9ba --- /dev/null +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h @@ -0,0 +1,512 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Phytium I2C adapter driver. + * + * Copyright (C) 2023-2024, Phytium Technology Co., Ltd. + */ +#ifndef I2C_PHYT_CORE_H__ +#define I2C_PHYT_CORE_H__ + +#include +#include +#include +#include +#include +#include + +#define I2C_PHYTIUM_V2_DRV_VERSION "1.0.1" + +#define FT_I2C_MSG_UNIT_SIZE 10 +#define FT_I2C_DATA_RESV_LEN 2 +#define FT_I2C_SHMEM_RX_ADDR_OFFSET 384 +#define FT_I2C_MSG_CMDDATA_SIZE 64 +#define FT_I2C_IN_INTERRUPT_MODE 0 +#define FT_I2C_ENABLE_INTERRUPT 1 +#define FT_I2C_REGFILE_AP2RV_INTR_MASK 0x20 +#define FT_I2C_REGFILE_AP2RV_INTR_STATE 0x24 +#define FT_I2C_REGFILE_RV2AP_INTR_MASK 0x28 +#define FT_I2C_REGFILE_RV2AP_INTR_STAT 0x2C +#define FT_I2C_REGFILE_RING 0x48 +#define FT_I2C_REGFILE_DEBUG 0x58 +#define FT_I2C_REGFILE_RV2AP_INTR_CLEAR 0x74 +#define FT_I2C_TRANS_FRAME_START (BIT(0)) +#define FT_I2C_TRANS_FRAME_END (BIT(1)) +#define FT_I2C_TRANS_FRAME_RESTART (BIT(2)) +#define FT_I2C_REGFILE_TX_RING_OFFSET 8 +#define FT_I2C_REGFILE_TX_RING_MASK GENMASK(13, 8) + +#define FT_I2C_REGFILE_HEARTBIT_VAL BIT(2) +#define FT_I2C_LOG_SIZE_LOW_SHIFT 4 +#define FT_I2C_LOG_SIZE_MASK GENMASK(7, FT_I2C_LOG_SIZE_LOW_SHIFT) +#define FT_I2C_LOG_ADDR_SHIFT 10 +#define FT_I2C_LOG_ADDR_LOW_SHIFT 8 +#define FT_I2C_LOG_ADDR_MASK GENMASK(29, FT_I2C_LOG_ADDR_LOW_SHIFT) +#define FT_I2C_LOG_ADDR_LOCK_VAL BIT(31) +#define FT_I2C_LOG_ADDR_LOG_FLAG BIT(3) +#define FT_I2C_LOG_ADDR_LOG_ALIVE BIT(1) +#define FT_I2C_LOG_ADDR_LOG_DEBUG BIT(0) +#define FT_I2C_REGFILE_DISABLE_INTR_VAL GENMASK(31, 0) +#define FT_I2C_REGFILE_ENABLE_INTR_VAL 0 +#define FT_I2C_REGFILE_AP2RV_SET_INTR_VAL BIT(4) + +#define FT_I2C_RV2AP_INTR_BIT4 BIT(4) + +#define FT_I2C_ADAPTER_MODULE_ON 1 +#define FT_I2C_ADAPTER_MODULE_OFF 2 +#define FT_I2C_ADAPTER_MODULE_RESET 3 + +#define FT_I2C_BUS_SPEED_STANARD_MODE 1 +#define FT_I2C_BUS_SPEED_FAST_MODE 2 +#define FT_I2C_BUS_SPEED_HIGH_MODE 3 +#define FT_I2C_BUS_SPEED_TRANS_PARAM_MODE 0 +#define FT_I2C_BUS_SPEED_CALC_MODE 1 + +#define FT_I2C_REGFILE_TX_HEAD 0 +#define FT_I2C_REGFILE_TX_TAIL 0x04 +#define FT_I2C_REGFILE_HEAD 0x08 +#define FT_I2C_REGFILE_TAIL 0x0C + +#define FT_IC_DEFAULT_FUNCTIONALITY \ + (I2C_FUNC_I2C | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |\ + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |\ + I2C_FUNC_SMBUS_I2C_BLOCK) + +#define FT_I2C_VIRT_RV_SHMEM_TX_MSG_MAX_CNT 1 + +#define FT_I2C_MASTER_MODE_FLAG 1 +#define FT_I2C_SLAVE_MODE_FLAG 0 + +#define FT_I2C_RESTART_FLAG 1 +#define FT_I2C_NOT_RESTART_FLAG 0 + +#define FT_I2C_SLAVE_DATA_IN 0 +#define FT_I2C_SLAVE_DATA_OUT 1 + +#define FT_I2C_SPEED_100K 100000 +#define FT_I2C_SPEED_400K 400000 +#define FT_I2C_SPEED_1000K 1000000 +#define FT_I2C_SPEED_3400K 3400000 + +#define FT_IC_CON_MASTER 0x1 +#define FT_IC_CON_SPEED_STD 0x2 +#define FT_IC_CON_SPEED_FAST 0x4 +#define FT_IC_CON_SPEED_HIGH 0x6 +#define FT_IC_CON_SPEED_MASK 0x6 +#define FT_IC_CON_10BITADDR_SLAVE 0x8 +#define FT_IC_CON_10BITADDR_MASTER 0x10 +#define FT_IC_CON_RESTART_EN 0x20 +#define FT_IC_CON_SLAVE_DISABLE 0x40 +#define FT_IC_CON_STOP_DET_IFADDRESSED 0x80 +#define FT_I2C_CON_STOP_DET_IFADDR_MASK 0x80 +#define FT_IC_CON_TX_EMPTY_CTRL 0x100 +#define FT_IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200 +#define FT_I2C_CON_RX_FIFO_FULL_HLD_MASK 0x200 + +#define FT_IC_CON 0x0 +#define FT_IC_TAR 0x4 +#define FT_IC_SAR 0x8 +#define FT_IC_DATA_CMD 0x10 +#define FT_IC_SS_SCL_HCNT 0x14 +#define FT_IC_SS_SCL_LCNT 0x18 +#define FT_IC_FS_SCL_HCNT 0x1c +#define FT_IC_FS_SCL_LCNT 0x20 +#define FT_IC_HS_SCL_HCNT 0x24 +#define FT_IC_HS_SCL_LCNT 0x28 +#define FT_IC_INTR_STAT 0x2c +#define FT_IC_INTR_MASK 0x30 +#define FT_IC_RAW_INTR_STAT 0x34 +#define FT_IC_RX_TL 0x38 +#define FT_IC_TX_TL 0x3c +#define FT_IC_CLR_INTR 0x40 +#define FT_IC_CLR_RX_UNDER 0x44 +#define FT_IC_CLR_RX_OVER 0x48 +#define FT_IC_CLR_TX_OVER 0x4c +#define FT_IC_CLR_RD_REQ 0x50 +#define FT_IC_CLR_TX_ABRT 0x54 +#define FT_IC_CLR_RX_DONE 0x58 +#define FT_IC_CLR_ACTIVITY 0x5c +#define FT_IC_CLR_STOP_DET 0x60 +#define FT_IC_CLR_START_DET 0x64 +#define FT_IC_CLR_GEN_CALL 0x68 +#define FT_IC_ENABLE 0x6c +#define FT_IC_STATUS 0x70 +#define FT_IC_TXFLR 0x74 +#define FT_IC_RXFLR 0x78 +#define FT_IC_SDA_HOLD 0x7c +#define FT_IC_TX_ABRT_SOURCE 0x80 +#define FT_IC_ENABLE_STATUS 0x9c +#define FT_IC_SMBCLK_LOW_MEXT 0xa8 +#define FT_IC_SMBCLK_LOW_TIMEOUT 0xac +#define FT_IC_SMBDAT_STUCK_TIMEOUT 0xb4 +#define FT_IC_CLR_SMBCLK_EXT_LOW_TIMEOUT 0xbc +#define FT_IC_CLR_SMBCLK_TMO_LOW_TIMEOUT 0xc0 +#define FT_IC_CLR_SMBDAT_LOW_TIMEOUT 0xc4 +#define FT_IC_CLR_SMBALERT_IN_N 0xd0 + +#define FT_IC_INTR_RX_UNDER 0x001 +#define FT_IC_INTR_RX_OVER 0x002 +#define FT_IC_INTR_RX_FULL 0x004 +#define FT_IC_INTR_TX_OVER 0x008 +#define FT_IC_INTR_TX_EMPTY 0x010 +#define FT_IC_INTR_RD_REQ 0x020 +#define FT_IC_INTR_TX_ABRT 0x040 +#define FT_IC_INTR_RX_DONE 0x080 +#define FT_IC_INTR_ACTIVITY 0x100 +#define FT_IC_INTR_STOP_DET 0x200 +#define FT_IC_INTR_START_DET 0x400 +#define FT_IC_INTR_GEN_CALL 0x800 +#define FT_IC_INTR_SMBCLK_EXT_LOW_TIMEOUT 0x1000 +#define FT_IC_INTR_SMBCLK_TMO_LOW_TIMEOUT 0x2000 +#define FT_IC_INTR_SMBSDA_LOW_TIMEOUT 0x4000 +#define FT_IC_INTR_SMBALERT_IN_N 0x20000 + +#define FT_IC_INTR_DEFAULT_MASK \ + (FT_IC_INTR_RX_FULL | FT_IC_INTR_TX_ABRT | FT_IC_INTR_STOP_DET) +#define FT_IC_INTR_MASTER_MASK (FT_IC_INTR_DEFAULT_MASK | FT_IC_INTR_TX_EMPTY) +#define FT_IC_INTR_SLAVE_MASK \ + (FT_IC_INTR_DEFAULT_MASK | FT_IC_INTR_RX_DONE | FT_IC_INTR_RX_UNDER | FT_IC_INTR_RD_REQ) +#define FT_IC_INTR_SMBUS_MASK \ + (FT_IC_INTR_MASTER_MASK | FT_IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | \ + FT_IC_INTR_SMBCLK_TMO_LOW_TIMEOUT | FT_IC_INTR_SMBSDA_LOW_TIMEOUT) + +#define FT_IC_INTR_SMBUS_TIME_MASK \ + (FT_IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | \ + FT_IC_INTR_SMBCLK_TMO_LOW_TIMEOUT | FT_IC_INTR_SMBSDA_LOW_TIMEOUT) + +#define FT_IC_STATUS_ACTIVITY 0x1 +#define FT_IC_STATUS_TFE BIT(2) +#define FT_IC_STATUS_MASTER_ACTIVITY BIT(5) +#define FT_IC_STATUS_SLAVE_ACTIVITY BIT(6) + +#define FT_IC_SDA_HOLD_RX_SHIFT 16 +#define FT_IC_SDA_HOLD_RX_MASK GENMASK(23, IC_SDA_HOLD_RX_SHIFT) + +#define FT_IC_ERR_TX_ABRT 3 + +#define FT_IC_TAR_10BITADDR_MASTER BIT(12) + +#define FT_IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3)) +#define FT_IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2) + +#define FT_STATUS_IDLE 0x0 +#define FT_STATUS_WRITE_IN_PROGRESS 0x1 +#define FT_STATUS_READ_IN_PROGRESS 0x2 + +/* + * operation modes + */ +#define phyt_IC_MASTER 0 +#define phyt_IC_SLAVE 1 + +#define FT_ABRT_7B_ADDR_NOACK 0 +#define FT_ABRT_10ADDR1_NOACK 1 +#define FT_ABRT_10ADDR2_NOACK 2 +#define FT_ABRT_TXDATA_NOACK 3 +#define FT_ABRT_GCALL_NOACK 4 +#define FT_ABRT_GCALL_READ 5 +#define FT_ABRT_SBYTE_ACKDET 7 +#define FT_ABRT_SBYTE_NORSTRT 9 +#define FT_ABRT_10B_RD_NORSTRT 10 +#define FT_ABRT_MASTER_DIS 11 +#define FT_ARB_LOST 12 +#define FT_ABRT_SLAVE_FLUSH_TXFIFO 13 +#define FT_ABRT_SLAVE_ARBLOST 14 +#define FT_ABRT_SLAVE_RD_INTX 15 + +#define FT_IC_TX_ABRT_7B_ADDR_NOACK (1UL << FT_ABRT_7B_ADDR_NOACK) +#define FT_IC_TX_ABRT_10ADDR1_NOACK (1UL << FT_ABRT_10ADDR1_NOACK) +#define FT_IC_TX_ABRT_10ADDR2_NOACK (1UL << FT_ABRT_10ADDR2_NOACK) +#define FT_IC_TX_ABRT_TXDATA_NOACK (1UL << FT_ABRT_TXDATA_NOACK) +#define FT_IC_TX_ABRT_GCALL_NOACK (1UL << FT_ABRT_GCALL_NOACK) +#define FT_IC_TX_ABRT_GCALL_READ (1UL << FT_ABRT_GCALL_READ) +#define FT_IC_TX_ABRT_SBYTE_ACKDET (1UL << FT_ABRT_SBYTE_ACKDET) +#define FT_IC_TX_ABRT_SBYTE_NORSTRT (1UL << FT_ABRT_SBYTE_NORSTRT) +#define FT_IC_TX_ABRT_10B_RD_NORSTRT (1UL << FT_ABRT_10B_RD_NORSTRT) +#define FT_IC_TX_ABRT_MASTER_DIS (1UL << FT_ABRT_MASTER_DIS) +#define FT_IC_TX_ARB_LOST (1UL << FT_ARB_LOST) +#define FT_IC_RX_ABRT_SLAVE_RD_INTX (1UL << FT_ABRT_SLAVE_RD_INTX) +#define FT_IC_RX_ABRT_SLAVE_ARBLOST (1UL << FT_ABRT_SLAVE_ARBLOST) +#define FT_IC_RX_ABRT_SLAVE_FLUSH_TXFIFO (1UL << FT_ABRT_SLAVE_FLUSH_TXFIFO) + +#define FT_IC_TX_ABRT_NOACK \ + (FT_IC_TX_ABRT_7B_ADDR_NOACK | FT_IC_TX_ABRT_10ADDR1_NOACK |\ + FT_IC_TX_ABRT_10ADDR2_NOACK | FT_IC_TX_ABRT_TXDATA_NOACK |\ + FT_IC_TX_ABRT_GCALL_NOACK) +#define FT_CONTROLLER_TYPE_IIC 0 +#define FT_CONTROLLER_TYPE_SMBUS 1 + +#define FT_I2C_MSG_COMPLETE_OK 1 +#define FT_I2C_MSG_COMPLETE_UNKNOWN 0 + +#define FT_I2C_CON_MASTER_MODE_MASK 0x01 +#define FT_I2C_CON_SLAVE_MODE_MASK (1 << 6) +#define FT_I2C_CON_RESTART_MASK 0x20 + +#define FT_I2C_CON_ADDR_MODE_MASK (1 << 3) +#define FT_I2C_ADDR_7BIT_MODE (0) + +#define FT_I2C_TRANS_TIMEOUT 0xF0 + +#define FT_ACCESS_INTR_MASK 0x00000004 +#define FT_DEFAULT_CLOCK_FREQUENCY 100000000 + +#define FT_I2C_MSG_DATA_LEN 56 +#define FT_I2C_SINGLE_BUF_LEN 51 +#define FT_I2C_SINGLE_FRAME_CNT 32 + +#define RV_ANSWER_DATA_SIZE 122 +#define RV_ANSWER_DATA_CONTINUE 1 +#define RV_ANSWER_DATA_END 0 + +enum phyti2c_status_code { + FT_I2C_SUCCESS = 0, + FT_I2C_TIMEOUT, + FT_I2C_CNT_ERR, + FT_I2C_TX_ABRT, + FT_I2C_INT_ERR, + FT_I2C_BLOCK_SIZE, + FT_I2C_INVALID_ADDR, + FT_I2C_TRANS_PACKET_FAIL, + /*The RV result must put above*/ + FT_I2C_RUNNING, + FT_I2C_CHECK_STATUS_ERR +}; + +struct i2c_ft_rv_event_mng { + u8 event_cnt; +}; + +enum phyti2c_msg_cmd_id { + PHYTI2C_MSG_CMD_DEFAULT = 0, + PHYTI2C_MSG_CMD_SET, + PHYTI2C_MSG_CMD_GET, + PHYTI2C_MSG_CMD_DATA, + PHYTI2C_MSG_CMD_REPORT, + PHYTI2C_SYS_PROTOCOL, +}; + +enum phyti2c_msg_default_subid { + PHYTI2C_MSG_CMD_DEFAULT_RESV = 0, + PHYTI2C_MSG_CMD_DEFAULT_RESUME, + PHYTI2C_MSG_CMD_DEFAULT_SLV +}; + +struct i2c_ft_default_cfg_msg { + u32 ss_hcnt; + u32 ss_lcnt; + u32 fs_hcnt; + u32 fs_lcnt; + u32 hs_hcnt; + u32 hs_lcnt; + u32 sda_hold; + u32 tx_fifo_thr; + u32 rx_fifo_thr; + u32 smbclk_mext; + u32 smbclk_timeout; + u32 smbdat_timeout; + u32 cfg; + u32 intr_mask; +}; + +enum phyti2c_set_subid { + PHYTI2C_MSG_CMD_SET_MODULE_EN = 0, + PHYTI2C_MSG_CMD_SET_MODE, + PHYTI2C_MSG_CMD_SET_RESTART, + PHYTI2C_MSG_CMD_SET_ADDR_MODE, + PHYTI2C_MSG_CMD_SET_SPEED, + PHYTI2C_MSG_CMD_SET_INT_TL, + PHYTI2C_MSG_CMD_SET_SDA_HOLD, + PHYTI2C_MSG_CMD_SET_INTERRUPT, + PHYTI2C_MSG_CMD_SET_RX_FIFO_FULL, + PHYTI2C_MSG_CMD_SET_STOP_DET_IF_ADDRESSED, + PHYTI2C_MSG_CMD_SET_WRITE_PROTECT, + PHYTI2C_MSG_CMD_SET_SMBCLK_LOW_MEXT, + PHYTI2C_MSG_CMD_SET_SMBCLK_LOW_TIMEOUT, + PHYTI2C_MSG_CMD_SET_SMBDAT_STUCK_TIMEOUT, + PHYTI2C_MSG_CMD_SET_ADDR, + PHYTI2C_MSG_CMD_SET_SUSPEND +}; + +enum phyti2c_data_subid { + PHYTI2C_MSG_CMD_DATA_XFER = 0, + PHYTI2C_MSG_CMD_DATA_SLAVE +}; + +enum phyti2c_report_subid { + PHYTI2C_MSG_CMD_SMBCLK_EXT_LOW_TIMEOUT = 0, + PHYTI2C_MSG_CMD_SMBCLK_TMO_LOW_TIMEOUT, + PHYTI2C_MSG_CMD_SMBSDA_LOW_TIMEOUT, + PHYTI2C_MSG_CMD_SMBALERT_IN_N, + PHYTI2C_MSG_CMD_SLAVE_EVENT, +}; + +struct i2c_phyt_transfer { + u32 tx_cmd_cnt; + u32 cur_cmd_cnt; + u32 cur_index; + u32 opt_finish_len; + u32 tx_ring_cnt; + bool is_need_check; + bool is_last_frame; +}; + +struct i2c_phyt_dev { + struct device *dev; + void __iomem *base; + void __iomem *log_addr; + void *tx_shmem_addr; + void *rx_shmem_addr; + u8 *tx_buf; + u8 *rx_buf; + struct clk *clk; + struct reset_control *rst; + struct i2c_client *slave; + struct i2c_client *ara; + struct i2c_msg *msgs; + struct timer_list timer; + struct phytium_pci_i2c *controller; + + int module; + int irq; + u32 intr_mask; + u32 flags; + u32 total_shmem_len; + u32 total_cnt; + int mode; + struct i2c_phyt_transfer mng; + struct completion cmd_complete; + struct i2c_adapter adapter; + struct i2c_smbus_alert_setup alert_data; + + bool complete_flag; + u32 abort_source; + + int msgs_num; + int msg_err; + u32 tx_buf_len; + u32 rx_buf_len; + + u32 master_cfg; + u32 slave_cfg; + u32 functionality; + + u8 real_index[FT_I2C_SINGLE_FRAME_CNT]; + struct i2c_timings timings; + u32 sda_hold_time; + u16 ss_hcnt; + u16 ss_lcnt; + u16 fs_hcnt; + u16 fs_lcnt; + u16 fp_hcnt; + u16 fp_lcnt; + u16 hs_hcnt; + u16 hs_lcnt; + + bool alive_enabled; + bool debug_enabled; + u32 log_size; + bool pm_disabled; + spinlock_t shmem_spin; + + u32 (*get_clk_rate_khz)(struct i2c_phyt_dev *dev); + void (*disable)(struct i2c_phyt_dev *dev); + void (*disable_int)(struct i2c_phyt_dev *dev); + void (*watchdog)(struct i2c_phyt_dev *dev); + int (*init)(struct i2c_phyt_dev *dev); +}; + +struct phyt_msg_head { + u8 resv; + u8 seq; + u8 cmd_type; + u8 cmd_subid; + u16 len; + u8 status1; + u8 status0; +}; + +struct phyt_msg_info { + struct phyt_msg_head head; + u8 data[FT_I2C_MSG_DATA_LEN]; +}; + +struct i2c_ft_trans_msg_info { + u16 addr; + u16 flags; + u8 type; +} __packed; + +struct i2c_phyt_bus_speed_info { + u8 speed_mode; + u8 calc_en; + u16 scl_hcnt; + u16 scl_lcnt; + u32 sda_hold; +}; + +struct i2c_phyt_fifo_threshold { + u8 rx_fifo_threshold; + u8 tx_fifo_threshold; +}; + +struct i2c_phyt_sda_hold_info { + u32 sda_hold; +}; + +static inline void i2c_phyt_write_reg(struct i2c_phyt_dev *dev, + u32 reg_offset, u32 reg_value) +{ + writel_relaxed(reg_value, dev->base + reg_offset); +} + +static inline u32 i2c_phyt_read_reg(struct i2c_phyt_dev *dev, u32 reg_offset) +{ + return readl_relaxed(dev->base + reg_offset); +} + +unsigned long i2c_phyt_clk_rate(struct i2c_phyt_dev *dev); +int i2c_phyt_prepare_clk(struct i2c_phyt_dev *dev, bool prepare); +u32 i2c_phyt_func(struct i2c_adapter *adap); +u32 i2c_phyt_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset); +u32 i2c_phyt_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); +void i2c_phyt_default_cfg(struct i2c_phyt_dev *dev, + struct i2c_ft_default_cfg_msg *buf); +void i2c_phyt_set_suspend(struct i2c_phyt_dev *dev); +void i2c_phyt_enable_debug(struct i2c_phyt_dev *dev); +void i2c_phyt_disable_debug(struct i2c_phyt_dev *dev); +void i2c_phyt_show_log(struct i2c_phyt_dev *dev); +void i2c_phyt_enable_alive(struct i2c_phyt_dev *dev); +void i2c_phyt_disable_alive(struct i2c_phyt_dev *dev); +void i2c_phyt_common_watchdog(struct i2c_phyt_dev *dev); +int i2c_phyt_malloc_log_mem(struct i2c_phyt_dev *dev); +int i2c_phyt_rv_data_cmd_handle(struct phyt_msg_info *rx_msg); +int i2c_phyt_master_probe(struct i2c_phyt_dev *dev); +void i2c_phyt_handle_tx_abort(struct i2c_phyt_dev *dev); +int i2c_phyt_check_result(struct i2c_phyt_dev *dev); +void i2c_phyt_set_cmd8(struct i2c_phyt_dev *dev, u8 sub_cmd, u8 data); +void i2c_phyt_set_cmd16(struct i2c_phyt_dev *dev, u8 sub_cmd, u16 data); +void i2c_phyt_set_cmd32(struct i2c_phyt_dev *dev, u8 sub_cmd, u32 data); +void i2c_phyt_common_set_cmd(struct i2c_phyt_dev *dev, + struct phyt_msg_info *i2c_mng_msg, u8 cmd, u8 sub_cmd); +void i2c_phyt_disable_int(struct i2c_phyt_dev *dev); +void i2c_phyt_set_int_tl(struct i2c_phyt_dev *dev, u8 tx_threshold, + u8 rx_threshold); +int i2c_phyt_slave_probe(struct i2c_phyt_dev *dev); +void i2c_phyt_set_module_en(struct i2c_phyt_dev *dev, u8 data); +void i2c_phyt_set_sda_hold(struct i2c_phyt_dev *dev, u32 data); +void i2c_phyt_disable(struct i2c_phyt_dev *dev); +int i2c_phyt_slave_event_process(struct i2c_phyt_dev *dev, + struct phyt_msg_info *rx_msg, u32 head, u32 tail); +void i2c_phyt_data_cmd8_array(struct i2c_phyt_dev *dev, u8 sub_cmd, u8 *data, + int len); +void i2c_phyt_slave_isr_handle(struct i2c_phyt_dev *dev); +void i2c_phyt_master_isr_handle(struct i2c_phyt_dev *dev); +int i2c_phyt_master_smbus_alert_process(struct i2c_phyt_dev *dev); +void i2c_phyt_common_regfile_enable_int(struct i2c_phyt_dev *dev); +void i2c_phyt_common_regfile_disable_int(struct i2c_phyt_dev *dev); +void i2c_phyt_common_regfile_clear_rv2ap_int(struct i2c_phyt_dev *dev, u32 stat); +void i2c_phyt_notify_rv(struct i2c_phyt_dev *dev, bool need_check); +int i2c_phyt_check_status(struct i2c_phyt_dev *dev, struct phyt_msg_info *msg); +void i2c_phyt_set_int_interrupt(struct i2c_phyt_dev *dev, + u32 is_enable, u32 intr_mask); +#endif diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c new file mode 100644 index 0000000000..559a59aeca --- /dev/null +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2C adapter driver. + * + * Copyright (C) 2023-2024, Phytium Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c-phyt-core.h" + +#define FT_DEFAULT_TIMEOUT (FT_DEFAULT_CLOCK_FREQUENCY / 1000 * 35) + +#define FT_I2C_MSG_MNG_SIZE 8 +#define FT_I2C_SHMEM_STORE_OFFSET (FT_I2C_MSG_MNG_SIZE + sizeof(struct i2c_ft_trans_msg_info)) + +static int i2c_phyt_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num); +static void i2c_phyt_master_parse_data(struct i2c_phyt_dev *dev, + struct phyt_msg_info *shmem_msg, + int index); +static int i2c_phyt_master_default_init(struct i2c_phyt_dev *dev); +static int i2c_phyt_master_set_timings_master(struct i2c_phyt_dev *dev); + +static const struct i2c_algorithm i2c_phyt_master_algo = { + .master_xfer = i2c_phyt_master_xfer, + .functionality = i2c_phyt_func, +}; + + +static int i2c_phyt_master_set_timings_master(struct i2c_phyt_dev *dev) +{ + const char *mode_str, *fp_str = ""; + u32 sda_falling_time, scl_falling_time; + struct i2c_timings *t = &dev->timings; + u32 ic_clk; + int ret = 0; + + /* Set standard and fast speed dividers for high/low periods */ + sda_falling_time = t->sda_fall_ns ?: 300; /* ns */ + scl_falling_time = t->scl_fall_ns ?: 300; /* ns */ + + /* Calculate SCL timing parameters for standard mode if not set */ + if (!dev->ss_hcnt || !dev->ss_lcnt) { + ic_clk = i2c_phyt_clk_rate(dev); + dev->ss_hcnt = + i2c_phyt_scl_hcnt(ic_clk, 4000,/* tHD;STA = tHIGH = 4.0 us */ + sda_falling_time, 0,/* 0: DW default, 1: Ideal */ + 0); /* No offset */ + dev->ss_lcnt = i2c_phyt_scl_lcnt(ic_clk, 4700, /* tLOW = 4.7 us */ + scl_falling_time, 0); /* No offset */ + } + dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", dev->ss_hcnt, dev->ss_lcnt); + /* + * Set SCL timing parameters for fast mode or fast mode plus. Only + * difference is the timing parameter values since the registers are + * the same. + */ + if (t->bus_freq_hz == 1000000) { + /* + * Check are fast mode plus parameters available and use + * fast mode if not. + */ + if (dev->fp_hcnt && dev->fp_lcnt) { + dev->fs_hcnt = dev->fp_hcnt; + dev->fs_lcnt = dev->fp_lcnt; + fp_str = " Plus"; + } + } + /* + * Calculate SCL timing parameters for fast mode if not set. They are + * needed also in high speed mode. + */ + if (!dev->fs_hcnt || !dev->fs_lcnt) { + ic_clk = i2c_phyt_clk_rate(dev); + dev->fs_hcnt = + i2c_phyt_scl_hcnt(ic_clk, 600, /* tHD;STA = tHIGH = 0.6 us */ + sda_falling_time, 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + dev->fs_lcnt = i2c_phyt_scl_lcnt(ic_clk, 1300, /* tLOW = 1.3 us */ + scl_falling_time, 0); /* No offset */ + } + dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", fp_str, dev->fs_hcnt, dev->fs_lcnt); + + if (dev->hs_hcnt && dev->hs_lcnt) + dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n", dev->hs_hcnt, + dev->hs_lcnt); + + switch (dev->master_cfg & FT_IC_CON_SPEED_MASK) { + case FT_IC_CON_SPEED_STD: + mode_str = "Standard Mode"; + break; + case FT_IC_CON_SPEED_HIGH: + mode_str = "High Speed Mode"; + break; + default: + mode_str = "Fast Mode"; + } + dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str); + + return ret; +} + +int i2c_phyt_master_smbus_alert_process(struct i2c_phyt_dev *dev) +{ + if (dev->ara) + i2c_handle_smbus_alert(dev->ara); + else + dev_dbg(dev->dev, "alert do nothing\n"); + + return 0; +} + +static int i2c_phyt_master_update_new_msg(struct i2c_phyt_dev *dev) +{ + struct phyt_msg_info shmem_msg; + struct phyt_msg_info *tx_shmem = (struct phyt_msg_info *)dev->tx_shmem_addr; + struct i2c_ft_trans_msg_info *i2c_msg_info; + struct i2c_msg *msgs = dev->msgs; + int remain_len; + + /*Check the current index and the len are valid */ + if ((dev->mng.cur_index >= dev->msgs_num) || + (msgs[dev->mng.cur_index].len < dev->mng.opt_finish_len)) + return -EINVAL; + + memset(&shmem_msg, 0, sizeof(struct phyt_msg_info)); + i2c_msg_info = (struct i2c_ft_trans_msg_info *)&shmem_msg.data[0]; + /*Get the remain len*/ + remain_len = msgs[dev->mng.cur_index].len - dev->mng.opt_finish_len; + if (!remain_len) { + /*Use the next index*/ + if (dev->mng.cur_index + 1 >= dev->msgs_num) + return FT_I2C_RUNNING; + + dev->mng.cur_index++; + + if ((msgs[dev->mng.cur_index - 1].flags & I2C_M_RD) != + (msgs[dev->mng.cur_index].flags & I2C_M_RD)) + i2c_msg_info->type = FT_I2C_TRANS_FRAME_RESTART; + + dev->mng.opt_finish_len = 0; + remain_len = msgs[dev->mng.cur_index].len; + } + + /*Set the Head.len and Head.seq*/ + shmem_msg.head.len = remain_len; + if (remain_len > FT_I2C_SINGLE_BUF_LEN) { + shmem_msg.head.len = FT_I2C_SINGLE_BUF_LEN; + } else { + if (dev->mng.cur_index + 1 >= dev->msgs_num) { + i2c_msg_info->type |= FT_I2C_TRANS_FRAME_END; + dev->mng.is_last_frame = true; + } + } + /*Set other info of the Head*/ + shmem_msg.head.seq = dev->total_cnt--; + shmem_msg.head.cmd_type = PHYTI2C_MSG_CMD_DATA; + shmem_msg.head.cmd_subid = PHYTI2C_MSG_CMD_DATA_XFER; + shmem_msg.head.status0 = FT_I2C_MSG_COMPLETE_UNKNOWN; + + /*store addr flags of struct i2c_msg*/ + i2c_msg_info->addr = msgs[dev->mng.cur_index].addr; + i2c_msg_info->flags = msgs[dev->mng.cur_index].flags; + + dev->total_shmem_len = FT_I2C_SHMEM_STORE_OFFSET; + /*store data*/ + if (!(msgs[dev->mng.cur_index].flags & I2C_M_RD)) { + memcpy(&shmem_msg.data[sizeof(struct i2c_ft_trans_msg_info)], + &msgs[dev->mng.cur_index].buf[dev->mng.opt_finish_len], + shmem_msg.head.len); + dev->total_shmem_len += shmem_msg.head.len; + } + + dev->mng.opt_finish_len += shmem_msg.head.len; + /*update new data to shmem*/ + memcpy(&tx_shmem[dev->mng.tx_cmd_cnt % dev->mng.tx_ring_cnt], + &shmem_msg, dev->total_shmem_len); + + dev->real_index[dev->mng.tx_cmd_cnt % dev->mng.tx_ring_cnt] = + dev->mng.cur_index; + /*Update the Tx_Tail*/ + dev->mng.tx_cmd_cnt++; + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_TAIL, + dev->mng.tx_cmd_cnt % dev->mng.tx_ring_cnt); + return FT_I2C_RUNNING; +} + +static int i2c_phyt_master_handle(struct i2c_phyt_dev *dev) +{ + struct phyt_msg_info *tx_shmem = (struct phyt_msg_info *)dev->tx_shmem_addr; + struct phyt_msg_info shmem_msg; + u32 tx_head; + int ret; + + if (dev->mng.cur_cmd_cnt >= dev->mng.tx_cmd_cnt) + return 0; + + /*Get the index of general msg*/ + tx_head = dev->mng.cur_cmd_cnt % dev->mng.tx_ring_cnt; + dev->mng.cur_cmd_cnt++; + + /*Read the general_msg*/ + memcpy(&shmem_msg, &tx_shmem[tx_head], sizeof(struct phyt_msg_info)); + ret = i2c_phyt_check_status(dev, &shmem_msg); + + /*Error*/ + if (ret) + return -EINVAL; + + /*no check cmd*/ + if (!dev->mng.is_need_check) + return 0; + + i2c_phyt_master_parse_data(dev, &shmem_msg, tx_head); + /*No need to update new data*/ + if (dev->mng.is_last_frame) { + if (dev->mng.cur_cmd_cnt == dev->mng.tx_cmd_cnt) + return 0;/*This is last frame */ + return FT_I2C_RUNNING;/*There are some msgs needed to process*/ + } + return i2c_phyt_master_update_new_msg(dev); +} + +static int i2c_phyt_master_calc_total_frame_cnt(struct i2c_phyt_dev *dev) +{ + int i, frame_cnt = 0, len = 0, remain_len; + struct i2c_msg *msgs = dev->msgs; + + for (i = 0; i < dev->msgs_num; i++) { + len = 0; + remain_len = msgs[i].len; + while (len < remain_len) { + if (remain_len > FT_I2C_SINGLE_BUF_LEN) + len += FT_I2C_SINGLE_BUF_LEN; + else + len = remain_len; + + frame_cnt++; + } + } + return frame_cnt; +} + +static void i2c_phyt_master_xfer_single_frame(struct i2c_phyt_dev *dev) +{ + struct phyt_msg_info i2c_mng_msg; + struct i2c_ft_trans_msg_info *i2c_msg_info; + struct phyt_msg_info *shmem_msg = + (struct phyt_msg_info *)dev->tx_shmem_addr; + struct i2c_msg *msgs = dev->msgs; + int i, len = 0, remain_len; + + dev->total_cnt = i2c_phyt_master_calc_total_frame_cnt(dev); + if (dev->total_cnt) + dev->total_cnt--; + + /*orign info*/ + for (i = 0; i < dev->msgs_num; i++) { + remain_len = msgs[i].len; + dev->mng.opt_finish_len = 0; + + while (dev->mng.tx_cmd_cnt < dev->mng.tx_ring_cnt - 1) { + dev->total_shmem_len = 0; + memset(&i2c_mng_msg, 0, sizeof(i2c_mng_msg)); + + i2c_mng_msg.head.seq = dev->total_cnt--; + /*Set the Head.len*/ + len = remain_len; + if (remain_len >= FT_I2C_SINGLE_BUF_LEN) + len = FT_I2C_SINGLE_BUF_LEN; + i2c_mng_msg.head.len = len; + + /*Set other info of the Head*/ + i2c_mng_msg.head.cmd_type = PHYTI2C_MSG_CMD_DATA; + i2c_mng_msg.head.cmd_subid = PHYTI2C_MSG_CMD_DATA_XFER; + i2c_mng_msg.head.status0 = FT_I2C_MSG_COMPLETE_UNKNOWN; + + /*Store addr flags of struct i2c_msg*/ + i2c_msg_info = + (struct i2c_ft_trans_msg_info *)&i2c_mng_msg.data[0]; + i2c_msg_info->addr = (u16)msgs[i].addr; + i2c_msg_info->flags = (u16)msgs[i].flags; + + /*Set the Head.seq (Start)*/ + if (!dev->mng.tx_cmd_cnt) + i2c_msg_info->type = FT_I2C_TRANS_FRAME_START; + + /*Set the Head.seq (Restart)*/ + if ((i) && ((msgs[i - 1].flags & I2C_M_RD) != + (msgs[i].flags & I2C_M_RD))) { + if (remain_len == msgs[i].len) + i2c_msg_info->type = FT_I2C_TRANS_FRAME_RESTART; + } + /*Set the Head.seq (End)*/ + if (((i + 1) >= dev->msgs_num) && (len == remain_len)) { + dev->mng.is_last_frame = true; + i2c_msg_info->type |= FT_I2C_TRANS_FRAME_END; + } + + + dev->total_shmem_len = FT_I2C_SHMEM_STORE_OFFSET; + /*store data*/ + if (!(msgs[i].flags & I2C_M_RD)) { + memcpy(&i2c_mng_msg.data[sizeof( + struct i2c_ft_trans_msg_info)], + &msgs[i].buf[dev->mng.opt_finish_len], len); + dev->total_shmem_len += len; + } + /*Update to share memory*/ + memcpy(&shmem_msg[dev->mng.tx_cmd_cnt], &i2c_mng_msg, + dev->total_shmem_len); + + /*Store the index of the i2c_msgs\the current index and the finished len*/ + dev->real_index[dev->mng.tx_cmd_cnt] = i; + dev->mng.cur_index = i; + dev->mng.opt_finish_len += len; + /*Record the count of AP2RV's msg*/ + dev->mng.tx_cmd_cnt++; + + remain_len -= len; + if (!remain_len) + break; + } + } + /*Update the Tx_Tail*/ + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_TAIL, dev->mng.tx_cmd_cnt); + i2c_phyt_notify_rv(dev, true); +} + +static int i2c_phyt_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct i2c_phyt_dev *dev = i2c_get_adapdata(adapter); + int ret; + + pm_runtime_get_sync(dev->dev); + + dev->msgs = msgs; + dev->msgs_num = num; + dev->msg_err = 0; + dev->abort_source = 0; + dev->rx_buf_len = 0; + dev->mng.tx_cmd_cnt = 0; + dev->mng.cur_cmd_cnt = 0; + dev->mng.is_last_frame = false; + + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_HEAD, 0); + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_TAIL, 0); + + i2c_phyt_master_xfer_single_frame(dev); + + ret = i2c_phyt_check_result(dev); + if (!ret) { + ret = num; + } else { + if (dev->abort_source != FT_I2C_SUCCESS) { + i2c_phyt_handle_tx_abort(dev); + if ((dev->abort_source & BIT(FT_I2C_TIMEOUT)) || + (dev->abort_source & BIT(FT_I2C_TRANS_PACKET_FAIL))) { + i2c_phyt_set_module_en( + dev, FT_I2C_ADAPTER_MODULE_RESET); + i2c_phyt_check_result(dev); + i2c_phyt_master_default_init(dev); + ret = -ETIMEDOUT; + goto done; + } + ret = -EINVAL; + } + } + +done: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + return ret; +} + +static void +i2c_phyt_master_parse_data(struct i2c_phyt_dev *dev, + struct phyt_msg_info *shmem_msg, + int index) +{ + int real_index; + struct i2c_ft_trans_msg_info *i2c_msg_info; + struct i2c_msg *msgs = dev->msgs; + + i2c_msg_info = (struct i2c_ft_trans_msg_info *)&shmem_msg->data[0]; + /*Find the real index of i2c_msgs*/ + real_index = dev->real_index[index]; + if (real_index >= dev->msgs_num) + return; + + if (i2c_msg_info->flags & I2C_M_RD) { + memcpy(&msgs[real_index].buf[dev->rx_buf_len], + &shmem_msg->data[sizeof(struct i2c_ft_trans_msg_info)], + shmem_msg->head.len); + dev->rx_buf_len += shmem_msg->head.len; + } +} + +static int i2c_phyt_master_default_init(struct i2c_phyt_dev *dev) +{ + int ret; + struct i2c_ft_default_cfg_msg cfg_info; + + cfg_info.ss_hcnt = dev->ss_hcnt; + cfg_info.ss_lcnt = dev->ss_lcnt; + cfg_info.fs_hcnt = dev->fs_hcnt; + cfg_info.fs_lcnt = dev->fs_lcnt; + cfg_info.hs_hcnt = dev->hs_hcnt; + cfg_info.hs_lcnt = dev->hs_lcnt; + cfg_info.sda_hold = dev->sda_hold_time; + cfg_info.tx_fifo_thr = 3; + cfg_info.rx_fifo_thr = 0; + cfg_info.smbclk_mext = FT_DEFAULT_TIMEOUT; + cfg_info.smbclk_timeout = FT_DEFAULT_TIMEOUT; + cfg_info.smbdat_timeout = FT_DEFAULT_TIMEOUT; + cfg_info.cfg = dev->master_cfg; + cfg_info.intr_mask = dev->intr_mask; + i2c_phyt_default_cfg(dev, &cfg_info); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set module open: %d\n", ret); + return ret; + } + + return 0; +} + +static irqreturn_t i2c_phyt_master_regfile_isr(int this_irq, void *dev_id) +{ + struct i2c_phyt_dev *dev = (struct i2c_phyt_dev *)dev_id; + struct phyt_msg_info *rx_msg = (struct phyt_msg_info *)dev->rx_shmem_addr; + u32 stat; + int ret; + + stat = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_RV2AP_INTR_STAT); + + if (!(stat & FT_I2C_RV2AP_INTR_BIT4)) { + dev_warn(dev->dev, "unexpect i2c_phyt regfile intr, stat:%#x\n", stat); + return IRQ_NONE; + } + + i2c_phyt_common_regfile_clear_rv2ap_int(dev, stat); + + if (dev->complete_flag) { + if (rx_msg->head.cmd_type == PHYTI2C_MSG_CMD_REPORT) + goto done; + + ret = i2c_phyt_master_handle(dev); + if (ret == FT_I2C_RUNNING) + return IRQ_HANDLED; + + dev->complete_flag = false; + dev->mng.cur_cmd_cnt = 0; + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_HEAD, 0); + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_TAIL, 0); + complete(&dev->cmd_complete); + return IRQ_HANDLED; + } +done: + i2c_phyt_master_isr_handle(dev); + + return IRQ_HANDLED; +} + +static int i2c_phyt_enable_smbus_alert(struct i2c_phyt_dev *i2c_dev) +{ + struct i2c_adapter *adap = &i2c_dev->adapter; + struct i2c_smbus_alert_setup setup; + + setup.irq = 0; + i2c_dev->ara = i2c_new_smbus_alert_device(adap, &setup); + + if (IS_ERR(i2c_dev->ara)) + return PTR_ERR(i2c_dev->ara); + + return 0; +} + +int i2c_phyt_master_probe(struct i2c_phyt_dev *dev) +{ + struct i2c_adapter *adapter = &dev->adapter; + unsigned long irq_flags; + int ret; + u32 alert = 0; + + init_completion(&dev->cmd_complete); + + i2c_phyt_common_regfile_disable_int(dev); + i2c_phyt_common_regfile_clear_rv2ap_int(dev, 0); + + irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND; + ret = devm_request_irq(dev->dev, dev->irq, i2c_phyt_master_regfile_isr, irq_flags, + dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "failure requesting irq %i: %d\n", + dev->irq, ret); + return ret; + } + + i2c_phyt_common_regfile_enable_int(dev); + + ret = i2c_phyt_master_set_timings_master(dev); + if (ret) { + dev_err(dev->dev, "master set times\n"); + return ret; + } + + dev->intr_mask = FT_IC_INTR_SMBUS_TIME_MASK; + ret = i2c_phyt_master_default_init(dev); + if (ret) { + dev_err(dev->dev, "master default init\n"); + return ret; + } + dev->init = i2c_phyt_master_default_init; + dev->disable = i2c_phyt_set_suspend; + dev->disable_int = i2c_phyt_disable_int; + + /* XXX: should be initialized in firmware, remove it in future */ + snprintf(adapter->name, sizeof(adapter->name), "Phytium I2C_2.0 Master adapter"); + adapter->retries = 3; + adapter->algo = &i2c_phyt_master_algo; + adapter->dev.parent = dev->dev; + i2c_set_adapdata(adapter, dev); + /* + * Increment PM usage count during adapter registration in order to + * avoid possible spurious runtime suspend when adapter device is + * registered to the device core and immediate resume in case bus has + * registered I2C slaves that do I2C transfers in their probe. + */ + pm_runtime_get_noresume(dev->dev); + + ret = i2c_add_numbered_adapter(adapter); + if (ret) { + dev_err(dev->dev, "fail to add adapter: %d\n", ret); + goto exit_probe; + } + + if (of_property_read_bool(dev->dev->of_node, "smbus-alert")) + alert = 1; + else if (has_acpi_companion(dev->dev)) + fwnode_property_read_u32_array(dev->dev->fwnode, "smbus_alert", &alert, 1); + + if (alert) { + ret = i2c_phyt_enable_smbus_alert(dev); + if (ret) { + dev_err(dev->dev, + "failed to enable SMBus alert protocol (%d)\n", ret); + } + dev->intr_mask |= FT_IC_INTR_SMBALERT_IN_N; + i2c_phyt_set_int_interrupt(dev, FT_I2C_ENABLE_INTERRUPT, dev->intr_mask); + ret = i2c_phyt_check_result(dev); + if (ret) + dev_warn(dev->dev, "fail to set interrupt: %d\n", ret); + } else + dev_info(dev->dev, "find no alert\n"); + +exit_probe: + pm_runtime_put_noidle(dev->dev); + dev_info(dev->dev, "i2c_2.0 master probe ok\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_phyt_master_probe); + +MODULE_DESCRIPTION("Phytium I2C bus master adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-platform.c b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-platform.c new file mode 100644 index 0000000000..45ad303f28 --- /dev/null +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-platform.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium I2C adapter driver. + * + * Copyright (C) 2023-2024, 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 "i2c-phyt-core.h" + +#define DRV_NAME "i2c-phytium-v2-platform" + +static u32 i2c_phyt_get_clk_rate_khz(struct i2c_phyt_dev *dev) +{ + return clk_get_rate(dev->clk)/1000; +} + +static ssize_t debug_show(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct i2c_phyt_dev *adapter_dev = dev_get_drvdata(dev); + ssize_t ret; + u32 reg; + + reg = i2c_phyt_read_reg(adapter_dev, FT_I2C_REGFILE_DEBUG); + ret = sprintf(buf, "%x\n", reg); + + return ret; +} + +static ssize_t debug_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t size) +{ + u8 loc, dis_en, status = 0; + char *p; + char *token; + long value; + u32 reg; + struct i2c_phyt_dev *adapter_dev = dev_get_drvdata(dev); + + dev_info(dev, "echo alive(1)/debug(0) enable(1)/disable(0) > debug\n"); + dev_info(dev, "Example:echo 0 1 > debug; Enable Debug Function\n"); + + p = kmalloc(size, GFP_KERNEL); + strscpy(p, buf, size); + + token = strsep(&p, " "); + if (!token) + return -EINVAL; + + status = kstrtol(token, 0, &value); + if (status) + return status; + loc = (u8)value; + + token = strsep(&p, " "); + if (!token) + return -EINVAL; + + status = kstrtol(token, 0, &value); + if (status) + return status; + dis_en = value; + + reg = i2c_phyt_read_reg(adapter_dev, FT_I2C_REGFILE_DEBUG); + + if (loc == 1) { + if (dis_en == 1) { + adapter_dev->alive_enabled = true; + reg |= BIT(loc); + } else if (dis_en == 0) { + adapter_dev->alive_enabled = false; + reg &= ~BIT(loc); + } + } else if (loc == 0) { + if (dis_en == 1) { + adapter_dev->debug_enabled = true; + reg |= BIT(loc); + } else if (dis_en == 0) { + adapter_dev->debug_enabled = false; + reg &= ~BIT(loc); + } + } + + i2c_phyt_write_reg(adapter_dev, FT_I2C_REGFILE_DEBUG, reg); + + dev_info(dev, "reg write reg =0x%x, loc = %d, val=%d\n", reg, loc, dis_en); + + kfree(p); + return size; +} +static DEVICE_ATTR_RW(debug); + +static struct attribute *i2c_ft_device_attrs[] = { + &dev_attr_debug.attr, + NULL, +}; + +static const struct attribute_group i2c_ft_device_group = { + .attrs = i2c_ft_device_attrs, +}; + +#ifdef CONFIG_ACPI +static void i2c_phyt_acpi_params(struct platform_device *pdev, char method[], + u16 *hcnt, u16 *lcnt, u32 *sda_hold) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + union acpi_object *obj; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, method, NULL, &buf))) + return; + + obj = (union acpi_object *)buf.pointer; + if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 3) { + const union acpi_object *objs = obj->package.elements; + + *hcnt = (u16)objs[0].integer.value; + *lcnt = (u16)objs[1].integer.value; + *sda_hold = (u32)objs[2].integer.value; + } + + kfree(buf.pointer); +} + +static int i2c_phyt_acpi_configure(struct platform_device *pdev) +{ + struct i2c_phyt_dev *dev = platform_get_drvdata(pdev); + struct i2c_timings *t = &dev->timings; + u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0; + const struct acpi_device_id *id; + + dev->adapter.nr = -1; + + /* + * Try to get SDA hold time and *CNT values from an ACPI method for + * selected speed modes. + */ + i2c_phyt_acpi_params(pdev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, &ss_ht); + i2c_phyt_acpi_params(pdev, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, &fp_ht); + i2c_phyt_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht); + i2c_phyt_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht); + + switch (t->bus_freq_hz) { + case FT_I2C_SPEED_100K: + dev->sda_hold_time = ss_ht; + break; + case FT_I2C_SPEED_1000K: + dev->sda_hold_time = fp_ht; + break; + case FT_I2C_SPEED_3400K: + dev->sda_hold_time = hs_ht; + break; + case FT_I2C_SPEED_400K: + default: + dev->sda_hold_time = fs_ht; + break; + } + + id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); + if (id && id->driver_data) + dev->flags |= (u32)id->driver_data; + + return 0; +} + +static const struct acpi_device_id i2c_phyt_acpi_match[] = { + { "PHYT0059", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, i2c_phyt_acpi_match); +#else +static inline int i2c_phyt_acpi_configure(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif + +static void i2c_phyt_configure_master(struct i2c_phyt_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + + dev->functionality = I2C_FUNC_10BIT_ADDR | FT_IC_DEFAULT_FUNCTIONALITY; + + dev->master_cfg = FT_IC_CON_MASTER | FT_IC_CON_SLAVE_DISABLE | + FT_IC_CON_RESTART_EN; + + dev->mode = phyt_IC_MASTER; + + switch (t->bus_freq_hz) { + case FT_I2C_SPEED_100K: + dev->master_cfg |= FT_IC_CON_SPEED_STD; + break; + case FT_I2C_SPEED_3400K: + dev->master_cfg |= FT_IC_CON_SPEED_HIGH; + break; + default: + dev->master_cfg |= FT_IC_CON_SPEED_FAST; + } +} + +static void i2c_phyt_configure_slave(struct i2c_phyt_dev *dev) +{ + dev->functionality = I2C_FUNC_SLAVE | FT_IC_DEFAULT_FUNCTIONALITY; + + dev->slave_cfg = FT_IC_CON_RX_FIFO_FULL_HLD_CTRL | + FT_IC_CON_RESTART_EN | FT_IC_CON_STOP_DET_IFADDRESSED; + + dev->mode = phyt_IC_SLAVE; +} + + +static void i2c_phyt_timer_handle(struct timer_list *t) +{ + struct i2c_phyt_dev *dev = from_timer(dev, t, timer); + + if (dev->alive_enabled && dev->watchdog) + dev->watchdog(dev); + + mod_timer(&dev->timer, jiffies + msecs_to_jiffies(2000)); +} + +static int i2c_phyt_plat_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adap; + struct i2c_phyt_dev *dev; + struct i2c_timings *t; + u32 acpi_speed; + struct resource *reg_mem, *share_mem; + int irq, ret, i; + static const int supported_speeds[] = { + 0, FT_I2C_SPEED_100K, FT_I2C_SPEED_400K, FT_I2C_SPEED_1000K, FT_I2C_SPEED_3400K + }; + + + irq = platform_get_irq(pdev, 0); + + if (irq < 0) { + dev_err(&pdev->dev, "Err:irq :%d,exit\n", irq); + return irq; + } + + dev = devm_kzalloc(&pdev->dev, sizeof(struct i2c_phyt_dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->irq = irq; + /*find regfile info*/ + reg_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!reg_mem) { + dev_err(&pdev->dev, "Err:can't find valid regfile mem,exit\n"); + return -ENOMEM; + } + /*get regfile base address*/ + dev->base = devm_ioremap_resource(&pdev->dev, reg_mem); + if (IS_ERR(dev->base)) { + dev_err(&pdev->dev, "dev->base is err exit\n"); + return PTR_ERR(dev->base); + } + /*find share mem info*/ + share_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!share_mem) { + dev_err(&pdev->dev, "Err:can't find valid shmem,exit\n"); + return -ENOMEM; + } + + /*set tx share mem start addr*/ + dev->tx_shmem_addr = devm_ioremap_wc(&pdev->dev, share_mem->start, + resource_size(share_mem)); + if (IS_ERR(dev->tx_shmem_addr)) { + dev_err(&pdev->dev, "tx_shmem_addr is err\n"); + return PTR_ERR(dev->tx_shmem_addr); + } + + /*set rx share mem start addr*/ + dev->mng.tx_ring_cnt = (i2c_phyt_read_reg(dev, FT_I2C_REGFILE_RING) & + FT_I2C_REGFILE_TX_RING_MASK) >> FT_I2C_REGFILE_TX_RING_OFFSET; + if (!dev->mng.tx_ring_cnt || (dev->mng.tx_ring_cnt > 8)) { + dev_err(&pdev->dev, "failed set tx ring cnt:%d\n", dev->mng.tx_ring_cnt); + return -EINVAL; + } + dev->rx_shmem_addr = dev->tx_shmem_addr + + dev->mng.tx_ring_cnt * sizeof(struct phyt_msg_info); + + dev->dev = &pdev->dev; + platform_set_drvdata(pdev, dev); + + ret = i2c_phyt_malloc_log_mem(dev); + if (ret) + return -ENOMEM; + + dev->timer.expires = jiffies + msecs_to_jiffies(3000); + timer_setup(&dev->timer, i2c_phyt_timer_handle, 0); + add_timer(&dev->timer); + + dev->watchdog = i2c_phyt_common_watchdog; + + i2c_phyt_disable_debug(dev); + dev->debug_enabled = false; + + dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(dev->rst)) { + if (PTR_ERR(dev->rst) == -EPROBE_DEFER) { + dev_err(&pdev->dev, "dev rst not null\n"); + return -EPROBE_DEFER; + } + } else { + reset_control_deassert(dev->rst); + } + + t = &dev->timings; + i2c_parse_fw_timings(&pdev->dev, t, false); + + acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev); + + /* + * Some DSTDs use a non standard speed, round down to the lowest + * standard speed. + */ + for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) { + if (acpi_speed < supported_speeds[i]) + break; + } + acpi_speed = supported_speeds[i - 1]; + + /* + * Find bus speed from the "clock-frequency" device property, ACPI + * or by using fast mode if neither is set. + */ + if (acpi_speed && t->bus_freq_hz) + t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed); + else if (acpi_speed || t->bus_freq_hz) + t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed); + else + t->bus_freq_hz = FT_I2C_SPEED_400K; + + if (has_acpi_companion(&pdev->dev)) + i2c_phyt_acpi_configure(pdev); + + /* + * Only standard mode at 100kHz, fast mode at 400kHz, + * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported. + */ + if (t->bus_freq_hz != FT_I2C_SPEED_100K && t->bus_freq_hz != FT_I2C_SPEED_400K && + t->bus_freq_hz != FT_I2C_SPEED_1000K && t->bus_freq_hz != FT_I2C_SPEED_3400K) { + dev_err(&pdev->dev, + "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n", + t->bus_freq_hz); + ret = -EINVAL; + goto exit_reset; + } + + if (i2c_detect_slave_mode(&pdev->dev)) + i2c_phyt_configure_slave(dev); + else + i2c_phyt_configure_master(dev); + + dev->clk = devm_clk_get(&pdev->dev, NULL); + + if (!i2c_phyt_prepare_clk(dev, true)) { + u64 clk_khz; + + dev->get_clk_rate_khz = i2c_phyt_get_clk_rate_khz; + clk_khz = dev->get_clk_rate_khz(dev); + + if (!dev->sda_hold_time && t->sda_hold_ns) + dev->sda_hold_time = + div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000); + } + + dev->adapter.nr = pdev->id; + + adap = &dev->adapter; + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; + ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); + adap->dev.of_node = pdev->dev.of_node; + adap->timeout = HZ; + adap->dev.fwnode = pdev->dev.fwnode; + dev_pm_set_driver_flags(&pdev->dev, + DPM_FLAG_SMART_PREPARE | + DPM_FLAG_SMART_SUSPEND | + DPM_FLAG_MAY_SKIP_RESUME); + + /* The code below assumes runtime PM to be disabled. */ + WARN_ON(pm_runtime_enabled(&pdev->dev)); + + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + + pm_runtime_enable(&pdev->dev); + + if (dev->mode == phyt_IC_SLAVE) + ret = i2c_phyt_slave_probe(dev); + else + ret = i2c_phyt_master_probe(dev); + + if (ret) + goto exit_probe; + + if (sysfs_create_group(&dev->dev->kobj, &i2c_ft_device_group)) + dev_warn(&pdev->dev, "failed create sysfs\n"); + + i2c_phyt_enable_alive(dev); + dev->alive_enabled = true; + + return ret; + +exit_probe: + pm_runtime_disable(dev->dev); +exit_reset: + del_timer(&dev->timer); + if (!IS_ERR_OR_NULL(dev->rst)) + reset_control_assert(dev->rst); + return ret; +} + +static int i2c_phyt_plat_remove(struct platform_device *pdev) +{ + struct i2c_phyt_dev *dev = platform_get_drvdata(pdev); + + dev_info(&pdev->dev, "i2c_2.0 remove\n"); + pm_runtime_get_sync(&pdev->dev); + + i2c_del_adapter(&dev->adapter); + sysfs_remove_group(&dev->dev->kobj, &i2c_ft_device_group); + dev->disable(dev); + /*disable alive function*/ + i2c_phyt_disable_alive(dev); + del_timer(&dev->timer); + + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(dev->dev); + + if (!IS_ERR_OR_NULL(dev->rst)) + reset_control_assert(dev->rst); + + i2c_phyt_common_regfile_disable_int(dev); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id i2c_phyt_of_match[] = { + { .compatible = "phytium,i2c-2.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_phyt_of_match); +#endif + +static int __maybe_unused i2c_phyt_plat_suspend(struct device *dev) +{ + struct i2c_phyt_dev *idev = dev_get_drvdata(dev); + + idev->disable(idev); + + i2c_phyt_disable_alive(idev); + idev->alive_enabled = false; + + i2c_phyt_prepare_clk(idev, false); + + return 0; +} + +static int __maybe_unused i2c_phyt_plat_resume(struct device *dev) +{ + struct i2c_phyt_dev *idev = dev_get_drvdata(dev); + + i2c_phyt_prepare_clk(idev, true); + idev->init(idev); + + i2c_phyt_enable_alive(idev); + idev->alive_enabled = true; + + return 0; +} + +static const struct dev_pm_ops i2c_phyt_dev_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(i2c_phyt_plat_suspend, + i2c_phyt_plat_resume) + SET_RUNTIME_PM_OPS(i2c_phyt_plat_suspend, + i2c_phyt_plat_resume, NULL) +}; + +static struct platform_driver i2c_phyt_driver = { + .probe = i2c_phyt_plat_probe, + .remove = i2c_phyt_plat_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(i2c_phyt_of_match), + .acpi_match_table = ACPI_PTR(i2c_phyt_acpi_match), + .pm = &i2c_phyt_dev_pm_ops, + }, +}; +module_platform_driver(i2c_phyt_driver); + +MODULE_ALIAS("platform:i2c-2.0"); +MODULE_AUTHOR("Wu Jinyong "); +MODULE_DESCRIPTION("Phytium I2C bus adapter"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(I2C_PHYTIUM_V2_DRV_VERSION); diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-slave.c b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-slave.c new file mode 100644 index 0000000000..5675a1974f --- /dev/null +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-slave.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2C adapter driver (slave only). + * + * Copyright (C) 2023-2024, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c-phyt-core.h" + +#define FT_I2C_RX_FIFO_FULL_ENABLE 1 +#define FT_I2C_RX_FIFO_FULL_DISABLE 0 + +#define FT_I2C_CON_STOP_DET_IFADDR_ENABLE 1 +#define FT_I2C_CON_STOP_DET_IFADDR_DISABLE 0 + +#define FT_I2C_SLAVE_EVNET_MAX_CNT 56 +#define FT_I2C_SLAVE_DATA_INDEX(index) (index+1) +/*one is cmd,the next one is data*/ +#define FT_I2C_SLAVE_RX_INFO_SIZE 2 + +int i2c_phyt_slave_event_process(struct i2c_phyt_dev *dev, + struct phyt_msg_info *rx_msg, u32 rx_head, u32 rx_tail) +{ + u8 buf[32] = {1}; + u32 head = rx_head, tail = rx_tail; + + if (!dev || !dev->slave) { + dev_err(dev->dev, "dev is null\n"); + return -ENXIO; + } + + if ((head >= FT_I2C_SLAVE_EVNET_MAX_CNT) || (tail >= FT_I2C_SLAVE_EVNET_MAX_CNT)) { + dev_err(dev->dev, "head:%d, tail:%d\n", head, tail); + return -EINVAL; + } + + while (head != tail) { + if (head % FT_I2C_SLAVE_RX_INFO_SIZE) + head++; + + i2c_slave_event(dev->slave, rx_msg->data[head], + &(rx_msg->data[FT_I2C_SLAVE_DATA_INDEX(head)])); + /*send the read data to RV*/ + if ((rx_msg->data[head] == I2C_SLAVE_READ_REQUESTED) || + (rx_msg->data[head] == I2C_SLAVE_READ_PROCESSED)) { + /*buf[0] is used to stor size,and fixed to 1*/ + buf[1] = rx_msg->data[FT_I2C_SLAVE_DATA_INDEX(head)]; + i2c_phyt_data_cmd8_array(dev, PHYTI2C_MSG_CMD_DATA_SLAVE, + buf, 2); + } + head += FT_I2C_SLAVE_RX_INFO_SIZE; + if (head >= FT_I2C_SLAVE_EVNET_MAX_CNT) + head = 0; + } + + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_HEAD, head); + + return 0; +} + +static irqreturn_t i2c_phyt_slave_regfile_isr(int this_irq, void *dev_id) +{ + u32 stat; + struct i2c_phyt_dev *dev = (struct i2c_phyt_dev *)dev_id; + + stat = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_RV2AP_INTR_STAT); + + if (!(stat & FT_I2C_RV2AP_INTR_BIT4)) { + dev_warn(dev->dev, "intr stat=%#x\n", stat); + return IRQ_NONE; + } + + i2c_phyt_common_regfile_clear_rv2ap_int(dev, stat); + + if (dev->complete_flag) { + dev->complete_flag = false; + complete(&dev->cmd_complete); + } else { + i2c_phyt_slave_isr_handle(dev); + i2c_phyt_show_log(dev); + } + + return IRQ_HANDLED; +} + +static int i2c_phyt_slave_init(struct i2c_phyt_dev *dev) +{ + u8 slave_mode = FT_I2C_MASTER_MODE_FLAG; + u8 rx_fifo_full_en = FT_I2C_RX_FIFO_FULL_DISABLE; + u8 stop_det_ifaddr_en = FT_I2C_CON_STOP_DET_IFADDR_DISABLE; + int ret; + + i2c_phyt_set_module_en(dev, FT_I2C_ADAPTER_MODULE_OFF); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set module off: %d\n", ret); + return ret; + } + + if (dev->sda_hold_time) { + i2c_phyt_set_sda_hold(dev, dev->sda_hold_time); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set sda hold time: %d\n", ret); + return ret; + } + } + + /*set slave mode*/ + if ((dev->mode & FT_I2C_CON_MASTER_MODE_MASK) == phyt_IC_SLAVE) + slave_mode = FT_I2C_SLAVE_MODE_FLAG; + else { + dev_err(dev->dev, "dev is not in slave mode\n"); + return -EINVAL; + } + + i2c_phyt_set_cmd8(dev, PHYTI2C_MSG_CMD_SET_MODE, slave_mode); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set mode: %d\n", ret); + return ret; + } + + i2c_phyt_set_int_tl(dev, 0, 0); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set int tl: %d\n", ret); + return ret; + } + + if ((dev->slave_cfg & FT_I2C_CON_RX_FIFO_FULL_HLD_MASK) == + FT_IC_CON_RX_FIFO_FULL_HLD_CTRL) + rx_fifo_full_en = FT_I2C_RX_FIFO_FULL_ENABLE; + + i2c_phyt_set_cmd8(dev, PHYTI2C_MSG_CMD_SET_RX_FIFO_FULL, rx_fifo_full_en); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set rx fifo full: %d\n", ret); + return ret; + } + + if ((dev->slave_cfg & FT_I2C_CON_STOP_DET_IFADDR_MASK) == FT_IC_CON_STOP_DET_IFADDRESSED) + stop_det_ifaddr_en = FT_I2C_CON_STOP_DET_IFADDR_ENABLE; + + i2c_phyt_set_cmd8(dev, PHYTI2C_MSG_CMD_SET_STOP_DET_IF_ADDRESSED, + stop_det_ifaddr_en); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set stop det ifaddr: %d\n", ret); + return ret; + } + i2c_phyt_set_int_interrupt(dev, FT_I2C_ENABLE_INTERRUPT, FT_IC_INTR_SLAVE_MASK); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set interrupt: %d\n", ret); + return ret; + } + + return 0; +} + +static int i2c_phyt_reg_slave(struct i2c_client *slave) +{ + int ret; + struct i2c_phyt_dev *dev = i2c_get_adapdata(slave->adapter); + + if (dev->slave) + return -EBUSY; + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + + pm_runtime_get_sync(dev->dev); + + /* + * Set slave address in the IC_SAR register, + * the address to which the i2c responds. + */ + i2c_phyt_set_module_en(dev, FT_I2C_ADAPTER_MODULE_OFF); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set module off: %d\n", ret); + return ret; + } + + i2c_phyt_set_cmd8(dev, PHYTI2C_MSG_CMD_SET_ADDR_MODE, FT_I2C_ADDR_7BIT_MODE); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set addr mode: %d\n", ret); + return ret; + } + + i2c_phyt_set_cmd8(dev, PHYTI2C_MSG_CMD_SET_ADDR, slave->addr); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set addr: %d\n", ret); + return ret; + } + + dev->slave = slave; + + i2c_phyt_set_module_en(dev, FT_I2C_ADAPTER_MODULE_ON); + ret = i2c_phyt_check_result(dev); + if (ret) { + dev_err(dev->dev, "fail to set module on: %d\n", ret); + return ret; + } + + return 0; +} + +static int i2c_phyt_unreg_slave(struct i2c_client *slave) +{ + struct i2c_phyt_dev *dev = i2c_get_adapdata(slave->adapter); + + dev->disable_int(dev); + dev->disable(dev); + dev->slave = NULL; + pm_runtime_put(dev->dev); + + return 0; +} + +static const struct i2c_algorithm i2c_phyt_slave_algo = { + .functionality = i2c_phyt_func, + .reg_slave = i2c_phyt_reg_slave, + .unreg_slave = i2c_phyt_unreg_slave, +}; + + +int i2c_phyt_slave_probe(struct i2c_phyt_dev *dev) +{ + struct i2c_adapter *adap = &dev->adapter; + int ret; + + init_completion(&dev->cmd_complete); + + i2c_phyt_common_regfile_disable_int(dev); + i2c_phyt_common_regfile_clear_rv2ap_int(dev, 0); + + ret = devm_request_irq(dev->dev, dev->irq, i2c_phyt_slave_regfile_isr, IRQF_SHARED, + dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "failure requesting irq %i: %d\n", + dev->irq, ret); + return ret; + } + + i2c_phyt_common_regfile_enable_int(dev); + + dev->init = i2c_phyt_slave_init; + dev->disable = i2c_phyt_disable; + dev->disable_int = i2c_phyt_disable_int; + + snprintf(adap->name, sizeof(adap->name), + "Phytium I2C_2.0 Slave adapter"); + adap->retries = 3; + adap->algo = &i2c_phyt_slave_algo; + adap->dev.parent = dev->dev; + + ret = i2c_phyt_slave_init(dev); + if (ret) + return ret; + + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_HEAD, 0); + i2c_set_adapdata(adap, dev); + + ret = i2c_add_numbered_adapter(adap); + if (ret) + dev_err(dev->dev, "failure adding adapter: %d\n", ret); + + dev_info(dev->dev, "i2c_2.0 slave probe ok\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_phyt_slave_probe); -- Gitee From ae5a2277b2dd8611c0be36b8e329f2f490501652 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Tue, 18 Feb 2025 09:45:32 +0800 Subject: [PATCH 069/154] i2c:phytium: Combine normal mode into master-slave switching mode This patch combines normal mode into master-slave switching mode,the application can use master mode or slave mode or master-slave switching mode. Mainline: Open-Source Signed-off-by: Wu Jinyong Signed-off-by: Wang Yinfeng Change-Id: I0fd85eb1f1a933433d434758dcca871b2a7821e7 --- drivers/i2c/busses/Makefile | 2 +- drivers/i2c/busses/i2c-phytium-common.c | 41 +- drivers/i2c/busses/i2c-phytium-core.h | 43 +- ...i2c-phytium-master.c => i2c-phytium-mix.c} | 512 ++++++++++++++---- drivers/i2c/busses/i2c-phytium-pci.c | 10 +- drivers/i2c/busses/i2c-phytium-platform.c | 48 +- 6 files changed, 480 insertions(+), 176 deletions(-) rename drivers/i2c/busses/{i2c-phytium-master.c => i2c-phytium-mix.c} (51%) diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index c7818ae750..a94ccad302 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -124,7 +124,7 @@ obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o obj-$(CONFIG_I2C_ZX2967) += i2c-zx2967.o obj-$(CONFIG_I2C_PHYTIUM_CORE) += i2c-phytium-core.o -i2c-phytium-core-objs := i2c-phytium-common.o i2c-phytium-master.o i2c-phytium-slave.o +i2c-phytium-core-objs := i2c-phytium-common.o i2c-phytium-mix.o obj-$(CONFIG_I2C_PHYTIUM_PCI) += i2c-phytium-pci.o obj-$(CONFIG_I2C_PHYTIUM_PLATFORM) += i2c-phytium-platform.o diff --git a/drivers/i2c/busses/i2c-phytium-common.c b/drivers/i2c/busses/i2c-phytium-common.c index d5f1ce2469..b008e6a10b 100644 --- a/drivers/i2c/busses/i2c-phytium-common.c +++ b/drivers/i2c/busses/i2c-phytium-common.c @@ -2,8 +2,6 @@ /* * Phytium I2C adapter driver. * - * Based on the TI DAVINCI I2C adapter driver. - * * Copyright (c) 2021-2024 Phytium Technology Co., Ltd. */ #include @@ -17,6 +15,7 @@ #include #include #include +#include #include "i2c-phytium-core.h" @@ -93,22 +92,7 @@ int i2c_phytium_set_sda_hold(struct phytium_i2c_dev *dev) void __i2c_phytium_disable(struct phytium_i2c_dev *dev) { - int timeout = 100; - - do { - __i2c_phytium_disable_nowait(dev); - if ((phytium_readl(dev, IC_ENABLE_STATUS) & 1) == 0) - return; - - /* - * Wait 10 times the signaling period of the highest I2C - * transfer supported by the driver (for 400KHz this is - * 25us). - */ - usleep_range(25, 250); - } while (timeout--); - - dev_warn(dev->dev, "timeout in disabling adapter\n"); + __i2c_phytium_disable_nowait(dev); } unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev) @@ -131,23 +115,12 @@ int i2c_phytium_prepare_clk(struct phytium_i2c_dev *dev, bool prepare) } EXPORT_SYMBOL_GPL(i2c_phytium_prepare_clk); -int i2c_phytium_wait_bus_not_busy(struct phytium_i2c_dev *dev) +int i2c_phytium_check_bus_not_busy(struct phytium_i2c_dev *dev) { - int timeout = 20; /* 20 ms */ - - while (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) { - if (timeout <= 0) { - dev_warn(dev->dev, "timeout waiting for bus ready\n"); - i2c_recover_bus(&dev->adapter); - - if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) - return -ETIMEDOUT; - return 0; - } - timeout--; - usleep_range(1000, 1100); - } - + if (dev->slave_state != SLAVE_STATE_IDLE) + return -EAGAIN; + if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) + return -ETIMEDOUT; return 0; } diff --git a/drivers/i2c/busses/i2c-phytium-core.h b/drivers/i2c/busses/i2c-phytium-core.h index 726a4f9f4f..cd37d3ea68 100644 --- a/drivers/i2c/busses/i2c-phytium-core.h +++ b/drivers/i2c/busses/i2c-phytium-core.h @@ -8,9 +8,13 @@ #include #include #include +#include + +#define I2C_PHYTIUM_DRV_VERSION "1.0.0" #define IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \ I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_QUICK | \ I2C_FUNC_SMBUS_BYTE_DATA | \ I2C_FUNC_SMBUS_WORD_DATA | \ I2C_FUNC_SMBUS_BLOCK_DATA | \ @@ -88,7 +92,6 @@ #define IC_INTR_SMBALERT_IN_N 0x20000 #define IC_INTR_DEFAULT_MASK (IC_INTR_RX_FULL | \ - IC_INTR_TX_ABRT | \ IC_INTR_STOP_DET) #define IC_INTR_MASTER_MASK (IC_INTR_DEFAULT_MASK | \ IC_INTR_TX_EMPTY) @@ -99,7 +102,8 @@ #define IC_INTR_SMBUS_MASK (IC_INTR_MASTER_MASK | \ IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | \ IC_INTR_SMBCLK_TMO_LOW_TIMEOUT | \ - IC_INTR_SMBSDA_LOW_TIMEOUT) + IC_INTR_SMBSDA_LOW_TIMEOUT | \ + IC_INTR_SMBALERT_IN_N) #define IC_STATUS_ACTIVITY 0x1 #define IC_STATUS_TFE BIT(2) @@ -110,6 +114,15 @@ #define IC_SDA_HOLD_RX_MASK GENMASK(23, IC_SDA_HOLD_RX_SHIFT) #define IC_ERR_TX_ABRT 0x1 +#define IC_ERR_SMBLK_READ_ZERO BIT(31) + +#define IC_ENABLE_EN BIT(0) +#define IC_ENABLE_ALERT_EN BIT(5) +#define IC_ENABLE_RXDATA_ADVANCE_EN BIT(11) +#define IC_ENABLE_MASK (IC_ENABLE_EN | \ + IC_ENABLE_ALERT_EN | \ + IC_ENABLE_RXDATA_ADVANCE_EN) +#define IC_DISABLE_MASK IC_ENABLE_ALERT_EN #define IC_TAR_10BITADDR_MASTER BIT(12) @@ -126,6 +139,15 @@ #define PHYTIUM_IC_MASTER 0 #define PHYTIUM_IC_SLAVE 1 +#if IS_ENABLED(CONFIG_I2C_SLAVE) +enum i2c_slave_state { + SLAVE_STATE_IDLE, + SLAVE_STATE_RECV, + SLAVE_STATE_SEND, + SLAVE_STATE_REQUEST, + SLAVE_STATE_RESPONSE +}; +#endif #define ABRT_7B_ADDR_NOACK 0 #define ABRT_10ADDR1_NOACK 1 #define ABRT_10ADDR2_NOACK 2 @@ -173,6 +195,12 @@ struct phytium_i2c_dev { struct clk *clk; struct reset_control *rst; int mode; + + u32 capability; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + enum i2c_slave_state slave_state; +#endif + spinlock_t i2c_lock; struct i2c_client *slave; u32 (*get_clk_rate_khz)(struct phytium_i2c_dev *dev); @@ -215,6 +243,7 @@ struct phytium_i2c_dev { u16 hs_lcnt; bool pm_disabled; + bool first_time_init_master; void (*disable)(struct phytium_i2c_dev *dev); void (*disable_int)(struct phytium_i2c_dev *dev); int (*init)(struct phytium_i2c_dev *dev); @@ -228,7 +257,7 @@ u32 phytium_readl(struct phytium_i2c_dev *dev, int offset); void phytium_writel(struct phytium_i2c_dev *dev, u32 b, int offset); unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev); int i2c_phytium_prepare_clk(struct phytium_i2c_dev *dev, bool prepare); -int i2c_phytium_wait_bus_not_busy(struct phytium_i2c_dev *dev); +int i2c_phytium_check_bus_not_busy(struct phytium_i2c_dev *dev); int i2c_phytium_handle_tx_abort(struct phytium_i2c_dev *dev); u32 i2c_phytium_func(struct i2c_adapter *adap); void i2c_phytium_disable(struct phytium_i2c_dev *dev); @@ -239,16 +268,14 @@ u32 i2c_phytium_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); static inline void __i2c_phytium_enable(struct phytium_i2c_dev *dev) { - phytium_writel(dev, 1, IC_ENABLE); + phytium_writel(dev, IC_ENABLE_MASK, IC_ENABLE); } static inline void __i2c_phytium_disable_nowait(struct phytium_i2c_dev *dev) { - phytium_writel(dev, 0, IC_ENABLE); + phytium_writel(dev, IC_DISABLE_MASK, IC_ENABLE); } void __i2c_phytium_disable(struct phytium_i2c_dev *dev); - +void i2c_phytium_configure_master(struct phytium_i2c_dev *dev); extern int i2c_phytium_probe(struct phytium_i2c_dev *dev); - -extern int i2c_phytium_probe_slave(struct phytium_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-phytium-master.c b/drivers/i2c/busses/i2c-phytium-mix.c similarity index 51% rename from drivers/i2c/busses/i2c-phytium-master.c rename to drivers/i2c/busses/i2c-phytium-mix.c index b478e5a53b..748042c9fe 100644 --- a/drivers/i2c/busses/i2c-phytium-master.c +++ b/drivers/i2c/busses/i2c-phytium-mix.c @@ -4,6 +4,7 @@ * * Copyright (c) 2021-2024 Phytium Technology Co., Ltd. */ +#include #include #include #include @@ -17,6 +18,78 @@ #include "i2c-phytium-core.h" +#define I2C_TRANS_READ_CMD BIT(8) +#define I2C_TRANS_STOP_CMD BIT(9) +#define I2C_TRANS_RESTART_CMD BIT(10) +#define I2C_QUICK_CMD_BIT_SET BIT(13) +#define DEFAULT_TIMEOUT (DEFAULT_CLOCK_FREQUENCY / 1000 * 35) + +static int i2c_phytium_recover_controller(struct phytium_i2c_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->i2c_lock, flags); + + reset_control_reset(dev->rst); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->slave_state = SLAVE_STATE_IDLE; + dev->status = STATUS_IDLE; + if (dev->slave) + phytium_writel(dev, dev->slave->addr, IC_SAR); +#endif + + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} + +static int i2c_phytium_init_slave(struct phytium_i2c_dev *dev) +{ + /* Disable the adapter. */ + __i2c_phytium_disable(dev); + + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); + + /* Configure Tx/Rx FIFO threshold levels. */ + phytium_writel(dev, 0, IC_TX_TL); + phytium_writel(dev, 0, IC_RX_TL); + dev->mode = PHYTIUM_IC_SLAVE; + + /* Configure the I2C slave. */ + dev->slave_cfg = IC_CON_RX_FIFO_FULL_HLD_CTRL | IC_CON_RESTART_EN | + IC_CON_STOP_DET_IFADDRESSED; + phytium_writel(dev, dev->slave_cfg, IC_CON); + phytium_writel(dev, IC_INTR_SLAVE_MASK, IC_INTR_MASK); + + return 0; +} + +void i2c_phytium_configure_master(struct phytium_i2c_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + + dev->functionality = I2C_FUNC_10BIT_ADDR | IC_DEFAULT_FUNCTIONALITY | + I2C_FUNC_SLAVE; + + dev->master_cfg = + IC_CON_MASTER | IC_CON_SLAVE_DISABLE | IC_CON_RESTART_EN; + + dev->mode = PHYTIUM_IC_MASTER; + + switch (t->bus_freq_hz) { + case 100000: + dev->master_cfg |= IC_CON_SPEED_STD; + break; + case 3400000: + dev->master_cfg |= IC_CON_SPEED_HIGH; + break; + default: + dev->master_cfg |= IC_CON_SPEED_FAST; + } +} +EXPORT_SYMBOL_GPL(i2c_phytium_configure_master); + static int i2c_phytium_init_master(struct phytium_i2c_dev *dev) { /* Disable the adapter */ @@ -35,7 +108,7 @@ static int i2c_phytium_init_master(struct phytium_i2c_dev *dev) phytium_writel(dev, dev->hs_hcnt, IC_HS_SCL_HCNT); phytium_writel(dev, dev->hs_lcnt, IC_HS_SCL_LCNT); } - + dev->mode = PHYTIUM_IC_MASTER; /* Write SDA hold time if supported */ if (dev->sda_hold_time) phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); @@ -45,11 +118,29 @@ static int i2c_phytium_init_master(struct phytium_i2c_dev *dev) phytium_writel(dev, 0, IC_RX_TL); /* Configure the I2C master */ + if (dev->first_time_init_master == false) { + i2c_phytium_configure_master(dev); + dev->first_time_init_master = true; + } phytium_writel(dev, dev->master_cfg, IC_CON); return 0; } +#if IS_ENABLED(CONFIG_I2C_SLAVE) +static void i2c_phytium_change_mode(int target_mode, struct phytium_i2c_dev *dev) +{ + if (target_mode == PHYTIUM_IC_MASTER) { + dev->disable_int(dev); + dev->disable(dev); + i2c_phytium_init_master(dev); + } else { + i2c_phytium_init_slave(dev); + __i2c_phytium_enable(dev); + } +} +#endif + static void i2c_phytium_xfer_init(struct phytium_i2c_dev *dev) { struct i2c_msg *msgs = dev->msgs; @@ -58,6 +149,9 @@ static void i2c_phytium_xfer_init(struct phytium_i2c_dev *dev) /* Disable the adapter */ __i2c_phytium_disable(dev); + if ((dev->capability & I2C_FUNC_SMBUS_QUICK) && (msgs[0].len == 0)) + ic_tar = I2C_QUICK_CMD_BIT_SET; + /* If the slave address is 10-bit address, enable 10BITADDR */ ic_con = phytium_readl(dev, IC_CON); if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { @@ -99,23 +193,26 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) u8 *buf = dev->tx_buf; bool need_restart = false; - intr_mask = IC_INTR_MASTER_MASK; + intr_mask = IC_INTR_SMBUS_MASK; for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { u32 flags = msgs[dev->msg_write_idx].flags; if (msgs[dev->msg_write_idx].addr != addr) { - dev_err(dev->dev, - "%s: invalid target address\n", __func__); dev->msg_err = -EINVAL; break; } + /* If the register dose not support smbus block read function, + * then use the operation of program code + */ + if (!(dev->capability & I2C_FUNC_SMBUS_BLOCK_DATA) && + (flags & I2C_M_RECV_LEN)) + msgs[dev->msg_write_idx].len = 1 + I2C_SMBUS_BLOCK_MAX; if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { /* new i2c_msg */ buf = msgs[dev->msg_write_idx].buf; buf_len = msgs[dev->msg_write_idx].len; - if ((dev->master_cfg & IC_CON_RESTART_EN) && (dev->msg_write_idx > 0)) need_restart = true; @@ -123,16 +220,14 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) tx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_TXFLR); rx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_RXFLR); - while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { u32 cmd = 0; - if (dev->msg_write_idx == dev->msgs_num - 1 && - buf_len == 1 && !(flags & I2C_M_RECV_LEN)) - cmd |= BIT(9); + if (dev->msg_write_idx == dev->msgs_num - 1 && buf_len == 1) + cmd |= I2C_TRANS_STOP_CMD; if (need_restart) { - cmd |= BIT(10); + cmd |= I2C_TRANS_RESTART_CMD; need_restart = false; } @@ -141,7 +236,7 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) if (dev->rx_outstanding >= dev->rx_fifo_depth) break; - phytium_writel(dev, cmd | 0x100, IC_DATA_CMD); + phytium_writel(dev, cmd | I2C_TRANS_READ_CMD, IC_DATA_CMD); rx_limit--; dev->rx_outstanding++; } else { @@ -159,22 +254,30 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop * the transaction here. */ - if (buf_len > 0 || flags & I2C_M_RECV_LEN) { + if (buf_len > 0) { /* more bytes to be written */ dev->status |= STATUS_WRITE_IN_PROGRESS; break; } - dev->status &= ~STATUS_WRITE_IN_PROGRESS; } + /*This is use for quick cmd*/ + if ((dev->capability & I2C_FUNC_SMBUS_QUICK) && (msgs[0].len == 0)) { + if (msgs[0].flags == I2C_M_RD) + phytium_writel(dev, I2C_TRANS_STOP_CMD | I2C_TRANS_READ_CMD, + IC_DATA_CMD); + else + phytium_writel(dev, I2C_TRANS_STOP_CMD, IC_DATA_CMD); + } + if (dev->msg_write_idx == dev->msgs_num) intr_mask &= ~IC_INTR_TX_EMPTY; if (dev->msg_err) intr_mask = 0; - phytium_writel(dev, intr_mask, IC_INTR_MASK); + phytium_writel(dev, intr_mask, IC_INTR_MASK); } static u8 i2c_phytium_recv_len(struct phytium_i2c_dev *dev, u8 len) @@ -186,10 +289,17 @@ static u8 i2c_phytium_recv_len(struct phytium_i2c_dev *dev, u8 len) * Adjust the buffer length and mask the flag * after receiving the first byte. */ + if (len > I2C_SMBUS_BLOCK_MAX) + len = 0; + len += (flags & I2C_CLIENT_PEC) ? 2 : 1; dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding); - msgs[dev->msg_read_idx].len = len; - msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; + + /* require adding one byte to trigger the i2c_phytium_xfer finishing the transaction. */ + if (dev->tx_buf_len == 0) { + dev->tx_buf_len = 1; + len = dev->rx_outstanding + 1; + } return len; } @@ -221,9 +331,19 @@ static void i2c_phytium_read(struct phytium_i2c_dev *dev) *buf = phytium_readl(dev, IC_DATA_CMD); /* Ensure length byte is a valid value */ - if (flags & I2C_M_RECV_LEN && - *buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) { + if (flags & I2C_M_RECV_LEN) { + msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; len = i2c_phytium_recv_len(dev, *buf); + if (*buf > I2C_SMBUS_BLOCK_MAX || *buf == 0) { + dev_err(dev->dev, + "The value %d is out of the SMBus range [1~32]\n", + *buf); + *buf = 0; + dev->cmd_err = IC_ERR_SMBLK_READ_ZERO; + } else { + msgs[dev->msg_read_idx].len = *buf + + ((flags & I2C_CLIENT_PEC) ? 2 : 1); + } } buf++; dev->rx_outstanding--; @@ -233,23 +353,30 @@ static void i2c_phytium_read(struct phytium_i2c_dev *dev) dev->status |= STATUS_READ_IN_PROGRESS; dev->rx_buf_len = len; dev->rx_buf = buf; - break; + return; } - dev->status &= ~STATUS_READ_IN_PROGRESS; } } -static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num) +static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], + int num) { struct phytium_i2c_dev *dev = i2c_get_adapdata(adapter); int ret; + unsigned long flags; dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); - pm_runtime_get_sync(dev->dev); + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) { + dev_err(dev->dev, "pm runtime get sync err.\n"); + goto pm_exit; + } + spin_lock_irqsave(&dev->i2c_lock, flags); reinit_completion(&dev->cmd_complete); + dev->msgs = msgs; dev->msgs_num = num; dev->cmd_err = 0; @@ -260,22 +387,30 @@ static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], dev->abort_source = 0; dev->rx_outstanding = 0; - ret = i2c_phytium_wait_bus_not_busy(dev); + ret = i2c_phytium_check_bus_not_busy(dev); if (ret < 0) goto done; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_cfg) + i2c_phytium_change_mode(PHYTIUM_IC_MASTER, dev); +#endif + /* Start the transfers */ i2c_phytium_xfer_init(dev); - + spin_unlock_irqrestore(&dev->i2c_lock, flags); /* Wait for tx to complete */ - if (!wait_for_completion_timeout(&dev->cmd_complete, adapter->timeout)) { + if (!wait_for_completion_timeout(&dev->cmd_complete, + adapter->timeout)) { dev_err(dev->dev, "controller timed out\n"); - i2c_recover_bus(&dev->adapter); - i2c_phytium_init_master(dev); + i2c_phytium_recover_controller(dev); + spin_lock_irqsave(&dev->i2c_lock, flags); + if (!dev->slave_cfg) + i2c_phytium_init_master(dev); ret = -ETIMEDOUT; goto done; } - + spin_lock_irqsave(&dev->i2c_lock, flags); __i2c_phytium_disable_nowait(dev); if (dev->msg_err) { @@ -290,29 +425,99 @@ static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], /* We have got an error */ if (dev->cmd_err == IC_ERR_TX_ABRT) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); ret = i2c_phytium_handle_tx_abort(dev); + spin_lock_irqsave(&dev->i2c_lock, flags); goto done; } - if (dev->status) - dev_err(dev->dev, "transfer terminated early.\n"); - ret = -EIO; done: +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_cfg) + i2c_phytium_change_mode(PHYTIUM_IC_SLAVE, dev); + dev->status = STATUS_IDLE; + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_unlock_irqrestore(&dev->i2c_lock, flags); + +pm_exit: + pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); return ret; } +#if IS_ENABLED(CONFIG_I2C_SLAVE) +static int i2c_phytium_reg_slave(struct i2c_client *slave) +{ + int ret = 0; + unsigned long flags; + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + + spin_lock_irqsave(&dev->i2c_lock, flags); + + if (dev->slave) { + ret = -EBUSY; + goto abort; + } + if (slave->flags & I2C_CLIENT_TEN) { + ret = -EAFNOSUPPORT; + goto abort; + } + pm_runtime_get_sync(dev->dev); + + /* + * Set slave address in the IC_SAR register, + * the address to which the i2c responds. + */ + i2c_phytium_init_slave(dev); + phytium_writel(dev, slave->addr, IC_SAR); + dev->slave = slave; + dev->mode = PHYTIUM_IC_SLAVE; + __i2c_phytium_enable(dev); + + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + dev->rx_outstanding = 0; + +abort: + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return ret; +} + +static int i2c_phytium_unreg_slave(struct i2c_client *slave) +{ + unsigned long flags; + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + + spin_lock_irqsave(&dev->i2c_lock, flags); + + dev->disable_int(dev); + dev->disable(dev); + dev->slave = NULL; + pm_runtime_put(dev->dev); + + dev->mode = PHYTIUM_IC_MASTER; + i2c_phytium_init_master(dev); + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} +#endif + static const struct i2c_algorithm i2c_phytium_algo = { .master_xfer = i2c_phytium_xfer, .functionality = i2c_phytium_func, -}; - -static const struct i2c_adapter_quirks i2c_phytium_quirks = { - .flags = I2C_AQ_NO_ZERO_LEN, +#if IS_ENABLED(CONFIG_I2C_SLAVE) + .reg_slave = i2c_phytium_reg_slave, + .unreg_slave = i2c_phytium_unreg_slave, +#endif }; static u32 i2c_phytium_read_clear_intrbits(struct phytium_i2c_dev *dev) @@ -330,7 +535,8 @@ static u32 i2c_phytium_read_clear_intrbits(struct phytium_i2c_dev *dev) if (stat & IC_INTR_RD_REQ) phytium_readl(dev, IC_CLR_RD_REQ); if (stat & IC_INTR_TX_ABRT) { - dev->abort_source = phytium_readl(dev, IC_TX_ABRT_SOURCE); + if (dev->mode == PHYTIUM_IC_MASTER) + dev->abort_source = phytium_readl(dev, IC_TX_ABRT_SOURCE); phytium_readl(dev, IC_CLR_TX_ABRT); } if (stat & IC_INTR_RX_DONE) @@ -357,12 +563,14 @@ static u32 i2c_phytium_read_clear_intrbits(struct phytium_i2c_dev *dev) static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) { - u32 stat; + u32 stat, raw_stat; + raw_stat = phytium_readl(dev, IC_RAW_INTR_STAT); stat = i2c_phytium_read_clear_intrbits(dev); /* SMBus interrupt */ - if (stat & (IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | IC_INTR_SMBCLK_TMO_LOW_TIMEOUT)) { + if (stat & + (IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | IC_INTR_SMBCLK_TMO_LOW_TIMEOUT)) { phytium_writel(dev, phytium_readl(dev, IC_ENABLE) & (~BIT(6)), IC_ENABLE); phytium_writel(dev, phytium_readl(dev, IC_ENABLE) | BIT(4), @@ -379,15 +587,25 @@ static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) if (stat & IC_INTR_SMBALERT_IN_N && dev->ara) i2c_handle_smbus_alert(dev->ara); - if (stat & IC_INTR_TX_ABRT) { - dev->cmd_err |= IC_ERR_TX_ABRT; - dev->status = STATUS_IDLE; - - /* Anytime TX_ABRT is set, the contents of the tx/rx - * buffers are flushed. Make sure to skip them. - */ - phytium_writel(dev, 0, IC_INTR_MASK); - goto abort; + if (raw_stat & IC_INTR_TX_ABRT) { + if (stat & IC_INTR_TX_ABRT) { + dev->cmd_err |= IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + + /* Anytime TX_ABRT is set, the contents of the tx/rx + * buffers are flushed. Make sure to skip them. + */ + phytium_writel(dev, 0, IC_INTR_MASK); + goto abort; + } else if (stat & IC_INTR_STOP_DET) { + dev->cmd_err |= IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + phytium_readl(dev, IC_CLR_TX_ABRT); + phytium_writel(dev, 0, IC_INTR_MASK); + + complete(&dev->cmd_complete); + return 0; + } } if (stat & IC_INTR_RX_FULL) @@ -397,10 +615,9 @@ static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) i2c_phytium_xfer_msg(dev); abort: - if ((stat & (IC_INTR_TX_ABRT | IC_INTR_STOP_DET)) || - dev->msg_err) + if ((stat & IC_INTR_TX_ABRT) || (stat & IC_INTR_STOP_DET) || dev->msg_err) { complete(&dev->cmd_complete); - else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { + } else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { /* Workaround to trigger pending interrupt */ stat = phytium_readl(dev, IC_INTR_MASK); i2c_phytium_disable_int(dev); @@ -410,6 +627,93 @@ static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) return 0; } +static int i2c_phytium_irq_handler_slave(struct phytium_i2c_dev *dev) +{ + u32 raw_stat, stat, enabled; + u8 val, slave_activity; + + enabled = phytium_readl(dev, IC_ENABLE); + raw_stat = phytium_readl(dev, IC_RAW_INTR_STAT); + slave_activity = + ((phytium_readl(dev, IC_STATUS) & IC_STATUS_SLAVE_ACTIVITY) >> + 6); + + if (!enabled || !(raw_stat & ~IC_INTR_ACTIVITY) || !dev->slave) + return 0; + + stat = i2c_phytium_read_clear_intrbits(dev); + + if (stat & IC_INTR_RX_FULL) { + if (dev->status != STATUS_WRITE_IN_PROGRESS) { + dev->status = STATUS_WRITE_IN_PROGRESS; + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, + &val); + dev->slave_state = SLAVE_STATE_RECV; + } + do { + val = phytium_readl(dev, IC_DATA_CMD); + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, + &val); + val = phytium_readl(dev, IC_STATUS); + } while (val & BIT(3)); + } + + if (stat & IC_INTR_RD_REQ) { + if (slave_activity) { + phytium_readl(dev, IC_CLR_RD_REQ); + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_REQUESTED, &val); + dev->status |= STATUS_READ_IN_PROGRESS; + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + dev->slave_state = SLAVE_STATE_SEND; + + } else { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_PROCESSED, &val); + } + phytium_writel(dev, val, IC_DATA_CMD); + } + } + + if (stat & IC_INTR_STOP_DET) { + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->status = STATUS_IDLE; + dev->slave_state = SLAVE_STATE_IDLE; +#endif + } + + return 1; +} + +static irqreturn_t i2c_phytium_isr(int this_irq, void *dev_id) +{ + struct phytium_i2c_dev *dev = dev_id; + u32 stat, enabled; + + spin_lock(&dev->i2c_lock); + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->mode == PHYTIUM_IC_SLAVE) { + i2c_phytium_irq_handler_slave(dev); + spin_unlock(&dev->i2c_lock); + return IRQ_HANDLED; + } +#endif + enabled = phytium_readl(dev, IC_ENABLE); + stat = phytium_readl(dev, IC_RAW_INTR_STAT); + if (!enabled || !(stat & ~IC_INTR_ACTIVITY)) { + spin_unlock(&dev->i2c_lock); + return IRQ_NONE; + } + + i2c_phytium_irq_handler_master(dev); + spin_unlock(&dev->i2c_lock); + return IRQ_HANDLED; +} + static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) { const char *mode_str, *fp_str = ""; @@ -425,24 +729,21 @@ static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) /* Calculate SCL timing parameters for standard mode if not set */ if (!dev->ss_hcnt || !dev->ss_lcnt) { ic_clk = i2c_phytium_clk_rate(dev); - dev->ss_hcnt = - i2c_phytium_scl_hcnt(ic_clk, - 4000, /* tHD;STA = tHIGH = 4.0 us */ - sda_falling_time, - 0, /* 0: DW default, 1: Ideal */ - 0); /* No offset */ + dev->ss_hcnt = i2c_phytium_scl_hcnt( + ic_clk, 4000, /* tHD;STA = tHIGH = 4.0 us */ + sda_falling_time, 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ dev->ss_lcnt = - i2c_phytium_scl_lcnt(ic_clk, - 4700, /* tLOW = 4.7 us */ - scl_falling_time, - 0); /* No offset */ + i2c_phytium_scl_lcnt(ic_clk, 4700, /* tLOW = 4.7 us */ + scl_falling_time, + 0); /* No offset */ } - dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", - dev->ss_hcnt, dev->ss_lcnt); + dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", dev->ss_hcnt, + dev->ss_lcnt); /* * Set SCL timing parameters for fast mode or fast mode plus. Only - * difference is the timing parameter values since the registers are - * the same. + * difference is the timing parameter values since the registers + * are the same. */ if (t->bus_freq_hz == 1000000) { /* @@ -461,20 +762,17 @@ static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) */ if (!dev->fs_hcnt || !dev->fs_lcnt) { ic_clk = i2c_phytium_clk_rate(dev); - dev->fs_hcnt = - i2c_phytium_scl_hcnt(ic_clk, - 600, /* tHD;STA = tHIGH = 0.6 us */ - sda_falling_time, - 0, /* 0: DW default, 1: Ideal */ - 0); /* No offset */ + dev->fs_hcnt = i2c_phytium_scl_hcnt( + ic_clk, 600, /* tHD;STA = tHIGH = 0.6 us */ + sda_falling_time, 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ dev->fs_lcnt = - i2c_phytium_scl_lcnt(ic_clk, - 1300, /* tLOW = 1.3 us */ - scl_falling_time, - 0); /* No offset */ + i2c_phytium_scl_lcnt(ic_clk, 1300, /* tLOW = 1.3 us */ + scl_falling_time, + 0); /* No offset */ } - dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", - fp_str, dev->fs_hcnt, dev->fs_lcnt); + dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", fp_str, + dev->fs_hcnt, dev->fs_lcnt); if (dev->hs_hcnt && dev->hs_lcnt) dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n", @@ -500,19 +798,18 @@ static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) return ret; } -static irqreturn_t i2c_phytium_isr(int this_irq, void *dev_id) +static int i2c_phytium_enable_smbus_alert(struct phytium_i2c_dev *i2c_dev) { - struct phytium_i2c_dev *dev = dev_id; - u32 stat, enabled; + struct i2c_adapter *adap = &i2c_dev->adapter; + struct i2c_smbus_alert_setup setup; - enabled = phytium_readl(dev, IC_ENABLE); - stat = phytium_readl(dev, IC_RAW_INTR_STAT); - if (!enabled || !(stat & ~IC_INTR_ACTIVITY)) - return IRQ_NONE; + setup.irq = 0; + i2c_dev->ara = i2c_new_smbus_alert_device(adap, &setup); - i2c_phytium_irq_handler_master(dev); + if (IS_ERR(i2c_dev->ara)) + return PTR_ERR(i2c_dev->ara); - return IRQ_HANDLED; + return 0; } int i2c_phytium_probe(struct phytium_i2c_dev *dev) @@ -520,13 +817,21 @@ int i2c_phytium_probe(struct phytium_i2c_dev *dev) struct i2c_adapter *adapter = &dev->adapter; unsigned long irq_flags; int ret; + u32 alert = 0; init_completion(&dev->cmd_complete); + if (dev->mode == PHYTIUM_IC_MASTER) { + snprintf(adapter->name, sizeof(adapter->name), + "Phytium I2C Adapter"); + dev->init = i2c_phytium_init_master; + } else if (dev->mode == PHYTIUM_IC_SLAVE) { + snprintf(adapter->name, sizeof(adapter->name), + "Phytium I2C Slave Adapter"); + dev->init = i2c_phytium_init_slave; + } - dev->init = i2c_phytium_init_master; dev->disable = i2c_phytium_disable; dev->disable_int = i2c_phytium_disable_int; - ret = i2c_phytium_set_timings_master(dev); if (ret) return ret; @@ -535,16 +840,14 @@ int i2c_phytium_probe(struct phytium_i2c_dev *dev) if (ret) return ret; - /* XXX: should be initialized in firmware, remove it in future */ -#define DEFAULT_TIMEOUT (DEFAULT_CLOCK_FREQUENCY / 1000 * 35) - phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_MEXT); - phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_TIMEOUT); - phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBDAT_STUCK_TIMEOUT); + if (dev->mode == PHYTIUM_IC_MASTER) { + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_MEXT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_TIMEOUT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBDAT_STUCK_TIMEOUT); + } - snprintf(adapter->name, sizeof(adapter->name), "Phytium I2C adapter"); adapter->retries = 3; adapter->algo = &i2c_phytium_algo; - adapter->quirks = &i2c_phytium_quirks; adapter->dev.parent = dev->dev; i2c_set_adapdata(adapter, dev); @@ -554,7 +857,8 @@ int i2c_phytium_probe(struct phytium_i2c_dev *dev) ret = devm_request_irq(dev->dev, dev->irq, i2c_phytium_isr, irq_flags, dev_name(dev->dev), dev); if (ret) { - dev_err(dev->dev, "failed to request irq %i: %d\n", dev->irq, ret); + dev_err(dev->dev, "failed to request irq %i: %d\n", dev->irq, + ret); return ret; } @@ -568,11 +872,27 @@ int i2c_phytium_probe(struct phytium_i2c_dev *dev) ret = i2c_add_numbered_adapter(adapter); if (ret) dev_err(dev->dev, "fail to add adapter: %d\n", ret); + + if (of_property_read_bool(dev->dev->of_node, "smbus-alert")) + alert = 1; + else if (has_acpi_companion(dev->dev)) + fwnode_property_read_u32_array(dev->dev->fwnode, "smbus_alert", &alert, 1); + + if (alert) { + ret = i2c_phytium_enable_smbus_alert(dev); + if (ret) { + dev_err(dev->dev, + "failed to enable SMBus alert protocol (%d)\n", ret); + } + } + pm_runtime_put_noidle(dev->dev); return ret; } EXPORT_SYMBOL_GPL(i2c_phytium_probe); -MODULE_DESCRIPTION("Phytium I2C bus master adapter"); +MODULE_AUTHOR("Wu Jinyong "); +MODULE_DESCRIPTION("Phytium I2C bus controller adapter"); MODULE_LICENSE("GPL"); +MODULE_VERSION(I2C_PHYTIUM_DRV_VERSION); diff --git a/drivers/i2c/busses/i2c-phytium-pci.c b/drivers/i2c/busses/i2c-phytium-pci.c index e02667ea4a..11db371264 100644 --- a/drivers/i2c/busses/i2c-phytium-pci.c +++ b/drivers/i2c/busses/i2c-phytium-pci.c @@ -177,6 +177,10 @@ static int i2c_phytium_pci_probe(struct pci_dev *pdev, dev->irq = pdev->irq; dev->flags |= controller->flags; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_lock_init(&dev->i2c_lock); dev->functionality = controller->functionality | IC_DEFAULT_FUNCTIONALITY; dev->master_cfg = controller->bus_cfg; if (controller->scl_sda_cfg) { @@ -199,6 +203,8 @@ static int i2c_phytium_pci_probe(struct pci_dev *pdev, ACPI_COMPANION_SET(&adapter->dev, ACPI_COMPANION(&pdev->dev)); adapter->nr = controller->bus_num; + dev->capability = 0; + dev->first_time_init_master = true; ret = i2c_phytium_probe(dev); if (ret) goto out; @@ -230,8 +236,8 @@ static void i2c_phytium_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id i2_phytium_pci_ids[] = { - { PCI_DEVICE(0x1db7, 0xdc32), 0, 0, octopus_i2c }, - { PCI_DEVICE(0x1db7, 0xdc30), 0, 0, octopus_i2c }, + { PCI_VDEVICE(PHYTIUM, 0xdc32), octopus_i2c }, + { PCI_VDEVICE(PHYTIUM, 0xdc30), octopus_i2c }, { } }; MODULE_DEVICE_TABLE(pci, i2_phytium_pci_ids); diff --git a/drivers/i2c/busses/i2c-phytium-platform.c b/drivers/i2c/busses/i2c-phytium-platform.c index d9160c428c..f33aeb32cb 100644 --- a/drivers/i2c/busses/i2c-phytium-platform.c +++ b/drivers/i2c/busses/i2c-phytium-platform.c @@ -64,9 +64,7 @@ static int phytium_i2c_acpi_configure(struct platform_device *pdev) struct phytium_i2c_dev *dev = platform_get_drvdata(pdev); struct i2c_timings *t = &dev->timings; u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0; - acpi_handle handle = ACPI_HANDLE(&pdev->dev); const struct acpi_device_id *id; - struct acpi_device *adev; dev->adapter.nr = -1; dev->tx_fifo_depth = 32; @@ -101,9 +99,6 @@ static int phytium_i2c_acpi_configure(struct platform_device *pdev) if (id && id->driver_data) dev->flags |= (u32)id->driver_data; - if (acpi_bus_get_device(handle, &adev)) - return -ENODEV; - return 0; } @@ -119,29 +114,6 @@ static inline int phytium_i2c_acpi_configure(struct platform_device *pdev) } #endif -static void i2c_phytium_configure_master(struct phytium_i2c_dev *dev) -{ - struct i2c_timings *t = &dev->timings; - - dev->functionality = I2C_FUNC_10BIT_ADDR | IC_DEFAULT_FUNCTIONALITY; - - dev->master_cfg = IC_CON_MASTER | IC_CON_SLAVE_DISABLE | - IC_CON_RESTART_EN; - - dev->mode = PHYTIUM_IC_MASTER; - - switch (t->bus_freq_hz) { - case 100000: - dev->master_cfg |= IC_CON_SPEED_STD; - break; - case 3400000: - dev->master_cfg |= IC_CON_SPEED_HIGH; - break; - default: - dev->master_cfg |= IC_CON_SPEED_FAST; - } -} - static void i2c_phytium_configure_slave(struct phytium_i2c_dev *dev) { dev->functionality = I2C_FUNC_SLAVE | IC_DEFAULT_FUNCTIONALITY; @@ -180,6 +152,11 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->irq = irq; + dev->first_time_init_master = false; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_lock_init(&dev->i2c_lock); platform_set_drvdata(pdev, dev); dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); @@ -231,11 +208,12 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) goto exit_reset; } - if (i2c_detect_slave_mode(&pdev->dev)) + if (i2c_detect_slave_mode(&pdev->dev)) { i2c_phytium_configure_slave(dev); - else + } else { + dev->first_time_init_master = true; i2c_phytium_configure_master(dev); - + } dev->clk = devm_clk_get(&pdev->dev, NULL); if (!i2c_phytium_prepare_clk(dev, true)) { u64 clk_khz; @@ -257,6 +235,9 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) adap->class = I2C_CLASS_DEPRECATED; ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); adap->dev.of_node = pdev->dev.of_node; + adap->dev.fwnode = pdev->dev.fwnode; + + dev->capability = 0; dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE | @@ -272,10 +253,7 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - if (dev->mode == PHYTIUM_IC_SLAVE) - ret = i2c_phytium_probe_slave(dev); - else - ret = i2c_phytium_probe(dev); + ret = i2c_phytium_probe(dev); if (ret) goto exit_probe; -- Gitee From 9818367822eade0c211cab487d2c0629a153bb1b Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Tue, 18 Feb 2025 10:13:35 +0800 Subject: [PATCH 070/154] i2c-2.0: phytium: Fixed the bug of combining two interrupts. When an I2C interrupt is triggered,the CPU is processing other interrupts at this time,or there are more interrupts queued in front of the I2C interrupt,resulting in this current I2C interrupt not being able to respond in time.Then another I2C interrupt comes,so that by the time the I2C interrupt is responded to,the interrupt handler can only be triggered once,and only one transaction is handled,thus leading to incomplete transaction processing. By using interrupt plus polling,when an interrupt is triggered, all pending transactions are polled. Mainline: NA Signed-off-by: Wu Jinyong Signed-off-by: Wang Yinfeng Change-Id: I695e63ca221550a011006a58b0ce47b00ee823ef --- .../busses/phytium_i2c_v2/i2c-phyt-common.c | 2 +- .../i2c/busses/phytium_i2c_v2/i2c-phyt-core.h | 2 +- .../busses/phytium_i2c_v2/i2c-phyt-master.c | 38 ++++++++++++------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c index 023b3c0e78..f9fe6d876b 100644 --- a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c @@ -159,7 +159,7 @@ void i2c_phyt_show_log(struct i2c_phyt_dev *dev) dev_info(dev->dev, "log len :%d,addr: 0x%llx,size:%d\n", len, (u64)dev->log_addr, dev->log_size); if (len > FT_LOG_LINE_MAX_LEN) { - for (i = 0; i < len; i += FT_LOG_LINE_MAX_LEN) + for (i = 0; i + FT_LOG_LINE_MAX_LEN < len; i += FT_LOG_LINE_MAX_LEN) dev_info(dev->dev, "(log)%.*s\n", FT_LOG_LINE_MAX_LEN, &plog[i]); } else { dev_info(dev->dev, "(log)%.*s\n", FT_LOG_LINE_MAX_LEN, &plog[0]); diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h index dbe540b9ba..1ed19d20d2 100644 --- a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h @@ -14,7 +14,7 @@ #include #include -#define I2C_PHYTIUM_V2_DRV_VERSION "1.0.1" +#define I2C_PHYTIUM_V2_DRV_VERSION "1.0.2" #define FT_I2C_MSG_UNIT_SIZE 10 #define FT_I2C_DATA_RESV_LEN 2 diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c index 559a59aeca..24c8c7bcac 100644 --- a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c @@ -444,7 +444,7 @@ static irqreturn_t i2c_phyt_master_regfile_isr(int this_irq, void *dev_id) { struct i2c_phyt_dev *dev = (struct i2c_phyt_dev *)dev_id; struct phyt_msg_info *rx_msg = (struct phyt_msg_info *)dev->rx_shmem_addr; - u32 stat; + u32 stat, head, tail; int ret; stat = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_RV2AP_INTR_STAT); @@ -456,21 +456,31 @@ static irqreturn_t i2c_phyt_master_regfile_isr(int this_irq, void *dev_id) i2c_phyt_common_regfile_clear_rv2ap_int(dev, stat); - if (dev->complete_flag) { - if (rx_msg->head.cmd_type == PHYTI2C_MSG_CMD_REPORT) - goto done; + if (!dev->mng.tx_ring_cnt) { + dev_err(dev->dev, "tx_ring_cnt is zero\n"); + return IRQ_NONE; + } + head = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_TX_HEAD) % dev->mng.tx_ring_cnt; + tail = dev->mng.cur_cmd_cnt % dev->mng.tx_ring_cnt; + do { + tail++; + tail %= dev->mng.tx_ring_cnt; + if (dev->complete_flag) { + if (rx_msg->head.cmd_type == PHYTI2C_MSG_CMD_REPORT) + goto done; - ret = i2c_phyt_master_handle(dev); - if (ret == FT_I2C_RUNNING) - return IRQ_HANDLED; + ret = i2c_phyt_master_handle(dev); + if (ret == FT_I2C_RUNNING) + continue; - dev->complete_flag = false; - dev->mng.cur_cmd_cnt = 0; - i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_HEAD, 0); - i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_TAIL, 0); - complete(&dev->cmd_complete); - return IRQ_HANDLED; - } + dev->complete_flag = false; + dev->mng.cur_cmd_cnt = 0; + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_HEAD, 0); + i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_TAIL, 0); + complete(&dev->cmd_complete); + return IRQ_HANDLED; + } + } while (tail != head); done: i2c_phyt_master_isr_handle(dev); -- Gitee From 75657a12761ebb183b4c07a211ea411a12549dc3 Mon Sep 17 00:00:00 2001 From: Feng Jun Date: Fri, 28 Feb 2025 17:34:30 +0800 Subject: [PATCH 071/154] dt-bindings: uart: Add bindings for Phytium uart-v2 controllor This patch documents the DT bindings for the Phytium uart-v2 controller. Mainline: Open-Source Signed-off-by: Feng Jun Signed-off-by: Wang Yinfeng Change-Id: I8b6a149b9f548ec3bca88a5dba1ffccb1168ed32 --- .../bindings/serial/phytium,uart-2.0.yaml | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/phytium,uart-2.0.yaml diff --git a/Documentation/devicetree/bindings/serial/phytium,uart-2.0.yaml b/Documentation/devicetree/bindings/serial/phytium,uart-2.0.yaml new file mode 100644 index 0000000000..df2c8bca6a --- /dev/null +++ b/Documentation/devicetree/bindings/serial/phytium,uart-2.0.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/phytium,uart-v2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium serial UART v2 + +maintainers: + - Hengyu Lan + +allOf: + - $ref: serial.yaml# + +# Need a custom select here or 'arm,primecell' will match on lots of nodes +select: + properties: + compatible: + contains: + enum: + - phytium,uart-2.0 + required: + - compatible + +properties: + compatible: + items: + - const: phytium,uart-2.0 + + reg: + maxItems: 2 + + interrupts: + maxItems: 1 + + clocks: + description: + When present, the first clock listed must correspond to + the clock named UARTCLK on the IP block, i.e. the clock + to the external serial line, whereas the second clock + must correspond to the PCLK clocking the internal logic + of the block. Just listing one clock (the first one) is + deprecated. + maxItems: 2 + + clock-names: + items: + - const: uartclk + - const: apb_pclk + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + uart@27011000 { + compatible = "phytium,uart-2.0"; + reg = <0x0 0x27011000 0x0 0x1000>, + <0x0 0x26fe4000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_100mhz &sysclk_100mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; +... -- Gitee From d01f1cdb53d4aa48e56e03a9a767e996a259026b Mon Sep 17 00:00:00 2001 From: Feng Jun Date: Fri, 28 Feb 2025 18:11:14 +0800 Subject: [PATCH 072/154] uart: Phytium: Add support for phytium uart-v2 driver This patch adds driver support for phytium uart-v2 controller. Mainline: Open-Source Signed-off-by: Feng Jun Signed-off-by: Wang Yinfeng Change-Id: Ie940b5a4349a7ce34cc64544be4c5335a78e2d06 --- drivers/tty/serial/Kconfig | 20 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/phytium-uart-v2.c | 1384 ++++++++++++++++++++++++++ drivers/tty/serial/phytium-uart-v2.h | 159 +++ 4 files changed, 1564 insertions(+) create mode 100644 drivers/tty/serial/phytium-uart-v2.c create mode 100644 drivers/tty/serial/phytium-uart-v2.h diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 4d6ac2bc77..c4376d13d2 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -84,6 +84,26 @@ config SERIAL_PHYTIUM_PCI If unsure, say N. +config SERIAL_PHYTIUM_UART_V2 + tristate "Phytium V2 serial port support" + depends on ARCH_PHYTIUM + select SERIAL_CORE + help + This driver supports the Phytium UART V2 controller on platform adapters. + If you want to compile this driver into the kernel, say Y here. To + compile this driver as a module, choose M here. + + If unsure, say N. + +config SERIAL_PHYTIUM_V2_DEBUG + bool "Phytium UART V2 debug/heartbeat function support" + depends on SERIAL_PHYTIUM_UART_V2 + help + This driver supports the Phytium UART V2 controller Debug on platform adapters. + If you want to enable debug, say Y here. + + If unsure, say N. + config SERIAL_EARLYCON_ARM_SEMIHOST bool "Early console using ARM semihosting" depends on ARM64 || ARM diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index f215657dc3..ddcf7c46e4 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o obj-$(CONFIG_SERIAL_PHYTIUM_PCI) += phytium-uart.o +obj-$(CONFIG_SERIAL_PHYTIUM_UART_V2) += phytium-uart-v2.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/phytium-uart-v2.c b/drivers/tty/serial/phytium-uart-v2.c new file mode 100644 index 0000000000..4905e25aff --- /dev/null +++ b/drivers/tty/serial/phytium-uart-v2.c @@ -0,0 +1,1384 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Driver for phytium uart v2 + * Copyright (c) 2020-2025, 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 "phytium-uart-v2.h" +#define cmd_id_type uint8_t +#define cmd_subid_type uint8_t +#define DEFAULT_CLK 10000000 +#define PHYT_UART_DRV_VER "1.1.0" +/* + * We wrap our port structure around the generic uart_port. + */ +struct phytium_uart_port { + struct uart_port port; + unsigned int old_cr; /* state during shutdown */ + unsigned int old_status; + char type[12]; + struct device *dev; + struct clk *clk; + void __iomem *shmem_base; + bool m_buf_empty; + bool heartbeat_enable_flag; + bool debug_enable_flag; + struct timer_list alive_timer; +}; + +/* define msg format */ +struct msg { + u16 module_id; + u8 cmd_id; + u8 cmd_subid; + u16 length; + u16 complete; + u8 data[120]; +}; + +static unsigned int phytium_uart_read(const struct phytium_uart_port *pup, + unsigned int reg) +{ + void __iomem *addr = pup->port.membase + reg; + + return readl_relaxed(addr); +} + +static void phytium_uart_write(unsigned int val, + const struct phytium_uart_port *pup, unsigned int reg) +{ + void __iomem *addr = pup->port.membase + reg; + + writel_relaxed(val, addr); +} + +/* func for filling with msg */ +static void msg_fill(struct msg *msg, u16 module_id, + u8 cmd_id, u8 cmd_subid, u16 complete) +{ + msg->module_id = module_id; + msg->cmd_id = cmd_id; + msg->cmd_subid = cmd_subid; + msg->complete = complete; + + memset(msg->data, 0, sizeof(msg->data)); +} + +static int tx_ring_buffer_is_full(struct phytium_uart_port *pup) +{ + u16 tx_tail, tx_head; + + tx_tail = phytium_uart_read(pup, REG_TX_TAIL) & BUFFER_POINTER_MASK; + tx_head = phytium_uart_read(pup, REG_TX_HEAD) & BUFFER_POINTER_MASK; + + if (((tx_tail + 1) % TX_BUFFER_SIZE) == tx_head) + return 1; + + return 0; +} + +static int tx_ring_buffer_is_empty(struct phytium_uart_port *pup) +{ + u16 tx_tail, tx_head; + + tx_tail = phytium_uart_read(pup, REG_TX_TAIL) & BUFFER_POINTER_MASK; + tx_head = phytium_uart_read(pup, REG_TX_HEAD) & BUFFER_POINTER_MASK; + + if (tx_tail == tx_head) + return 1; + + return 0; +} + +static void PHYT_MSG_INSERT(struct phytium_uart_port *pup, struct msg *msg) +{ + u16 tx_tail, tx_head; + + tx_tail = phytium_uart_read(pup, REG_TX_TAIL) & BUFFER_POINTER_MASK; + tx_head = phytium_uart_read(pup, REG_TX_HEAD) & BUFFER_POINTER_MASK; + + pr_debug("TX_TAIL:%x, TX_HEAD:%x", tx_tail, tx_head); + + while (tx_ring_buffer_is_full(pup)) + cpu_relax(); + + memcpy(pup->shmem_base + TX_MSG_SIZE * tx_tail, + msg, sizeof(struct msg)); + + /* updata tx tail pointer */ + tx_tail = (tx_tail + 1) % TX_BUFFER_SIZE; + phytium_uart_write(tx_tail, pup, REG_TX_TAIL); + + phytium_uart_write(PHYT_INT_TRIGGER_BIT, pup, REG_PHYT_INT_STATE); +} + + +static int rx_ring_buffer_is_empty(struct phytium_uart_port *pup) +{ + u16 rx_tail, rx_head; + + rx_tail = phytium_uart_read(pup, REG_RX_TAIL) & BUFFER_POINTER_MASK; + rx_head = phytium_uart_read(pup, REG_RX_HEAD) & BUFFER_POINTER_MASK; + if (rx_tail == rx_head) + return 1; + return 0; +} + + +static void phytium_fifo_to_tty(struct phytium_uart_port *pup) +{ + struct msg *handler_msg; + int sysrq; + unsigned int ch, flag, fifotaken; + u16 count = 0; + u16 rx_head, rx_tail; + + while (!rx_ring_buffer_is_empty(pup)) { + rx_head = (phytium_uart_read(pup, REG_RX_HEAD) + & BUFFER_POINTER_MASK); + rx_tail = (phytium_uart_read(pup, REG_RX_TAIL) + & BUFFER_POINTER_MASK); + + /* get operator pointer of rv data msg */ + handler_msg = (struct msg *)(pup->shmem_base + TX_MSG_SIZE + * TX_BUFFER_SIZE + RX_MSG_SIZE * rx_head); + pr_debug("handler_msg = %p, rx_head=%d, rx_tail=%d\n", + handler_msg, rx_head, rx_tail); + if (!handler_msg) { + pr_err("%s cannot get msg!\n", __func__); + return; + } + count = handler_msg->length / 2; + + rx_head = (rx_head + 1) % RX_BUFFER_SIZE; + phytium_uart_write(rx_head, pup, REG_RX_HEAD); + + for (fifotaken = 0; fifotaken != RX_DATA_MAXINUM; fifotaken++) { + if (count == 0) + break; + count--; + /* Take chars maxinum 60 * 2 bytes from the MSG */ + ch = (handler_msg->data[2 * fifotaken]); + ch |= (handler_msg->data[2 * fifotaken + 1] << 8); + ch |= DATA_DUMMY_RX; + flag = TTY_NORMAL; + pup->port.icount.rx++; + + if (unlikely(ch & DATA_ERROR)) { + if (ch & DATA_BE) { + ch &= ~(DATA_FE | DATA_PE); + pup->port.icount.brk++; + if (uart_handle_break(&pup->port)) + continue; + } else if (ch & DATA_PE) + pup->port.icount.parity++; + else if (ch & DATA_FE) + pup->port.icount.frame++; + if (ch & DATA_OE) + pup->port.icount.overrun++; + + ch &= pup->port.read_status_mask; + + if (ch & DATA_BE) + flag = TTY_BREAK; + else if (ch & DATA_PE) + flag = TTY_PARITY; + else if (ch & DATA_FE) + flag = TTY_FRAME; + } + + spin_unlock(&pup->port.lock); + sysrq = uart_handle_sysrq_char(&pup->port, + ch & CHAR_MASK); + spin_lock(&pup->port.lock); + + if (!sysrq) + uart_insert_char(&pup->port, ch, + DATA_OE, ch, flag); + } + } +} + +static void phytium_rx_chars(struct phytium_uart_port *pup) +__releases(&pup->port.lock) +__acquires(&pup->port.lock) +{ + phytium_fifo_to_tty(pup); + + spin_unlock(&pup->port.lock); + + tty_flip_buffer_push(&pup->port.state->port); + + spin_lock(&pup->port.lock); +} + +static void phytium_stop_tx(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + unsigned int int_mask; + + if (!tx_ring_buffer_is_empty(pup)) + return; + /* mask tx msg tail pointer int*/ + int_mask = phytium_uart_read(pup, REG_RP_INT_MASK); + phytium_uart_write(int_mask | PHYT_MSG_DATA_COMPLETED, + pup, REG_RP_INT_MASK); + +} + +static bool phytium_tx_xchar(struct phytium_uart_port *pup, unsigned char c, + bool from_irq) +{ + struct msg msg; + struct msg *handler_msg; + u16 count = 1; + + msg_fill(&msg, UART_MODULE_ID, MSG_DATA, MSG_TX_DATA, 0); + + handler_msg = (struct msg *)(pup->shmem_base); + + if (unlikely(!from_irq) && !(handler_msg->complete & MSG_COMPLETE)) + return false; + msg.data[0] = c; + msg.length = count; + + /* set 1 to wait rv clear out */ + phytium_uart_write(1, pup, REG_CHECK_TX); + + PHYT_MSG_INSERT(pup, &msg); + + pup->port.icount.tx++; + + return true; + +} + +static bool phytium_tx_chars(struct phytium_uart_port *pup, bool from_irq) +{ + struct circ_buf *xmit = &pup->port.state->xmit; + struct msg msg; + int i = 0; + int count = TX_DATA_MAXINUM; + u16 datasize = 0; + + msg_fill(&msg, UART_MODULE_ID, MSG_DATA, MSG_TX_DATA, 0); + + if (pup->port.x_char) { + if (!phytium_tx_xchar(pup, pup->port.x_char, from_irq)) + return true; + pup->port.x_char = 0; + --count; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&pup->port)) { + phytium_stop_tx(&pup->port); + return false; + } + + if (tx_ring_buffer_is_full(pup)) + return false; + do { + if (count-- == 0) + break; + datasize++; + + msg.length = datasize; + msg.data[datasize - 1] = xmit->buf[xmit->tail]; + + pup->port.icount.tx++; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } while (!uart_circ_empty(xmit)); + + PHYT_MSG_INSERT(pup, &msg); + + /* if xmit is empty, break */ + if (uart_circ_empty(xmit)) + pr_debug("xmit is empty at i=%d\n", i); + /* If tx_chars is called by start_tx or xmit + * data is full enough to fill msg, don't wakeup + * write again. + */ + if (uart_circ_chars_pending(xmit) <= TX_DATA_MAXINUM) + uart_write_wakeup(&pup->port); + if (uart_circ_empty(xmit)) { + phytium_stop_tx(&pup->port); + return false; + } + return true; +} + +static void phytium_modem_status(struct phytium_uart_port *pup) +{ + struct msg msg; + struct msg *handler_msg; + unsigned int status, delta; + u16 old_tx_tail; + + msg_fill(&msg, UART_MODULE_ID, MSG_GET, MSG_GET_MODEM, 0); + + old_tx_tail = phytium_uart_read(pup, REG_TX_TAIL) & BUFFER_POINTER_MASK; + + PHYT_MSG_INSERT(pup, &msg); + + handler_msg = + (struct msg *)(pup->shmem_base + TX_MSG_SIZE * old_tx_tail); + + while (!tx_ring_buffer_is_empty(pup)) + cpu_relax(); + + status = handler_msg->data[0]; + + delta = status ^ pup->old_status; + pup->old_status = status; + + if (!delta) + return; + + if (delta & MODEM_DCD) + uart_handle_dcd_change(&pup->port, status & MODEM_DCD); + + if (delta & MODEM_DSR) + pup->port.icount.dsr++; + + if (delta & MODEM_CTS) + uart_handle_cts_change(&pup->port, status & MODEM_CTS); + + wake_up_interruptible(&pup->port.state->port.delta_msr_wait); +} + +static irqreturn_t phytium_uart_interrupt(int irq, void *uart_port) +{ + struct phytium_uart_port *pup = uart_port; + unsigned long flags; + unsigned int status; + int handled = 0; + + spin_lock_irqsave(&pup->port.lock, flags); + status = phytium_uart_read(pup, REG_RP_INT_STATE); + if (status) { + /* clear rx_tail,tx_data_complete interrupts */ + phytium_uart_write(status & ~(TX_HEAD_INT | RX_TAIL_INT | + PHYT_MSG_DATA_COMPLETED | MODEM_INT), + pup, REG_RP_INT_STATE); + + phytium_uart_write(TX_HEAD_INT | RX_TAIL_INT | + PHYT_MSG_DATA_COMPLETED | MODEM_INT, + pup, REG_RP_INT_STATE_CLR); + + if (status & RX_TAIL_INT) + phytium_rx_chars(pup); + + if (status & MODEM_INT) + phytium_modem_status(pup); + + if (status & PHYT_MSG_DATA_COMPLETED) + phytium_tx_chars(pup, true); + + handled = 1; + } + spin_unlock_irqrestore(&pup->port.lock, flags); + + return IRQ_RETVAL(handled); +} + +static unsigned int phytium_tx_empty(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + unsigned int status; + + status = tx_ring_buffer_is_full(pup); + return status ? 0 : TIOCSER_TEMT; +} + + +static void phytium_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + struct msg msg; + unsigned int status; + cmd_subid_type cmd_subid; + enum phytuart_set_subid cmd = PHYTUART_MSG_CMD_SET_MCTRL; + + cmd_subid = getHexValue(cmd); + + msg_fill(&msg, UART_MODULE_ID, MSG_SET, cmd_subid, 0); + + msg.data[3] = (mctrl >> 24) & MSG_DATA_MASK; + msg.data[2] = (mctrl >> 16) & MSG_DATA_MASK; + msg.data[1] = (mctrl >> 8) & MSG_DATA_MASK; + msg.data[0] = (mctrl) & MSG_DATA_MASK; + + if (port->status & UPSTAT_AUTORTS) { + status = 1; + msg.data[7] = (status >> 24) & MSG_DATA_MASK; + msg.data[6] = (status >> 16) & MSG_DATA_MASK; + msg.data[5] = (status >> 8) & MSG_DATA_MASK; + msg.data[4] = (status) & MSG_DATA_MASK; + } + + PHYT_MSG_INSERT(pup, &msg); + +} + +static unsigned int phytium_get_mctrl(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + unsigned int cr = 0; + unsigned int status; + struct msg msg; + struct msg *handler_msg; + u16 old_tx_tail; + + old_tx_tail = phytium_uart_read(pup, REG_TX_TAIL) & BUFFER_POINTER_MASK; + + msg_fill(&msg, UART_MODULE_ID, MSG_GET, MSG_GET_MODEM, 0); + + PHYT_MSG_INSERT(pup, &msg); + handler_msg = + (struct msg *)(pup->shmem_base + TX_MSG_SIZE * old_tx_tail); + while (!tx_ring_buffer_is_empty(pup)) + cpu_relax(); + status = handler_msg->data[0]; + + if (status & MODEM_CTS) + cr |= TIOCM_CTS; + if (status & MODEM_DSR) + cr |= TIOCM_DSR; + if (status & MODEM_CAR) + cr |= TIOCM_CAR; + if (status & MODEM_RNG) + cr |= TIOCM_RNG; + if (status & MODEM_RTS) + cr |= TIOCM_RTS; + if (status & MODEM_DTR) + cr |= TIOCM_DTR; + if (status & MODEM_OUT1) + cr |= TIOCM_OUT1; + if (status & MODEM_OUT2) + cr |= TIOCM_OUT2; + + return cr; +} + +static void phytium_start_tx(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + unsigned int int_mask = 0; + + /* unmask tx_ring_buffer interrupt */ + if (phytium_tx_chars(pup, false)) { + int_mask = phytium_uart_read(pup, REG_RP_INT_MASK); + phytium_uart_write(int_mask & ~PHYT_MSG_DATA_COMPLETED, + pup, REG_RP_INT_MASK); + } +} + +static void phytium_stop_rx(struct uart_port *port) +{ + + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + struct msg msg; + cmd_subid_type cmd_subid; + unsigned int int_mask; + enum phytuart_set_subid cmd = PHYTUART_MSG_CMD_SET_ERROR_IM; + + cmd_subid = getHexValue(cmd); + + /* mask rx tail int */ + int_mask = phytium_uart_read(pup, REG_RP_INT_MASK); + phytium_uart_write(int_mask | RX_TAIL_INT, pup, REG_RP_INT_MASK); + + msg_fill(&msg, UART_MODULE_ID, MSG_SET, cmd_subid, 0); + + PHYT_MSG_INSERT(pup, &msg); + +} + +static void phytium_throttle_rx(struct uart_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + phytium_stop_rx(port); + + spin_unlock_irqrestore(&port->lock, flags); +} + + +static void phytium_enable_ms(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + struct msg msg; + cmd_subid_type cmd_subid; + + enum phytuart_set_subid cmd = PHYTUART_MSG_CMD_SET_MODEM_IM; + + cmd_subid = getHexValue(cmd); + + msg_fill(&msg, UART_MODULE_ID, MSG_SET, cmd_subid, 0); + + msg.data[0] = 0xf; + + PHYT_MSG_INSERT(pup, &msg); + +} + +static void phytium_break_ctl(struct uart_port *port, int break_state) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + struct msg msg; + unsigned long flags; + unsigned int ctrl = 1; + cmd_subid_type cmd_subid; + enum phytuart_set_subid cmd = PHYTUART_MSG_CMD_SET_BREAK_EN; + + cmd_subid = getHexValue(cmd); + msg_fill(&msg, UART_MODULE_ID, MSG_SET, cmd_subid, 0); + spin_lock_irqsave(&pup->port.lock, flags); + if (break_state == -1) { + + msg.data[3] = (ctrl >> 24) & MSG_DATA_MASK; + msg.data[2] = (ctrl >> 16) & MSG_DATA_MASK; + msg.data[1] = (ctrl >> 8) & MSG_DATA_MASK; + msg.data[0] = (ctrl) & MSG_DATA_MASK; + } + + PHYT_MSG_INSERT(pup, &msg); + + spin_unlock_irqrestore(&pup->port.lock, flags); + +} + +static int phytium_hwinit(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + struct msg msg; + int retval; + u64 uart_clk; + unsigned int int_mask; + unsigned int status; + cmd_subid_type cmd_subid; + enum phytuart_set_subid cmd = PHYTUART_MSG_CMD_SET_HWINIT; + + cmd_subid = getHexValue(cmd); + + msg_fill(&msg, UART_MODULE_ID, MSG_SET, cmd_subid, 0); + + pinctrl_pm_select_default_state(port->dev); + + retval = clk_prepare_enable(pup->clk); + if (retval) + return retval; + if (has_acpi_companion(pup->port.dev)) { + device_property_read_u64(pup->port.dev, "clock-frequency", + &uart_clk); + if (uart_clk) { + if (uart_clk > 100000000 || ('n' == uart_clk)) { + dev_err(pup->port.dev, "uartclk get from acpi is error or NULL!\n"); + pup->port.uartclk = DEFAULT_CLK; + } else + pup->port.uartclk = (uint32_t)uart_clk; + } else { + dev_err(pup->port.dev, "have no acpi clk, use default clk!\n"); + pup->port.uartclk = DEFAULT_CLK; + } + } else + pup->port.uartclk = clk_get_rate(pup->clk); + + PHYT_MSG_INSERT(pup, &msg); + + status = phytium_uart_read(pup, REG_RP_INT_STATE); + phytium_uart_write(status & ~RX_TAIL_INT, pup, REG_RP_INT_STATE); + phytium_uart_write(RX_TAIL_INT, pup, REG_RP_INT_STATE_CLR); + /* unmask rx tail int */ + int_mask = phytium_uart_read(pup, REG_RP_INT_MASK); + phytium_uart_write(int_mask & ~RX_TAIL_INT, pup, REG_RP_INT_MASK); + + return 0; +} + +static int phytium_uart_allocate_irq(struct phytium_uart_port *pup) +{ + if (!pup->port.irq) + return -EINVAL; + + return request_irq(pup->port.irq, phytium_uart_interrupt, + IRQF_SHARED, DRV_NAME, pup); +} + +static void phytium_enable_interrupts(struct phytium_uart_port *pup) +{ + unsigned long flags; + unsigned int i; + u16 rx_head, rx_tail; + unsigned int status; + unsigned int int_mask; + + unsigned int buffer_size = RX_BUFFER_SIZE; + + spin_lock_irqsave(&pup->port.lock, flags); + + /* clear out rx-int-status */ + status = phytium_uart_read(pup, REG_RP_INT_STATE); + phytium_uart_write(status & ~RX_TAIL_INT, pup, REG_RP_INT_STATE); + /* when enable int, first empty rx ring buffer */ + for (i = 0; i < buffer_size * 2; i++) { + if (rx_ring_buffer_is_empty(pup)) + break; + rx_head = phytium_uart_read(pup, REG_RX_HEAD) + & BUFFER_POINTER_MASK; + rx_head = (rx_head + 1) % buffer_size; + phytium_uart_write(rx_head, pup, REG_RX_HEAD); + } + + /* enable rx_tail int */ + rx_tail = phytium_uart_read(pup, REG_RX_TAIL); + phytium_uart_write(rx_tail | RX_TAIL_INT_ENABLE, pup, REG_RX_TAIL); + + /* unmask rx tail int */ + int_mask = phytium_uart_read(pup, REG_RP_INT_MASK); + phytium_uart_write(int_mask & ~RX_TAIL_INT, pup, REG_RP_INT_MASK); + spin_unlock_irqrestore(&pup->port.lock, flags); +} + +static void phytium_unthrottle_rx(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + phytium_enable_interrupts(pup); +} + + +static int phytium_startup(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + struct msg msg; + struct msg *handler_msg; + cmd_subid_type cmd_subid; + int ret = 0; + u16 old_tx_tail; + + enum phytuart_set_subid cmd = PHYTUART_MSG_CMD_SET_STARTUP; + + cmd_subid = getHexValue(cmd); + + ret = phytium_hwinit(port); + if (ret) + goto out; + + ret = phytium_uart_allocate_irq(pup); + if (ret) + goto out; + + spin_lock_irq(&pup->port.lock); + + msg_fill(&msg, UART_MODULE_ID, MSG_SET, cmd_subid, 0); + + PHYT_MSG_INSERT(pup, &msg); + + spin_unlock_irq(&pup->port.lock); + + msg_fill(&msg, UART_MODULE_ID, MSG_GET, MSG_GET_MODEM, 0); + + old_tx_tail = phytium_uart_read(pup, REG_TX_TAIL) & BUFFER_POINTER_MASK; + + PHYT_MSG_INSERT(pup, &msg); + + handler_msg = (struct msg *) + (pup->shmem_base + TX_MSG_SIZE * old_tx_tail); + + while (!tx_ring_buffer_is_empty(pup)) + cpu_relax(); + + pup->old_status = handler_msg->data[0]; + + phytium_enable_interrupts(pup); + + return 0; +out: + return ret; +} + +static void phytium_disable_uart(struct phytium_uart_port *pup) +{ + + struct msg msg; + cmd_subid_type cmd_subid; + + enum phytuart_set_subid cmd = PHYTUART_MSG_CMD_SET_DISABLE_UART; + + cmd_subid = getHexValue(cmd); + + msg_fill(&msg, UART_MODULE_ID, MSG_SET, cmd_subid, 0); + + /* set none flow control status */ + pup->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); + spin_lock_irq(&pup->port.lock); + + PHYT_MSG_INSERT(pup, &msg); + + spin_unlock_irq(&pup->port.lock); + +} + +static void phytium_disable_interrupts(struct phytium_uart_port *pup) +{ + struct msg msg; + + /* reset uart */ + msg_fill(&msg, UART_MODULE_ID, MSG_DEFAULT, MSG_DEFAULT_SUBID, 0); + + spin_lock_irq(&pup->port.lock); + + PHYT_MSG_INSERT(pup, &msg); + + /* clear all RP INT STATUS */ + phytium_uart_write(0, pup, REG_RP_INT_STATE); + phytium_uart_write(0xffff, pup, REG_RP_INT_STATE_CLR); + spin_unlock_irq(&pup->port.lock); +} + +static void phytium_shutdown(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + phytium_disable_interrupts(pup); + + free_irq(pup->port.irq, pup); + + phytium_disable_uart(pup); + + /* Shut down the clock producer */ + clk_disable_unprepare(pup->clk); + + /* Optionally let pins go into sleep states */ + pinctrl_pm_select_sleep_state(port->dev); + +} + +static void +phytium_setup_status_masks(struct uart_port *port, struct ktermios *termios) +{ + port->read_status_mask = DATA_OE | CHAR_MASK; + + if (termios->c_iflag & INPCK) + port->read_status_mask |= DATA_FE | DATA_PE; + + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= DATA_BE; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= DATA_FE | DATA_PE; + + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= DATA_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= DATA_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= DATA_DUMMY_RX; +} + +static void +phytium_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + struct msg msg; + u64 uart_clk; + unsigned long flags; + unsigned int baud, quot; + u8 databits = 0; + u8 stopbits = 0; + u8 parity_en = 0; + u8 parodd = 0; + u8 cmspar = 0; + u8 crtscts = 0; + cmd_subid_type cmd_subid1, cmd_subid2; + + enum phytuart_set_subid cmd1 = PHYTUART_MSG_CMD_SET_BAUD; + enum phytuart_set_subid cmd2 = PHYTUART_MSG_CMD_SET_TERMIOS; + + cmd_subid1 = getHexValue(cmd1); + cmd_subid2 = getHexValue(cmd2); + + msg_fill(&msg, UART_MODULE_ID, MSG_SET, cmd_subid1, 0); + + spin_lock_irqsave(&port->lock, flags); + /* Ask the core to calculate the divisor for us. */ + if (has_acpi_companion(pup->port.dev)) { + device_property_read_u64(pup->port.dev, "clock-frequency", + &uart_clk); + if (uart_clk) { + if (uart_clk > 100000000 || ('n' == uart_clk)) { + dev_err(pup->port.dev, "uartclk get from acpi is error or NULL!\n"); + port->uartclk = DEFAULT_CLK; + } else + port->uartclk = (uint32_t)uart_clk; + } else { + dev_err(pup->port.dev, "have no acpi clk, use default clk!\n"); + port->uartclk = DEFAULT_CLK; + } + } + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk/16); + dev_info(pup->port.dev, "get baud to set:%d\n", baud); + if (baud > port->uartclk/16) + quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); + else + quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); + + /* set baud */ + msg.data[7] = (port->uartclk >> 24) & MSG_DATA_MASK; + msg.data[6] = (port->uartclk >> 16) & MSG_DATA_MASK; + msg.data[5] = (port->uartclk >> 8) & MSG_DATA_MASK; + msg.data[4] = (port->uartclk) & MSG_DATA_MASK; + msg.data[3] = (baud >> 24) & MSG_DATA_MASK; + msg.data[2] = (baud >> 16) & MSG_DATA_MASK; + msg.data[1] = (baud >> 8) & MSG_DATA_MASK; + msg.data[0] = (baud) & MSG_DATA_MASK; + + PHYT_MSG_INSERT(pup, &msg); + + switch (termios->c_cflag & CSIZE) { + case CS5: + databits = 0; + break; + case CS6: + databits = 1; + break; + case CS7: + databits = 2; + break; + default: /* CS8 */ + databits = 3; + break; + } + + if (termios->c_cflag & CSTOPB) + stopbits = 1; + if (termios->c_cflag & PARENB) { + parity_en = 1; + if (!(termios->c_cflag & PARODD)) + parodd = 0; + if (termios->c_cflag & CMSPAR) + cmspar = 1; + + + } + + /* + * Update the per-port timeout. + */ + + uart_update_timeout(port, termios->c_cflag, baud); + + phytium_setup_status_masks(port, termios); + + if (UART_ENABLE_MS(port, termios->c_cflag)) + phytium_enable_ms(port); + + if (termios->c_cflag & CRTSCTS) { + crtscts = 1; + port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; + } else { + crtscts = 0; + port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); + } + + + msg_fill(&msg, UART_MODULE_ID, MSG_SET, cmd_subid2, 0); + + msg.data[0] = databits & MSG_DATA_MASK; + msg.data[1] = stopbits & MSG_DATA_MASK; + msg.data[2] = parity_en & MSG_DATA_MASK; + msg.data[3] = parodd & MSG_DATA_MASK; + msg.data[4] = cmspar & MSG_DATA_MASK; + msg.data[5] = crtscts & MSG_DATA_MASK; + + PHYT_MSG_INSERT(pup, &msg); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *phytium_type(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + return pup->port.type == PORT_PHYTIUM ? pup->type : NULL; +} + +static void phytium_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_PHYTIUM; +} + +static int phytium_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_PHYTIUM) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= nr_irqs) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + if (port->mapbase != (unsigned long) ser->iomem_base) + ret = -EINVAL; + return ret; +} + +static const struct uart_ops phytium_uart_ops = { + .tx_empty = phytium_tx_empty, + .set_mctrl = phytium_set_mctrl, + .get_mctrl = phytium_get_mctrl, + .stop_tx = phytium_stop_tx, + .start_tx = phytium_start_tx, + .stop_rx = phytium_stop_rx, + .throttle = phytium_throttle_rx, + .unthrottle = phytium_unthrottle_rx, + .enable_ms = phytium_enable_ms, + .break_ctl = phytium_break_ctl, + .startup = phytium_startup, + .shutdown = phytium_shutdown, + .set_termios = phytium_set_termios, + .type = phytium_type, + .config_port = phytium_config_port, + .verify_port = phytium_verify_port, +}; + +static struct phytium_uart_port *uart_ports[UART_NR]; + +static struct uart_driver phytium_uart = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttyVS", + .nr = UART_NR, +}; + +static void phytium_unregister_port(struct phytium_uart_port *pup) +{ + int i; + bool busy = false; + + for (i = 0; i < ARRAY_SIZE(uart_ports); i++) { + if (uart_ports[i] == pup) + uart_ports[i] = NULL; + else if (uart_ports[i]) + busy = true; + } + + if (!busy) + uart_unregister_driver(&phytium_uart); +} + +static int phytium_find_free_port(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(uart_ports); i++) + if (uart_ports[i] == NULL) + return i; + + return -EBUSY; +} + +static int phytium_register_port(struct phytium_uart_port *pup) +{ + struct msg msg; + int rc, i; + + /* mask TX_HEAD_INT at the beginning, because now we are + * unable to deal with it yet + */ + phytium_uart_write(TX_HEAD_INT, pup, REG_RP_INT_MASK); + + msg_fill(&msg, UART_MODULE_ID, MSG_DEFAULT, MSG_DEFAULT, 0); + PHYT_MSG_INSERT(pup, &msg); + + /* clear all redundant RP INT STATUS at the beginning */ + phytium_uart_write(0, pup, REG_RP_INT_STATE); + phytium_uart_write(0xffff, pup, REG_RP_INT_STATE_CLR); + if (!phytium_uart.state) { + rc = uart_register_driver(&phytium_uart); + if (rc < 0) { + dev_err(pup->port.dev, + "Failed to register Phytium platform UART driver\n"); + for (i = 0; i < ARRAY_SIZE(uart_ports); i++) + if (uart_ports[i] == pup) + uart_ports[i] = NULL; + return rc; + } + } + + rc = uart_add_one_port(&phytium_uart, &pup->port); + if (rc) + phytium_unregister_port(pup); + + return rc; +} + +#if defined(SERIAL_PHYTIUM_V2_DEBUG) +static int phytium_uart_enable_debug(struct phytium_uart_port *pup, + bool new_enable_flag) +{ + u32 dbg_regval; + static bool old_enable_flag; + + if (old_enable_flag == new_enable_flag) { + pr_warn("PHYUART:set enable debug with repeative operation.\n"); + return -1; + } + old_enable_flag = new_enable_flag; + dbg_regval = phytium_uart_read(pup, PHYUART_DBG_REG); + pr_info("PHYUART: %s debug_regval %x\n", __func__, dbg_regval); + if (!old_enable_flag && (dbg_regval & PHYUART_DBG_ENABLE_MASK)) + dbg_regval &= ~PHYUART_DBG_ENABLE_MASK; + else if (dbg_regval && !(dbg_regval & PHYUART_DBG_ENABLE_MASK)) + dbg_regval |= PHYUART_DBG_ENABLE_MASK; + + pr_info("final PHYUART: %s debug_regval %x\n", __func__, dbg_regval); + phytium_uart_write(dbg_regval, pup, PHYUART_DBG_REG); + return 0; +} + +static int phytium_uart_enable_heartbeat(struct phytium_uart_port *pup, + bool new_heartbeat_flag) +{ + u32 dbg_regval; + static bool old_heartbeat_flag; + + if (old_heartbeat_flag == new_heartbeat_flag) { + pr_warn("PHYUART:set heartbeat with repeative operation.\n"); + return -1; + } + old_heartbeat_flag = new_heartbeat_flag; + dbg_regval = phytium_uart_read(pup, PHYUART_DBG_REG); + pr_info("PHYUART: %s dbg_regval %x\n", __func__, dbg_regval); + if (!old_heartbeat_flag && (dbg_regval & PHYUART_DBG_HEARTBEAT_MASK)) + dbg_regval &= ~PHYUART_DBG_HEARTBEAT_MASK; + else if (dbg_regval && !(dbg_regval & PHYUART_DBG_HEARTBEAT_MASK)) + dbg_regval |= PHYUART_DBG_HEARTBEAT_MASK + | PHYUART_DBG_HEARTBEAT_ENABLE_MASK; + + pr_info("final PHYUART: %s dbg_regval %x\n", __func__, dbg_regval); + phytium_uart_write(dbg_regval, pup, PHYUART_DBG_REG); + return 0; +} + +static void alive_timer_routine(struct timer_list *tlist) +{ + struct phytium_uart_port *pup; + u32 dbg_regval; + + pup = from_timer(pup, tlist, alive_timer); + if (!pup) { + pr_err("PHYUART: get uart port error.\n"); + return; + } + + dbg_regval = phytium_uart_read(pup, PHYUART_DBG_REG); + pr_debug("PHYUART: %s debug_regval 0x%x\n", __func__, dbg_regval); + phytium_uart_write(dbg_regval | PHYUART_DBG_HEARTBEAT_MASK, + pup, PHYUART_DBG_REG); + mod_timer(&pup->alive_timer, jiffies + msecs_to_jiffies(5000)); +} + +static ssize_t debug_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct phytium_uart_port *pup = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", pup->debug_enable_flag); +} + +static ssize_t debug_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct phytium_uart_port *pup = dev_get_drvdata(dev); + int enable; + int ret; + + ret = sscanf(buf, "%d\n", &enable); + if (ret == 0) { + ret = -EINVAL; + return ret; + } + pup->debug_enable_flag = enable; + phytium_uart_enable_debug(pup, pup->debug_enable_flag); + return count; +} + +static ssize_t heartbeat_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct phytium_uart_port *pup = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", pup->heartbeat_enable_flag); +} + +static ssize_t heartbeat_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct phytium_uart_port *pup = dev_get_drvdata(dev); + int heartbeat_enable; + int ret; + + ret = sscanf(buf, "%d\n", &heartbeat_enable); + if (ret == 0) { + ret = -EINVAL; + return ret; + } + pup->heartbeat_enable_flag = heartbeat_enable; + phytium_uart_enable_heartbeat(pup, pup->heartbeat_enable_flag); + return count; +} +static DEVICE_ATTR_RW(debug_enable); +static DEVICE_ATTR_RW(heartbeat_enable); +#endif +static int phytium_uart_probe(struct platform_device *pdev) +{ + struct phytium_uart_port *pup; + struct resource *res_rf, *res_sm; + int portnr, ret; + u64 uart_clk; + + portnr = phytium_find_free_port(); + if (portnr < 0) + return portnr; + + pup = devm_kzalloc(&pdev->dev, sizeof(struct phytium_uart_port), + GFP_KERNEL); + if (!pup) + return -ENOMEM; + + if (pdev->dev.of_node) { + pup->clk = devm_clk_get(&pdev->dev, "uartclk"); + if (IS_ERR(pup->clk)) { + dev_err(&pdev->dev, "Device clock not found.\n"); + ret = PTR_ERR(pup->clk); + goto free; + } + } + + res_rf = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_rf) { + dev_err(&pdev->dev, "UNABLE TO GET REGFILE RESOURCE!\n"); + goto free; + } + + res_sm = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_sm) { + dev_err(&pdev->dev, "UNABLE TO GET SHMEM RESOURCE!\n"); + goto free; + } + + pup->shmem_base = devm_ioremap_resource(&pdev->dev, res_sm); + + pup->dev = &pdev->dev; + pup->port.irq = platform_get_irq(pdev, 0); + + ret = clk_prepare_enable(pup->clk); + if (ret) { + dev_err(&pdev->dev, "PHYUART:clk_prepare_enable error.\n"); + goto free; + } + + pup->port.dev = &pdev->dev; + if (has_acpi_companion(pup->port.dev)) { + device_property_read_u64(pup->port.dev, "clock-frequency", + &uart_clk); + if (uart_clk) { + if (uart_clk > 100000000 || ('n' == uart_clk)) { + dev_err(pup->port.dev, "uartclk get from acpi is error or NULL!\n"); + pup->port.uartclk = DEFAULT_CLK; + } else + pup->port.uartclk = (uint32_t)uart_clk; + } else { + dev_err(pup->port.dev, "have no acpi clk, use default clk!\n"); + pup->port.uartclk = DEFAULT_CLK; + } + } else + pup->port.uartclk = clk_get_rate(pup->clk); + pup->port.mapbase = res_rf->start; + pup->port.membase = devm_ioremap_resource(&pdev->dev, res_rf); + pup->port.iotype = UPIO_MEM32; + pup->port.ops = &phytium_uart_ops; + pup->port.dev = &pdev->dev; + pup->port.fifosize = UART_FIFOSIZE; + pup->port.flags = UPF_BOOT_AUTOCONF; + pup->port.line = portnr; + uart_ports[portnr] = pup; + pup->old_cr = 0; + pup->m_buf_empty = true; + snprintf(pup->type, sizeof(pup->type), "phytium,uart-v2"); +#if defined(SERIAL_PHYTIUM_V2_DEBUG) + pup->debug_enable_flag = false; + pup->heartbeat_enable_flag = false; + + phytium_uart_enable_heartbeat(pup, true); + phytium_uart_enable_debug(pup, true); + + pup->alive_timer.expires = jiffies + msecs_to_jiffies(5000); + timer_setup(&pup->alive_timer, alive_timer_routine, 0); + add_timer(&pup->alive_timer); + ret = device_create_file(&pdev->dev, + &dev_attr_debug_enable); + if (ret < 0) { + dev_err(&pdev->dev, "PHYUART: device_create_debug file error.\n"); + goto debug_enable_free; + } + ret = device_create_file(&pdev->dev, + &dev_attr_heartbeat_enable); + if (ret < 0) { + dev_err(&pdev->dev, "PHYUART: device_create_heartbeat file error.\n"); + goto heartbeat_enable_free; + } +#endif + platform_set_drvdata(pdev, pup); + return phytium_register_port(pup); + +#if defined(SERIAL_PHYTIUM_V2_DEBUG) +heartbeat_enable_free: + device_remove_file(pup->dev, &dev_attr_heartbeat_enable); +debug_enable_free: + device_remove_file(pup->dev, &dev_attr_debug_enable); +#endif +free: + kfree(pup); + return -1; +} + +static int phytium_uart_remove(struct platform_device *pdev) +{ + struct phytium_uart_port *pup = platform_get_drvdata(pdev); + + uart_remove_one_port(&phytium_uart, &pup->port); + + phytium_unregister_port(pup); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int phytium_uart_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct phytium_uart_port *pup = platform_get_drvdata(pdev); + + if (pup) + uart_suspend_port(&phytium_uart, &pup->port); + + return 0; +} + +static int phytium_uart_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct phytium_uart_port *pup = platform_get_drvdata(pdev); + + if (pup) + uart_resume_port(&phytium_uart, &pup->port); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(phytium_dev_pm_ops, + phytium_uart_suspend, phytium_uart_resume); + +/* Match table for OF platform binding */ +static const struct of_device_id phytium_uart_of_ids[] = { + { .compatible = "phytium,uart-2.0", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, phytium_uart_of_ids); + +static const struct acpi_device_id __maybe_unused phytium_uart_acpi_match[] = { + { "PHYT0055", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, phytium_uart_acpi_match); + +static struct platform_driver phytium_uart_driver = { + .probe = phytium_uart_probe, + .remove = phytium_uart_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = phytium_uart_of_ids, + .acpi_match_table = ACPI_PTR(phytium_uart_acpi_match), + .pm = &phytium_dev_pm_ops, + }, +}; + +static int __init phytium_uart_init(void) +{ + pr_info("Serial: Phytium uart v2 driver\n"); + return platform_driver_register(&phytium_uart_driver); +} + +static void __exit phytium_uart_exit(void) +{ + platform_driver_unregister(&phytium_uart_driver); +} +arch_initcall(phytium_uart_init); +module_exit(phytium_uart_exit); + +MODULE_AUTHOR("Lan Hengyu "); +MODULE_DESCRIPTION("Phytium serial port driver for uart v2"); +MODULE_VERSION(PHYT_UART_DRV_VER); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/phytium-uart-v2.h b/drivers/tty/serial/phytium-uart-v2.h new file mode 100644 index 0000000000..90e8487830 --- /dev/null +++ b/drivers/tty/serial/phytium-uart-v2.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2020-2025, 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 +#include + +#define DRV_NAME "ttyVS" +#define MSG_COMPLETE 0x1 +#define UART_NR 14 + +#define TX_DATA_MAXINUM 120 +#define RX_DATA_MAXINUM ((TX_DATA_MAXINUM / 2) + 1) + +#define DATA_OE (1 << 11) +#define DATA_BE (1 << 10) +#define DATA_PE (1 << 9) +#define DATA_FE (1 << 8) +#define DATA_ERROR (DATA_OE|DATA_BE|DATA_PE|DATA_FE) +#define DATA_DUMMY_RX (1 << 16) + +#define REG_TX_HEAD 0x0 /* tx_ring_buffer head pointer reg */ +#define REG_TX_TAIL 0x4 /* tx_ring_buffer tail pointer reg */ +#define REG_RX_HEAD 0x8 /* rx_ring_buffer tail pointer reg */ +#define REG_RX_TAIL 0xc /* rx_ring_buffer tail pointer reg */ +#define REG_PHYT_INT_STATE 0x24 +#define REG_RP_INT_STATE 0x2c +#define REG_RP_INT_STATE_CLR 0x74 +#define REG_PHYT_INT_MASK 0x20 +#define REG_RP_INT_MASK 0x28 +#define REG_FAKE_DR 0x30 +#define REG_FAKE_FR 0x34 +#define REG_CHECK_TX 0x4c + +#define MODEM_CTS 0x1 +#define MODEM_DSR 0x2 +#define MODEM_DCD 0x004 +#define MODEM_CAR MODEM_DCD +#define MODEM_RNG 0x8 +#define MODEM_RTS 0x10 +#define MODEM_DTR 0x20 +#define MODEM_OUT1 0x40 +#define MODEM_OUT2 0x80 + +#define TX_HEAD_INT 0x1 +#define RX_TAIL_INT 0x2 +#define PHYT_INT_TRIGGER_BIT 0x10 +#define PHYT_MSG_DATA_COMPLETED 0x10 +#define MODEM_INT 0x20 + +#define UART_MODULE_ID 0x1 +#define MSG_DEFAULT 0x0 +#define MSG_DEFAULT_SUBID 0x10 +#define MSG_SET 0x1 +#define MSG_GET 0x2 +#define MSG_DATA 0x3 +#define MSG_GET_MODEM 0x3 +#define MSG_GET_RX_EMPTY 0x4 +#define MSG_TX_DATA 0x0 +#define RX_TAIL_INT_ENABLE 0x10000 +#define MSG_DATA_MASK 0xff +#define BUFFER_POINTER_MASK 0xffff +#define CHAR_MASK 255 + +#define TX_BUFFER_SIZE 8 +#define RX_BUFFER_SIZE 8 +#define BUFFER_SIZE 20 +#define UART_FIFOSIZE 64 +#define TX_MSG_SIZE 0x80 +#define RX_MSG_SIZE 0x80 +#define RX_CHARS_MAX 28 + +/* uart debug mechanism */ +#define PHYUART_DBG_REG 0x58 +/* uart debug register mask bit */ +#define PHYUART_DBG_ENABLE_MASK 0x1 +#define PHYUART_DBG_HEARTBEAT_ENABLE_MASK (0x1 << 1) +#define PHYUART_DBG_HEARTBEAT_MASK (0x1 << 2) +#define PHYUART_DBG_LOG_EXIST_MASK (0x1 << 3) +#define PHYUART_DBG_SIZE_MASK (0xf << 4) +#define PHYUART_DBG_ADDR_MASK (0x3fff << 8) + +/* enum all type-set subid */ +enum phytuart_set_subid { + /* enable/disable */ + PHYTUART_MSG_CMD_SET_DEVICE_EN = 0x0, + /* set baud */ + PHYTUART_MSG_CMD_SET_BAUD, + /* set trans bit width 0: 5,1: 6,2: 7,3:8 */ + PHYTUART_MSG_CMD_SET_DATABIT, + /* set stop bit,0:1 bit, 1: 2bit */ + PHYTUART_MSG_CMD_SET_STOPBIT, + /* set parity enable, 0:DISABLE, 1:ENABLE */ + PHYTUART_MSG_CMD_SET_PARITY_EN, + /* set parity bit, 0:ODD, 1: EVEN */ + PHYTUART_MSG_CMD_SET_PARITY_EVEN_SET, + /* set 0/1 parity enable */ + PHYTUART_MSG_CMD_SET_PARITY_STICK_SET, + /* set break signal,0:no break 1:send break */ + PHYTUART_MSG_CMD_SET_BREAK_EN, + /* set RX en, 0:disable, single byte, 1:enable,*/ + PHYTUART_MSG_CMD_SET_RX_BUFFER_EN, + /* set TX en, 0:disable,single byte, 1:enable */ + PHYTUART_MSG_CMD_SET_TX_BUFFER_EN, + /* set TX enable, 0:disable, 1:enable */ + PHYTUART_MSG_CMD_SET_TX_EN, + /* set RX enable, 0:disable, 1:enable */ + PHYTUART_MSG_CMD_SET_RX_EN, + /* set loop enable, 0:disable, 1:enable */ + PHYTUART_MSG_CMD_SET_LOOP_EN, + PHYTUART_MSG_CMD_SET_RTS_EN, + PHYTUART_MSG_CMD_SET_CTS_EN, + PHYTUART_MSG_CMD_SET_DTR_SET, + PHYTUART_MSG_CMD_SET_RTS_SET, + PHYTUART_MSG_CMD_SET_DTE_DCD_SET, + PHYTUART_MSG_CMD_SET_DTE_RI_SET, + PHYTUART_MSG_CMD_SET_RX_IM, + PHYTUART_MSG_CMD_SET_TX_IM, + PHYTUART_MSG_CMD_SET_ERROR_IM, + + PHYTUART_MSG_CMD_SET_MODEM_IM, + PHYTUART_MSG_CMD_SET_STARTUP, + PHYTUART_MSG_CMD_SET_HWINIT, + PHYTUART_MSG_CMD_SET_MCTRL, + PHYTUART_MSG_CMD_SET_TERMIOS, + PHYTUART_MSG_CMD_SET_DISABLE_UART, +}; + +/* for trans subid to hex */ +uint8_t getHexValue(enum phytuart_set_subid cmd) +{ + return (uint8_t)cmd; +} -- Gitee From d5132a38ebf0d22d52f2e03712ad6985c45fb186 Mon Sep 17 00:00:00 2001 From: Wang Min Date: Sun, 18 Feb 2024 16:04:45 +0800 Subject: [PATCH 073/154] media: phytium-jpeg: Fix the bug of switching resolution The timer30 and timer31 trigger interrupt threads, should be an atomic operation. There shouldn't be disturbed by other threads during the interval between the two interrupts. Mainline: NA Signed-off-by: Wang Min Signed-off-by: Wang Yinfeng Change-Id: Ic91ab683a0be5c6b66b3380aeed38120d45bce82 --- drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c index f37db30301..2e81aaf16f 100644 --- a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c +++ b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c @@ -900,7 +900,6 @@ static void phytium_jpeg_irq_res_change(struct phytium_jpeg_dev *jpeg_dev, ulong delay) { dev_info(jpeg_dev->dev, "Source resolution is changed, resetting\n"); - set_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); phytium_jpeg_off(jpeg_dev); @@ -1117,6 +1116,7 @@ static irqreturn_t phytium_jpeg_timer30_irq(int irq, void *arg) /* call SE to poweroff JPEG Engine */ arm_smccc_smc(0xc300fff4, 0x9, 0x2, 0x80000020, 0, 0, 0, 0, &res); + set_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); /* set JPEG Engine's status is poweroff */ set_bit(VIDEO_POWEROFF, &jpeg_dev->status); dev_info(jpeg_dev->dev, "timer30 set jpeg status 0x%lx\n", jpeg_dev->status); -- Gitee From e3272c26c92003113d25700b209f17d011b90b58 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 21 Feb 2025 10:24:35 +0800 Subject: [PATCH 074/154] net/phytmac: Set the read timeout period to the NIC register Set the timeout period when obtaining the NIC register status to avoid endless loop. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Iac1bb82bba21679f6dbbfdccc664c37fe27c93a7 --- drivers/net/ethernet/phytium/phytmac.h | 4 +++- drivers/net/ethernet/phytium/phytmac_v1.c | 10 +++++----- drivers/net/ethernet/phytium/phytmac_v2.c | 10 +++++----- drivers/net/ethernet/phytium/phytmac_v2.h | 2 ++ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 4b09a5a608..c7c560c7da 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.36" +#define PHYTMAC_DRIVER_VERSION "1.0.37" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -55,6 +55,8 @@ #define DEFAULT_MSG_RING_SIZE 16 +#define PHYTMAC_MDIO_TIMEOUT 1000000 /* in usecs */ + #define PHYTMAC_CAPS_JUMBO 0x00000001 #define PHYTMAC_CAPS_PTP 0x00000002 #define PHYTMAC_CAPS_BD_RD_PREFETCH 0x00000004 diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 705b486520..defe187fb1 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -560,13 +560,13 @@ static int phytmac_set_wake(struct phytmac *pdata, int wake) static void phytmac_mdio_idle(struct phytmac *pdata) { u32 val; + int ret; /* wait for end of transfer */ - val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); - while (!(val & PHYTMAC_BIT(MDIO_IDLE))) { - cpu_relax(); - val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); - } + ret = readx_poll_timeout(PHYTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(MDIO_IDLE), + 1, PHYTMAC_MDIO_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mdio wait for idle time out!"); } static int phytmac_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, int is_c45) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 46b10b3006..8bbf0fcd2e 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -439,13 +439,13 @@ static void phytmac_v2_get_hw_stats(struct phytmac *pdata) static void phytmac_v2_mdio_idle(struct phytmac *pdata) { u32 val; + int ret; /* wait for end of transfer */ - val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); - while (!(val & PHYTMAC_BIT(MIDLE))) { - cpu_relax(); - val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); - } + ret = readx_poll_timeout(PHYTMAC_READ_NSR, pdata, val, val & PHYTMAC_BIT(MIDLE), + 1, PHYTMAC_MDIO_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mdio wait for idle time out!"); } static int phytmac_v2_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, int is_c45) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 486be9a618..25e120e5b9 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -241,6 +241,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_RETRY_TIMES 50000 +#define PHYTMAC_READ_NSR(pdata) PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS) + enum phytmac_msg_cmd_id { PHYTMAC_MSG_CMD_DEFAULT = 0, PHYTMAC_MSG_CMD_SET, -- Gitee From f3a6a65641d7711987d53057cc26cc1547a61d5d Mon Sep 17 00:00:00 2001 From: huxianghua Date: Tue, 26 Mar 2024 16:47:13 +0800 Subject: [PATCH 075/154] drivers/perf: phytium: Refactor for Phytium SoC DDR controller PMU. Generalize Phytium SoC DDR controller PMU supporting with phytium subdir. Mainline: Open-Source Signed-off-by: Hu Xianghua Signed-off-by: Wang Yinfeng Change-Id: I5a2b898002454f08e4874d786661b46eed3951e7 --- drivers/perf/Kconfig | 2 + drivers/perf/Makefile | 1 + drivers/perf/phytium/Kconfig | 19 + drivers/perf/phytium/Makefile | 3 + drivers/perf/phytium/phytium_ddr_pmu.c | 726 +++++++++++++++++++++++++ 5 files changed, 751 insertions(+) create mode 100644 drivers/perf/phytium/Kconfig create mode 100644 drivers/perf/phytium/Makefile create mode 100644 drivers/perf/phytium/phytium_ddr_pmu.c diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index 130327ff0b..8f0f1fa193 100644 --- a/drivers/perf/Kconfig +++ b/drivers/perf/Kconfig @@ -132,4 +132,6 @@ config ARM_SPE_PMU source "drivers/perf/hisilicon/Kconfig" +source "drivers/perf/phytium/Kconfig" + endmenu diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile index 5365fd56f8..94e36e8e68 100644 --- a/drivers/perf/Makefile +++ b/drivers/perf/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_QCOM_L3_PMU) += qcom_l3_pmu.o obj-$(CONFIG_THUNDERX2_PMU) += thunderx2_pmu.o obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o obj-$(CONFIG_ARM_SPE_PMU) += arm_spe_pmu.o +obj-$(CONFIG_PHYTIUM_PMU) += phytium/ diff --git a/drivers/perf/phytium/Kconfig b/drivers/perf/phytium/Kconfig new file mode 100644 index 0000000000..230455b930 --- /dev/null +++ b/drivers/perf/phytium/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +menuconfig PHYTIUM_PMU + bool "Phytium PMU support" + help + Say Y here if you want to support Phytium performance monitoring + unit (PMU) drivers. + +if PHYTIUM_PMU + +config PHYT_DDR_PMU + tristate "Phytium SoC DDR PMU driver" + depends on (ARCH_PHYTIUM && ACPI) || COMPILE_TEST + default m + help + Provides support for Phytium SoC DDR Controller performance + monitoring unit (PMU). + +endif + diff --git a/drivers/perf/phytium/Makefile b/drivers/perf/phytium/Makefile new file mode 100644 index 0000000000..9635d3f0d2 --- /dev/null +++ b/drivers/perf/phytium/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +# +obj-$(CONFIG_PHYT_DDR_PMU) += phytium_ddr_pmu.o diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c new file mode 100644 index 0000000000..ebe6647a72 --- /dev/null +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -0,0 +1,726 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SoC DDR performance monitoring unit support + * + * Copyright (c) 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 + +#undef pr_fmt +#define pr_fmt(fmt) "phytium_ddr_pmu: " fmt + +#define PHYTIUM_DDR_MAX_COUNTERS 8 + +#define DDR_START_TIMER 0x000 +#define DDR_STOP_TIMER 0x004 +#define DDR_CLEAR_EVENT 0x008 +#define DDR_SET_TIMER_L 0x00c +#define DDR_SET_TIMER_H 0x010 +#define DDR_TRIG_MODE 0x014 +#define DDR_NOW_STATE 0x0e0 +#define DDR_EVENT_CYCLES 0x0e4 +#define DDR_TPOINT_END_L 0x0e4 +#define DDR_TPOINT_END_H 0x0e8 +#define DDR_STATE_STOP 0x0ec +#define DDR_EVENT_RXREQ 0x100 +#define DDR_EVENT_RXDAT 0x104 +#define DDR_EVENT_TXDAT 0x108 +#define DDR_EVENT_RXREQ_RNS 0x10c +#define DDR_EVENT_RXREQ_WNSP 0x110 +#define DDR_EVENT_RXREQ_WNSF 0x114 +#define DDR_EVENT_BANDWIDTH 0x200 +#define DDR_W_DATA_BASE 0x200 +#define DDR_CLK_FRE 0xe00 +#define DDR_DATA_WIDTH 0xe04 + +#define to_phytium_ddr_pmu(p) (container_of(p, struct phytium_ddr_pmu, pmu)) + +static int phytium_ddr_pmu_hp_state; + +struct phytium_ddr_pmu_hwevents { + struct perf_event *hw_events[PHYTIUM_DDR_MAX_COUNTERS]; + DECLARE_BITMAP(used_mask, PHYTIUM_DDR_MAX_COUNTERS); +}; + +struct phytium_ddr_pmu { + struct device *dev; + void __iomem *base; + void __iomem *csr_base; + struct pmu pmu; + struct phytium_ddr_pmu_hwevents pmu_events; + u32 die_id; + u32 ddr_id; + u32 pmu_id; + int bit_idx; + int on_cpu; + int irq; + struct hlist_node node; +}; + +#define GET_DDR_EVENTID(hwc) (hwc->config_base & 0x7) +#define EVENT_VALID(idx) ((idx >= 0) && (idx < PHYTIUM_DDR_MAX_COUNTERS)) + +static const u32 ddr_counter_reg_offset[] = { + DDR_EVENT_CYCLES, DDR_EVENT_RXREQ, DDR_EVENT_RXDAT, + DDR_EVENT_TXDAT, DDR_EVENT_RXREQ_RNS, DDR_EVENT_RXREQ_WNSP, + DDR_EVENT_RXREQ_WNSF, DDR_EVENT_BANDWIDTH +}; + +ssize_t phytium_ddr_pmu_format_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + + return sprintf(buf, "%s\n", (char *)eattr->var); +} + +ssize_t phytium_ddr_pmu_event_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + + return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var); +} + +static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct phytium_ddr_pmu *ddr_pmu = + to_phytium_ddr_pmu(dev_get_drvdata(dev)); + + return cpumap_print_to_pagebuf(true, buf, cpumask_of(ddr_pmu->on_cpu)); +} + +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ + { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ + .attr.attr) + +#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ + (void *)_config) + +#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ + (unsigned long)_config) + +static struct attribute *phytium_ddr_pmu_format_attr[] = { + PHYTIUM_DDR_PMU_FORMAT_ATTR(event, "config:0-2"), + PHYTIUM_DDR_PMU_FORMAT_ATTR(timer, "config1:0-31"), + NULL, +}; + +static const struct attribute_group phytium_ddr_pmu_format_group = { + .name = "format", + .attrs = phytium_ddr_pmu_format_attr, +}; + +static struct attribute *phytium_ddr_pmu_events_attr[] = { + PHYTIUM_DDR_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq, 0x01), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxdat, 0x02), + PHYTIUM_DDR_PMU_EVENT_ATTR(txdat, 0x03), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_RNS, 0x04), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_WNSP, 0x05), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_WNSF, 0x06), + PHYTIUM_DDR_PMU_EVENT_ATTR(bandwidth, 0x07), + NULL, +}; + +static const struct attribute_group phytium_ddr_pmu_events_group = { + .name = "events", + .attrs = phytium_ddr_pmu_events_attr, +}; + +static DEVICE_ATTR_RO(cpumask); + +static struct attribute *phytium_ddr_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group phytium_ddr_pmu_cpumask_attr_group = { + .attrs = phytium_ddr_pmu_cpumask_attrs, +}; + +static const struct attribute_group *phytium_ddr_pmu_attr_groups[] = { + &phytium_ddr_pmu_format_group, + &phytium_ddr_pmu_events_group, + &phytium_ddr_pmu_cpumask_attr_group, + NULL, +}; + +static u32 phytium_ddr_pmu_get_event_timer(struct perf_event *event) +{ + return FIELD_GET(GENMASK(31, 0), event->attr.config1); +} + +static u64 phytium_ddr_pmu_read_counter(struct phytium_ddr_pmu *ddr_pmu, + struct hw_perf_event *hwc) +{ + u32 idx = GET_DDR_EVENTID(hwc); + u32 cycle_l, cycle_h, w_data, ddr_data_width; + u64 val64 = 0; + int i; + u32 counter_offset = ddr_counter_reg_offset[idx]; + + if (!EVENT_VALID(idx)) { + dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + switch (idx) { + case 0: + cycle_l = readl(ddr_pmu->base + counter_offset); + cycle_h = readl(ddr_pmu->base + counter_offset + 4); + val64 = (u64)cycle_h << 32 | (u64)cycle_l; + break; + case 7: + ddr_data_width = readl(ddr_pmu->base + DDR_DATA_WIDTH); + for (i = 0; i < (ddr_data_width / 8); i++) { + w_data = readl(ddr_pmu->base + counter_offset + 4 * i); + val64 += w_data; + } + break; + default: + val64 = readl(ddr_pmu->base + counter_offset); + break; + } + + return val64; +} + +static void phytium_ddr_pmu_enable_clk(struct phytium_ddr_pmu *ddr_pmu) +{ + u32 val; + + val = readl(ddr_pmu->csr_base); + val |= 0xF; + writel(val, ddr_pmu->csr_base); +} + +static void phytium_ddr_pmu_disable_clk(struct phytium_ddr_pmu *ddr_pmu) +{ + u32 val; + + val = readl(ddr_pmu->csr_base); + val &= ~(0xF); + writel(val, ddr_pmu->csr_base); +} + +static void phytium_ddr_pmu_clear_all_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x1, ddr_pmu->base + DDR_CLEAR_EVENT); +} + +static void phytium_ddr_pmu_start_all_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x1, ddr_pmu->base + DDR_START_TIMER); +} + +static void phytium_ddr_pmu_stop_all_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x1, ddr_pmu->base + DDR_STOP_TIMER); +} + +static void phytium_ddr_pmu_set_timer(struct phytium_ddr_pmu *ddr_pmu, + u32 th_val) +{ + u32 val; + + val = readl(ddr_pmu->base + DDR_SET_TIMER_L); + val = readl(ddr_pmu->base + DDR_SET_TIMER_H); + + writel(th_val, ddr_pmu->base + DDR_SET_TIMER_L); + writel(0, ddr_pmu->base + DDR_SET_TIMER_H); +} + +static void phytium_ddr_pmu_reset_timer(struct phytium_ddr_pmu *ddr_pmu) +{ + u32 val; + + val = readl(ddr_pmu->base + DDR_SET_TIMER_L); + val = readl(ddr_pmu->base + DDR_SET_TIMER_H); + + writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_L); + writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_H); +} + +static unsigned long +phytium_ddr_pmu_get_stop_state(struct phytium_ddr_pmu *ddr_pmu) +{ + unsigned long val; + + val = (unsigned long)readl(ddr_pmu->base + DDR_STATE_STOP); + return val; +} + +static unsigned long +phytium_ddr_pmu_get_irq_flag(struct phytium_ddr_pmu *ddr_pmu) +{ + unsigned long val; + + val = (unsigned long)readl(ddr_pmu->csr_base + 4); + return val; +} + +static int phytium_ddr_pmu_mark_event(struct perf_event *event) +{ + struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); + unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; + struct hw_perf_event *hwc = &event->hw; + + int idx = GET_DDR_EVENTID(hwc); + + if (test_bit(idx, used_mask)) + return -EAGAIN; + + set_bit(idx, used_mask); + + return idx; +} + +static void phytium_ddr_pmu_unmark_event(struct phytium_ddr_pmu *ddr_pmu, + int idx) +{ + if (!EVENT_VALID(idx)) { + dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + clear_bit(idx, ddr_pmu->pmu_events.used_mask); +} + +int phytium_ddr_pmu_event_init(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + struct phytium_ddr_pmu *ddr_pmu; + u32 event_timer; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) + return -EOPNOTSUPP; + + ddr_pmu = to_phytium_ddr_pmu(event->pmu); + + if (event->cpu < 0) { + dev_warn(ddr_pmu->dev, "Can't provide per-task data!\n"); + return -EINVAL; + } + + if (event->attr.config > PHYTIUM_DDR_MAX_COUNTERS) + return -EINVAL; + + if (ddr_pmu->on_cpu == -1) + return -EINVAL; + + event_timer = phytium_ddr_pmu_get_event_timer(event); + if (event_timer != 0) + phytium_ddr_pmu_set_timer(ddr_pmu, event_timer); + + hwc->idx = -1; + hwc->config_base = event->attr.config; + + event->cpu = ddr_pmu->on_cpu; + + return 0; +} + +void phytium_ddr_pmu_event_update(struct perf_event *event) +{ + struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u64 delta; + + delta = phytium_ddr_pmu_read_counter(ddr_pmu, hwc); + local64_add(delta, &event->count); +} + +void phytium_ddr_pmu_event_start(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state = 0; + perf_event_update_userpage(event); +} + +void phytium_ddr_pmu_event_stop(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state |= PERF_HES_STOPPED; + + if (flags & PERF_EF_UPDATE) + phytium_ddr_pmu_event_update(event); +} + +int phytium_ddr_pmu_event_add(struct perf_event *event, int flags) +{ + struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx; + + hwc->state |= PERF_HES_STOPPED; + + idx = phytium_ddr_pmu_mark_event(event); + if (idx < 0) + return idx; + + event->hw.idx = idx; + ddr_pmu->pmu_events.hw_events[idx] = event; + + return 0; +} + +void phytium_ddr_pmu_event_del(struct perf_event *event, int flags) +{ + struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + unsigned long val; + u32 event_timer; + + phytium_ddr_pmu_event_stop(event, PERF_EF_UPDATE); + val = phytium_ddr_pmu_get_irq_flag(ddr_pmu); + val = phytium_ddr_pmu_get_stop_state(ddr_pmu); + phytium_ddr_pmu_unmark_event(ddr_pmu, hwc->idx); + + event_timer = phytium_ddr_pmu_get_event_timer(event); + if (event_timer != 0) + phytium_ddr_pmu_reset_timer(ddr_pmu); + + perf_event_update_userpage(event); + ddr_pmu->pmu_events.hw_events[hwc->idx] = NULL; +} + +void phytium_ddr_pmu_enable(struct pmu *pmu) +{ + struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(pmu); + int event_added = bitmap_weight(ddr_pmu->pmu_events.used_mask, + PHYTIUM_DDR_MAX_COUNTERS); + + if (event_added) { + phytium_ddr_pmu_clear_all_counters(ddr_pmu); + phytium_ddr_pmu_start_all_counters(ddr_pmu); + } +} + +void phytium_ddr_pmu_disable(struct pmu *pmu) +{ + struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(pmu); + int event_added = bitmap_weight(ddr_pmu->pmu_events.used_mask, + PHYTIUM_DDR_MAX_COUNTERS); + + if (event_added) + phytium_ddr_pmu_stop_all_counters(ddr_pmu); +} + +static const struct acpi_device_id phytium_ddr_pmu_acpi_match[] = { + { + "PHYT0043", + }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, phytium_ddr_pmu_acpi_match); + +static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) +{ + struct phytium_ddr_pmu *ddr_pmu = dev_id; + struct perf_event *event; + unsigned long overflown, stop_state; + int idx; + unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; + + int event_added = bitmap_weight(used_mask, PHYTIUM_DDR_MAX_COUNTERS); + + overflown = phytium_ddr_pmu_get_irq_flag(ddr_pmu); + + if (!test_bit(ddr_pmu->bit_idx, &overflown)) + return IRQ_NONE; + + stop_state = phytium_ddr_pmu_get_stop_state(ddr_pmu); + + if (bitmap_weight(&stop_state, 6)) { + for_each_set_bit(idx, used_mask, PHYTIUM_DDR_MAX_COUNTERS) { + event = ddr_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + phytium_ddr_pmu_event_update(event); + } + phytium_ddr_pmu_clear_all_counters(ddr_pmu); + phytium_ddr_pmu_start_all_counters(ddr_pmu); + + return IRQ_HANDLED; + } + + if (!event_added) { + phytium_ddr_pmu_clear_all_counters(ddr_pmu); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, + struct platform_device *pdev) +{ + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, + phytium_ddr_pmu_overflow_handler, + IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_SHARED, + dev_name(&pdev->dev), ddr_pmu); + if (ret < 0) { + dev_err(&pdev->dev, "Fail to request IRQ:%d ret:%d\n", irq, + ret); + return ret; + } + + ddr_pmu->irq = irq; + + return 0; +} + +static int phytium_ddr_pmu_init_data(struct platform_device *pdev, + struct phytium_ddr_pmu *ddr_pmu) +{ + struct resource *res, *clkres; + + if (device_property_read_u32(&pdev->dev, "phytium,die-id", + &ddr_pmu->die_id)) { + dev_err(&pdev->dev, "Can not read phytium,die-id!\n"); + return -EINVAL; + } + + if (device_property_read_u32(&pdev->dev, "phytium,ddr-id", + &ddr_pmu->ddr_id)) { + dev_err(&pdev->dev, "Can not read phytium,ddr-id!\n"); + return -EINVAL; + } + + if (device_property_read_u32(&pdev->dev, "phytium,pmu-id", + &ddr_pmu->pmu_id)) { + dev_err(&pdev->dev, "Can not read ddr pmu-id!\n"); + return -EINVAL; + } + + ddr_pmu->bit_idx = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ddr_pmu->base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(ddr_pmu->base)) { + dev_err(&pdev->dev, + "ioremap failed for ddr_pmu base resource\n"); + return PTR_ERR(ddr_pmu->base); + } + + clkres = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!clkres) { + dev_err(&pdev->dev, "failed for get ddr_pmu clk resource.\n"); + return -EINVAL; + } + + ddr_pmu->csr_base = + devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); + if (IS_ERR(ddr_pmu->csr_base)) { + dev_err(&pdev->dev, + "ioremap failed for ddr_pmu csr resource\n"); + return PTR_ERR(ddr_pmu->csr_base); + } + + return 0; +} + +static int phytium_ddr_pmu_dev_probe(struct platform_device *pdev, + struct phytium_ddr_pmu *ddr_pmu) +{ + int ret; + + ret = phytium_ddr_pmu_init_data(pdev, ddr_pmu); + if (ret) + return ret; + + ret = phytium_ddr_pmu_init_irq(ddr_pmu, pdev); + if (ret) + return ret; + + ddr_pmu->dev = &pdev->dev; + ddr_pmu->on_cpu = raw_smp_processor_id(); + WARN_ON(irq_set_affinity_hint(ddr_pmu->irq, cpumask_of(ddr_pmu->on_cpu))); + + return 0; +} + +static int phytium_ddr_pmu_probe(struct platform_device *pdev) +{ + struct phytium_ddr_pmu *ddr_pmu; + char *name; + int ret; + + ddr_pmu = devm_kzalloc(&pdev->dev, sizeof(*ddr_pmu), GFP_KERNEL); + if (!ddr_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, ddr_pmu); + + ret = phytium_ddr_pmu_dev_probe(pdev, ddr_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance_nocalls( + phytium_ddr_pmu_hp_state, &ddr_pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); + return ret; + } + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_ddr%u_pmu%u", + ddr_pmu->die_id, ddr_pmu->ddr_id, + ddr_pmu->pmu_id); + ddr_pmu->pmu = (struct pmu){ + .name = name, + .module = THIS_MODULE, + .task_ctx_nr = perf_invalid_context, + .event_init = phytium_ddr_pmu_event_init, + .pmu_enable = phytium_ddr_pmu_enable, + .pmu_disable = phytium_ddr_pmu_disable, + .add = phytium_ddr_pmu_event_add, + .del = phytium_ddr_pmu_event_del, + .start = phytium_ddr_pmu_event_start, + .stop = phytium_ddr_pmu_event_stop, + .read = phytium_ddr_pmu_event_update, + .attr_groups = phytium_ddr_pmu_attr_groups, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, + }; + + ret = perf_pmu_register(&ddr_pmu->pmu, name, -1); + if (ret) { + dev_err(ddr_pmu->dev, "DDR PMU register failed!\n"); + cpuhp_state_remove_instance_nocalls( + phytium_ddr_pmu_hp_state, + &ddr_pmu->node); + } + + phytium_ddr_pmu_enable_clk(ddr_pmu); + + pr_info("Phytium DDR PMU: "); + pr_info(" die_id = %d ddr_id = %d pmu_id = %d.\n", ddr_pmu->die_id, + ddr_pmu->ddr_id, ddr_pmu->pmu_id); + + return ret; +} + +static int phytium_ddr_pmu_remove(struct platform_device *pdev) +{ + struct phytium_ddr_pmu *ddr_pmu = platform_get_drvdata(pdev); + + phytium_ddr_pmu_disable_clk(ddr_pmu); + + perf_pmu_unregister(&ddr_pmu->pmu); + cpuhp_state_remove_instance_nocalls( + phytium_ddr_pmu_hp_state, &ddr_pmu->node); + + return 0; +} + +static struct platform_driver phytium_ddr_pmu_driver = { + .driver = { + .name = "phytium_ddr_pmu", + .acpi_match_table = ACPI_PTR(phytium_ddr_pmu_acpi_match), + .suppress_bind_attrs = true, + }, + .probe = phytium_ddr_pmu_probe, + .remove = phytium_ddr_pmu_remove, +}; + +int phytium_ddr_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct phytium_ddr_pmu *ddr_pmu = + hlist_entry_safe(node, struct phytium_ddr_pmu, node); + unsigned int target; + cpumask_t available_cpus; + + if (ddr_pmu->on_cpu != cpu) + return 0; + + cpumask_and(&available_cpus, + cpumask_of_node(ddr_pmu->die_id), cpu_online_mask); + target = cpumask_any_but(&available_cpus, cpu); + if (target >= nr_cpu_ids) { + target = cpumask_any_but(cpu_online_mask, cpu); + if (target >= nr_cpu_ids) + return 0; + } + + perf_pmu_migrate_context(&ddr_pmu->pmu, cpu, target); + WARN_ON(irq_set_affinity_hint(ddr_pmu->irq, cpumask_of(target))); + ddr_pmu->on_cpu = target; + + return 0; +} + +static int __init phytium_ddr_pmu_module_init(void) +{ + int ret; + + phytium_ddr_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/phytium/ddrpmu:offline", + NULL, phytium_ddr_pmu_offline_cpu); + if (phytium_ddr_pmu_hp_state < 0) { + pr_err("DDR PMU: setup hotplug, phytium_ddr_pmu_hp_state = %d\n", + phytium_ddr_pmu_hp_state); + return phytium_ddr_pmu_hp_state; + } + + ret = platform_driver_register(&phytium_ddr_pmu_driver); + if (ret) + cpuhp_remove_multi_state( + phytium_ddr_pmu_hp_state); + + return ret; +} +module_init(phytium_ddr_pmu_module_init); + +static void __exit phytium_ddr_pmu_module_exit(void) +{ + platform_driver_unregister(&phytium_ddr_pmu_driver); + cpuhp_remove_multi_state(phytium_ddr_pmu_hp_state); +} +module_exit(phytium_ddr_pmu_module_exit); + +MODULE_DESCRIPTION("Phytium DDR PMU driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hu Xianghua "); -- Gitee From 91325e80fab64127a7cfcb1c2358a628a410bf96 Mon Sep 17 00:00:00 2001 From: Hu Xianghua Date: Mon, 3 Mar 2025 13:43:30 +0800 Subject: [PATCH 076/154] drivers/perf: phytium: Refactor for Phytium SoC PCIe controller PMU. Generalize Phytium SoC PCIe controller PMU supporting with phytium subdir. Mainline: Open-Source Signed-off-by: Hu Xianghua Signed-off-by: Wang Yinfeng Change-Id: I97e545338f425dbceedad01705c9bb75f17fcace --- drivers/perf/phytium/Kconfig | 8 + drivers/perf/phytium/Makefile | 1 + drivers/perf/phytium/phytium_pcie_pmu.c | 873 ++++++++++++++++++++++++ 3 files changed, 882 insertions(+) create mode 100644 drivers/perf/phytium/phytium_pcie_pmu.c diff --git a/drivers/perf/phytium/Kconfig b/drivers/perf/phytium/Kconfig index 230455b930..e9dabba722 100644 --- a/drivers/perf/phytium/Kconfig +++ b/drivers/perf/phytium/Kconfig @@ -15,5 +15,13 @@ config PHYT_DDR_PMU Provides support for Phytium SoC DDR Controller performance monitoring unit (PMU). +config PHYT_PCIE_PMU + tristate "Phytium SoC PCIE PMU driver" + depends on (ARCH_PHYTIUM && ACPI) || COMPILE_TEST + default m + help + Provides support for Phytium SoC PCIe Controller performance + monitoring unit (PMU). + endif diff --git a/drivers/perf/phytium/Makefile b/drivers/perf/phytium/Makefile index 9635d3f0d2..af37afc692 100644 --- a/drivers/perf/phytium/Makefile +++ b/drivers/perf/phytium/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 # obj-$(CONFIG_PHYT_DDR_PMU) += phytium_ddr_pmu.o +obj-$(CONFIG_PHYT_PCIE_PMU) += phytium_pcie_pmu.o diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c new file mode 100644 index 0000000000..03463f4425 --- /dev/null +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -0,0 +1,873 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium Soc PCIe performance monitoring unit support + * + * Copyright (c) 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 + +#undef pr_fmt +#define pr_fmt(fmt) "phytium_pcie_pmu: " fmt + +#define PHYTIUM_PCIE_MAX_COUNTERS 18 + +#define PCIE_START_TIMER 0x000 +#define PCIE_STOP_TIMER 0x004 +#define PCIE_CLEAR_EVENT 0x008 +#define PCIE_SET_TIMER_L 0x00c +#define PCIE_SET_TIMER_H 0x010 +#define PCIE_TRIG_MODE 0x014 + +#define PCIE_NOW_STATE 0x0e0 +#define PCIE_EVENT_CYCLES 0x0e4 +#define PCIE_TPOINT_END_L 0x0e4 +#define PCIE_TPOINT_END_H 0x0e8 +#define PCIE_STATE_STOP 0x0ec + +#define PCIE_EVENT_AW 0x100 +#define PCIE_EVENT_W_LAST 0x104 +#define PCIE_EVENT_B 0x108 +#define PCIE_EVENT_AR 0x10c +#define PCIE_EVENT_R_LAST 0x110 +#define PCIE_EVENT_R_FULL 0x114 +#define PCIE_EVENT_R_ERR 0x118 +#define PCIE_EVENT_W_ERR 0x11c +#define PCIE_EVENT_DELAY_RD 0x120 +#define PCIE_EVENT_DELAY_WR 0x124 +#define PCIE_EVENT_RD_MAX 0x128 +#define PCIE_EVENT_RD_MIN 0x12c +#define PCIE_EVENT_WR_MAX 0x130 +#define PCIE_EVENT_WR_MIN 0x134 + +#define PCIE_EVENT_W_DATA 0x200 +#define PCIE_W_DATA_BASE 0x200 + +#define PCIE_EVENT_RDELAY_TIME 0x300 +#define PCIE_RDELAY_TIME_BASE 0x300 + +#define PCIE_EVENT_WDELAY_TIME 0x700 +#define PCIE_WDELAY_TIME_BASE 0x700 + +#define PCIE_CLK_FRE 0xe00 +#define PCIE_DATA_WIDTH 0xe04 + +#define to_phytium_pcie_pmu(p) (container_of(p, struct phytium_pcie_pmu, pmu)) + +static int phytium_pcie_pmu_hp_state; + +struct phytium_pcie_pmu_hwevents { + struct perf_event *hw_events[PHYTIUM_PCIE_MAX_COUNTERS]; + DECLARE_BITMAP(used_mask, PHYTIUM_PCIE_MAX_COUNTERS); +}; + +struct phytium_pcie_pmu { + struct device *dev; + void __iomem *base; + void __iomem *csr_base; + void __iomem *irq_reg; + struct pmu pmu; + struct phytium_pcie_pmu_hwevents pmu_events; + u32 die_id; + u32 pmu_id; + int on_cpu; + int irq; + struct hlist_node node; + int ctrler_id; + int real_ctrler; + u32 clk_bits; +}; + +#define GET_PCIE_EVENTID(hwc) (hwc->config_base & 0x1F) + +#define EVENT_VALID(idx) ((idx >= 0) && (idx < PHYTIUM_PCIE_MAX_COUNTERS)) + +static const u32 pcie_counter_reg_offset[] = { + PCIE_EVENT_CYCLES, PCIE_EVENT_AW, PCIE_EVENT_W_LAST, + PCIE_EVENT_B, PCIE_EVENT_AR, PCIE_EVENT_R_LAST, + PCIE_EVENT_R_FULL, PCIE_EVENT_R_ERR, PCIE_EVENT_W_ERR, + PCIE_EVENT_DELAY_RD, PCIE_EVENT_DELAY_WR, PCIE_EVENT_RD_MAX, + PCIE_EVENT_RD_MIN, PCIE_EVENT_WR_MAX, PCIE_EVENT_WR_MIN, + PCIE_EVENT_W_DATA, PCIE_EVENT_RDELAY_TIME, PCIE_EVENT_WDELAY_TIME +}; + +ssize_t phytium_pcie_pmu_format_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + + return sprintf(buf, "%s\n", (char *)eattr->var); +} + +ssize_t phytium_pcie_pmu_event_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + + return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var); +} + +static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct phytium_pcie_pmu *pcie_pmu = + to_phytium_pcie_pmu(dev_get_drvdata(dev)); + + return cpumap_print_to_pagebuf(true, buf, cpumask_of(pcie_pmu->on_cpu)); +} + +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ + { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ + .attr.attr) + +#define PHYTIUM_PCIE_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_pcie_pmu_format_sysfs_show, \ + (void *)_config) + +#define PHYTIUM_PCIE_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_pcie_pmu_event_sysfs_show, \ + (unsigned long)_config) + +static struct attribute *phytium_pcie_pmu_format_attr[] = { + PHYTIUM_PCIE_PMU_FORMAT_ATTR(event, "config:0-4"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(ctrler, "config:8-10"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(timer, "config1:0-31"), + NULL, +}; + +static const struct attribute_group phytium_pcie_pmu_format_group = { + .name = "format", + .attrs = phytium_pcie_pmu_format_attr, +}; + +static struct attribute *phytium_pcie_pmu_events_attr[] = { + PHYTIUM_PCIE_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_PCIE_PMU_EVENT_ATTR(aw, 0x01), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_last, 0x02), + PHYTIUM_PCIE_PMU_EVENT_ATTR(b, 0x03), + PHYTIUM_PCIE_PMU_EVENT_ATTR(ar, 0x04), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_last, 0x05), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_full, 0x06), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_err, 0x07), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_err, 0x08), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd, 0x09), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr, 0x0a), + PHYTIUM_PCIE_PMU_EVENT_ATTR(rd_max, 0x0b), + PHYTIUM_PCIE_PMU_EVENT_ATTR(rd_min, 0x0c), + PHYTIUM_PCIE_PMU_EVENT_ATTR(wr_max, 0x0d), + PHYTIUM_PCIE_PMU_EVENT_ATTR(wr_min, 0x0e), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_data, 0x0f), + PHYTIUM_PCIE_PMU_EVENT_ATTR(rdelay_time, 0x10), + PHYTIUM_PCIE_PMU_EVENT_ATTR(wdelay_time, 0x11), + NULL, +}; + +static const struct attribute_group phytium_pcie_pmu_events_group = { + .name = "events", + .attrs = phytium_pcie_pmu_events_attr, +}; + +static DEVICE_ATTR_RO(cpumask); + +static struct attribute *phytium_pcie_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group phytium_pcie_pmu_cpumask_attr_group = { + .attrs = phytium_pcie_pmu_cpumask_attrs, +}; + +static const struct attribute_group *phytium_pcie_pmu_attr_groups[] = { + &phytium_pcie_pmu_format_group, + &phytium_pcie_pmu_events_group, + &phytium_pcie_pmu_cpumask_attr_group, + NULL, +}; + +static u32 phytium_pcie_pmu_get_event_ctrler(struct perf_event *event) +{ + return FIELD_GET(GENMASK(10, 8), event->attr.config); +} + +static u32 phytium_pcie_pmu_get_event_timer(struct perf_event *event) +{ + return FIELD_GET(GENMASK(31, 0), event->attr.config1); +} + +static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, + struct hw_perf_event *hwc) +{ + u32 idx = GET_PCIE_EVENTID(hwc); + u32 cycle_l, cycle_h, rdelay_l, rdelay_h, w_data, wdelay_l, wdelay_h, + pcie_data_width; + u64 val64 = 0; + int i; + u32 counter_offset = pcie_counter_reg_offset[idx]; + + if (!EVENT_VALID(idx)) { + dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + switch (idx) { + case 0: + cycle_l = readl(pcie_pmu->base + counter_offset); + cycle_h = readl(pcie_pmu->base + counter_offset + 4); + val64 = (u64)cycle_h << 32 | (u64)cycle_l; + break; + case 15: + pcie_data_width = readl(pcie_pmu->base + PCIE_DATA_WIDTH); + for (i = 0; i < (pcie_data_width / 8); i++) { + w_data = readl(pcie_pmu->base + counter_offset + 4 * i); + val64 += w_data; + } + break; + case 16: + for (i = 0; i <= 127; i = i + 2) { + rdelay_l = + readl(pcie_pmu->base + counter_offset + 4 * i); + rdelay_h = readl(pcie_pmu->base + counter_offset + + 4 * i + 4); + val64 += (u64)rdelay_h << 32 | (u64)rdelay_l; + } + break; + case 17: + for (i = 0; i <= 63; i++) { + wdelay_l = + readl(pcie_pmu->base + counter_offset + 4 * i); + wdelay_h = readl(pcie_pmu->base + counter_offset + + 4 * i + 4); + val64 += (u64)wdelay_h << 32 | (u64)wdelay_l; + } + break; + default: + val64 = readl(pcie_pmu->base + counter_offset); + break; + } + return val64; +} + +static void phytium_pcie_pmu_enable_clk(struct phytium_pcie_pmu *pcie_pmu) +{ + u32 val; + + val = readl(pcie_pmu->csr_base); + val |= (pcie_pmu->clk_bits); + writel(val, pcie_pmu->csr_base); +} + +static void phytium_pcie_pmu_disable_clk(struct phytium_pcie_pmu *pcie_pmu) +{ + u32 val; + + val = readl(pcie_pmu->csr_base); + val &= ~(pcie_pmu->clk_bits); + writel(val, pcie_pmu->csr_base); +} + +static void phytium_pcie_pmu_select_ctrler(struct phytium_pcie_pmu *pcie_pmu) +{ + u32 val, offset = 0; + + if (pcie_pmu->pmu_id != 2) + offset = 0xc; + + val = readl(pcie_pmu->csr_base + offset); + + if (pcie_pmu->pmu_id == 2) { + val &= 0xffffffcf; + val |= pcie_pmu->real_ctrler; + } else { + val &= 0xfffffffc; + val |= pcie_pmu->real_ctrler; + } + + writel(val, pcie_pmu->csr_base + offset); +} + +static void +phytium_pcie_pmu_clear_all_counters(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0x1, pcie_pmu->base + PCIE_CLEAR_EVENT); +} + +static void +phytium_pcie_pmu_start_all_counters(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0x1, pcie_pmu->base + PCIE_START_TIMER); +} + +static void +phytium_pcie_pmu_stop_all_counters(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0x1, pcie_pmu->base + PCIE_STOP_TIMER); +} + +static void phytium_pcie_pmu_set_timer(struct phytium_pcie_pmu *pcie_pmu, + u32 th_val) +{ + u32 val; + + val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); + val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); + + writel(th_val, pcie_pmu->base + PCIE_SET_TIMER_L); + writel(0, pcie_pmu->base + PCIE_SET_TIMER_H); +} + +static void phytium_pcie_pmu_reset_timer(struct phytium_pcie_pmu *pcie_pmu) +{ + u32 val; + + val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); + val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); + + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_L); + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_H); +} + +static unsigned long +phytium_pcie_pmu_get_stop_state(struct phytium_pcie_pmu *pcie_pmu) +{ + unsigned long val; + + val = (unsigned long)readl(pcie_pmu->base + PCIE_STATE_STOP); + return val; +} + +static unsigned long +phytium_pcie_pmu_get_irq_flag(struct phytium_pcie_pmu *pcie_pmu) +{ + unsigned long val; + + val = (unsigned long)readl(pcie_pmu->irq_reg); + return val; +} + +static int phytium_pcie_pmu_mark_event(struct perf_event *event) +{ + struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(event->pmu); + unsigned long *used_mask = pcie_pmu->pmu_events.used_mask; + struct hw_perf_event *hwc = &event->hw; + + int idx = GET_PCIE_EVENTID(hwc); + + if (test_bit(idx, used_mask)) + return -EAGAIN; + + set_bit(idx, used_mask); + + return idx; +} + +static void phytium_pcie_pmu_unmark_event(struct phytium_pcie_pmu *pcie_pmu, + int idx) +{ + if (!EVENT_VALID(idx)) { + dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + clear_bit(idx, pcie_pmu->pmu_events.used_mask); +} + +int phytium_pcie_pmu_event_init(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + struct phytium_pcie_pmu *pcie_pmu; + u32 event_ctrler, event_timer; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) + return -EOPNOTSUPP; + + pcie_pmu = to_phytium_pcie_pmu(event->pmu); + + if (event->cpu < 0) { + dev_warn(pcie_pmu->dev, "Can't provide per-task data!\n"); + return -EINVAL; + } + + if ((event->attr.config & 0x1F) > PHYTIUM_PCIE_MAX_COUNTERS) + return -EINVAL; + + if (pcie_pmu->on_cpu == -1) + return -EINVAL; + + event_timer = phytium_pcie_pmu_get_event_timer(event); + if (event_timer != 0) + phytium_pcie_pmu_set_timer(pcie_pmu, event_timer); + + event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); + switch (pcie_pmu->pmu_id) { + case 0: + if (event_ctrler != 0) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu0!\n", + event_ctrler); + return -EINVAL; + } + break; + case 1: + if ((event_ctrler < 1) || (event_ctrler > 3)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu1!\n", + event_ctrler); + return -EINVAL; + } + break; + case 2: + if ((event_ctrler < 4) || (event_ctrler > 7)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu2!\n", + event_ctrler); + return -EINVAL; + } + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); + return -EINVAL; + } + + pcie_pmu->ctrler_id = event_ctrler; + switch (pcie_pmu->pmu_id) { + case 0: + case 1: + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + break; + case 2: + pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); + return -EINVAL; + } + phytium_pcie_pmu_select_ctrler(pcie_pmu); + + hwc->idx = -1; + hwc->config_base = event->attr.config; + + event->cpu = pcie_pmu->on_cpu; + return 0; +} + +void phytium_pcie_pmu_event_update(struct perf_event *event) +{ + struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u64 delta; + + delta = phytium_pcie_pmu_read_counter(pcie_pmu, hwc); + local64_add(delta, &event->count); +} + +void phytium_pcie_pmu_event_start(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state = 0; + perf_event_update_userpage(event); +} + +void phytium_pcie_pmu_event_stop(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state |= PERF_HES_STOPPED; + + if (flags & PERF_EF_UPDATE) + phytium_pcie_pmu_event_update(event); +} + +int phytium_pcie_pmu_event_add(struct perf_event *event, int flags) +{ + struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx; + + hwc->state |= PERF_HES_STOPPED; + + idx = phytium_pcie_pmu_mark_event(event); + if (idx < 0) + return idx; + + event->hw.idx = idx; + pcie_pmu->pmu_events.hw_events[idx] = event; + + return 0; +} + +void phytium_pcie_pmu_event_del(struct perf_event *event, int flags) +{ + struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + unsigned long val; + u32 event_timer; + + phytium_pcie_pmu_event_stop(event, PERF_EF_UPDATE); + val = phytium_pcie_pmu_get_irq_flag(pcie_pmu); + val = phytium_pcie_pmu_get_stop_state(pcie_pmu); + phytium_pcie_pmu_unmark_event(pcie_pmu, hwc->idx); + + event_timer = phytium_pcie_pmu_get_event_timer(event); + if (event_timer != 0) + phytium_pcie_pmu_reset_timer(pcie_pmu); + + perf_event_update_userpage(event); + pcie_pmu->pmu_events.hw_events[hwc->idx] = NULL; +} + +void phytium_pcie_pmu_enable(struct pmu *pmu) +{ + struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(pmu); + int event_added = bitmap_weight(pcie_pmu->pmu_events.used_mask, + PHYTIUM_PCIE_MAX_COUNTERS); + + if (event_added) { + phytium_pcie_pmu_clear_all_counters(pcie_pmu); + phytium_pcie_pmu_start_all_counters(pcie_pmu); + } +} + +void phytium_pcie_pmu_disable(struct pmu *pmu) +{ + struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(pmu); + int event_added = bitmap_weight(pcie_pmu->pmu_events.used_mask, + PHYTIUM_PCIE_MAX_COUNTERS); + + if (event_added) + phytium_pcie_pmu_stop_all_counters(pcie_pmu); +} + +static const struct acpi_device_id phytium_pcie_pmu_acpi_match[] = { + { + "PHYT0044", + }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, phytium_pcie_pmu_acpi_match); + +static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) +{ + struct phytium_pcie_pmu *pcie_pmu = dev_id; + struct perf_event *event; + unsigned long overflown, stop_state; + int idx; + unsigned long *used_mask = pcie_pmu->pmu_events.used_mask; + int event_added = bitmap_weight(used_mask, PHYTIUM_PCIE_MAX_COUNTERS); + + overflown = phytium_pcie_pmu_get_irq_flag(pcie_pmu); + + if (!test_bit(pcie_pmu->pmu_id + 4, &overflown)) + return IRQ_NONE; + + stop_state = phytium_pcie_pmu_get_stop_state(pcie_pmu); + + if (bitmap_weight(&stop_state, 6)) { + for_each_set_bit(idx, used_mask, PHYTIUM_PCIE_MAX_COUNTERS) { + event = pcie_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + phytium_pcie_pmu_event_update(event); + } + phytium_pcie_pmu_clear_all_counters(pcie_pmu); + phytium_pcie_pmu_start_all_counters(pcie_pmu); + + return IRQ_HANDLED; + } + if (!event_added) { + phytium_pcie_pmu_clear_all_counters(pcie_pmu); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, + struct platform_device *pdev) +{ + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, + phytium_pcie_pmu_overflow_handler, + IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_SHARED, + dev_name(&pdev->dev), pcie_pmu); + if (ret < 0) { + dev_err(&pdev->dev, "Fail to request IRQ:%d ret:%d\n", irq, + ret); + return ret; + } + + pcie_pmu->irq = irq; + + return 0; +} + +static int phytium_pcie_pmu_init_data(struct platform_device *pdev, + struct phytium_pcie_pmu *pcie_pmu) +{ + struct resource *res, *clkres, *irqres; + + if (device_property_read_u32(&pdev->dev, "phytium,die-id", + &pcie_pmu->die_id)) { + dev_err(&pdev->dev, "Can not read phytium,die-id!\n"); + return -EINVAL; + } + + if (device_property_read_u32(&pdev->dev, "phytium,pmu-id", + &pcie_pmu->pmu_id)) { + dev_err(&pdev->dev, "Can not read phytium,pmu-id!\n"); + return -EINVAL; + } + + switch (pcie_pmu->pmu_id) { + case 0: + pcie_pmu->clk_bits = 0x1; + break; + case 1: + pcie_pmu->clk_bits = 0xe; + break; + case 2: + pcie_pmu->clk_bits = 0xf; + break; + default: + dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); + break; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcie_pmu->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pcie_pmu->base)) { + dev_err(&pdev->dev, "ioremap failed for pcie_pmu resource\n"); + return PTR_ERR(pcie_pmu->base); + } + + clkres = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!clkres) { + dev_err(&pdev->dev, "failed for get pcie_pmu clk resource.\n"); + return -EINVAL; + } + + pcie_pmu->csr_base = + devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); + if (IS_ERR(pcie_pmu->csr_base)) { + dev_err(&pdev->dev, + "ioremap failed for pcie_pmu csr resource\n"); + return PTR_ERR(pcie_pmu->csr_base); + } + + irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!irqres) { + dev_err(&pdev->dev, + "failed for get pcie_pmu irq reg resource.\n"); + return -EINVAL; + } + + pcie_pmu->irq_reg = + devm_ioremap(&pdev->dev, irqres->start, resource_size(irqres)); + if (IS_ERR(pcie_pmu->irq_reg)) { + dev_err(&pdev->dev, + "ioremap failed for pcie_pmu irq resource\n"); + return PTR_ERR(pcie_pmu->irq_reg); + } + + return 0; +} + +static int phytium_pcie_pmu_dev_probe(struct platform_device *pdev, + struct phytium_pcie_pmu *pcie_pmu) +{ + int ret; + + ret = phytium_pcie_pmu_init_data(pdev, pcie_pmu); + if (ret) + return ret; + + ret = phytium_pcie_pmu_init_irq(pcie_pmu, pdev); + if (ret) + return ret; + pcie_pmu->dev = &pdev->dev; + pcie_pmu->on_cpu = raw_smp_processor_id(); + WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(pcie_pmu->on_cpu))); + pcie_pmu->ctrler_id = -1; + + return 0; +} + +static int phytium_pcie_pmu_probe(struct platform_device *pdev) +{ + struct phytium_pcie_pmu *pcie_pmu; + char *name; + int ret; + + pcie_pmu = devm_kzalloc(&pdev->dev, sizeof(*pcie_pmu), GFP_KERNEL); + if (!pcie_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, pcie_pmu); + + ret = phytium_pcie_pmu_dev_probe(pdev, pcie_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance_nocalls( + phytium_pcie_pmu_hp_state, &pcie_pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); + return ret; + } + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", + pcie_pmu->die_id, pcie_pmu->pmu_id); + pcie_pmu->pmu = (struct pmu){ + .name = name, + .module = THIS_MODULE, + .task_ctx_nr = perf_invalid_context, + .event_init = phytium_pcie_pmu_event_init, + .pmu_enable = phytium_pcie_pmu_enable, + .pmu_disable = phytium_pcie_pmu_disable, + .add = phytium_pcie_pmu_event_add, + .del = phytium_pcie_pmu_event_del, + .start = phytium_pcie_pmu_event_start, + .stop = phytium_pcie_pmu_event_stop, + .read = phytium_pcie_pmu_event_update, + .attr_groups = phytium_pcie_pmu_attr_groups, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, + }; + + ret = perf_pmu_register(&pcie_pmu->pmu, name, -1); + if (ret) { + dev_err(pcie_pmu->dev, "PCIE PMU register failed!\n"); + cpuhp_state_remove_instance_nocalls( + phytium_pcie_pmu_hp_state, + &pcie_pmu->node); + } + + phytium_pcie_pmu_enable_clk(pcie_pmu); + + pr_info("Phytium PCIe PMU: "); + pr_info("die_id = %d pmu_id = %d.\n", pcie_pmu->die_id, + pcie_pmu->pmu_id); + + return ret; +} + +static int phytium_pcie_pmu_remove(struct platform_device *pdev) +{ + struct phytium_pcie_pmu *pcie_pmu = platform_get_drvdata(pdev); + + phytium_pcie_pmu_disable_clk(pcie_pmu); + + perf_pmu_unregister(&pcie_pmu->pmu); + cpuhp_state_remove_instance_nocalls( + phytium_pcie_pmu_hp_state, &pcie_pmu->node); + + return 0; +} + +static struct platform_driver phytium_pcie_pmu_driver = { + .driver = { + .name = "phytium_pcie_pmu", + .acpi_match_table = ACPI_PTR(phytium_pcie_pmu_acpi_match), + .suppress_bind_attrs = true, + }, + .probe = phytium_pcie_pmu_probe, + .remove = phytium_pcie_pmu_remove, +}; + +int phytium_pcie_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct phytium_pcie_pmu *pcie_pmu = + hlist_entry_safe(node, struct phytium_pcie_pmu, node); + unsigned int target; + cpumask_t available_cpus; + + if (pcie_pmu->on_cpu != cpu) + return 0; + + cpumask_and(&available_cpus, + cpumask_of_node(pcie_pmu->die_id), cpu_online_mask); + + target = cpumask_any_but(&available_cpus, cpu); + if (target >= nr_cpu_ids) { + target = cpumask_any_but(cpu_online_mask, cpu); + if (target >= nr_cpu_ids) + return 0; + } + + perf_pmu_migrate_context(&pcie_pmu->pmu, cpu, target); + WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(target))); + pcie_pmu->on_cpu = target; + + return 0; +} + +static int __init phytium_pcie_pmu_module_init(void) +{ + int ret; + + phytium_pcie_pmu_hp_state = + cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/phytium/pciepmu:offline", NULL, + phytium_pcie_pmu_offline_cpu); + if (phytium_pcie_pmu_hp_state < 0) { + pr_err("PCIE PMU: setup hotplug, ret = %d\n", + phytium_pcie_pmu_hp_state); + return phytium_pcie_pmu_hp_state; + } + + ret = platform_driver_register(&phytium_pcie_pmu_driver); + if (ret) + cpuhp_remove_multi_state( + phytium_pcie_pmu_hp_state); + + return ret; +} +module_init(phytium_pcie_pmu_module_init); + +static void __exit phytium_pcie_pmu_module_exit(void) +{ + platform_driver_unregister(&phytium_pcie_pmu_driver); + cpuhp_remove_multi_state(phytium_pcie_pmu_hp_state); +} +module_exit(phytium_pcie_pmu_module_exit); + +MODULE_DESCRIPTION("Phytium PCIe PMU driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hu Xianghua "); -- Gitee From b573c7fb3d8c2ff719a0ce190e3cbf58e02c24c2 Mon Sep 17 00:00:00 2001 From: huxianghua Date: Tue, 26 Mar 2024 17:03:44 +0800 Subject: [PATCH 077/154] drivers/perf: phytium: Perf Optimization for SoC DDR controller PMU Perf optimizing offline and online function for ltp-cpuhotplug test. Mainline: Open-Source Signed-off-by: Hu Xianghua Signed-off-by: Wang Yinfeng Change-Id: I6aa7862fad9139db264500752bfd1bb0af99c85b --- drivers/perf/phytium/phytium_ddr_pmu.c | 57 +++++++++++++++++++------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index ebe6647a72..a31bf29c87 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -118,16 +118,16 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, return cpumap_print_to_pagebuf(true, buf, cpumask_of(ddr_pmu->on_cpu)); } -#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ - (&((struct dev_ext_attribute[]){ \ +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ .attr.attr) -#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ +#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ (void *)_config) -#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ +#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ (unsigned long)_config) @@ -578,8 +578,7 @@ static int phytium_ddr_pmu_dev_probe(struct platform_device *pdev, return ret; ddr_pmu->dev = &pdev->dev; - ddr_pmu->on_cpu = raw_smp_processor_id(); - WARN_ON(irq_set_affinity_hint(ddr_pmu->irq, cpumask_of(ddr_pmu->on_cpu))); + ddr_pmu->on_cpu = -1; return 0; } @@ -600,7 +599,7 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) if (ret) return ret; - ret = cpuhp_state_add_instance_nocalls( + ret = cpuhp_state_add_instance( phytium_ddr_pmu_hp_state, &ddr_pmu->node); if (ret) { dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); @@ -666,6 +665,29 @@ static struct platform_driver phytium_ddr_pmu_driver = { .remove = phytium_ddr_pmu_remove, }; +int phytium_ddr_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct phytium_ddr_pmu *ddr_pmu = + hlist_entry_safe(node, struct phytium_ddr_pmu, node); + + if (!cpumask_test_cpu(cpu, cpumask_of_node(ddr_pmu->die_id))) + return 0; + + if (ddr_pmu->on_cpu != -1) { + if (!cpumask_test_cpu(ddr_pmu->on_cpu, cpumask_of_node(ddr_pmu->die_id))) { + perf_pmu_migrate_context(&ddr_pmu->pmu, ddr_pmu->on_cpu, cpu); + ddr_pmu->on_cpu = cpu; + WARN_ON(irq_set_affinity_hint(ddr_pmu->irq, cpumask_of(cpu))); + } + return 0; + } + + ddr_pmu->on_cpu = cpu; + WARN_ON(irq_set_affinity_hint(ddr_pmu->irq, cpumask_of(cpu))); + + return 0; +} + int phytium_ddr_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) { struct phytium_ddr_pmu *ddr_pmu = @@ -676,13 +698,18 @@ int phytium_ddr_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) if (ddr_pmu->on_cpu != cpu) return 0; - cpumask_and(&available_cpus, - cpumask_of_node(ddr_pmu->die_id), cpu_online_mask); - target = cpumask_any_but(&available_cpus, cpu); + if (cpumask_and(&available_cpus, cpumask_of_node(ddr_pmu->die_id), cpu_online_mask) && + cpumask_andnot(&available_cpus, &available_cpus, cpumask_of(cpu))) + target = cpumask_last(&available_cpus); + else { + cpumask_andnot(&available_cpus, cpu_online_mask, cpumask_of(cpu)); + target = cpumask_last(&available_cpus); + } + if (target >= nr_cpu_ids) { - target = cpumask_any_but(cpu_online_mask, cpu); - if (target >= nr_cpu_ids) - return 0; + dev_err(ddr_pmu->dev, "offline cpu%d with no target to migrate.\n", + cpu); + return 0; } perf_pmu_migrate_context(&ddr_pmu->pmu, cpu, target); @@ -697,8 +724,8 @@ static int __init phytium_ddr_pmu_module_init(void) int ret; phytium_ddr_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, - "perf/phytium/ddrpmu:offline", - NULL, phytium_ddr_pmu_offline_cpu); + "perf/phytium/ddrpmu:online", + phytium_ddr_pmu_online_cpu, phytium_ddr_pmu_offline_cpu); if (phytium_ddr_pmu_hp_state < 0) { pr_err("DDR PMU: setup hotplug, phytium_ddr_pmu_hp_state = %d\n", phytium_ddr_pmu_hp_state); -- Gitee From 15598fffbd7cd623c3613b5fe86beb014f458749 Mon Sep 17 00:00:00 2001 From: huxianghua Date: Tue, 26 Mar 2024 17:08:32 +0800 Subject: [PATCH 078/154] drivers/perf: phytium: Perf Optimization for SoC PCIe controller PMU Perf optimizing offline and online function for ltp-cpuhotplug test. Mainline: Open-Source Signed-off-by: Hu Xianghua Signed-off-by: Wang Yinfeng Change-Id: I47622a09436680afafcb36f9a115cead1e6913ef --- drivers/perf/phytium/phytium_pcie_pmu.c | 48 +++++++++++++++++++------ 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index 03463f4425..e1dfbf11e6 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -723,8 +723,7 @@ static int phytium_pcie_pmu_dev_probe(struct platform_device *pdev, if (ret) return ret; pcie_pmu->dev = &pdev->dev; - pcie_pmu->on_cpu = raw_smp_processor_id(); - WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(pcie_pmu->on_cpu))); + pcie_pmu->on_cpu = -1; pcie_pmu->ctrler_id = -1; return 0; @@ -746,7 +745,7 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) if (ret) return ret; - ret = cpuhp_state_add_instance_nocalls( + ret = cpuhp_state_add_instance( phytium_pcie_pmu_hp_state, &pcie_pmu->node); if (ret) { dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); @@ -811,6 +810,29 @@ static struct platform_driver phytium_pcie_pmu_driver = { .remove = phytium_pcie_pmu_remove, }; +int phytium_pcie_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct phytium_pcie_pmu *pcie_pmu = + hlist_entry_safe(node, struct phytium_pcie_pmu, node); + + if (!cpumask_test_cpu(cpu, cpumask_of_node(pcie_pmu->die_id))) + return 0; + + if (pcie_pmu->on_cpu != -1) { + if (!cpumask_test_cpu(pcie_pmu->on_cpu, cpumask_of_node(pcie_pmu->die_id))) { + perf_pmu_migrate_context(&pcie_pmu->pmu, pcie_pmu->on_cpu, cpu); + pcie_pmu->on_cpu = cpu; + WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(cpu))); + } + return 0; + } + + pcie_pmu->on_cpu = cpu; + WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(cpu))); + + return 0; +} + int phytium_pcie_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) { struct phytium_pcie_pmu *pcie_pmu = @@ -821,14 +843,18 @@ int phytium_pcie_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) if (pcie_pmu->on_cpu != cpu) return 0; - cpumask_and(&available_cpus, - cpumask_of_node(pcie_pmu->die_id), cpu_online_mask); + if (cpumask_and(&available_cpus, cpumask_of_node(pcie_pmu->die_id), cpu_online_mask) && + cpumask_andnot(&available_cpus, &available_cpus, cpumask_of(cpu))) + target = cpumask_last(&available_cpus); + else { + cpumask_andnot(&available_cpus, cpu_online_mask, cpumask_of(cpu)); + target = cpumask_last(&available_cpus); + } - target = cpumask_any_but(&available_cpus, cpu); if (target >= nr_cpu_ids) { - target = cpumask_any_but(cpu_online_mask, cpu); - if (target >= nr_cpu_ids) - return 0; + dev_err(pcie_pmu->dev, "offline cpu%d with no target to migrate.\n", + cpu); + return 0; } perf_pmu_migrate_context(&pcie_pmu->pmu, cpu, target); @@ -844,8 +870,8 @@ static int __init phytium_pcie_pmu_module_init(void) phytium_pcie_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, - "perf/phytium/pciepmu:offline", NULL, - phytium_pcie_pmu_offline_cpu); + "perf/phytium/pciepmu:online", + phytium_pcie_pmu_online_cpu, phytium_pcie_pmu_offline_cpu); if (phytium_pcie_pmu_hp_state < 0) { pr_err("PCIE PMU: setup hotplug, ret = %d\n", phytium_pcie_pmu_hp_state); -- Gitee From 371ca5c2124199c99796ad21f32a274b95880092 Mon Sep 17 00:00:00 2001 From: Tan Rui Date: Fri, 10 Jan 2025 10:48:54 +0800 Subject: [PATCH 079/154] drivers/perf:phytium: Add version information to Phytium SoC DDR PMU Add version information to the driver for Phytium SoC DDR PMU to facilitate management. Mainline: Open-Source Signed-off-by: Tan Rui Signed-off-by: Hu Xianghua Signed-off-by: Wang Yinfeng Change-Id: I1fc8b66c45001c0175ce6142b0170bec9d1494bf --- drivers/perf/phytium/phytium_ddr_pmu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index a31bf29c87..13b91b95d4 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -30,6 +30,8 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt +#define DDR_PERF_DRIVER_VERSION "1.2.1" + #define PHYTIUM_DDR_MAX_COUNTERS 8 #define DDR_START_TIMER 0x000 @@ -750,4 +752,5 @@ module_exit(phytium_ddr_pmu_module_exit); MODULE_DESCRIPTION("Phytium DDR PMU driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(DDR_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); -- Gitee From 94bfce1fc9c656c523bc4dab5103aa42f09f1c90 Mon Sep 17 00:00:00 2001 From: Tan Rui Date: Fri, 10 Jan 2025 10:50:39 +0800 Subject: [PATCH 080/154] drivers/perf:phytium: Add version information to Phytium SoC PCIe PMU Add version information to the driver for Phytium SoC PCIe PMU to facilitate management. Mainline: Open-Source Signed-off-by: Tan Rui Signed-off-by: Hu Xianghua Signed-off-by: Wang Yinfeng Change-Id: I4897f92cc9dc77b9e0983384d792304b8cc408b4 --- drivers/perf/phytium/phytium_pcie_pmu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index e1dfbf11e6..9b73aded43 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -30,6 +30,8 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_pcie_pmu: " fmt +#define PCIE_PERF_DRIVER_VERSION "1.2.1" + #define PHYTIUM_PCIE_MAX_COUNTERS 18 #define PCIE_START_TIMER 0x000 @@ -896,4 +898,5 @@ module_exit(phytium_pcie_pmu_module_exit); MODULE_DESCRIPTION("Phytium PCIe PMU driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(PCIE_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); -- Gitee From 6d2206c6c17cec6bde310b9d0fab09401e1bc18d Mon Sep 17 00:00:00 2001 From: Tan Rui Date: Fri, 10 Jan 2025 11:04:30 +0800 Subject: [PATCH 081/154] drivers/perf: phytium: Add DDR PMU Support for PS240XX Add a new HID to differentiate between PS230XX and PS240XX DDR PMU and add support for PS240XX platforms. Mainline: Open-Source Signed-off-by: Tan Rui Signed-off-by: Hu Xianghua Signed-off-by: Wang Yinfeng Change-Id: I0b1642fb47b3f586a1f9388bfc476c79fe644c03 --- drivers/perf/phytium/phytium_ddr_pmu.c | 232 +++++++++++++------------ 1 file changed, 123 insertions(+), 109 deletions(-) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 13b91b95d4..4c3475ddf9 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -30,34 +31,40 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt -#define DDR_PERF_DRIVER_VERSION "1.2.1" - #define PHYTIUM_DDR_MAX_COUNTERS 8 - -#define DDR_START_TIMER 0x000 -#define DDR_STOP_TIMER 0x004 -#define DDR_CLEAR_EVENT 0x008 -#define DDR_SET_TIMER_L 0x00c -#define DDR_SET_TIMER_H 0x010 -#define DDR_TRIG_MODE 0x014 -#define DDR_NOW_STATE 0x0e0 -#define DDR_EVENT_CYCLES 0x0e4 -#define DDR_TPOINT_END_L 0x0e4 -#define DDR_TPOINT_END_H 0x0e8 -#define DDR_STATE_STOP 0x0ec -#define DDR_EVENT_RXREQ 0x100 -#define DDR_EVENT_RXDAT 0x104 -#define DDR_EVENT_TXDAT 0x108 -#define DDR_EVENT_RXREQ_RNS 0x10c -#define DDR_EVENT_RXREQ_WNSP 0x110 -#define DDR_EVENT_RXREQ_WNSF 0x114 -#define DDR_EVENT_BANDWIDTH 0x200 -#define DDR_W_DATA_BASE 0x200 -#define DDR_CLK_FRE 0xe00 -#define DDR_DATA_WIDTH 0xe04 +#define DDR_PERF_DRIVER_VERSION "1.3.0" + +#define DDR_START_TIMER 0x000 +#define DDR_STOP_TIMER 0x004 +#define DDR_CLEAR_EVENT 0x008 +#define DDR_SET_TIMER_L 0x00c +#define DDR_SET_TIMER_H 0x010 +#define DDR_TRIG_MODE 0x014 +#define DDR_NOW_STATE 0x0e0 +#define DDR_EVENT_CYCLES 0x0e4 +#define DDR_TPOINT_END_L 0x0e4 +#define DDR_TPOINT_END_H 0x0e8 +#define DDR_STATE_STOP 0x0ec +#define DDR_EVENT_RXREQ 0x100 +#define DDR_EVENT_RXDAT 0x104 +#define DDR_EVENT_TXDAT 0x108 +#define DDR_EVENT_RXREQ_RNS 0x10c +#define DDR_EVENT_RXREQ_WNSP 0x110 +#define DDR_EVENT_RXREQ_WNSF 0x114 +#define DDR_EVENT_BANDWIDTH 0x200 +#define DDR_W_DATA_BASE 0x200 +#define DDR_CLK_FRE 0xe00 +#define DDR_DATA_WIDTH 0xe04 + +#define DDR_PMU_OFL_STOP_TYPE_VAL 0x10 #define to_phytium_ddr_pmu(p) (container_of(p, struct phytium_ddr_pmu, pmu)) +enum { + DDRV1P0 = 0x01, + DDRV1P5 = 0x02, +}; + static int phytium_ddr_pmu_hp_state; struct phytium_ddr_pmu_hwevents { @@ -68,15 +75,17 @@ struct phytium_ddr_pmu_hwevents { struct phytium_ddr_pmu { struct device *dev; void __iomem *base; - void __iomem *csr_base; + void __iomem *cfg_base; + void __iomem *irq_reg; struct pmu pmu; struct phytium_ddr_pmu_hwevents pmu_events; u32 die_id; u32 ddr_id; u32 pmu_id; - int bit_idx; + int irq_bit; int on_cpu; int irq; + int ver; struct hlist_node node; }; @@ -90,8 +99,8 @@ static const u32 ddr_counter_reg_offset[] = { }; ssize_t phytium_ddr_pmu_format_sysfs_show(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { struct dev_ext_attribute *eattr; @@ -101,8 +110,8 @@ ssize_t phytium_ddr_pmu_format_sysfs_show(struct device *dev, } ssize_t phytium_ddr_pmu_event_sysfs_show(struct device *dev, - struct device_attribute *attr, - char *page) + struct device_attribute *attr, + char *page) { struct dev_ext_attribute *eattr; @@ -120,22 +129,21 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, return cpumap_print_to_pagebuf(true, buf, cpumask_of(ddr_pmu->on_cpu)); } -#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ - (&((struct dev_ext_attribute[]){ \ +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ .attr.attr) -#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ - PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ +#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ (void *)_config) -#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ - PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ - (unsigned long)_config) +#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ + (unsigned long)_config) static struct attribute *phytium_ddr_pmu_format_attr[] = { PHYTIUM_DDR_PMU_FORMAT_ATTR(event, "config:0-2"), - PHYTIUM_DDR_PMU_FORMAT_ATTR(timer, "config1:0-31"), NULL, }; @@ -179,11 +187,6 @@ static const struct attribute_group *phytium_ddr_pmu_attr_groups[] = { NULL, }; -static u32 phytium_ddr_pmu_get_event_timer(struct perf_event *event) -{ - return FIELD_GET(GENMASK(31, 0), event->attr.config1); -} - static u64 phytium_ddr_pmu_read_counter(struct phytium_ddr_pmu *ddr_pmu, struct hw_perf_event *hwc) { @@ -223,18 +226,24 @@ static void phytium_ddr_pmu_enable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - val = readl(ddr_pmu->csr_base); + if (ddr_pmu->ver == DDRV1P5) + return; + + val = readl(ddr_pmu->cfg_base); val |= 0xF; - writel(val, ddr_pmu->csr_base); + writel(val, ddr_pmu->cfg_base); } static void phytium_ddr_pmu_disable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - val = readl(ddr_pmu->csr_base); + if (ddr_pmu->ver == DDRV1P5) + return; + + val = readl(ddr_pmu->cfg_base); val &= ~(0xF); - writel(val, ddr_pmu->csr_base); + writel(val, ddr_pmu->cfg_base); } static void phytium_ddr_pmu_clear_all_counters(struct phytium_ddr_pmu *ddr_pmu) @@ -252,29 +261,6 @@ static void phytium_ddr_pmu_stop_all_counters(struct phytium_ddr_pmu *ddr_pmu) writel(0x1, ddr_pmu->base + DDR_STOP_TIMER); } -static void phytium_ddr_pmu_set_timer(struct phytium_ddr_pmu *ddr_pmu, - u32 th_val) -{ - u32 val; - - val = readl(ddr_pmu->base + DDR_SET_TIMER_L); - val = readl(ddr_pmu->base + DDR_SET_TIMER_H); - - writel(th_val, ddr_pmu->base + DDR_SET_TIMER_L); - writel(0, ddr_pmu->base + DDR_SET_TIMER_H); -} - -static void phytium_ddr_pmu_reset_timer(struct phytium_ddr_pmu *ddr_pmu) -{ - u32 val; - - val = readl(ddr_pmu->base + DDR_SET_TIMER_L); - val = readl(ddr_pmu->base + DDR_SET_TIMER_H); - - writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_L); - writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_H); -} - static unsigned long phytium_ddr_pmu_get_stop_state(struct phytium_ddr_pmu *ddr_pmu) { @@ -289,7 +275,7 @@ phytium_ddr_pmu_get_irq_flag(struct phytium_ddr_pmu *ddr_pmu) { unsigned long val; - val = (unsigned long)readl(ddr_pmu->csr_base + 4); + val = (unsigned long)readl(ddr_pmu->irq_reg); return val; } @@ -324,7 +310,6 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct phytium_ddr_pmu *ddr_pmu; - u32 event_timer; if (event->attr.type != event->pmu->type) return -ENOENT; @@ -345,10 +330,6 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) if (ddr_pmu->on_cpu == -1) return -EINVAL; - event_timer = phytium_ddr_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_ddr_pmu_set_timer(ddr_pmu, event_timer); - hwc->idx = -1; hwc->config_base = event->attr.config; @@ -407,17 +388,10 @@ void phytium_ddr_pmu_event_del(struct perf_event *event, int flags) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; - unsigned long val; - u32 event_timer; phytium_ddr_pmu_event_stop(event, PERF_EF_UPDATE); - val = phytium_ddr_pmu_get_irq_flag(ddr_pmu); - val = phytium_ddr_pmu_get_stop_state(ddr_pmu); - phytium_ddr_pmu_unmark_event(ddr_pmu, hwc->idx); - event_timer = phytium_ddr_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_ddr_pmu_reset_timer(ddr_pmu); + phytium_ddr_pmu_unmark_event(ddr_pmu, hwc->idx); perf_event_update_userpage(event); ddr_pmu->pmu_events.hw_events[hwc->idx] = NULL; @@ -445,10 +419,15 @@ void phytium_ddr_pmu_disable(struct pmu *pmu) phytium_ddr_pmu_stop_all_counters(ddr_pmu); } +void phytium_ddr_pmu_reset(struct phytium_ddr_pmu *ddr_pmu) +{ + phytium_ddr_pmu_disable_clk(ddr_pmu); + phytium_ddr_pmu_clear_all_counters(ddr_pmu); +} + static const struct acpi_device_id phytium_ddr_pmu_acpi_match[] = { - { - "PHYT0043", - }, + { "PHYT0043", }, + { "PHYT0067", }, {}, }; MODULE_DEVICE_TABLE(acpi, phytium_ddr_pmu_acpi_match); @@ -458,14 +437,13 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) struct phytium_ddr_pmu *ddr_pmu = dev_id; struct perf_event *event; unsigned long overflown, stop_state; - int idx; unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; - + int idx; int event_added = bitmap_weight(used_mask, PHYTIUM_DDR_MAX_COUNTERS); overflown = phytium_ddr_pmu_get_irq_flag(ddr_pmu); - if (!test_bit(ddr_pmu->bit_idx, &overflown)) + if (!test_bit(ddr_pmu->irq_bit, &overflown)) return IRQ_NONE; stop_state = phytium_ddr_pmu_get_stop_state(ddr_pmu); @@ -477,8 +455,10 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) continue; phytium_ddr_pmu_event_update(event); } + phytium_ddr_pmu_clear_all_counters(ddr_pmu); - phytium_ddr_pmu_start_all_counters(ddr_pmu); + if ((stop_state & DDR_PMU_OFL_STOP_TYPE_VAL) == 0) + phytium_ddr_pmu_start_all_counters(ddr_pmu); return IRQ_HANDLED; } @@ -491,6 +471,24 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) return IRQ_NONE; } +static int phytium_ddr_pmu_version(struct platform_device *pdev, + struct phytium_ddr_pmu *ddr_pmu) +{ + struct acpi_device *acpi_dev; + + acpi_dev = ACPI_COMPANION(&pdev->dev); + if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0043")) { + ddr_pmu->ver = DDRV1P0; + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0067")) { + ddr_pmu->ver = DDRV1P5; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + } + + return 0; +} + static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, struct platform_device *pdev) { @@ -518,7 +516,7 @@ static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, static int phytium_ddr_pmu_init_data(struct platform_device *pdev, struct phytium_ddr_pmu *ddr_pmu) { - struct resource *res, *clkres; + struct resource *res, *clkres, *irqres; if (device_property_read_u32(&pdev->dev, "phytium,die-id", &ddr_pmu->die_id)) { @@ -538,7 +536,7 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - ddr_pmu->bit_idx = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; + ddr_pmu->irq_bit = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ddr_pmu->base = devm_ioremap_resource(&pdev->dev, res); @@ -554,15 +552,29 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, dev_err(&pdev->dev, "failed for get ddr_pmu clk resource.\n"); return -EINVAL; } + ddr_pmu->cfg_base = devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); + if (IS_ERR(ddr_pmu->cfg_base)) { + dev_err(&pdev->dev, "ioremap failed for ddr_pmu clk resource\n"); + return PTR_ERR(ddr_pmu->cfg_base); + } - ddr_pmu->csr_base = - devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); - if (IS_ERR(ddr_pmu->csr_base)) { - dev_err(&pdev->dev, - "ioremap failed for ddr_pmu csr resource\n"); - return PTR_ERR(ddr_pmu->csr_base); + if (ddr_pmu->ver == DDRV1P5) { + irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!irqres) { + dev_err(&pdev->dev, "failed for get ddr_pmu irq resource.\n"); + return -EINVAL; + } + ddr_pmu->irq_reg = devm_ioremap(&pdev->dev, irqres->start, resource_size(irqres)); + if (IS_ERR(ddr_pmu->irq_reg)) { + dev_err(&pdev->dev, "ioremap failed for ddr_pmu irq resource\n"); + return PTR_ERR(ddr_pmu->irq_reg); + } + } else { + ddr_pmu->irq_reg = ddr_pmu->cfg_base + 0x4; } + phytium_ddr_pmu_reset(ddr_pmu); + return 0; } @@ -571,6 +583,10 @@ static int phytium_ddr_pmu_dev_probe(struct platform_device *pdev, { int ret; + ret = phytium_ddr_pmu_version(pdev, ddr_pmu); + if (ret) + return ret; + ret = phytium_ddr_pmu_init_data(pdev, ddr_pmu); if (ret) return ret; @@ -601,8 +617,8 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) if (ret) return ret; - ret = cpuhp_state_add_instance( - phytium_ddr_pmu_hp_state, &ddr_pmu->node); + ret = cpuhp_state_add_instance(phytium_ddr_pmu_hp_state, + &ddr_pmu->node); if (ret) { dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); return ret; @@ -624,22 +640,19 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) .stop = phytium_ddr_pmu_event_stop, .read = phytium_ddr_pmu_event_update, .attr_groups = phytium_ddr_pmu_attr_groups, - .capabilities = PERF_PMU_CAP_NO_EXCLUDE, }; ret = perf_pmu_register(&ddr_pmu->pmu, name, -1); if (ret) { dev_err(ddr_pmu->dev, "DDR PMU register failed!\n"); - cpuhp_state_remove_instance_nocalls( - phytium_ddr_pmu_hp_state, + cpuhp_state_remove_instance_nocalls(phytium_ddr_pmu_hp_state, &ddr_pmu->node); } phytium_ddr_pmu_enable_clk(ddr_pmu); - pr_info("Phytium DDR PMU: "); - pr_info(" die_id = %d ddr_id = %d pmu_id = %d.\n", ddr_pmu->die_id, - ddr_pmu->ddr_id, ddr_pmu->pmu_id); + pr_info("die%d_ddr%d_pmu%d on cpu%d.\n", ddr_pmu->die_id, + ddr_pmu->ddr_id, ddr_pmu->pmu_id, ddr_pmu->on_cpu); return ret; } @@ -651,8 +664,8 @@ static int phytium_ddr_pmu_remove(struct platform_device *pdev) phytium_ddr_pmu_disable_clk(ddr_pmu); perf_pmu_unregister(&ddr_pmu->pmu); - cpuhp_state_remove_instance_nocalls( - phytium_ddr_pmu_hp_state, &ddr_pmu->node); + cpuhp_state_remove_instance_nocalls(phytium_ddr_pmu_hp_state, + &ddr_pmu->node); return 0; } @@ -754,3 +767,4 @@ MODULE_DESCRIPTION("Phytium DDR PMU driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DDR_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); +MODULE_AUTHOR("Tan Rui "); -- Gitee From 4b1ba52d49ecb44c617e4d8929e60ad5ecaa1d37 Mon Sep 17 00:00:00 2001 From: Tan Rui Date: Fri, 10 Jan 2025 11:05:58 +0800 Subject: [PATCH 082/154] drivers/perf:phytium: Add PCIe PMU Support for PS240XX Add a new HID to differentiate between PS230XX and PS240XX PCIe PMU and add support for PS240XX platforms. Mainline: Open-Source Signed-off-by: Tan Rui Signed-off-by: Hu Xianghua Signed-off-by: Wang Yinfeng Change-Id: Ifb344d44b877232b4e7e25784036992272a6a12a --- drivers/perf/phytium/phytium_pcie_pmu.c | 413 ++++++++++++++---------- 1 file changed, 240 insertions(+), 173 deletions(-) diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index 9b73aded43..7f3294d168 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -30,52 +31,54 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_pcie_pmu: " fmt -#define PCIE_PERF_DRIVER_VERSION "1.2.1" +#define PCIE_PERF_DRIVER_VERSION "1.3.0" #define PHYTIUM_PCIE_MAX_COUNTERS 18 -#define PCIE_START_TIMER 0x000 -#define PCIE_STOP_TIMER 0x004 -#define PCIE_CLEAR_EVENT 0x008 -#define PCIE_SET_TIMER_L 0x00c -#define PCIE_SET_TIMER_H 0x010 -#define PCIE_TRIG_MODE 0x014 - -#define PCIE_NOW_STATE 0x0e0 -#define PCIE_EVENT_CYCLES 0x0e4 -#define PCIE_TPOINT_END_L 0x0e4 -#define PCIE_TPOINT_END_H 0x0e8 -#define PCIE_STATE_STOP 0x0ec - -#define PCIE_EVENT_AW 0x100 -#define PCIE_EVENT_W_LAST 0x104 -#define PCIE_EVENT_B 0x108 -#define PCIE_EVENT_AR 0x10c -#define PCIE_EVENT_R_LAST 0x110 -#define PCIE_EVENT_R_FULL 0x114 -#define PCIE_EVENT_R_ERR 0x118 -#define PCIE_EVENT_W_ERR 0x11c -#define PCIE_EVENT_DELAY_RD 0x120 -#define PCIE_EVENT_DELAY_WR 0x124 -#define PCIE_EVENT_RD_MAX 0x128 -#define PCIE_EVENT_RD_MIN 0x12c -#define PCIE_EVENT_WR_MAX 0x130 -#define PCIE_EVENT_WR_MIN 0x134 - -#define PCIE_EVENT_W_DATA 0x200 -#define PCIE_W_DATA_BASE 0x200 - -#define PCIE_EVENT_RDELAY_TIME 0x300 -#define PCIE_RDELAY_TIME_BASE 0x300 - -#define PCIE_EVENT_WDELAY_TIME 0x700 -#define PCIE_WDELAY_TIME_BASE 0x700 - -#define PCIE_CLK_FRE 0xe00 -#define PCIE_DATA_WIDTH 0xe04 +#define PCIE_START_TIMER 0x000 +#define PCIE_STOP_TIMER 0x004 +#define PCIE_CLEAR_EVENT 0x008 + +#define PCIE_EVENT_CYCLES 0x0e4 +#define PCIE_TPOINT_END_L 0x0e4 +#define PCIE_TPOINT_END_H 0x0e8 +#define PCIE_STATE_STOP 0x0ec + +#define PCIE_EVENT_AW 0x100 +#define PCIE_EVENT_W_LAST 0x104 +#define PCIE_EVENT_B 0x108 +#define PCIE_EVENT_AR 0x10c +#define PCIE_EVENT_R_LAST 0x110 +#define PCIE_EVENT_R_FULL 0x114 +#define PCIE_EVENT_R_ERR 0x118 +#define PCIE_EVENT_W_ERR 0x11c +#define PCIE_EVENT_DELAY_RD 0x120 +#define PCIE_EVENT_DELAY_WR 0x124 +#define PCIE_EVENT_RD_MAX 0x128 +#define PCIE_EVENT_RD_MIN 0x12c +#define PCIE_EVENT_WR_MAX 0x130 +#define PCIE_EVENT_WR_MIN 0x134 + +#define PCIE_EVENT_W_DATA 0x200 +#define PCIE_W_DATA_BASE 0x200 + +#define PCIE_EVENT_RDELAY_TIME 0x300 +#define PCIE_RDELAY_TIME_BASE 0x300 + +#define PCIE_EVENT_WDELAY_TIME 0x700 +#define PCIE_WDELAY_TIME_BASE 0x700 + +#define PCIE_DATA_WIDTH 0xe04 +#define PCIE_PMU_OFL_STOP_TYPE_VAL 0x10 #define to_phytium_pcie_pmu(p) (container_of(p, struct phytium_pcie_pmu, pmu)) +enum { + PCIEV1P0 = 0x1, + PCIEV1P5 = 0x2, +}; + + static int phytium_pcie_pmu_hp_state; struct phytium_pcie_pmu_hwevents { @@ -86,17 +89,20 @@ struct phytium_pcie_pmu_hwevents { struct phytium_pcie_pmu { struct device *dev; void __iomem *base; - void __iomem *csr_base; + void __iomem *cfg_base; void __iomem *irq_reg; struct pmu pmu; struct phytium_pcie_pmu_hwevents pmu_events; u32 die_id; + u32 pcie_id; u32 pmu_id; int on_cpu; int irq; + int irq_bit; struct hlist_node node; int ctrler_id; int real_ctrler; + int ver; u32 clk_bits; }; @@ -160,7 +166,6 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, static struct attribute *phytium_pcie_pmu_format_attr[] = { PHYTIUM_PCIE_PMU_FORMAT_ATTR(event, "config:0-4"), PHYTIUM_PCIE_PMU_FORMAT_ATTR(ctrler, "config:8-10"), - PHYTIUM_PCIE_PMU_FORMAT_ATTR(timer, "config1:0-31"), NULL, }; @@ -219,11 +224,6 @@ static u32 phytium_pcie_pmu_get_event_ctrler(struct perf_event *event) return FIELD_GET(GENMASK(10, 8), event->attr.config); } -static u32 phytium_pcie_pmu_get_event_timer(struct perf_event *event) -{ - return FIELD_GET(GENMASK(31, 0), event->attr.config1); -} - static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc) { @@ -233,12 +233,16 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, u64 val64 = 0; int i; u32 counter_offset = pcie_counter_reg_offset[idx]; + u32 rdelay_num = 127; if (!EVENT_VALID(idx)) { dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); return 0; } + if (pcie_pmu->ver == PCIEV1P5 && pcie_pmu->pmu_id == 3) + rdelay_num = 63; + switch (idx) { case 0: cycle_l = readl(pcie_pmu->base + counter_offset); @@ -253,7 +257,7 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, } break; case 16: - for (i = 0; i <= 127; i = i + 2) { + for (i = 0; i <= rdelay_num; i = i + 2) { rdelay_l = readl(pcie_pmu->base + counter_offset + 4 * i); rdelay_h = readl(pcie_pmu->base + counter_offset + @@ -281,38 +285,40 @@ static void phytium_pcie_pmu_enable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; - val = readl(pcie_pmu->csr_base); + val = readl(pcie_pmu->cfg_base); val |= (pcie_pmu->clk_bits); - writel(val, pcie_pmu->csr_base); + writel(val, pcie_pmu->cfg_base); } static void phytium_pcie_pmu_disable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; - val = readl(pcie_pmu->csr_base); + val = readl(pcie_pmu->cfg_base); val &= ~(pcie_pmu->clk_bits); - writel(val, pcie_pmu->csr_base); + writel(val, pcie_pmu->cfg_base); } static void phytium_pcie_pmu_select_ctrler(struct phytium_pcie_pmu *pcie_pmu) { - u32 val, offset = 0; - - if (pcie_pmu->pmu_id != 2) - offset = 0xc; + u32 val, offset; + u32 mask = 0xfffffffc; - val = readl(pcie_pmu->csr_base + offset); - - if (pcie_pmu->pmu_id == 2) { - val &= 0xffffffcf; - val |= pcie_pmu->real_ctrler; + if (pcie_pmu->ver == PCIEV1P0) { + if (pcie_pmu->pmu_id == 2) { + mask = 0xffffffcf; + offset = 0x0; + } else + offset = 0xc; } else { - val &= 0xfffffffc; - val |= pcie_pmu->real_ctrler; + offset = 0x170; } - writel(val, pcie_pmu->csr_base + offset); + val = readl(pcie_pmu->cfg_base + offset); + val &= mask; + val |= pcie_pmu->real_ctrler; + writel(val, pcie_pmu->cfg_base + offset); + } static void @@ -333,29 +339,6 @@ phytium_pcie_pmu_stop_all_counters(struct phytium_pcie_pmu *pcie_pmu) writel(0x1, pcie_pmu->base + PCIE_STOP_TIMER); } -static void phytium_pcie_pmu_set_timer(struct phytium_pcie_pmu *pcie_pmu, - u32 th_val) -{ - u32 val; - - val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); - val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); - - writel(th_val, pcie_pmu->base + PCIE_SET_TIMER_L); - writel(0, pcie_pmu->base + PCIE_SET_TIMER_H); -} - -static void phytium_pcie_pmu_reset_timer(struct phytium_pcie_pmu *pcie_pmu) -{ - u32 val; - - val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); - val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); - - writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_L); - writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_H); -} - static unsigned long phytium_pcie_pmu_get_stop_state(struct phytium_pcie_pmu *pcie_pmu) { @@ -405,7 +388,7 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct phytium_pcie_pmu *pcie_pmu; - u32 event_ctrler, event_timer; + u32 event_ctrler; if (event->attr.type != event->pmu->type) return -ENOENT; @@ -426,57 +409,83 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) if (pcie_pmu->on_cpu == -1) return -EINVAL; - event_timer = phytium_pcie_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_pcie_pmu_set_timer(pcie_pmu, event_timer); - - event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); - switch (pcie_pmu->pmu_id) { - case 0: - if (event_ctrler != 0) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu0!\n", - event_ctrler); - return -EINVAL; + if (pcie_pmu->ver == PCIEV1P5) { + event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); + if (pcie_pmu->pmu_id == 2) { + if (event_ctrler == 0) + event_ctrler = 2; + else if ((event_ctrler < 2) || (event_ctrler > 3)) { + dev_warn(pcie_pmu->dev, "Wrong ctrler id(%d) for pcie-pmu2!\n", + event_ctrler); + return -EINVAL; + } + if (pcie_pmu->ctrler_id != event_ctrler) { + pcie_pmu->ctrler_id = event_ctrler; + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + phytium_pcie_pmu_select_ctrler(pcie_pmu); + } + } else { + if (event_ctrler != 0) { + dev_warn(pcie_pmu->dev, "Don't need to set ctrler id(%d) for pcie-pmu%d!\n", + event_ctrler, pcie_pmu->pmu_id); + return -EINVAL; + } + pcie_pmu->ctrler_id = pcie_pmu->pmu_id; + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; } - break; - case 1: - if ((event_ctrler < 1) || (event_ctrler > 3)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu1!\n", - event_ctrler); + } else { + event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); + switch (pcie_pmu->pmu_id) { + case 0: + if (event_ctrler != 0) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu0!\n", + event_ctrler); + return -EINVAL; + } + break; + case 1: + if (event_ctrler == 0) + event_ctrler = 1; + else if ((event_ctrler < 1) || (event_ctrler > 3)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu1!\n", + event_ctrler); + return -EINVAL; + } + break; + case 2: + if (event_ctrler == 0) + event_ctrler = 4; + else if ((event_ctrler < 4) || (event_ctrler > 7)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu2!\n", + event_ctrler); + return -EINVAL; + } + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); return -EINVAL; } - break; - case 2: - if ((event_ctrler < 4) || (event_ctrler > 7)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu2!\n", - event_ctrler); + + pcie_pmu->ctrler_id = event_ctrler; + switch (pcie_pmu->pmu_id) { + case 0: + case 1: + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + break; + case 2: + pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); return -EINVAL; } - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - return -EINVAL; - } - - pcie_pmu->ctrler_id = event_ctrler; - switch (pcie_pmu->pmu_id) { - case 0: - case 1: - pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; - break; - case 2: - pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - return -EINVAL; + phytium_pcie_pmu_select_ctrler(pcie_pmu); } - phytium_pcie_pmu_select_ctrler(pcie_pmu); hwc->idx = -1; hwc->config_base = event->attr.config; @@ -535,17 +544,10 @@ void phytium_pcie_pmu_event_del(struct perf_event *event, int flags) { struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; - unsigned long val; - u32 event_timer; phytium_pcie_pmu_event_stop(event, PERF_EF_UPDATE); - val = phytium_pcie_pmu_get_irq_flag(pcie_pmu); - val = phytium_pcie_pmu_get_stop_state(pcie_pmu); - phytium_pcie_pmu_unmark_event(pcie_pmu, hwc->idx); - event_timer = phytium_pcie_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_pcie_pmu_reset_timer(pcie_pmu); + phytium_pcie_pmu_unmark_event(pcie_pmu, hwc->idx); perf_event_update_userpage(event); pcie_pmu->pmu_events.hw_events[hwc->idx] = NULL; @@ -573,10 +575,15 @@ void phytium_pcie_pmu_disable(struct pmu *pmu) phytium_pcie_pmu_stop_all_counters(pcie_pmu); } +void phytium_pcie_pmu_reset(struct phytium_pcie_pmu *pcie_pmu) +{ + phytium_pcie_pmu_disable_clk(pcie_pmu); + phytium_pcie_pmu_clear_all_counters(pcie_pmu); +} + static const struct acpi_device_id phytium_pcie_pmu_acpi_match[] = { - { - "PHYT0044", - }, + { "PHYT0044", }, + { "PHYT0068", }, {}, }; MODULE_DEVICE_TABLE(acpi, phytium_pcie_pmu_acpi_match); @@ -592,7 +599,7 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) overflown = phytium_pcie_pmu_get_irq_flag(pcie_pmu); - if (!test_bit(pcie_pmu->pmu_id + 4, &overflown)) + if (!test_bit(pcie_pmu->irq_bit, &overflown)) return IRQ_NONE; stop_state = phytium_pcie_pmu_get_stop_state(pcie_pmu); @@ -605,7 +612,8 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) phytium_pcie_pmu_event_update(event); } phytium_pcie_pmu_clear_all_counters(pcie_pmu); - phytium_pcie_pmu_start_all_counters(pcie_pmu); + if ((stop_state & PCIE_PMU_OFL_STOP_TYPE_VAL) == 0) + phytium_pcie_pmu_start_all_counters(pcie_pmu); return IRQ_HANDLED; } @@ -616,6 +624,25 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) return IRQ_NONE; } +static int phytium_pcie_pmu_version(struct platform_device *pdev, + struct phytium_pcie_pmu *pcie_pmu) +{ + struct acpi_device *acpi_dev; + + acpi_dev = ACPI_COMPANION(&pdev->dev); + if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0044")) { + pcie_pmu->ver = PCIEV1P0; + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0068")) { + pcie_pmu->ver = PCIEV1P5; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + + } + + return 0; +} + static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, struct platform_device *pdev) { @@ -641,7 +668,7 @@ static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, } static int phytium_pcie_pmu_init_data(struct platform_device *pdev, - struct phytium_pcie_pmu *pcie_pmu) + struct phytium_pcie_pmu *pcie_pmu) { struct resource *res, *clkres, *irqres; @@ -657,20 +684,46 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - switch (pcie_pmu->pmu_id) { - case 0: - pcie_pmu->clk_bits = 0x1; - break; - case 1: - pcie_pmu->clk_bits = 0xe; - break; - case 2: - pcie_pmu->clk_bits = 0xf; - break; - default: - dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - break; + if (pcie_pmu->ver == PCIEV1P0) { + switch (pcie_pmu->pmu_id) { + case 0: + pcie_pmu->clk_bits = 0x1; + break; + case 1: + pcie_pmu->clk_bits = 0xe; + break; + case 2: + pcie_pmu->clk_bits = 0xf; + break; + default: + dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", pcie_pmu->pmu_id); + break; + } + + pcie_pmu->irq_bit = pcie_pmu->pmu_id + 4; + } else { + if (device_property_read_u32(&pdev->dev, "phytium,pcie-id", &pcie_pmu->pcie_id)) { + dev_err(&pdev->dev, "Can not read phytium,pcie-id!\n"); + return -EINVAL; + } + + switch (pcie_pmu->pmu_id) { + case 0: + case 3: + pcie_pmu->clk_bits = 0x1; + break; + case 1: + pcie_pmu->clk_bits = 0x2; + break; + case 2: + pcie_pmu->clk_bits = 0xc; + break; + default: + dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", pcie_pmu->pmu_id); + break; + } + + pcie_pmu->irq_bit = pcie_pmu->pcie_id * 4 + pcie_pmu->pmu_id + 16; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -686,12 +739,12 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - pcie_pmu->csr_base = + pcie_pmu->cfg_base = devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); - if (IS_ERR(pcie_pmu->csr_base)) { + if (IS_ERR(pcie_pmu->cfg_base)) { dev_err(&pdev->dev, "ioremap failed for pcie_pmu csr resource\n"); - return PTR_ERR(pcie_pmu->csr_base); + return PTR_ERR(pcie_pmu->cfg_base); } irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); @@ -709,14 +762,20 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return PTR_ERR(pcie_pmu->irq_reg); } + phytium_pcie_pmu_reset(pcie_pmu); + return 0; } static int phytium_pcie_pmu_dev_probe(struct platform_device *pdev, - struct phytium_pcie_pmu *pcie_pmu) + struct phytium_pcie_pmu *pcie_pmu) { int ret; + ret = phytium_pcie_pmu_version(pdev, pcie_pmu); + if (ret) + return ret; + ret = phytium_pcie_pmu_init_data(pdev, pcie_pmu); if (ret) return ret; @@ -754,8 +813,12 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) return ret; } - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", - pcie_pmu->die_id, pcie_pmu->pmu_id); + if (pcie_pmu->ver == PCIEV1P0) + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", + pcie_pmu->die_id, pcie_pmu->pmu_id); + else + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie%u_pmu%u", + pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id); pcie_pmu->pmu = (struct pmu){ .name = name, .module = THIS_MODULE, @@ -769,7 +832,6 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) .stop = phytium_pcie_pmu_event_stop, .read = phytium_pcie_pmu_event_update, .attr_groups = phytium_pcie_pmu_attr_groups, - .capabilities = PERF_PMU_CAP_NO_EXCLUDE, }; ret = perf_pmu_register(&pcie_pmu->pmu, name, -1); @@ -782,9 +844,12 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) phytium_pcie_pmu_enable_clk(pcie_pmu); - pr_info("Phytium PCIe PMU: "); - pr_info("die_id = %d pmu_id = %d.\n", pcie_pmu->die_id, - pcie_pmu->pmu_id); + if (pcie_pmu->ver == PCIEV1P0) + pr_info("die%d_pcie_pmu%d on cpu%d.\n", + pcie_pmu->die_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); + else + pr_info("die%d_pcie%d_pmu%d on cpu%d.\n", + pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); return ret; } @@ -826,6 +891,7 @@ int phytium_pcie_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) pcie_pmu->on_cpu = cpu; WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(cpu))); } + return 0; } @@ -872,8 +938,8 @@ static int __init phytium_pcie_pmu_module_init(void) phytium_pcie_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, - "perf/phytium/pciepmu:online", - phytium_pcie_pmu_online_cpu, phytium_pcie_pmu_offline_cpu); + "perf/phytium/pciepmu:online", phytium_pcie_pmu_online_cpu, + phytium_pcie_pmu_offline_cpu); if (phytium_pcie_pmu_hp_state < 0) { pr_err("PCIE PMU: setup hotplug, ret = %d\n", phytium_pcie_pmu_hp_state); @@ -900,3 +966,4 @@ MODULE_DESCRIPTION("Phytium PCIe PMU driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(PCIE_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); +MODULE_AUTHOR("Tan Rui "); -- Gitee From 9f19157170e1a30b1450878c0db03c967b7f582b Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Tue, 18 Feb 2025 16:57:21 +0800 Subject: [PATCH 083/154] tacho: Phytium: Complement ACPI support for tacho driver This patch used to complement the acpi and set the phytium ACPI ID to PHYT0033. Mainline: Open-Source Signed-off-by: lanhengyu1395 Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: I7392e9bcc10788134af46c4a590b99a0bc18e0c8 --- drivers/hwmon/tacho-phytium.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/tacho-phytium.c b/drivers/hwmon/tacho-phytium.c index c89251f359..e96ab84d9b 100644 --- a/drivers/hwmon/tacho-phytium.c +++ b/drivers/hwmon/tacho-phytium.c @@ -54,7 +54,8 @@ #define TIMER_TACHO_UNDER_REG 0x34 #define TIMER_START_VALUE_REG 0x38 -#define TIMER_INT_CLR_MASK GENMASK(5, 0) +#define TIMER_INT_CLR_MASK GENMASK(5, 0) +#define TIMER_TACHO_DEFAULT_FREQ 0x2FAF080 enum tacho_modes { tacho_mode = 1, @@ -62,8 +63,8 @@ capture_mode, }; enum edge_modes { -rising_edge, falling_edge, +rising_edge, double_edge, }; @@ -281,6 +282,7 @@ static int phytium_tacho_probe(struct platform_device *pdev) struct resource *res; struct phytium_tacho *tacho; int ret; + u32 fre; tacho = devm_kzalloc(dev, sizeof(*tacho), GFP_KERNEL); if (!tacho) @@ -297,19 +299,20 @@ static int phytium_tacho_probe(struct platform_device *pdev) dev_err(&pdev->dev, "region map failed\n"); return PTR_ERR(tacho->base); } - if (dev->of_node) { + + tacho->freq = TIMER_TACHO_DEFAULT_FREQ; + if (!has_acpi_companion(tacho->dev)) { tacho->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(tacho->clk)) - return PTR_ERR(tacho->clk); - ret = clk_prepare_enable(tacho->clk); - if (ret) - return ret; - - tacho->freq = clk_get_rate(tacho->clk); - } else if (has_acpi_companion(dev)){ - if(fwnode_property_read_u32(dev_fwnode(dev),"clock-frequency", (u32 *)&(tacho->freq) ) <0) - tacho->freq = 50000000; - } + if (IS_ERR(tacho->clk) || clk_prepare_enable(tacho->clk)) + dev_err(&pdev->dev, "Tacho get clocks failed\n"); + else + tacho->freq = clk_get_rate(tacho->clk); + } else { + if (fwnode_property_read_u32(tacho->dev->fwnode, "clock-frequency", &fre)) + dev_err(&pdev->dev, "Tacho get clock-frequency failed\n"); + else + tacho->freq = fre; + } tacho->irq = platform_get_irq(pdev, 0); if (tacho->irq < 0) { -- Gitee From aec1bd629fc5a6ff23507688bcdc69ea66154807 Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Mon, 3 Mar 2025 15:12:59 +0800 Subject: [PATCH 084/154] dt-bindings: Phytium: Add snoop controller devicetree description This patch adds the DT description for the Phytium snoop controller. Mainline: NA Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: Ibd813bfec66024000b8cf25deae977229c157f1e --- .../bindings/misc/phytium,snoop-ctrl.yaml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/phytium,snoop-ctrl.yaml diff --git a/Documentation/devicetree/bindings/misc/phytium,snoop-ctrl.yaml b/Documentation/devicetree/bindings/misc/phytium,snoop-ctrl.yaml new file mode 100644 index 0000000000..db6f165a7d --- /dev/null +++ b/Documentation/devicetree/bindings/misc/phytium,snoop-ctrl.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/phytium,snoop-ctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium BMC Snoop Controller + +maintainers: + - Lan Hengyu + +description: + The snoop controller allows the BMC to listen on and record the data + bytes written by the Host to the target I/O ports. + +properties: + compatible: + items: + - enum: + - phytium,snoop-ctrl + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + snoop-ports: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Ports to snoop + +required: + - compatible + - interrupts + - snoop-ports + +examples: + - | + snoop: snoop@28010000 { + compatible = "phytium,snoop-ctrl"; + reg = <0x0 0x28010000 0x1000>; + interrupts = ; + + snoop-ports = <0x80>; + }; -- Gitee From 0283d290fd6fb8a6248c7efc9762e8065fbe015a Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Wed, 19 Feb 2025 09:59:15 +0800 Subject: [PATCH 085/154] dts: phytium: Add snoop node description for pe220x platforms Add the snoop node description for pe220x platforms to ensure its driver is functioning normally. Mainline: NA Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: I179f841b16b56768f24934c747d5ae4a83f4f090 --- arch/arm64/boot/dts/phytium/pe220x.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/phytium/pe220x.dtsi b/arch/arm64/boot/dts/phytium/pe220x.dtsi index 80c5fdcb65..67cd5c362b 100644 --- a/arch/arm64/boot/dts/phytium/pe220x.dtsi +++ b/arch/arm64/boot/dts/phytium/pe220x.dtsi @@ -277,6 +277,14 @@ uart3: uart@2800f000 { status = "disabled"; }; + snoop: snoop@28010000 { + compatible = "phytium,snoop-ctrl", "syscon"; + reg = <0x0 0x28010000 0x0 0x1000>; + interrupts = ; + snoop-ports = <0x80 0x81>; + status = "disabled"; + }; + lpc: lpc@28010000 { compatible = "simple-mfd", "syscon"; reg = <0x0 0x28010000 0x0 0x1000>; -- Gitee From e9a73d5229e927b030d0abcf8668612a13c84094 Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Wed, 19 Feb 2025 10:10:11 +0800 Subject: [PATCH 086/154] gpio: phytium: Set value first when setting output direction This patch sets the output value firstly when setting output direction to avoid brief low-level interference timing sequence. Mainline: Open-Source Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: I09d62c2dfd398675e7c7824ba4c4db8c18257003 --- drivers/gpio/gpio-phytium-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-phytium-core.c b/drivers/gpio/gpio-phytium-core.c index a1794f55a5..b7f3b9b7bd 100644 --- a/drivers/gpio/gpio-phytium-core.c +++ b/drivers/gpio/gpio-phytium-core.c @@ -128,14 +128,13 @@ int phytium_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, return -EINVAL; ddr = gpio->regs + GPIO_SWPORTA_DDR + (loc.port * GPIO_PORT_STRIDE); + phytium_gpio_set(gc, offset, value); raw_spin_lock_irqsave(&gpio->lock, flags); writel(readl(ddr) | BIT(loc.offset), ddr); raw_spin_unlock_irqrestore(&gpio->lock, flags); - phytium_gpio_set(gc, offset, value); - return 0; } EXPORT_SYMBOL_GPL(phytium_gpio_direction_output); -- Gitee From 9268460cbc80f850eb0a91e31a6e1a83843a2ef2 Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Wed, 19 Feb 2025 10:17:55 +0800 Subject: [PATCH 087/154] gpio: phytium: Improve gpio set affinity logic Improve gpio set affinity logic to ensure the correctness of sharing interruptions. Mainline: Open-Source Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: I8aa24b06809be004f2d0545074b80f526c5a721f --- drivers/gpio/gpio-phytium-core.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-phytium-core.c b/drivers/gpio/gpio-phytium-core.c index b7f3b9b7bd..d498af09bc 100644 --- a/drivers/gpio/gpio-phytium-core.c +++ b/drivers/gpio/gpio-phytium-core.c @@ -346,9 +346,17 @@ EXPORT_SYMBOL_GPL(phytium_gpio_get_direction); int phytium_gpio_irq_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { + int hwirq = irqd_to_hwirq(d); struct gpio_chip *chip_data = irq_data_get_irq_chip_data(d); - struct irq_chip *chip = irq_get_chip(*(chip_data->irq.parents)); - struct irq_data *data = irq_get_irq_data(*(chip_data->irq.parents)); + struct irq_chip *chip; + struct irq_data *data; + + if ((chip_data->irq.num_parents) == 1) + hwirq = 0; + + chip = irq_get_chip(chip_data->irq.parents[hwirq]); + data = irq_get_irq_data(chip_data->irq.parents[hwirq]); + if (chip && chip->irq_set_affinity) return chip->irq_set_affinity(data, mask_val, force); -- Gitee From 5d39e2b044ab81eba71f49f0d8b0c989a993a27c Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Thu, 11 Jul 2024 14:17:10 +0800 Subject: [PATCH 088/154] gpio: phytium: Optimize the logic of suspend and resume This patch disables interrupts when gpio suspends and recoveries interrupts at the end of resume. Add the function of suspend flag to prevent other processes from resuming interrupts in advance. Mainline: Open-Source Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: I149c6dd04e58a09df7d20758d4f6729c39edb467 --- drivers/gpio/gpio-phytium-core.c | 2 ++ drivers/gpio/gpio-phytium-core.h | 1 + drivers/gpio/gpio-phytium-pci.c | 7 ++++++- drivers/gpio/gpio-phytium-platform.c | 7 ++++++- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-phytium-core.c b/drivers/gpio/gpio-phytium-core.c index d498af09bc..2f9e9413cf 100644 --- a/drivers/gpio/gpio-phytium-core.c +++ b/drivers/gpio/gpio-phytium-core.c @@ -266,6 +266,8 @@ void phytium_gpio_irq_enable(struct irq_data *d) unsigned long flags; u32 val; + if (gpio->is_resuming) + return; /* Only port A can provide interrupt source */ if (irqd_to_hwirq(d) >= gpio->ngpio[0]) return; diff --git a/drivers/gpio/gpio-phytium-core.h b/drivers/gpio/gpio-phytium-core.h index d45a9ab119..f09a081cfd 100644 --- a/drivers/gpio/gpio-phytium-core.h +++ b/drivers/gpio/gpio-phytium-core.h @@ -64,6 +64,7 @@ struct phytium_gpio { struct irq_chip irq_chip; unsigned int ngpio[2]; int irq[32]; + int is_resuming; #ifdef CONFIG_PM_SLEEP struct phytium_gpio_ctx ctx; #endif diff --git a/drivers/gpio/gpio-phytium-pci.c b/drivers/gpio/gpio-phytium-pci.c index e68772dd7a..a5011a2bc2 100644 --- a/drivers/gpio/gpio-phytium-pci.c +++ b/drivers/gpio/gpio-phytium-pci.c @@ -77,6 +77,7 @@ static int phytium_gpio_pci_probe(struct pci_dev *pdev, const struct pci_device_ gpio->gc.label = dev_name(dev); gpio->gc.parent = dev; gpio->gc.owner = THIS_MODULE; + gpio->is_resuming = 0; girq = &gpio->gc.irq; girq->handler = handle_bad_irq; @@ -128,11 +129,13 @@ static int phytium_gpio_pci_suspend(struct device *dev) gpio->ctx.ext_portb = readl(gpio->regs + GPIO_EXT_PORTB); gpio->ctx.inten = readl(gpio->regs + GPIO_INTEN); + gpio->is_resuming = 1; gpio->ctx.intmask = readl(gpio->regs + GPIO_INTMASK); gpio->ctx.inttype_level = readl(gpio->regs + GPIO_INTTYPE_LEVEL); gpio->ctx.int_polarity = readl(gpio->regs + GPIO_INT_POLARITY); gpio->ctx.debounce = readl(gpio->regs + GPIO_DEBOUNCE); + writel(0, gpio->regs + GPIO_INTEN); raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; @@ -153,7 +156,6 @@ static int phytium_gpio_pci_resume(struct device *dev) writel(gpio->ctx.swportb_ddr, gpio->regs + GPIO_SWPORTB_DDR); writel(gpio->ctx.ext_portb, gpio->regs + GPIO_EXT_PORTB); - writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); writel(gpio->ctx.intmask, gpio->regs + GPIO_INTMASK); writel(gpio->ctx.inttype_level, gpio->regs + GPIO_INTTYPE_LEVEL); writel(gpio->ctx.int_polarity, gpio->regs + GPIO_INT_POLARITY); @@ -161,6 +163,9 @@ static int phytium_gpio_pci_resume(struct device *dev) writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); + gpio->is_resuming = 0; + raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; diff --git a/drivers/gpio/gpio-phytium-platform.c b/drivers/gpio/gpio-phytium-platform.c index 9fe3191f78..1c21360de6 100644 --- a/drivers/gpio/gpio-phytium-platform.c +++ b/drivers/gpio/gpio-phytium-platform.c @@ -97,6 +97,7 @@ static int phytium_gpio_probe(struct platform_device *pdev) gpio->gc.label = dev_name(dev); gpio->gc.parent = dev; gpio->gc.owner = THIS_MODULE; + gpio->is_resuming = 0; girq = &gpio->gc.irq; girq->handler = handle_bad_irq; @@ -145,11 +146,13 @@ static int phytium_gpio_suspend(struct device *dev) gpio->ctx.ext_portb = readl(gpio->regs + GPIO_EXT_PORTB); gpio->ctx.inten = readl(gpio->regs + GPIO_INTEN); + gpio->is_resuming = 1; gpio->ctx.intmask = readl(gpio->regs + GPIO_INTMASK); gpio->ctx.inttype_level = readl(gpio->regs + GPIO_INTTYPE_LEVEL); gpio->ctx.int_polarity = readl(gpio->regs + GPIO_INT_POLARITY); gpio->ctx.debounce = readl(gpio->regs + GPIO_DEBOUNCE); + writel(0, gpio->regs + GPIO_INTEN); raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; @@ -170,7 +173,6 @@ static int phytium_gpio_resume(struct device *dev) writel(gpio->ctx.swportb_ddr, gpio->regs + GPIO_SWPORTB_DDR); writel(gpio->ctx.ext_portb, gpio->regs + GPIO_EXT_PORTB); - writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); writel(gpio->ctx.intmask, gpio->regs + GPIO_INTMASK); writel(gpio->ctx.inttype_level, gpio->regs + GPIO_INTTYPE_LEVEL); writel(gpio->ctx.int_polarity, gpio->regs + GPIO_INT_POLARITY); @@ -178,6 +180,9 @@ static int phytium_gpio_resume(struct device *dev) writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); + gpio->is_resuming = 0; + raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; -- Gitee From 44481bda19c050d2005e65222acd2da5e93fbc53 Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Mon, 9 Sep 2024 15:34:00 +0800 Subject: [PATCH 089/154] gpio: phytium: Modify the irq handler logic In non-irq sharing mode, now it supports to handle the irq interrupts of its owm pin. This avoids the problem of irq being transmitted to the unwanted CPU. Mainline: Open-Source Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: I6a4ccbbafbe7c34f6053fad1dc808fd9afad7b3b --- drivers/gpio/gpio-phytium-core.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-phytium-core.c b/drivers/gpio/gpio-phytium-core.c index 2f9e9413cf..dce2fe4639 100644 --- a/drivers/gpio/gpio-phytium-core.c +++ b/drivers/gpio/gpio-phytium-core.c @@ -310,19 +310,36 @@ void phytium_gpio_irq_handler(struct irq_desc *desc) struct irq_chip *irqchip = irq_desc_get_chip(desc); unsigned long pending; int offset; + int index = -1; + unsigned int index_flag = 0; chained_irq_enter(irqchip, desc); pending = readl(gpio->regs + GPIO_INTSTATUS); + + if (gc->irq.num_parents > 1) { + for (index = 0 ; index < gc->irq.num_parents; index++) { + if (gc->irq.parents[index] == desc->irq_data.irq) { + index_flag = 1; + break; + } + } + if (index_flag == 0) { + pr_err("Can't find index for this gpio interrupt.\n"); + index = -1; + } + } + if (pending) { for_each_set_bit(offset, &pending, gpio->ngpio[0]) { - int gpio_irq = irq_find_mapping(gc->irq.domain, - offset); - generic_handle_irq(gpio_irq); - - if ((irq_get_trigger_type(gpio_irq) & - IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) - phytium_gpio_toggle_trigger(gpio, offset); + if (index == -1 || offset == index) { + int gpio_irq = irq_find_mapping(gc->irq.domain, + offset); + generic_handle_irq(gpio_irq); + if ((irq_get_trigger_type(gpio_irq) & + IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) + phytium_gpio_toggle_trigger(gpio, offset); + } } } -- Gitee From c0088c226a904f22108b22b14af0df6cb0a0ede9 Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Wed, 12 Feb 2025 17:42:12 +0800 Subject: [PATCH 090/154] gpio: phytium: Unable and clear irq before register When gpio irq is enabled by firmware, kernel will report soft lock and rcu, which will cause it to freeze. So unable and clear irq before registering irq in gpio driver. Mainline: Open-Source Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: I542b5b63b5297de3cbaaae8a04e8c9c265e16be5 --- drivers/gpio/gpio-phytium-pci.c | 3 +++ drivers/gpio/gpio-phytium-platform.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/gpio/gpio-phytium-pci.c b/drivers/gpio/gpio-phytium-pci.c index a5011a2bc2..0bec73380f 100644 --- a/drivers/gpio/gpio-phytium-pci.c +++ b/drivers/gpio/gpio-phytium-pci.c @@ -67,6 +67,9 @@ static int phytium_gpio_pci_probe(struct pci_dev *pdev, const struct pci_device_ raw_spin_lock_init(&gpio->lock); + writel(0, gpio->regs + GPIO_INTEN); + writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + gpio->gc.base = -1; gpio->gc.get_direction = phytium_gpio_get_direction; gpio->gc.direction_input = phytium_gpio_direction_input; diff --git a/drivers/gpio/gpio-phytium-platform.c b/drivers/gpio/gpio-phytium-platform.c index 1c21360de6..e80fab7c81 100644 --- a/drivers/gpio/gpio-phytium-platform.c +++ b/drivers/gpio/gpio-phytium-platform.c @@ -87,6 +87,9 @@ static int phytium_gpio_probe(struct platform_device *pdev) #endif raw_spin_lock_init(&gpio->lock); + writel(0, gpio->regs + GPIO_INTEN); + writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + gpio->gc.base = -1; gpio->gc.get_direction = phytium_gpio_get_direction; gpio->gc.direction_input = phytium_gpio_direction_input; -- Gitee From f8b459483b3fbe27c4080a03a84e370b6dd9585b Mon Sep 17 00:00:00 2001 From: Peng Min Date: Fri, 14 Feb 2025 16:13:26 +0800 Subject: [PATCH 091/154] qspi: phytium: Add QSPI driver version description After adding the driver version description, you can use the modinfo command to view the driver version information. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: I31556924c963c72eaf7a6c36b0ecd2b8034812a0 --- drivers/spi/spi-phytium-qspi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-phytium-qspi.c b/drivers/spi/spi-phytium-qspi.c index d044ff6082..e379466f5f 100755 --- a/drivers/spi/spi-phytium-qspi.c +++ b/drivers/spi/spi-phytium-qspi.c @@ -20,6 +20,7 @@ #include #include +#define DRIVER_VERSION "1.0.0" #define QSPI_FLASH_CAP_REG 0x00 #define QSPI_FLASH_CAP_NUM_SHIFT 3 @@ -813,3 +814,4 @@ module_platform_driver(phytium_qspi_driver); MODULE_AUTHOR("Chen Baozi "); MODULE_DESCRIPTION("Phytium Quad SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRIVER_VERSION); -- Gitee From 8171bd2abad8c3966e66a2ae408e49ad2d8e10b8 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Fri, 14 Feb 2025 16:23:19 +0800 Subject: [PATCH 092/154] qspi: phytium: Adapt flash different capacity based on CPU MPIDR For the capacity register, the old controller only supports multiple flash of the same size, while the new QSPI controller supports multiple flash of different size. In this driver, the CPU MPIDR register is used to distinguish different QSPI controller. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: I610344cd043b4f1e0b2b722d39248070c6402a74 --- drivers/spi/spi-phytium-qspi.c | 103 +++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-phytium-qspi.c b/drivers/spi/spi-phytium-qspi.c index e379466f5f..ba65c6af9a 100755 --- a/drivers/spi/spi-phytium-qspi.c +++ b/drivers/spi/spi-phytium-qspi.c @@ -20,9 +20,14 @@ #include #include -#define DRIVER_VERSION "1.0.0" +#define DRIVER_VERSION "1.0.1" + +#define PHYTIUM_CPU_PART_FTC862 0x862 + +#define MIDR_PHYTIUM_FTC862 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC862) #define QSPI_FLASH_CAP_REG 0x00 +#define QSPI_FLASH_CAP_NUM_SHIFT_NEW 16 #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 @@ -146,6 +151,8 @@ #define XFER_PROTO_2_2_2 0x5 #define XFER_PROTO_4_4_4 0x6 +#define WR_CFG_NODIR_VALUE 0x5000000 + struct phytium_qspi_flash { u32 cs; u32 clk_div; @@ -250,6 +257,43 @@ static int phytium_spi_nor_protocol_encode(const struct spi_mem_op *op, u32 *cod return ret; } +static int phytium_qspi_flash_capacity_encode_new(u32 size, + u32 *cap, int i) +{ + int ret = 0; + + switch (size) { + case SZ_4M: + *cap |= (0x0 >> (4 * i)); + break; + case SZ_8M: + *cap |= (0x1 >> (4 * i)); + break; + case SZ_16M: + *cap |= (0x2 >> (4 * i)); + break; + case SZ_32M: + *cap |= (0x3 >> (4 * i)); + break; + case SZ_64M: + *cap |= (0x4 >> (4 * i)); + break; + case SZ_128M: + *cap |= (0x5 >> (4 * i)); + break; + case SZ_256M: + *cap |= (0x6 >> (4 * i)); + break; + case SZ_512M: + *cap |= (0x7 >> (4 * i)); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + static int phytium_qspi_flash_capacity_encode(u32 size, u32 *cap) { int ret = 0; @@ -621,6 +665,7 @@ static int phytium_qspi_probe(struct platform_device *pdev) int i, ret; struct spi_mem *mem; struct spi_nor *nor; + bool new_capacity = false; ctrl = spi_alloc_master(dev, sizeof(*qspi)); if (!ctrl) @@ -687,6 +732,11 @@ static int phytium_qspi_probe(struct platform_device *pdev) &phytium_qspi_mem_ops_nodirmap : &phytium_qspi_mem_ops; + if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_PHYTIUM_FTC862) { + dev_warn(dev, "capacity register(0x00) is the latest design\n"); + new_capacity = true; + } + qspi->dev = dev; platform_set_drvdata(pdev, qspi); @@ -714,24 +764,37 @@ static int phytium_qspi_probe(struct platform_device *pdev) } } - 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; + if (!new_capacity) { + 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, + &qspi->flash_cap); + if (ret) { + dev_err(dev, "Flash size is invalid.\n"); + goto probe_setup_failed; + } + qspi->flash_cap |= (qspi->fnum - 1) << QSPI_FLASH_CAP_NUM_SHIFT; + } else { + for (i = 0; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) { + ret = phytium_qspi_flash_capacity_encode_new(qspi->flash[i].size, + &qspi->flash_cap, i); + if (ret) { + dev_err(dev, "Flash size is invalid.\n"); + goto probe_setup_failed; + } + } + qspi->flash_cap |= (qspi->fnum - 1) << QSPI_FLASH_CAP_NUM_SHIFT_NEW; } - ret = phytium_qspi_flash_capacity_encode(qspi->flash[0].size, - &qspi->flash_cap); - if (ret) { - dev_err(dev, "Flash size is invalid.\n"); - goto probe_setup_failed; - } - - qspi->flash_cap |= qspi->fnum << QSPI_FLASH_CAP_NUM_SHIFT; writel_relaxed(qspi->flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + } else { + writel_relaxed(WR_CFG_NODIR_VALUE, qspi->io_base + QSPI_WR_CFG_REG); } return 0; @@ -782,10 +845,14 @@ static int __maybe_unused phytium_qspi_resume(struct device *dev) { struct phytium_qspi *qspi = dev_get_drvdata(dev); - /* set rd_cfg reg, wr_cfg reg and flash_capacity reg after resume */ - writel_relaxed(qspi->rd_cfg_reg, qspi->io_base + QSPI_RD_CFG_REG); - writel_relaxed(qspi->wr_cfg_reg, qspi->io_base + QSPI_WR_CFG_REG); - writel_relaxed(qspi->flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + if (!qspi->nodirmap) { + /* set rd_cfg reg and flash_capacity reg after resume */ + writel_relaxed(qspi->rd_cfg_reg, qspi->io_base + QSPI_RD_CFG_REG); + writel_relaxed(qspi->flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + } else { + writel_relaxed(WR_CFG_NODIR_VALUE, qspi->io_base + QSPI_WR_CFG_REG); + } + return pm_runtime_force_resume(dev); } -- Gitee From baaad0d76b02e8a228ead5122a379e9aedbb5598 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Fri, 14 Feb 2025 16:26:40 +0800 Subject: [PATCH 093/154] qspi: phytium: Dynamically get the clock in the ACPI table Originally, the QSPI driver write the controller clock as a fixed value, but now it is modified to obtain dynamically. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: I935da410b1417a2e98c08cc0d5b5d4bfeb6b36db --- drivers/spi/spi-phytium-qspi.c | 51 ++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-phytium-qspi.c b/drivers/spi/spi-phytium-qspi.c index ba65c6af9a..68cf760136 100755 --- a/drivers/spi/spi-phytium-qspi.c +++ b/drivers/spi/spi-phytium-qspi.c @@ -5,6 +5,7 @@ * Copyright (c) 2022-2024 Phytium Technology Co., Ltd. */ +#include #include #include #include @@ -20,7 +21,7 @@ #include #include -#define DRIVER_VERSION "1.0.1" +#define DRIVER_VERSION "1.0.2" #define PHYTIUM_CPU_PART_FTC862 0x862 @@ -153,6 +154,8 @@ #define WR_CFG_NODIR_VALUE 0x5000000 +#define QSPI_DEFAULT_CLK 500000000 + struct phytium_qspi_flash { u32 cs; u32 clk_div; @@ -666,6 +669,7 @@ static int phytium_qspi_probe(struct platform_device *pdev) struct spi_mem *mem; struct spi_nor *nor; bool new_capacity = false; + u32 clk_rate = QSPI_DEFAULT_CLK; ctrl = spi_alloc_master(dev, sizeof(*qspi)); if (!ctrl) @@ -702,29 +706,34 @@ static int phytium_qspi_probe(struct platform_device *pdev) } 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; - } + if (dev->of_node) { + 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; - } + 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; - } + 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; + ret = clk_prepare_enable(qspi->clk); + if (ret) { + dev_err(dev, "Failed to enable PCLK of the controller.\n"); + goto probe_clk_failed; + } + } else if (has_acpi_companion(dev)) { + fwnode_property_read_u32(dev->fwnode, "spi-clock", &clk_rate); + qspi->clk_rate = clk_rate; } qspi->nodirmap = device_property_present(dev, "no-direct-mapping"); -- Gitee From 395979604de0325e8a09f277fb370b248187ad78 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Tue, 4 Mar 2025 10:23:49 +0800 Subject: [PATCH 094/154] Documentation: bindings: Add phytium spi-v2 devicetree description This patch document the DT bindings for the Phytium spi-v2 controller. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: Idc048a8ef016905d088b5ae599a1223f33fa343f --- .../bindings/spi/spi-phytium-v2.yaml | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-phytium-v2.yaml diff --git a/Documentation/devicetree/bindings/spi/spi-phytium-v2.yaml b/Documentation/devicetree/bindings/spi/spi-phytium-v2.yaml new file mode 100644 index 0000000000..15f332ef05 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-phytium-v2.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +title: Phytium SPI-V2 controller + +maintainers: + - Peng Min + +allOf: + - $ref: spi-v2-controller.yaml# + +properties: + compatible: + const: phytium,spi-2.0 + + reg: + minItems: 1 + description: address and length of the sharememory and regfile + + interrupts: + maxItems: 1 + description: should contain one interrupt + + clocks: + maxItems: 1 + description: spi clock phandle + +required: + - compatible + - "#address-cells" + - "#size-cells" + - reg + - interrupts + - clocks + - num-cs + +examples: + - | + + spi0: spi@2800c000 { + compatible = "phytium,spi-2.0"; + interrupts = ; + reg = <0x0 0x27014000 0x0 0x1000 0x0 0x27007000 0x0 0x1000>; + clocks = <&sysclk_48mhz>; + num-cs = <4>; + }; -- Gitee From 5cbf0bf4e74fc17a0977e9c250dc126437f9d4ce Mon Sep 17 00:00:00 2001 From: Peng Min Date: Tue, 4 Mar 2025 10:35:53 +0800 Subject: [PATCH 095/154] spi-v2: Phytium: Add phytium spi-v2 driver This patch adds driver support for phytium spi-v2 controller. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: I7b50c356893ce4d4daf9042c3930580baa78b6b7 --- drivers/spi/Kconfig | 12 + drivers/spi/Makefile | 4 +- drivers/spi/spi-phytium-common.c | 454 ++++++++++++++++++++++++++ drivers/spi/spi-phytium-plat-v2.c | 326 +++++++++++++++++++ drivers/spi/spi-phytium-v2.c | 517 ++++++++++++++++++++++++++++++ drivers/spi/spi-phytium.h | 161 +++++++++- 6 files changed, 1458 insertions(+), 16 deletions(-) create mode 100644 drivers/spi/spi-phytium-common.c create mode 100644 drivers/spi/spi-phytium-plat-v2.c create mode 100644 drivers/spi/spi-phytium-v2.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 07b4d4c436..11e7bac486 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -623,6 +623,18 @@ config SPI_PHYTIUM_QSPI If unsure, say N. +config SPI_PHYTIUM_V2 + tristate "spi phytium v2" + depends on ARCH_PHYTIUM || COMPILE_TEST + help + This config is similar to the "SPI_PHYTIUM" config. + +config SPI_PHYTIUM_PLAT_V2 + tristate "Phytium SPI-V2 controller platform support" + select SPI_PHYTIUM_V2 + help + This config is similar to the "SPI_PHYTIUM_PLAT" config. + 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 567bb20731..94fe3555c3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -86,7 +86,9 @@ 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_QSPI) += spi-phytium-qspi.o -obj-$(CONFIG_SPI_PHYTIUM) += spi-phytium-dma.o +obj-$(CONFIG_SPI_PHYTIUM_V2) += spi-phytium-common.o spi-phytium-v2.o +obj-$(CONFIG_SPI_PHYTIUM_PLAT_V2) += spi-phytium-plat-v2.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-common.c b/drivers/spi/spi-phytium-common.c new file mode 100644 index 0000000000..0ccd92dc44 --- /dev/null +++ b/drivers/spi/spi-phytium-common.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SPI core controller driver. + * + * Copyright (c) 2023-2024, 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 "spi-phytium.h" + +#ifdef CONFIG_SHOW_MSG +void spi_phytium_show_msg(struct msg *info) +{ + pr_err("module:0x%4x, cmd:0x%04x, sub:0x%04x\n", + info->seq, info->cmd_id, info->cmd_subid); + + pr_err("0x%02x 0x%02x 0x%02x 0x%02x", info->data[0], + info->data[1], info->data[2], info->data[3]); + + pr_err("0x%02x 0x%02x 0x%02x 0x%02x", info->data[4], + info->data[5], info->data[6], info->data[7]); + + pr_err("0x%02x 0x%02x 0x%02x 0x%02x", info->data[8], + info->data[9], info->data[10], info->data[11]); + + pr_err("0x%02x 0x%02x 0x%02x 0x%02x", info->data[12], + info->data[13], info->data[14], info->data[15]); +} +#else +void spi_phytium_show_msg(struct msg *info) { } +#endif + +void *memcpy_byte(void *_dest, const void *_src, size_t sz) +{ + while (sz >= 8) { + *(u64 *)_dest = *(u64 *)_src; + _dest += 8; + _src += 8; + sz -= 8; + } + + while (sz) { + *(u8 *)_dest = *(u8 *)_src; + _dest++; + _src++; + sz--; + } + + return _dest; +} + +int spi_phytium_print_status(struct phytium_spi *fts, u8 status0, + u8 status1) +{ + if (status1 == 0) + return 0; + + switch (status1) { + case 1: + pr_err("SPI Bus is busy.\n"); + break; + case 2: + pr_err("DMA queue transfer error.\n"); + break; + case 3: + pr_err("DMA transfer timeout.\n"); + break; + case 4: + pr_err("Operating flash timeout.\n"); + break; + case 5: + pr_err("spi_tx channel:DMA initialization failure.\n"); + break; + case 6: + pr_err("spi_rx channel:DMA initialization failure.\n"); + break; + case 7: + pr_err("DMA queue initialization failure.\n"); + break; + default: + pr_err("status=0x%x,Unknown error\n", status1); + break; + } + + return -1; +} + +int spi_phytium_check_result(struct phytium_spi *fts) +{ + unsigned long long ms = 300000; + struct msg *msg = (struct msg *)fts->tx_shmem_addr; + + reinit_completion(&fts->cmd_completion); + ms = wait_for_completion_timeout(&fts->cmd_completion, msecs_to_jiffies(ms)); + + if (ms == 0) { + dev_err(&fts->master->dev, "SPI controller timed out\n"); + return -1; + } + + return spi_phytium_print_status(fts, msg->status0, msg->status1); +} + +int spi_phytium_set(struct phytium_spi *fts) +{ + int ret; + + spi_phytium_show_msg(fts->msg); + phytium_write_regfile(fts, SPI_REGFILE_AP2RV_INTR_STATE, 0x10); + ret = spi_phytium_check_result(fts); + + return ret; +} + +void spi_phytium_default(struct phytium_spi *fts) +{ + memset(fts->msg, 0, sizeof(struct msg)); + + fts->msg->cmd_id = PHYTSPI_MSG_CMD_DEFAULT; + + spi_phytium_show_msg(fts->msg); + phytium_write_regfile(fts, SPI_REGFILE_AP2RV_INTR_STATE, 0x10); + spi_phytium_check_result(fts); +} +EXPORT_SYMBOL_GPL(spi_phytium_default); + +void spi_phytium_set_subid(struct phytium_spi *fts, u16 sub_cmd) +{ + fts->msg->cmd_id = PHYTSPI_MSG_CMD_SET; + fts->msg->cmd_subid = sub_cmd; +} + +void spi_phytium_set_cmd8(struct phytium_spi *fts, u16 sub_cmd, + u8 data) +{ + memset(fts->msg, 0, sizeof(struct msg)); + spi_phytium_set_subid(fts, sub_cmd); + fts->msg->data[0] = data; + spi_phytium_show_msg(fts->msg); + phytium_write_regfile(fts, SPI_REGFILE_AP2RV_INTR_STATE, 0x10); + spi_phytium_check_result(fts); +} +EXPORT_SYMBOL_GPL(spi_phytium_set_cmd8); + +void spi_phytium_set_cmd16(struct phytium_spi *fts, u16 sub_cmd, + u16 data) +{ + u16 *cp_data = (u16 *)&fts->msg->data[0]; + + memset(fts->msg, 0, sizeof(struct msg)); + spi_phytium_set_subid(fts, sub_cmd); + *cp_data = data; + spi_phytium_show_msg(fts->msg); + phytium_write_regfile(fts, SPI_REGFILE_AP2RV_INTR_STATE, 0x10); + spi_phytium_check_result(fts); +} +EXPORT_SYMBOL_GPL(spi_phytium_set_cmd16); + +void spi_phytium_set_cmd32(struct phytium_spi *fts, u16 sub_cmd, + u32 data) +{ + u32 *cp_data = (u32 *)&fts->msg->data[0]; + + memset(fts->msg, 0, sizeof(struct msg)); + spi_phytium_set_subid(fts, sub_cmd); + *cp_data = data; + spi_phytium_show_msg(fts->msg); + phytium_write_regfile(fts, SPI_REGFILE_AP2RV_INTR_STATE, 0x10); + spi_phytium_check_result(fts); +} +EXPORT_SYMBOL_GPL(spi_phytium_set_cmd32); + +void spi_phytium_data_subid(struct phytium_spi *fts, u16 sub_cmd) +{ + fts->msg->cmd_id = PHYTSPI_MSG_CMD_DATA; + fts->msg->cmd_subid = sub_cmd; +} + +void spi_phytium_write_pre(struct phytium_spi *fts, u8 cs, u8 dfs, u8 mode, u8 tmode, + u8 flags, u8 spi_write_flag) +{ + struct msg *msg; + u32 len; + u64 smem_tx; + u8 first = 1; + u64 tx_addr; + + len = min((u32)(fts->tx_end - fts->tx), (u32)SPI_TRANS_DATA_SIZE); + msg = (struct msg *)((u64)fts->msg + (sizeof(struct msg) + FLASH_PAGE_SIZE)*spi_write_flag); + memset(msg, 0, sizeof(struct msg)); + + msg->cmd_id = PHYTSPI_MSG_CMD_DATA; + if (spi_write_flag) { + if (len > 16 && fts->dma_get_ddrdata) + msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_FLASH_DMA_TX; + else + msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_FLASH_TX; + } else { + if (len > 16 && fts->dma_get_ddrdata) + msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_DMA_TX; + else + msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_TX; + } + + if (len > 16 && fts->dma_get_ddrdata) { + tx_addr = (u64)__virt_to_phys((u64)fts->tx); + if (!tx_addr) { + dev_err(&fts->master->dev, "tx address translation failed\n"); + return; + } + *(u64 *)&msg->data[0] = tx_addr; + } else { + smem_tx = (u64)msg + sizeof(struct msg); + memcpy((void *)smem_tx, fts->tx, len); + *(u64 *)&msg->data[0] = sizeof(struct msg); + } + + *(u32 *)&msg->data[8] = len; + fts->tx += len; + msg->data[12] = cs; + msg->data[13] = dfs; + msg->data[14] = mode; + msg->data[15] = tmode; + msg->data[16] = flags; + msg->data[17] = first; + first = 0; +} +EXPORT_SYMBOL_GPL(spi_phytium_write_pre); + +int spi_phytium_flash_erase(struct phytium_spi *fts, u8 cs, u8 dfs, u8 mode, + u8 tmode, u8 flags, u8 cmd) +{ + u32 len; + u64 smem_tx; + u8 first = 1; + u8 cmd_addr[8]; + int ret; + + len = (u32)(fts->tx_end - fts->tx); + + memset(fts->msg, 0, sizeof(struct msg)); + + fts->msg->cmd_id = PHYTSPI_MSG_CMD_DATA; + + if (cmd == SPINOR_OP_BE_4K || cmd == SPINOR_OP_CHIP_ERASE) + fts->msg->cmd_subid = PHYTSPI_MSG_CMD_FLASH_ERASE; + else if (cmd == SPINOR_OP_READ || cmd == SPINOR_OP_READ_FAST || + cmd == SPINOR_OP_READ_4B || cmd == SPINOR_OP_READ_FAST_4B) + fts->msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_TX; + + smem_tx = (u64)fts->msg + sizeof(struct msg); + + cmd_addr[0] = cmd; + if (cmd == SPINOR_OP_BE_4K || cmd == SPINOR_OP_READ || + cmd == SPINOR_OP_READ_FAST || cmd == SPINOR_OP_READ_4B || + cmd == SPINOR_OP_READ_FAST_4B) { + memcpy_byte((void *)&cmd_addr[1], fts->tx, len); + memcpy_byte((void *)smem_tx, (void *)&cmd_addr[0], len + 1); + *(u32 *)&fts->msg->data[8] = len + 1; + } else if (cmd == SPINOR_OP_CHIP_ERASE) { + memcpy_byte((void *)smem_tx, (void *)&cmd_addr[0], 1); + *(u32 *)&fts->msg->data[8] = len; + } + + *(u32 *)&fts->msg->data[0] = sizeof(struct msg); + fts->tx += len; + fts->msg->data[12] = cs; + fts->msg->data[13] = dfs; + fts->msg->data[14] = mode; + fts->msg->data[15] = tmode; + fts->msg->data[16] = flags; + fts->msg->data[17] = first; + + ret = spi_phytium_set(fts); + if (ret) { + dev_err(&fts->master->dev, "AP <-> RV interaction failed\n"); + return ret; + } + return ret; +} +EXPORT_SYMBOL_GPL(spi_phytium_flash_erase); + +int spi_phytium_flash_write(struct phytium_spi *fts, u8 cmd) +{ + u8 cmd_addr[8] = {0}; + + cmd_addr[0] = fts->len + 1; + cmd_addr[1] = cmd; + memcpy_byte((void *)&cmd_addr[2], fts->tx, fts->len); + + fts->msg->data[18] = cmd_addr[0]; + fts->msg->data[19] = cmd_addr[1]; + fts->msg->data[20] = cmd_addr[2]; + fts->msg->data[21] = cmd_addr[3]; + fts->msg->data[22] = cmd_addr[4]; + fts->msg->data[23] = cmd_addr[5]; + + return 0; +} +EXPORT_SYMBOL_GPL(spi_phytium_flash_write); + +int spi_phytium_write(struct phytium_spi *fts, u8 cs, u8 dfs, u8 mode, + u8 tmode, u8 flags, u8 spi_write_flag) +{ + int ret = 0; + u32 len; + u64 smem_tx; + u8 first = 1; + u64 tx_addr; + + if (spi_write_flag == 1) { + spi_phytium_set(fts); + if (ret) { + dev_err(&fts->master->dev, "AP <-> RV interaction failed\n"); + return ret; + } + } + + do { + len = min_t(u32, (u32)(fts->tx_end - fts->tx), + (u32)SPI_TRANS_DATA_SIZE); + + fts->msg->cmd_id = PHYTSPI_MSG_CMD_DATA; + + if (spi_write_flag == 2) { + if (len > 16 && fts->dma_get_ddrdata) + fts->msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_FLASH_DMA_TX; + else + fts->msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_FLASH_TX; + } else { + if (len > 16 && fts->dma_get_ddrdata) + fts->msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_DMA_TX; + else + fts->msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_TX; + } + + if (len > 16 && fts->dma_get_ddrdata) { + tx_addr = __virt_to_phys((u64)fts->tx); + if (!tx_addr) { + dev_err(&fts->master->dev, "tx address translation failed\n"); + return -1; + } + *(u64 *)&fts->msg->data[0] = tx_addr; + } else { + smem_tx = (u64)fts->msg + sizeof(struct msg); + memcpy_byte((void *)smem_tx, fts->tx, len); + *(u64 *)&fts->msg->data[0] = sizeof(struct msg); + } + + *(u32 *)&fts->msg->data[8] = len; + fts->tx += len; + fts->msg->data[12] = cs; + fts->msg->data[13] = dfs; + fts->msg->data[14] = mode; + fts->msg->data[15] = tmode; + fts->msg->data[16] = flags; + fts->msg->data[17] = first; + ret = spi_phytium_set(fts); + if (ret) { + dev_err(&fts->master->dev, "AP <-> RV interaction failed\n"); + return ret; + } + + first = 0; + } while (fts->tx_end > fts->tx); + + return ret; +} +EXPORT_SYMBOL_GPL(spi_phytium_write); + +int spi_phytium_read(struct phytium_spi *fts, u8 cs, u8 dfs, u8 mode, + u8 tmode, u8 flags) +{ + int ret; + u32 len; + u64 smem_rx; + u8 first = 1; + u64 rx_addr; + + do { + if (fts->dma_get_ddrdata) + len = min_t(u32, (u32)(fts->rx_end - fts->rx), + (u32)(fts->rx_end - fts->rx)); + else + len = min_t(u32, (u32)(fts->rx_end - fts->rx), 128); + + fts->msg->cmd_id = PHYTSPI_MSG_CMD_DATA; + + smem_rx = (u64)fts->msg + sizeof(struct msg); + + if (len > 16 && fts->dma_get_ddrdata) { + fts->msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_DMA_RX; + rx_addr = __virt_to_phys((u64)fts->rx); + if (!rx_addr) { + dev_err(&fts->master->dev, "rx address translation failed\n"); + return -1; + } + *(u64 *)&fts->msg->data[0] = rx_addr; + } else { + fts->msg->cmd_subid = PHYTSPI_MSG_CMD_DATA_RX; + *(u64 *)&fts->msg->data[0] = sizeof(struct msg); + } + + *(u32 *)&fts->msg->data[8] = len; + fts->msg->data[12] = cs; + fts->msg->data[13] = dfs; + fts->msg->data[14] = mode; + fts->msg->data[15] = tmode; + if (fts->rx_end <= fts->rx + len) + fts->msg->data[16] = flags; + else if (first == 1) + fts->msg->data[16] = 1; + else + fts->msg->data[16] = 0; + fts->msg->data[17] = first; + ret = spi_phytium_set(fts); + if (ret) { + dev_err(&fts->master->dev, "AP <-> RV interaction failed\n"); + return ret; + } + if (len <= 16 || !fts->dma_get_ddrdata) + memcpy_byte(fts->rx, (void *)smem_rx, len); + + fts->rx += len; + first = 0; + } while (fts->rx_end > fts->rx); + + return ret; +} +EXPORT_SYMBOL_GPL(spi_phytium_read); + +MODULE_AUTHOR("Peng Min "); +MODULE_DESCRIPTION("Phytium SPI adapter core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-phytium-plat-v2.c b/drivers/spi/spi-phytium-plat-v2.c new file mode 100644 index 0000000000..73fc2e5f09 --- /dev/null +++ b/drivers/spi/spi-phytium-plat-v2.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SPI core controller platform driver. + * + * Copyright (c) 2023-2024, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-phytium.h" + +#define DRIVER_NAME_PHYT "phytium_spi_2.0" +#define DRIVER_VERSION "1.0.6" + +#define PHYTIUM_CPU_PART_FTC872 0x872 + +#define MIDR_PHYTIUM_FTC872 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC872) + +static ssize_t debug_show(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct phytium_spi *fts = dev_get_drvdata(dev); + ssize_t ret; + u32 reg; + + reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG); + ret = sprintf(buf, "%x\n", reg); + + return ret; +} + +static ssize_t debug_store(struct device *dev, + struct device_attribute *da, const char *buf, + size_t size) +{ + u8 loc, dis_en, status = 0; + char *p; + char *token; + long value; + u32 reg; + struct phytium_spi *fts = dev_get_drvdata(dev); + + dev_info(dev, "echo alive(1)/debug(0) enable(1)/disable(0) > debug\n"); + dev_info(dev, "Example:echo 0 1 > debug; Enable Debug Function\n"); + + p = kmalloc(size, GFP_KERNEL); + if (!p) + return -ENOMEM; + strscpy(p, buf, size); + + token = strsep(&p, " "); + if (!token) + return -EINVAL; + + status = kstrtol(token, 0, &value); + if (status) + return status; + loc = (u8)value; + + token = strsep(&p, " "); + if (!token) + return -EINVAL; + + status = kstrtol(token, 0, &value); + if (status) + return status; + dis_en = value; + + reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG); + + if (loc == 1) { + if (dis_en == 1) { + fts->alive_enabled = true; + reg |= BIT(loc); + } else if (dis_en == 0) { + fts->alive_enabled = false; + reg &= ~BIT(loc); + } + fts->runtimes = 0; + } else if (loc == 0) { + if (dis_en == 1) { + fts->debug_enabled = true; + reg |= BIT(loc); + } else if (dis_en == 0) { + fts->debug_enabled = false; + reg &= ~BIT(loc); + } + } + + phytium_write_regfile(fts, SPI_REGFILE_DEBUG, reg); + + kfree(p); + + return size; +} + +static DEVICE_ATTR_RW(debug); + +static struct attribute *spi_phyt_device_attrs[] = { + &dev_attr_debug.attr, + NULL, +}; + +static const struct attribute_group spi_phyt_device_group = { + .attrs = spi_phyt_device_attrs, +}; + +static int spi_phyt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phytium_spi *fts; + struct resource *regfile_mem, *share_mem; + int ret; + int num_cs; + int cs_gpio; + int global_cs = 0; + int i; + u32 clk_rate = SPI_DEFAULT_CLK; + + fts = devm_kzalloc(&pdev->dev, sizeof(struct phytium_spi), + GFP_KERNEL); + if (!fts) + return -ENOMEM; + + regfile_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regfile_mem) { + dev_err(&pdev->dev, "no regfile_mem resource?\n"); + return -EINVAL; + } + + fts->regfile = devm_ioremap_resource(&pdev->dev, regfile_mem); + if (IS_ERR(fts->regfile)) { + dev_err(&pdev->dev, "fts->regfile map failed\n"); + return PTR_ERR(fts->regfile); + } + + share_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!share_mem) { + dev_err(&pdev->dev, "no share_mem resource?\n"); + return -EINVAL; + } + + fts->mem_tx_physic = (u32)share_mem->start + sizeof(struct msg); + fts->mem_rx_physic = (u32)share_mem->start + sizeof(struct msg); + + fts->tx_shmem_addr = devm_ioremap_resource(&pdev->dev, share_mem); + if (IS_ERR(fts->tx_shmem_addr)) { + dev_err(&pdev->dev, "fts->tx_shmem_addr map failed\n"); + return PTR_ERR(fts->tx_shmem_addr); + } + + fts->msg = (struct msg *)fts->tx_shmem_addr; + + fts->mem_tx = (u64)fts->msg + sizeof(struct msg); + fts->mem_rx = (u64)fts->msg + sizeof(struct msg); + + fts->irq = platform_get_irq(pdev, 0); + if (fts->irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return fts->irq; /* -ENXIO */ + } + + if (pdev->dev.of_node) { + fts->clk = devm_clk_get(&pdev->dev, NULL); + + if (IS_ERR(fts->clk)) + return PTR_ERR(fts->clk); + ret = clk_prepare_enable(fts->clk); + if (ret) + return ret; + + fts->max_freq = clk_get_rate(fts->clk); + } else if (has_acpi_companion(&pdev->dev)) { + fwnode_property_read_u32(dev->fwnode, "spi-clock", &clk_rate); + fts->max_freq = clk_rate; + } + + fts->bus_num = pdev->id; + device_property_read_u32(&pdev->dev, "reg-io-width", &fts->reg_io_width); + + num_cs = 4; + + device_property_read_u32(&pdev->dev, "num-cs", &num_cs); + + fts->num_cs = num_cs; + + if (pdev->dev.of_node) { + int i; + + for (i = 0; i < fts->num_cs; i++) { + cs_gpio = of_get_named_gpio(pdev->dev.of_node, + "cs-gpios", i); + + if (cs_gpio == -EPROBE_DEFER) { + ret = cs_gpio; + goto out; + } + + if (gpio_is_valid(cs_gpio)) { + ret = devm_gpio_request(&pdev->dev, cs_gpio, + dev_name(&pdev->dev)); + if (ret) + goto out; + } + } + } else if (has_acpi_companion(&pdev->dev)) { + int n; + int *cs; + struct gpio_desc *gpiod; + + n = gpiod_count(&pdev->dev, "cs"); + + cs = devm_kcalloc(&pdev->dev, n, sizeof(int), GFP_KERNEL); + fts->cs = cs; + + for (i = 0; i < n; i++) { + gpiod = devm_gpiod_get_index_optional(&pdev->dev, "cs", i, + GPIOD_OUT_LOW); + + if (IS_ERR(gpiod)) { + ret = PTR_ERR(gpiod); + goto out; + } + + cs_gpio = desc_to_gpio(gpiod); + cs[i] = cs_gpio; + } + } + + device_property_read_u32(&pdev->dev, "global-cs", &global_cs); + fts->global_cs = global_cs; + + fts->dma_get_ddrdata = false; + if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_PHYTIUM_FTC872) + fts->dma_get_ddrdata = true; + + ret = spi_phyt_add_host(&pdev->dev, fts); + if (ret) + goto out; + + platform_set_drvdata(pdev, fts); + + if (sysfs_create_group(&pdev->dev.kobj, &spi_phyt_device_group)) + dev_warn(&pdev->dev, "failed create sysfs\n"); + + return 0; + +out: + clk_disable_unprepare(fts->clk); + return ret; +} + +static int spi_phyt_remove(struct platform_device *pdev) +{ + struct phytium_spi *fts = platform_get_drvdata(pdev); + + spi_phyt_remove_host(fts); + sysfs_remove_group(&pdev->dev.kobj, &spi_phyt_device_group); + clk_disable_unprepare(fts->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int spi_phyt_suspend(struct device *dev) +{ + struct phytium_spi *fts = dev_get_drvdata(dev); + + return spi_phyt_suspend_host(fts); +} + +static int spi_phyt_resume(struct device *dev) +{ + struct phytium_spi *fts = dev_get_drvdata(dev); + + return spi_phyt_resume_host(fts); +} +#endif + +static SIMPLE_DEV_PM_OPS(spi_phyt_pm_ops, spi_phyt_suspend, spi_phyt_resume); + +static const struct of_device_id spi_phyt_of_match[] = { + { .compatible = "phytium,spi-2.0", .data = (void *)0 }, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, spi_phyt_of_match); + +static const struct acpi_device_id spi_phyt_acpi_match[] = { + {"PHYT0060", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, spi_phyt_acpi_match); + +static struct platform_driver spi_phyt_driver = { + .probe = spi_phyt_probe, + .remove = spi_phyt_remove, + .driver = { + .name = DRIVER_NAME_PHYT, + .of_match_table = of_match_ptr(spi_phyt_of_match), + .acpi_match_table = ACPI_PTR(spi_phyt_acpi_match), + .pm = &spi_phyt_pm_ops, + }, +}; +module_platform_driver(spi_phyt_driver); + +MODULE_AUTHOR("Peng Min "); +MODULE_DESCRIPTION("Platform Driver for Phytium SPI controller core"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/spi/spi-phytium-v2.c b/drivers/spi/spi-phytium-v2.c new file mode 100644 index 0000000000..81df999183 --- /dev/null +++ b/drivers/spi/spi-phytium-v2.c @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SPI core controller driver. + * + * Copyright (c) 2023-2024, Phytium Technology Co., Ltd.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spi-phytium.h" + +static inline void spi_phyt_enable_chip(struct phytium_spi *fts, u8 enable) +{ + u8 val = enable ? 1 : 2; + + spi_phytium_set_cmd8(fts, PHYTSPI_MSG_CMD_SET_MODULE_EN, val); +} + +static inline void spi_phyt_set_clk(struct phytium_spi *fts, u16 div) +{ + u32 new_div = div; + + spi_phytium_set_cmd32(fts, PHYTSPI_MSG_CMD_SET_BAUDR, new_div); +} + +static inline void spi_phyt_dma_reset(struct phytium_spi *fts, u8 enable) +{ + spi_phytium_set_cmd8(fts, PHYTSPI_MSG_CMD_SET_DMA_RESET, enable); +} + +static inline void spi_phyt_global_cs(struct phytium_spi *fts) +{ + u32 global_cs_en; + u16 cs; + + global_cs_en = GENMASK(fts->num_cs-1, 0) << fts->num_cs; + + cs = (u16)((0x1 << 8) | global_cs_en); + spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_CS, cs); +} + +static inline void spi_phyt_reset_chip(struct phytium_spi *fts) +{ + spi_phyt_dma_reset(fts, 1); + spi_phyt_enable_chip(fts, 0); + if (fts->global_cs) + spi_phyt_global_cs(fts); + spi_phyt_enable_chip(fts, 1); +} + +static inline void spi_phyt_shutdown_chip(struct phytium_spi *fts) +{ + spi_phyt_enable_chip(fts, 0); + spi_phyt_set_clk(fts, 0); +} + +struct phytium_spi_chip { + u8 poll_mode; + u8 type; + void (*cs_control)(u32 command); +}; + +struct chip_data { + u8 cs; + u8 tmode; + u8 type; + + u8 poll_mode; + + u16 clk_div; + u32 speed_hz; + void (*cs_control)(u32 command); +}; + +static void spi_phyt_set_cs(struct spi_device *spi, bool enable) +{ + struct phytium_spi *fts = spi_master_get_devdata(spi->master); + struct chip_data *chip = spi_get_ctldata(spi); + u32 origin; + u16 cs; + + if (fts->tx || fts->rx) + return; + + if (fts->msg->cmd_id == PHYTSPI_MSG_CMD_DATA && + fts->msg->cmd_subid == PHYTSPI_MSG_CMD_DATA_TX) + return; + + if (chip && chip->cs_control) + chip->cs_control(!enable); + + if (!enable) { + cs = BIT(spi->chip_select); + spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_CS, cs); + if (fts->global_cs) { + origin = (GENMASK(fts->num_cs-1, 0) << fts->num_cs) + | (1 << spi->chip_select); + cs = (0x1 << 8) | origin; + spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_CS, cs); + } + } else { + if (fts->global_cs) { + origin = (GENMASK(fts->num_cs-1, 0) << fts->num_cs) + & ~(1 << spi->chip_select); + cs = (0x1 << 8) | origin; + spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_CS, cs); + } + } +} + +static irqreturn_t spi_phyt_irq(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct phytium_spi *fts = spi_master_get_devdata(master); + + complete(&fts->cmd_completion); + writel_relaxed(0, fts->regfile + SPI_REGFILE_RV2AP_INTR_STATE); + writel_relaxed(0x10, fts->regfile + SPI_REGFILE_RV2AP_INT_CLEAN); + + return IRQ_HANDLED; +} + +static int spi_phyt_transfer_one(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *transfer) +{ + struct phytium_spi *fts = spi_master_get_devdata(master); + struct chip_data *chip = spi_get_ctldata(spi); + int ret = 0; + + fts->tx = (void *)transfer->tx_buf; + fts->tx_end = fts->tx + transfer->len; + fts->rx = transfer->rx_buf; + fts->rx_end = fts->rx + transfer->len; + fts->len = transfer->len; + + if (chip->cs_control) { + if (fts->rx && fts->tx) + chip->tmode = TMOD_TR; + else if (fts->rx) + chip->tmode = TMOD_RO; + else + chip->tmode = TMOD_TO; + } + + if (fts->tx) { + if ((*(u8 *)fts->tx == SPINOR_OP_WREN) && fts->spi_write_flag == 0) { + spi_phytium_write_pre(fts, spi->chip_select, + transfer->bits_per_word, spi->mode, + chip->tmode, 3, fts->spi_write_flag); + fts->spi_write_flag++; + return 0; + } + + if ((*(u8 *)fts->tx == SPINOR_OP_BE_4K) && (fts->spi_write_flag == 1) && + fts->flash_read == 0 && fts->flash_erase != 1) { + fts->spi_write_flag++; + fts->flash_erase = 1; + return 0; + } + + if ((*(u8 *)fts->tx == SPINOR_OP_CHIP_ERASE) && (fts->spi_write_flag == 1) && + fts->flash_read == 0 && fts->flash_erase == 0) { + ret = spi_phytium_flash_erase(fts, spi->chip_select, + transfer->bits_per_word, + spi->mode, chip->tmode, 3, SPINOR_OP_CHIP_ERASE); + fts->spi_write_flag = 0; + fts->flash_erase = 2; + } + + if ((*(u8 *)fts->tx == SPINOR_OP_READ || *(u8 *)fts->tx == SPINOR_OP_READ_FAST || + *(u8 *)fts->tx == SPINOR_OP_READ_4B || + *(u8 *)fts->tx == SPINOR_OP_READ_FAST_4B) && + fts->spi_write_flag == 0 && fts->flash_read == 0 && + fts->flash_erase == 0) { + fts->flash_cmd = *(u8 *)fts->tx; + fts->spi_write_flag++; + fts->flash_read = 1; + return 0; + } + + if ((fts->spi_write_flag == 1) && fts->flash_read == 0 && + fts->flash_write == 0 && ((*(u8 *)fts->tx == SPINOR_OP_PP) || + (*(u8 *)fts->tx == SPINOR_OP_PP_4B))) { + fts->flash_cmd = *(u8 *)fts->tx; + fts->spi_write_flag++; + fts->flash_write = 1; + return 0; + } + + if ((*(u8 *)fts->tx == SPINOR_OP_RDSR) && fts->flash_write == 3) { + fts->read_sr = 1; + fts->flash_write = 0; + return 0; + } + + if ((*(u8 *)fts->tx == SPINOR_OP_RDSR) && fts->flash_erase == 2) { + fts->read_sr = 1; + return 0; + } + } + + if (fts->read_sr) { + *(u8 *)(fts->rx) = 0; + fts->read_sr = 0; + return 0; + } + + if (fts->tx) { + if (fts->flash_erase == 1) { + ret = spi_phytium_flash_erase(fts, spi->chip_select, + transfer->bits_per_word, + spi->mode, chip->tmode, 3, SPINOR_OP_BE_4K); + if (ret) { + dev_err(&master->dev, "flash erase failed\n"); + return ret; + } + fts->spi_write_flag = 0; + fts->flash_erase++; + } else if (fts->flash_read) { + ret = spi_phytium_flash_erase(fts, spi->chip_select, + transfer->bits_per_word, + spi->mode, chip->tmode, 1, fts->flash_cmd); + if (ret) { + dev_err(&master->dev, "transfer read-command failed\n"); + return ret; + } + fts->spi_write_flag = 0; + fts->flash_read = 0; + } else if (fts->flash_write == 1) { + fts->flash_write++; + ret = spi_phytium_flash_write(fts, fts->flash_cmd); + if (ret) { + dev_err(&master->dev, "flash write failed\n"); + return ret; + } + } else if (fts->flash_erase == 2 && (*(u8 *)fts->tx == SPINOR_OP_WRDI)) { + ret = spi_phytium_write(fts, spi->chip_select, transfer->bits_per_word, + spi->mode, chip->tmode, 3, fts->spi_write_flag); + if (ret) { + dev_err(&master->dev, "transfer disable-command failed\n"); + return ret; + } + fts->flash_erase = 0; + } else { + ret = spi_phytium_write(fts, spi->chip_select, transfer->bits_per_word, + spi->mode, chip->tmode, 1, fts->spi_write_flag); + if (ret) { + dev_err(&master->dev, "write command failed\n"); + return ret; + } + if (fts->flash_write == 2) + fts->flash_write++; + fts->spi_write_flag = 0; + } + } + + if (fts->rx) { + ret = spi_phytium_read(fts, spi->chip_select, transfer->bits_per_word, + spi->mode, chip->tmode, 2); + if (ret) { + dev_err(&master->dev, "read data failed\n"); + return ret; + } + } + + return ret; +} + +static void spi_phyt_handle_err(struct spi_master *master, + struct spi_message *msg) +{ + struct phytium_spi *fts = spi_master_get_devdata(master); + + spi_phyt_reset_chip(fts); +} + +static int spi_phyt_setup(struct spi_device *spi) +{ + struct phytium_spi_chip *chip_info = NULL; + struct chip_data *chip; + struct spi_master *master = spi->master; + struct phytium_spi *fts = spi_master_get_devdata(master); + u8 data_width, scph, scpol, tmode; + u16 mode; + u16 clk_div; + + spi_phyt_enable_chip(fts, 0); + + clk_div = (fts->max_freq / spi->max_speed_hz + 1) & 0xfffe; + spi_phyt_set_clk(fts, clk_div); + fts->clk_div = clk_div; + + chip = spi_get_ctldata(spi); + if (!chip) { + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + spi_set_ctldata(spi, chip); + } + + chip_info = spi->controller_data; + + if (chip_info) { + if (chip_info->cs_control) + chip->cs_control = chip_info->cs_control; + + chip->poll_mode = chip_info->poll_mode; + chip->type = chip_info->type; + } + + chip->tmode = 0; + + data_width = spi->bits_per_word; + spi_phytium_set_cmd8(fts, PHYTSPI_MSG_CMD_SET_DATA_WIDTH, data_width); + + scph = spi->mode & (0x1); + scpol = spi->mode >> 1; + mode = (scph << 8) | scpol; + spi_phytium_set_cmd16(fts, PHYTSPI_MSG_CMD_SET_MODE, mode); + + tmode = chip->tmode; + spi_phytium_set_cmd8(fts, PHYTSPI_MSG_CMD_SET_TMOD, tmode); + + spi_phyt_enable_chip(fts, 1); + + return 0; +} + +static void spi_phyt_cleanup(struct spi_device *spi) +{ + struct chip_data *chip = spi_get_ctldata(spi); + + kfree(chip); + spi_set_ctldata(spi, NULL); +} + +void spi_phyt_enable_debug(struct phytium_spi *fts) +{ + u32 reg; + + reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG); + + phytium_write_regfile(fts, SPI_REGFILE_DEBUG, + reg | SPI_REGFILE_DEBUG_VAL); +} + +void spi_phyt_disable_debug(struct phytium_spi *fts) +{ + u32 reg; + + reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG); + reg &= ~SPI_REGFILE_DEBUG_VAL; + + phytium_write_regfile(fts, SPI_REGFILE_DEBUG, reg); +} + +void spi_phyt_disable_alive(struct phytium_spi *fts) +{ + u32 reg; + + reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG); + reg &= ~SPI_REGFILE_ALIVE_VAL; + + phytium_write_regfile(fts, SPI_REGFILE_DEBUG, reg); +} + +void spi_watchdog(struct phytium_spi *fts) +{ + u32 reg; + + reg = phytium_read_regfile(fts, SPI_REGFILE_DEBUG); + phytium_write_regfile(fts, SPI_REGFILE_DEBUG, + reg | SPI_REGFILE_HEARTBIT_VAL); +} + +static void spi_phyt_timer_handle(struct timer_list *t) +{ + struct phytium_spi *fts = from_timer(fts, t, timer); + + if (fts->alive_enabled && fts->watchdog) { + if (fts->runtimes < 20) + fts->watchdog(fts); + + fts->runtimes++; + } + + mod_timer(&fts->timer, jiffies + msecs_to_jiffies(10)); +} + +static void spi_phyt_hw_init(struct device *dev, struct phytium_spi *fts) +{ + spi_phytium_default(fts); +} + +int spi_phyt_add_host(struct device *dev, struct phytium_spi *fts) +{ + struct spi_master *master; + int ret; + + WARN_ON(fts == NULL); + + master = spi_alloc_master(dev, 0); + if (!master) + return -ENOMEM; + + fts->master = master; + snprintf(fts->name, sizeof(fts->name), "phytium_spi%d", fts->bus_num); + + init_completion(&fts->cmd_completion); + ret = devm_request_irq(dev, fts->irq, spi_phyt_irq, IRQF_SHARED, fts->name, master); + if (ret < 0) { + dev_err(dev, "can not get IRQ\n"); + goto err_free_master; + } + + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); + master->bus_num = fts->bus_num; + master->num_chipselect = fts->num_cs; + master->setup = spi_phyt_setup; + master->cleanup = spi_phyt_cleanup; + master->set_cs = spi_phyt_set_cs; + master->transfer_one = spi_phyt_transfer_one; + master->handle_err = spi_phyt_handle_err; + master->max_speed_hz = fts->max_freq; + master->dev.of_node = dev->of_node; + master->dev.fwnode = dev->fwnode; + master->flags = SPI_MASTER_GPIO_SS; + + spi_master_set_devdata(master, fts); + + spi_phyt_disable_debug(fts); + spi_phyt_disable_alive(fts); + fts->runtimes = 0; + fts->debug_enabled = false; + fts->alive_enabled = false; + + fts->watchdog = spi_watchdog; + + fts->timer.expires = jiffies + msecs_to_jiffies(50); + timer_setup(&fts->timer, spi_phyt_timer_handle, 0); + add_timer(&fts->timer); + + spi_phyt_hw_init(dev, fts); + + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(&master->dev, "problem registering spi master\n"); + goto err_exit; + } + + return 0; + +err_exit: + spi_phyt_enable_chip(fts, 0); +err_free_master: + spi_master_put(master); + return ret; +} +EXPORT_SYMBOL_GPL(spi_phyt_add_host); + +void spi_phyt_remove_host(struct phytium_spi *fts) +{ + del_timer(&fts->timer); + spi_phyt_shutdown_chip(fts); +} +EXPORT_SYMBOL_GPL(spi_phyt_remove_host); + +int spi_phyt_suspend_host(struct phytium_spi *fts) +{ + int ret; + + ret = spi_controller_suspend(fts->master); + if (ret) + return ret; + + spi_phyt_shutdown_chip(fts); + return 0; +} +EXPORT_SYMBOL_GPL(spi_phyt_suspend_host); + +int spi_phyt_resume_host(struct phytium_spi *fts) +{ + int ret; + + spi_phyt_hw_init(&fts->master->dev, fts); + + spi_phyt_enable_chip(fts, 0); + spi_phyt_set_clk(fts, fts->clk_div); + spi_phyt_enable_chip(fts, 1); + + ret = spi_controller_resume(fts->master); + if (ret) + dev_err(&fts->master->dev, "fail to start queue (%d)\n", ret); + return ret; +} +EXPORT_SYMBOL_GPL(spi_phyt_resume_host); + +MODULE_AUTHOR("Peng Min "); +MODULE_DESCRIPTION("Driver for Phytium SPI controller core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-phytium.h b/drivers/spi/spi-phytium.h index 003b08f8c5..c328ddfa07 100644 --- a/drivers/spi/spi-phytium.h +++ b/drivers/spi/spi-phytium.h @@ -1,4 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Phytium SPI controller driver. + * + * Copyright (c) 2019-2023, Phytium Technology Co., Ltd. + */ #ifndef PHYTIUM_SPI_HEADER_H #define PHYTIUM_SPI_HEADER_H @@ -54,6 +59,81 @@ #define SPI_WAIT_RETRIES 5 +#define SPI_REGFILE_SIZE (0x48) +#define SPI_REGFILE_AP2RV_INTR_STATE (0x24) +#define SPI_REGFILE_RV2AP_INTR_STATE (0x2c) +#define SPI_REGFILE_RV2AP_INT_CLEAN (0x74) +#define SPI_REGFILE_DEBUG (0x58) + +#define SPI_REGFILE_DEBUG_VAL BIT(0) +#define SPI_REGFILE_ALIVE_VAL BIT(1) +#define SPI_REGFILE_HEARTBIT_VAL BIT(2) + +#define SPI_MODULE_OPT_CMD 0x20 + +#define SPI_TRANS_DATA_SIZE 1024 +#define FLASH_PAGE_SIZE 256 + +#define SPI_MSG_COMPLETE_OK 1 +#define SPI_MSG_COMPLETE_KO 0 + +#define SPI_RESULT_IGNORE_LVL (0) +#define SPI_RESULT_WRITE_ISR_LVL (1) +#define SPI_RESULT_READ_ISR_LVL (2) + +#define SPI_SHMEM_TX_MSG_MAX_CNT 1 + +#define SPI_MASTER_TIMEOUT 8 + +#define SPI_DEFAULT_CLK 50000000 + +enum phytspi_msg_cmd_id { + PHYTSPI_MSG_CMD_DEFAULT = 0, + PHYTSPI_MSG_CMD_SET, + PHYTSPI_MSG_CMD_GET, + PHYTSPI_MSG_CMD_DATA, + PHYTSPI_MSG_CMD_REPORT, +}; + +enum phytspi_set_subid { + PHYTSPI_MSG_CMD_SET_MODULE_EN = 0, + PHYTSPI_MSG_CMD_SET_DATA_WIDTH, + PHYTSPI_MSG_CMD_SET_MODE, + PHYTSPI_MSG_CMD_SET_TMOD, + PHYTSPI_MSG_CMD_SET_BAUDR, + PHYTSPI_MSG_CMD_SET_INT_TI, + PHYTSPI_MSG_CMD_SET_NDF, + PHYTSPI_MSG_CMD_SET_CS, + PHYTSPI_MSG_CMD_SET_DMA_RESET, +}; + +enum phytspi_data_subid { + PHYTSPI_MSG_CMD_DATA_TX = 0, + PHYTSPI_MSG_CMD_DATA_RX, + PHYTSPI_MSG_CMD_DATA_FLASH_TX, + PHYTSPI_MSG_CMD_FLASH_ERASE, + PHYTSPI_MSG_CMD_DATA_DMA_TX, + PHYTSPI_MSG_CMD_DATA_DMA_RX, + PHYTSPI_MSG_CMD_DATA_FLASH_DMA_TX, +}; + +struct msg { + u8 reserved; + u8 seq; + u8 cmd_id; + u8 cmd_subid; + u16 len; + u8 status1; + u8 status0; + u8 data[56]; +}; + +struct spi_trans_msg_info { + u32 msg_total_num; + u32 shmem_data_addr; + int result; +}; + struct phytium_spi; struct phytium_spi_dma_ops { @@ -71,8 +151,21 @@ struct phytium_spi { char name[16]; void __iomem *regs; + void __iomem *regfile; + void __iomem *tx_shmem_addr; + void *rx_shmem_addr; + + struct msg *msg; + u32 mem_tx_physic; + u32 mem_rx_physic; + u64 mem_tx; + u64 mem_rx; + + u16 clk_div; + int module; + bool global_cs; - bool dma_en; + bool dma_en; unsigned long paddr; int irq; u32 fifo_len; @@ -92,9 +185,26 @@ struct phytium_spi { int dma_mapped; struct clk *clk; irqreturn_t (*transfer_handler)(struct phytium_spi *fts); - u32 current_freq; /* frequency in hz */ + + int cmd_err; + u32 cur_tx_tail; + struct completion cmd_completion; + + u8 spi_write_flag; + u8 flash_erase; + u8 flash_read; + u8 flash_write; + u8 read_sr; + u8 flash_cmd; + + bool debug_enabled; + bool alive_enabled; + struct timer_list timer; + u32 runtimes; // for debug + void (*watchdog)(struct phytium_spi *fts); /* DMA info */ + u32 current_freq; /* frequency in hz */ struct dma_chan *txchan; u32 txburst; struct dma_chan *rxchan; @@ -104,6 +214,8 @@ struct phytium_spi { dma_addr_t dma_addr; /* phy address of the Data register */ const struct phytium_spi_dma_ops *dma_ops; struct completion dma_completion; + + bool dma_get_ddrdata; }; static inline u32 phytium_readl(struct phytium_spi *fts, u32 offset) @@ -115,17 +227,14 @@ static inline u16 phytium_readw(struct phytium_spi *fts, u32 offset) { return __raw_readw(fts->regs + offset); } - static inline void phytium_writel(struct phytium_spi *fts, u32 offset, u32 val) { __raw_writel(val, fts->regs + offset); } - static inline void phytium_writew(struct phytium_spi *fts, u32 offset, u16 val) { __raw_writew(val, fts->regs + offset); } - static inline u32 phytium_read_io_reg(struct phytium_spi *fts, u32 offset) { switch (fts->reg_io_width) { @@ -136,8 +245,8 @@ static inline u32 phytium_read_io_reg(struct phytium_spi *fts, u32 offset) return phytium_readl(fts, offset); } } - -static inline void phytium_write_io_reg(struct phytium_spi *fts, u32 offset, u32 val) +static inline void phytium_write_io_reg(struct phytium_spi *fts, + u32 offset, u32 val) { switch (fts->reg_io_width) { case 2: @@ -149,17 +258,14 @@ static inline void phytium_write_io_reg(struct phytium_spi *fts, u32 offset, u32 break; } } - static inline void spi_enable_chip(struct phytium_spi *fts, int enable) { phytium_writel(fts, SSIENR, (enable ? 1 : 0)); } - static inline void spi_set_clk(struct phytium_spi *fts, u16 div) { phytium_writel(fts, BAUDR, div); } - static inline void spi_mask_intr(struct phytium_spi *fts, u32 mask) { u32 new_mask; @@ -167,7 +273,6 @@ static inline void spi_mask_intr(struct phytium_spi *fts, u32 mask) new_mask = phytium_readl(fts, IMR) & ~mask; phytium_writel(fts, IMR, new_mask); } - static inline void spi_umask_intr(struct phytium_spi *fts, u32 mask) { u32 new_mask; @@ -175,7 +280,6 @@ static inline void spi_umask_intr(struct phytium_spi *fts, u32 mask) new_mask = phytium_readl(fts, IMR) | mask; phytium_writel(fts, IMR, new_mask); } - static inline void spi_global_cs(struct phytium_spi *fts) { u32 global_cs_en, mask, setmask; @@ -183,10 +287,8 @@ static inline void spi_global_cs(struct phytium_spi *fts) mask = GENMASK(fts->num_cs-1, 0) << fts->num_cs; setmask = ~GENMASK(fts->num_cs-1, 0); global_cs_en = (phytium_readl(fts, GCSR) | mask) & setmask; - phytium_writel(fts, GCSR, global_cs_en); } - static inline void spi_reset_chip(struct phytium_spi *fts) { spi_enable_chip(fts, 0); @@ -195,7 +297,6 @@ static inline void spi_reset_chip(struct phytium_spi *fts) spi_mask_intr(fts, 0xff); spi_enable_chip(fts, 1); } - static inline void spi_shutdown_chip(struct phytium_spi *fts) { spi_enable_chip(fts, 0); @@ -203,6 +304,36 @@ static inline void spi_shutdown_chip(struct phytium_spi *fts) fts->current_freq = 0; } +static inline u32 phytium_read_regfile(struct phytium_spi *fts, u32 reg_off) +{ + return readl_relaxed(fts->regfile + reg_off); +} + +static inline void phytium_write_regfile(struct phytium_spi *fts, u32 reg_off, u32 val) +{ + writel_relaxed(val, fts->regfile + reg_off); +} + +extern void spi_phytium_default(struct phytium_spi *fts); +extern void spi_phytium_set_cmd8(struct phytium_spi *fts, u16 sub_cmd, u8 data); +extern void spi_phytium_set_cmd16(struct phytium_spi *fts, u16 sub_cmd, u16 data); +extern void spi_phytium_set_cmd32(struct phytium_spi *fts, u16 sub_cmd, u32 data); +extern void spi_phytium_data_cmd_write(struct phytium_spi *fts, u16 sub_cmd); +extern void spi_phytium_data_cmd_read(struct phytium_spi *fts, u16 sub_cmd); +extern void spi_phytium_write_pre(struct phytium_spi *fts, u8 cs, u8 dfs, u8 mode, u8 tmode, + u8 flags, u8 spi_write_flag); +extern int spi_phytium_flash_erase(struct phytium_spi *fts, u8 cs, u8 dfs, u8 mode, + u8 tmode, u8 flags, u8 cmd); +extern int spi_phytium_flash_write(struct phytium_spi *fts, u8 cmd); +extern int spi_phytium_write(struct phytium_spi *fts, u8 cs, u8 dfs, u8 mode, + u8 tmode, u8 flags, u8 spi_write_flag); +extern int spi_phytium_read(struct phytium_spi *fts, u8 cs, u8 dfs, u8 mode, + u8 tmode, u8 flags); +extern int spi_phyt_add_host(struct device *dev, struct phytium_spi *fts); +extern void spi_phyt_remove_host(struct phytium_spi *fts); +extern int spi_phyt_suspend_host(struct phytium_spi *fts); +extern int spi_phyt_resume_host(struct phytium_spi *fts); + extern int phytium_spi_add_host(struct device *dev, struct phytium_spi *fts); extern void phytium_spi_remove_host(struct phytium_spi *fts); extern int phytium_spi_suspend_host(struct phytium_spi *fts); -- Gitee From 49701c175f08a6404f03963c27026d7376195e1d Mon Sep 17 00:00:00 2001 From: Peng Min Date: Tue, 25 Feb 2025 16:51:43 +0800 Subject: [PATCH 096/154] qspi: phytium: Fix the cmd_port register incorrectly configured issue The dummy bit of the cmd_port register is incorrectly configured, which will results in a 1-1-4 indirect read error. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: I3e6c67ac4c01414e933d7a448edd97a66510bb17 --- drivers/spi/spi-phytium-qspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-phytium-qspi.c b/drivers/spi/spi-phytium-qspi.c index 68cf760136..57586fb3a8 100755 --- a/drivers/spi/spi-phytium-qspi.c +++ b/drivers/spi/spi-phytium-qspi.c @@ -446,8 +446,8 @@ static int phytium_qspi_exec_op(struct spi_mem *mem, if (op->dummy.nbytes) { cmd |= QSPI_CMD_PORT_LATENCY_MASK; - cmd |= ((op->dummy.nbytes * 8) / op->dummy.buswidth) << - QSPI_CMD_PORT_LATENCY_SHIFT; + cmd |= ((op->dummy.nbytes * 8 - 1) / op->dummy.buswidth) << + QSPI_CMD_PORT_DUMMY_SHIFT; } if (op->data.nbytes) { -- Gitee From e22b01b6d9689c6b3ee7b34e94e155ded519ac83 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Tue, 4 Mar 2025 17:36:16 +0800 Subject: [PATCH 097/154] qspi: phytium: Slove the capacity register configured error issue Functions that configure capacity registers should shift left instead of right, which will lead to capacity register configured error. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: I138c90d143e1b107ea747125e98a79c3b76efda4 --- drivers/spi/spi-phytium-qspi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-phytium-qspi.c b/drivers/spi/spi-phytium-qspi.c index 57586fb3a8..6350d7c0cd 100755 --- a/drivers/spi/spi-phytium-qspi.c +++ b/drivers/spi/spi-phytium-qspi.c @@ -267,28 +267,28 @@ static int phytium_qspi_flash_capacity_encode_new(u32 size, switch (size) { case SZ_4M: - *cap |= (0x0 >> (4 * i)); + *cap |= (0x0 << (4 * i)); break; case SZ_8M: - *cap |= (0x1 >> (4 * i)); + *cap |= (0x1 << (4 * i)); break; case SZ_16M: - *cap |= (0x2 >> (4 * i)); + *cap |= (0x2 << (4 * i)); break; case SZ_32M: - *cap |= (0x3 >> (4 * i)); + *cap |= (0x3 << (4 * i)); break; case SZ_64M: - *cap |= (0x4 >> (4 * i)); + *cap |= (0x4 << (4 * i)); break; case SZ_128M: - *cap |= (0x5 >> (4 * i)); + *cap |= (0x5 << (4 * i)); break; case SZ_256M: - *cap |= (0x6 >> (4 * i)); + *cap |= (0x6 << (4 * i)); break; case SZ_512M: - *cap |= (0x7 >> (4 * i)); + *cap |= (0x7 << (4 * i)); break; default: ret = -EINVAL; -- Gitee From 10045eb07d6b1e459a2b89cbf699065110d57481 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 18 Sep 2024 11:26:41 +0800 Subject: [PATCH 098/154] uaccess: add generic fallback version of copy_mc_to_user() x86/powerpc has it's implementation of copy_mc_to_user(), we add generic fallback in include/linux/uaccess.h prepare for other architechures to enable CONFIG_ARCH_HAS_COPY_MC. Signed-off-by: Tong Tiangen Signed-off-by: Wang Yinfeng Change-Id: I15e9cf6b1bb19b2ecffd9603463a4f56f4d6de92 --- arch/powerpc/include/asm/uaccess.h | 1 + include/linux/uaccess.h | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index 6b808bcdec..809e05ab4a 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -388,6 +388,7 @@ copy_mc_to_user(void __user *to, const void *from, unsigned long n) return n; } +#define copy_mc_to_user copy_mc_to_user #endif #ifdef __powerpc64__ diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 20668760da..f3a37b3e87 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -224,6 +224,15 @@ copy_mc_to_kernel(void *dst, const void *src, size_t cnt) } #endif +#ifndef copy_mc_to_user +static inline unsigned long __must_check +copy_mc_to_user(void *dst, const void *src, size_t cnt) +{ + check_object_size(src, cnt, true); + return raw_copy_to_user(dst, src, cnt); +} +#endif + static __always_inline void pagefault_disabled_inc(void) { current->pagefault_disabled++; -- Gitee From 6835377637a3c070c05d87fca6006c19ec44a6a1 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 18 Sep 2024 11:28:15 +0800 Subject: [PATCH 099/154] arm64: extable: add new extable type "__mc_ex_table" A new type of extable is added, which is specially used fixup for machine check safe. In order to keep kabi consistency, we cannot add type and data members to struct exception_table_entry, so we put the fixup entry to new extable separately. Signed-off-by: Tong Tiangen Signed-off-by: Wang Yinfeng Change-Id: Icbf3eb5a82c9451fadbd06e1a20782f64b31b6d9 --- arch/arm64/Kconfig | 3 + arch/arm64/include/asm/assembler.h | 15 + arch/arm64/include/asm/uaccess.h | 10 + include/asm-generic/vmlinux.lds.h | 19 +- include/linux/extable.h | 23 ++ include/linux/kabi.h | 509 +++++++++++++++++++++++++++++ include/linux/module.h | 10 + kernel/extable.c | 29 ++ kernel/module.c | 38 +++ scripts/sorttable.h | 26 ++ 10 files changed, 681 insertions(+), 1 deletion(-) create mode 100644 include/linux/kabi.h diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 13cf137da9..ec6860357e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1094,6 +1094,9 @@ config ARCH_WANT_HUGE_PMD_SHARE config ARCH_HAS_CACHE_LINE_SIZE def_bool y +config ARCH_HAS_MC_EXTABLE + bool + config ARCH_ENABLE_SPLIT_PMD_PTLOCK def_bool y if PGTABLE_LEVELS > 2 diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 011e681a23..6188c9c301 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -136,6 +136,21 @@ alternative_endif .popsection .endm +/* + * Emit an entry into the machine check exception table + */ +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE + .macro _asm_mc_extable, from, to + .pushsection __mc_ex_table, "a" + .align 3 + .long (\from - .), (\to - .) + .popsection + .endm +#else + .macro _asm_mc_extable, from, to + .endm +#endif + #define USER(l, x...) \ 9999: x; \ _asm_extable 9999b, l diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 385a189f7d..feb73906bd 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -102,6 +102,16 @@ static inline unsigned long __range_ok(const void __user *addr, unsigned long si " .long (" #from " - .), (" #to " - .)\n" \ " .popsection\n" +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE +#define _ASM_MC_EXTABLE(from, to) \ + " .pushsection __mc_ex_table, \"a\"\n" \ + " .align 3\n" \ + " .long (" #from " - .), (" #to " - .)\n" \ + " .popsection\n" +#else +#define _ASM_MC_EXTABLE(from, to) +#endif + /* * User access enabling/disabling. */ diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 44103f9487..359916b4f2 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -76,7 +76,9 @@ * alignment. */ #ifdef RO_EXCEPTION_TABLE_ALIGN -#define RO_EXCEPTION_TABLE EXCEPTION_TABLE(RO_EXCEPTION_TABLE_ALIGN) +#define RO_EXCEPTION_TABLE \ + EXCEPTION_TABLE(RO_EXCEPTION_TABLE_ALIGN) \ + MC_EXCEPTION_TABLE(RO_EXCEPTION_TABLE_ALIGN) #else #define RO_EXCEPTION_TABLE #endif @@ -676,6 +678,21 @@ __stop___ex_table = .; \ } +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE +/* + * Machine Check Exception table + */ +#define MC_EXCEPTION_TABLE(align) \ + . = ALIGN(align); \ + __mc_ex_table : AT(ADDR(__mc_ex_table) - LOAD_OFFSET) { \ + __start___mc_ex_table = .; \ + KEEP(*(__mc_ex_table)) \ + __stop___mc_ex_table = .; \ + } +#else +#define MC_EXCEPTION_TABLE(align) +#endif + /* * .BTF */ diff --git a/include/linux/extable.h b/include/linux/extable.h index 4ab9e78f31..e608f8a8df 100644 --- a/include/linux/extable.h +++ b/include/linux/extable.h @@ -19,18 +19,41 @@ void trim_init_extable(struct module *m); /* Given an address, look for it in the exception tables */ const struct exception_table_entry *search_exception_tables(unsigned long add); +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE +const struct exception_table_entry *search_mc_exception_tables(unsigned long add); +#else +static inline const struct exception_table_entry * +search_mc_exception_tables(unsigned long add) +{ + return NULL; +} +#endif const struct exception_table_entry * search_kernel_exception_table(unsigned long addr); #ifdef CONFIG_MODULES /* For extable.c to search modules' exception tables. */ const struct exception_table_entry *search_module_extables(unsigned long addr); +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE +const struct exception_table_entry *search_module_mc_extables(unsigned long addr); +#else +static inline const struct exception_table_entry * +search_module_mc_extables(unsigned long addr) +{ + return NULL; +} +#endif #else static inline const struct exception_table_entry * search_module_extables(unsigned long addr) { return NULL; } +static inline const struct exception_table_entry * +search_module_mc_extables(unsigned long addr) +{ + return NULL; +} #endif /*CONFIG_MODULES*/ #ifdef CONFIG_BPF_JIT diff --git a/include/linux/kabi.h b/include/linux/kabi.h new file mode 100644 index 0000000000..fe3213c0f5 --- /dev/null +++ b/include/linux/kabi.h @@ -0,0 +1,509 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * kabi.h - openEuler kABI abstraction header + * + * Copyright (c) 2014 Don Zickus + * Copyright (c) 2015-2018 Jiri Benc + * Copyright (c) 2015 Sabrina Dubroca, Hannes Frederic Sowa + * Copyright (c) 2016-2018 Prarit Bhargava + * Copyright (c) 2017 Paolo Abeni, Larry Woodman + * Copyright (c) 2021 Xie XiuQi + * + * This file is released under the GPLv2. + * See the file COPYING for more details. + * + * These kabi macros hide the changes from the kabi checker and from the + * process that computes the exported symbols' checksums. + * They have 2 variants: one (defined under __GENKSYMS__) used when + * generating the checksums, and the other used when building the kernel's + * binaries. + * + * The use of these macros does not guarantee that the usage and modification + * of code is correct. As with all openEuler only changes, an engineer must + * explain why the use of the macro is valid in the patch containing the + * changes. + * + * The macro helpers are derived from RHEL "include/linux/rh_kabi.h" + * Mostly debrand from RHEL. + */ + +#ifndef _LINUX_KABI_H +#define _LINUX_KABI_H + +#include +#include + +/* + * NOTE + * Unless indicated otherwise, don't use ';' after these macros as it + * messes up the kABI checker by changing what the resulting token string + * looks like. Instead let the macros add the ';' so it can be properly + * hidden from the kABI checker (mainly for KABI_EXTEND, but applied to + * most macros for uniformity). + * + * KABI_CONST + * Adds a new const modifier to a function parameter preserving the old + * checksum. + * + * KABI_ADD_MODIFIER + * Adds a new modifier to a function parameter or a typedef, preserving + * the old checksum. Useful e.g. for adding rcu annotations or changing + * int to unsigned. Beware that this may change the semantics; if you're + * sure this is safe, always explain why binary compatibility with 3rd + * party modules is retained. + * + * KABI_DEPRECATE + * Marks the element as deprecated and make it unusable by modules while + * keeping a hole in its place to preserve binary compatibility. + * + * # define KABI_BROKEN_INSERT_ENUM(_new) _new, + * # define KABI_BROKEN_REMOVE_ENUM(_orig) + * KABI_DEPRECATE_FN + * Marks the function pointer as deprecated and make it unusable by modules + * while keeping a hole in its place to preserve binary compatibility. + * + * KABI_EXTEND + * Adds a new field to a struct. This must always be added to the end of + * the struct. Before using this macro, make sure this is actually safe + * to do - there is a number of conditions under which it is *not* safe. + * In particular (but not limited to), this macro cannot be used: + * - if the struct in question is embedded in another struct, or + * - if the struct is allocated by drivers either statically or + * dynamically, or + * - if the struct is allocated together with driver data (an example of + * such behavior is struct net_device or struct request). + * + * KABI_EXTEND_WITH_SIZE + * Adds a new element (usually a struct) to a struct and reserves extra + * space for the new element. The provided 'size' is the total space to + * be added in longs (i.e. it's 8 * 'size' bytes), including the size of + * the added element. It is automatically checked that the new element + * does not overflow the reserved space, now nor in the future. However, + * no attempt is done to check the content of the added element (struct) + * for kABI conformance - kABI checking inside the added element is + * effectively switched off. + * For any struct being added by KABI_EXTEND_WITH_SIZE, it is + * recommended its content to be documented as not covered by kABI + * guarantee. + * + * KABI_FILL_HOLE + * Fills a hole in a struct. + * + * Warning: only use if a hole exists for _all_ arches. Use pahole to verify. + * + * KABI_RENAME + * Renames an element without changing its type. This macro can be used in + * bitfields, for example. + * + * NOTE: does not include the final ';' + * + * KABI_REPLACE + * Replaces the _orig field by the _new field. The size of the occupied + * space is preserved, it's fine if the _new field is smaller than the + * _orig field. If a _new field is larger or has a different alignment, + * compilation will abort. + * + * + * KABI_HIDE_INCLUDE + * Hides the given include file from kABI checksum computations. This is + * used when a newly added #include makes a previously opaque struct + * visible. + * + * Example usage: + * #include KABI_HIDE_INCLUDE() + * + * KABI_FAKE_INCLUDE + * Pretends inclusion of the given file for kABI checksum computations. + * This is used when upstream removed a particular #include but that made + * some structures opaque that were previously visible and is causing kABI + * checker failures. + * + * Example usage: + * #include KABI_FAKE_INCLUDE() + * + * KABI_RESERVE + * Adds a reserved field to a struct. This is done prior to kABI freeze + * for structs that cannot be expanded later using KABI_EXTEND (for + * example because they are embedded in another struct or because they are + * allocated by drivers or because they use unusual memory layout). The + * size of the reserved field is 'unsigned long' and is assumed to be + * 8 bytes. + * + * The argument is a number unique for the given struct; usually, multiple + * KABI_RESERVE macros are added to a struct with numbers starting from + * one. + * + * Example usage: + * struct foo { + * int a; + * KABI_RESERVE(1) + * KABI_RESERVE(2) + * }; + * + * KABI_USE + * Simple wrappers to replace standard openEuler reserved elements. + * + * KABI_AUX_EMBED + * KABI_AUX_PTR + * Adds an extenstion of a struct in the form of "auxiliary structure". + * This is done prior to kABI freeze for structs that cannot be expanded + * later using KABI_EXTEND. See also KABI_RESERVED, these two + * approaches can (and often are) combined. + * + * To use this for 'struct foo' (the "base structure"), define a new + * structure called 'struct foo_resvd'; this new struct is called "auxiliary + * structure". Then add KABI_AUX_EMBED or KABI_AUX_PTR to the end + * of the base structure. The argument is the name of the base structure, + * without the 'struct' keyword. + * + * KABI_AUX_PTR stores a pointer to the aux structure in the base + * struct. The lifecycle of the aux struct needs to be properly taken + * care of. + * + * KABI_AUX_EMBED embeds the aux struct into the base struct. This + * cannot be used when the base struct is itself embedded into another + * struct, allocated in an array, etc. + * + * Both approaches (ptr and embed) work correctly even when the aux struct + * is allocated by modules. To ensure this, the code responsible for + * allocation/assignment of the aux struct has to properly set the size of + * the aux struct; see the KABI_AUX_SET_SIZE and KABI_AUX_INIT_SIZE + * macros. + * + * New fields can be later added to the auxiliary structure, always to its + * end. Note the auxiliary structure cannot be shrunk in size later (i.e., + * fields cannot be removed, only deprecated). Any code accessing fields + * from the aux struct must guard the access using the KABI_AUX macro. + * The access itself is then done via a '_resvd' field in the base struct. + * + * The auxiliary structure is not guaranteed for access by modules unless + * explicitly commented as such in the declaration of the aux struct + * itself or some of its elements. + * + * Example: + * + * struct foo_resvd { + * int newly_added; + * }; + * + * struct foo { + * bool big_hammer; + * KABI_AUX_PTR(foo) + * }; + * + * void use(struct foo *f) + * { + * if (KABI_AUX(f, foo, newly_added)) + * f->_resvd->newly_added = 123; + * else + * // the field 'newly_added' is not present in the passed + * // struct, fall back to old behavior + * f->big_hammer = true; + * } + * + * static struct foo_resvd my_foo_resvd { + * .newly_added = 0; + * } + * + * static struct foo my_foo = { + * .big_hammer = false, + * ._resvd = &my_foo_resvd, + * KABI_AUX_INIT_SIZE(foo) + * }; + * + * KABI_USE_AUX_PTR + * Creates an auxiliary structure post kABI freeze. This works by using + * two reserved fields (thus there has to be two reserved fields still + * available) and converting them to KABI_AUX_PTR. + * + * Example: + * + * struct foo_resvd { + * }; + * + * struct foo { + * int a; + * KABI_RESERVE(1) + * KABI_USE_AUX_PTR(2, 3, foo) + * }; + * + * KABI_AUX_SET_SIZE + * KABI_AUX_INIT_SIZE + * Calculates and stores the size of the auxiliary structure. + * + * KABI_AUX_SET_SIZE is for dynamically allocated base structs, + * KABI_AUX_INIT_SIZE is for statically allocated case structs. + * + * These macros must be called from the allocation (KABI_AUX_SET_SIZE) + * or declaration (KABI_AUX_INIT_SIZE) site, regardless of whether + * that happens in the kernel or in a module. Without calling one of + * these macros, the aux struct will appear to have no fields to the + * kernel. + * + * Note: since KABI_AUX_SET_SIZE is intended to be invoked outside of + * a struct definition, it does not add the semicolon and must be + * terminated by semicolon by the caller. + * + * KABI_AUX + * Verifies that the given field exists in the given auxiliary structure. + * This MUST be called prior to accessing that field; failing to do that + * may lead to invalid memory access. + * + * The first argument is a pointer to the base struct, the second argument + * is the name of the base struct (without the 'struct' keyword), the + * third argument is the field name. + * + * This macro works for structs extended by either of KABI_AUX_EMBED, + * KABI_AUX_PTR and KABI_USE_AUX_PTR. + * + * KABI_FORCE_CHANGE + * Force change of the symbol checksum. The argument of the macro is a + * version for cases we need to do this more than once. + * + * This macro does the opposite: it changes the symbol checksum without + * actually changing anything about the exported symbol. It is useful for + * symbols that are not whitelisted, we're changing them in an + * incompatible way and want to prevent 3rd party modules to silently + * corrupt memory. Instead, by changing the symbol checksum, such modules + * won't be loaded by the kernel. This macro should only be used as a + * last resort when all other KABI workarounds have failed. + * + * KABI_EXCLUDE + * !!! WARNING: DANGEROUS, DO NOT USE unless you are aware of all the !!! + * !!! implications. This should be used ONLY EXCEPTIONALLY and only !!! + * !!! under specific circumstances. Very likely, this macro does not !!! + * !!! do what you expect it to do. Note that any usage of this macro !!! + * !!! MUST be paired with a KABI_FORCE_CHANGE annotation of !!! + * !!! a suitable symbol (or an equivalent safeguard) and the commit !!! + * !!! log MUST explain why the chosen solution is appropriate. !!! + * + * Exclude the element from checksum generation. Any such element is + * considered not to be part of the kABI whitelist and may be changed at + * will. Note however that it's the responsibility of the developer + * changing the element to ensure 3rd party drivers using this element + * won't panic, for example by not allowing them to be loaded. That can + * be achieved by changing another, non-whitelisted symbol they use, + * either by nature of the change or by using KABI_FORCE_CHANGE. + * + * Also note that any change to the element must preserve its size. Change + * of the size is not allowed and would constitute a silent kABI breakage. + * Beware that the KABI_EXCLUDE macro does not do any size checks. + * + * KABI_BROKEN_INSERT + * KABI_BROKEN_REMOVE + * Insert a field to the middle of a struct / delete a field from a struct. + * Note that this breaks kABI! It can be done only when it's certain that + * no 3rd party driver can validly reach into the struct. A typical + * example is a struct that is: both (a) referenced only through a long + * chain of pointers from another struct that is part of a whitelisted + * symbol and (b) kernel internal only, it should have never been visible + * to genksyms in the first place. + * + * Another example are structs that are explicitly exempt from kABI + * guarantee but we did not have enough foresight to use KABI_EXCLUDE. + * In this case, the warning for KABI_EXCLUDE applies. + * + * A detailed explanation of correctness of every KABI_BROKEN_* macro + * use is especially important. + * + * KABI_BROKEN_INSERT_BLOCK + * KABI_BROKEN_REMOVE_BLOCK + * A version of KABI_BROKEN_INSERT / REMOVE that allows multiple fields + * to be inserted or removed together. All fields need to be terminated + * by ';' inside(!) the macro parameter. The macro itself must not be + * terminated by ';'. + * + * KABI_BROKEN_REPLACE + * Replace a field by a different one without doing any checking. This + * allows replacing a field by another with a different size. Similarly + * to other KABI_BROKEN macros, use of this indicates a kABI breakage. + * + * KABI_BROKEN_INSERT_ENUM + * KABI_BROKEN_REMOVE_ENUM + * Insert a field to the middle of an enumaration type / delete a field from + * an enumaration type. Note that this can break kABI especially if the + * number of enum fields is used in an array within a structure. It can be + * done only when it is certain that no 3rd party driver will use the + * enumeration type or a structure that embeds an array with size determined + * by an enumeration type. + * + * KABI_EXTEND_ENUM + * Adds a new field to an enumeration type. This must always be added to + * the end of the enum. Before using this macro, make sure this is actually + * safe to do. + */ +#ifdef __GENKSYMS__ + +# define KABI_CONST +# define KABI_ADD_MODIFIER(_new) +# define KABI_EXTEND(_new) +# define KABI_FILL_HOLE(_new) +# define KABI_FORCE_CHANGE(ver) __attribute__((kabi_change ## ver)) +# define KABI_RENAME(_orig, _new) _orig +# define KABI_HIDE_INCLUDE(_file) +# define KABI_FAKE_INCLUDE(_file) _file +# define KABI_BROKEN_INSERT(_new) +# define KABI_BROKEN_REMOVE(_orig) _orig; +# define KABI_BROKEN_INSERT_BLOCK(_new) +# define KABI_BROKEN_REMOVE_BLOCK(_orig) _orig +# define KABI_BROKEN_REPLACE(_orig, _new) _orig; +# define KABI_BROKEN_INSERT_ENUM(_new) +# define KABI_BROKEN_REMOVE_ENUM(_orig) _orig, +# define KABI_EXTEND_ENUM(_new) + +# define _KABI_DEPRECATE(_type, _orig) _type _orig +# define _KABI_DEPRECATE_FN(_type, _orig, _args...) _type (*_orig)(_args) +# define _KABI_REPLACE(_orig, _new) _orig +# define _KABI_EXCLUDE(_elem) + +#else + +# define KABI_ALIGN_WARNING ". Disable CONFIG_KABI_SIZE_ALIGN_CHECKS if debugging." + +# define KABI_CONST const +# define KABI_ADD_MODIFIER(_new) _new +# define KABI_EXTEND(_new) _new; +# define KABI_FILL_HOLE(_new) _new; +# define KABI_FORCE_CHANGE(ver) +# define KABI_RENAME(_orig, _new) _new +# define KABI_HIDE_INCLUDE(_file) _file +# define KABI_FAKE_INCLUDE(_file) +# define KABI_BROKEN_INSERT(_new) _new; +# define KABI_BROKEN_REMOVE(_orig) +# define KABI_BROKEN_INSERT_BLOCK(_new) _new +# define KABI_BROKEN_REMOVE_BLOCK(_orig) +# define KABI_BROKEN_REPLACE(_orig, _new) _new; +# define KABI_BROKEN_INSERT_ENUM(_new) _new, +# define KABI_BROKEN_REMOVE_ENUM(_orig) +# define KABI_EXTEND_ENUM(_new) _new, + +#if IS_BUILTIN(CONFIG_KABI_SIZE_ALIGN_CHECKS) +# define __KABI_CHECK_SIZE_ALIGN(_orig, _new) \ + union { \ + _Static_assert(sizeof(struct{_new;}) <= sizeof(struct{_orig;}), \ + __FILE__ ":" __stringify(__LINE__) ": " __stringify(_new) " is larger than " __stringify(_orig) KABI_ALIGN_WARNING); \ + _Static_assert(__alignof__(struct{_new;}) <= __alignof__(struct{_orig;}), \ + __FILE__ ":" __stringify(__LINE__) ": " __stringify(_orig) " is not aligned the same as " __stringify(_new) KABI_ALIGN_WARNING); \ + } +# define __KABI_CHECK_SIZE(_item, _size) \ + _Static_assert(sizeof(struct{_item;}) <= _size, \ + __FILE__ ":" __stringify(__LINE__) ": " __stringify(_item) " is larger than the reserved size (" __stringify(_size) " bytes)" KABI_ALIGN_WARNING) +#else +# define __KABI_CHECK_SIZE_ALIGN(_orig, _new) +# define __KABI_CHECK_SIZE(_item, _size) +#endif + +#define KABI_UNIQUE_ID __PASTE(kabi_hidden_, __LINE__) + +# define _KABI_DEPRECATE(_type, _orig) _type kabi_reserved_##_orig +# define _KABI_DEPRECATE_FN(_type, _orig, _args...) \ + _type (* kabi_reserved_##_orig)(_args) +#ifdef CONFIG_KABI_RESERVE +# define _KABI_REPLACE(_orig, _new) \ + union { \ + _new; \ + struct { \ + _orig; \ + } KABI_UNIQUE_ID; \ + __KABI_CHECK_SIZE_ALIGN(_orig, _new); \ + } +#else +# define _KABI_REPLACE(_orig, _new) KABI_BROKEN_REPLACE(_orig, _new) +#endif + +# define _KABI_EXCLUDE(_elem) _elem + +#endif /* __GENKSYMS__ */ + +/* semicolon added wrappers for the KABI_REPLACE macros */ +# define KABI_DEPRECATE(_type, _orig) _KABI_DEPRECATE(_type, _orig); +# define KABI_DEPRECATE_FN(_type, _orig, _args...) \ + _KABI_DEPRECATE_FN(_type, _orig, _args); +# define KABI_REPLACE(_orig, _new) _KABI_REPLACE(_orig, _new); +/* + * Macro for breaking up a random element into two smaller chunks using an + * anonymous struct inside an anonymous union. + */ +# define KABI_REPLACE2(orig, _new1, _new2) KABI_REPLACE(orig, struct{ _new1; _new2;}) + +/* + * We tried to standardize on openEuler reserved names. These wrappers + * leverage those common names making it easier to read and find in the + * code. + */ +#ifdef CONFIG_KABI_RESERVE +# define _KABI_RESERVE(n) u64 kabi_reserved##n +#else +# define _KABI_RESERVE(n) +#endif +# define KABI_RESERVE(n) _KABI_RESERVE(n); +/* + * Simple wrappers to replace standard openEuler reserved elements. + */ +# define KABI_USE(n, _new) KABI_REPLACE(_KABI_RESERVE(n), _new) +/* + * Macros for breaking up a reserved element into two smaller chunks using + * an anonymous struct inside an anonymous union. + */ +# define KABI_USE2(n, _new1, _new2) KABI_REPLACE(_KABI_RESERVE(n), struct{ _new1; _new2; }) + +#define KABI_EXCLUDE(_elem) _KABI_EXCLUDE(_elem); + +#define KABI_EXTEND_WITH_SIZE(_new, _size) \ + KABI_EXTEND(union { \ + _new; \ + unsigned long KABI_UNIQUE_ID[_size]; \ + __KABI_CHECK_SIZE(_new, 8 * (_size)); \ + }) + +#define _KABI_AUX_PTR(_struct) \ + size_t _struct##_size_resvd; \ + _KABI_EXCLUDE(struct _struct##_resvd *_resvd) +#define KABI_AUX_PTR(_struct) \ + _KABI_AUX_PTR(_struct); + +#define _KABI_AUX_EMBED(_struct) \ + size_t _struct##_size_resvd; \ + _KABI_EXCLUDE(struct _struct##_resvd _resvd) +#define KABI_AUX_EMBED(_struct) \ + _KABI_AUX_EMBED(_struct); + +#define KABI_USE_AUX_PTR(n1, n2, _struct) \ + KABI_USE(n1, n2, \ + struct { KABI_AUX_PTR(_struct) }) + +/* + * KABI_AUX_SET_SIZE calculates and sets the size of the extended struct and + * stores it in the size_resvd field for structs that are dynamically allocated. + * This macro MUST be called when expanding a base struct with + * KABI_SIZE_AND_EXTEND, and it MUST be called from the allocation site + * regardless of being allocated in the kernel or a module. + * Note: since this macro is intended to be invoked outside of a struct, + * a semicolon is necessary at the end of the line where it is invoked. + */ +#define KABI_AUX_SET_SIZE(_name, _struct) ({ \ + (_name)->_struct##_size_resvd = sizeof(struct _struct##_resvd); \ +}) + +/* + * KABI_AUX_INIT_SIZE calculates and sets the size of the extended struct and + * stores it in the size_resvd field for structs that are statically allocated. + * This macro MUST be called when expanding a base struct with + * KABI_SIZE_AND_EXTEND, and it MUST be called from the declaration site + * regardless of being allocated in the kernel or a module. + */ +#define KABI_AUX_INIT_SIZE(_struct) \ + ._struct##_size_resvd = sizeof(struct _struct##_resvd), + +/* + * KABI_AUX verifies allocated memory exists. This MUST be called to + * verify that memory in the _resvd struct is valid, and can be called + * regardless if KABI_SIZE_AND_EXTEND or KABI_SIZE_AND_EXTEND_PTR is + * used. + */ +#define KABI_AUX(_ptr, _struct, _field) ({ \ + size_t __off = offsetof(struct _struct##_resvd, _field); \ + (_ptr)->_struct##_size_resvd > __off ? true : false; \ +}) + +#endif /* _LINUX_KABI_H */ diff --git a/include/linux/module.h b/include/linux/module.h index 6264617bab..16537f2478 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -29,6 +29,7 @@ #include #include +#include /* Not Yet Implemented */ #define MODULE_SUPPORTED_DEVICE(name) @@ -423,6 +424,11 @@ struct module { /* Startup function. */ int (*init)(void); +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE + /* there is 8-byte hole on all platforms */ + KABI_FILL_HOLE(unsigned int num_mc_exentries) +#endif + /* Core layout: rbtree is accessed frequently, so keep together. */ struct module_layout core_layout __module_layout_align; struct module_layout init_layout; @@ -534,6 +540,10 @@ struct module { struct error_injection_entry *ei_funcs; unsigned int num_ei_funcs; #endif + +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE + KABI_USE(1, struct exception_table_entry *mc_extable) +#endif } ____cacheline_aligned __randomize_layout; #ifndef MODULE_ARCH_INIT #define MODULE_ARCH_INIT {} diff --git a/kernel/extable.c b/kernel/extable.c index b0ea5eb0c3..0ebc05fd72 100644 --- a/kernel/extable.c +++ b/kernel/extable.c @@ -28,6 +28,11 @@ DEFINE_MUTEX(text_mutex); extern struct exception_table_entry __start___ex_table[]; extern struct exception_table_entry __stop___ex_table[]; +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE +extern struct exception_table_entry __start___mc_ex_table[]; +extern struct exception_table_entry __stop___mc_ex_table[]; +#endif + /* Cleared by build time tools if the table is already sorted. */ u32 __initdata __visible main_extable_sort_needed = 1; @@ -39,6 +44,14 @@ void __init sort_main_extable(void) pr_notice("Sorting __ex_table...\n"); sort_extable(__start___ex_table, __stop___ex_table); } + +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE + if (main_extable_sort_needed && + &__stop___mc_ex_table > &__start___mc_ex_table) { + pr_notice("Sorting __mc_ex_table...\n"); + sort_extable(__start___mc_ex_table, __stop___mc_ex_table); + } +#endif } /* Given an address, look for it in the kernel exception table */ @@ -62,6 +75,22 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr) return e; } +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE +/* Given an address, look for it in the machine check exception table */ +const +struct exception_table_entry *search_mc_exception_tables(unsigned long addr) +{ + const struct exception_table_entry *e; + + e = search_extable(__start___mc_ex_table, + __stop___mc_ex_table - __start___mc_ex_table, addr); + if (!e) + e = search_module_mc_extables(addr); + + return e; +} +#endif + int init_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_sinittext && diff --git a/kernel/module.c b/kernel/module.c index 72a5dcdccf..39aa598d87 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3410,6 +3410,11 @@ static int find_module_sections(struct module *mod, struct load_info *info) mod->extable = section_objs(info, "__ex_table", sizeof(*mod->extable), &mod->num_exentries); +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE + mod->mc_extable = section_objs(info, "__mc_ex_table", + sizeof(*mod->mc_extable), &mod->num_mc_exentries); +#endif + if (section_addr(info, "__obsparm")) pr_warn("%s: Ignoring obsolete parameters\n", mod->name); @@ -3647,6 +3652,10 @@ static int post_relocation(struct module *mod, const struct load_info *info) /* Sort exception table now relocations are done. */ sort_extable(mod->extable, mod->extable + mod->num_exentries); +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE + sort_extable(mod->mc_extable, mod->mc_extable + mod->num_mc_exentries); +#endif + /* Copy relocated percpu area over. */ percpu_modcopy(mod, (void *)info->sechdrs[info->index.pcpu].sh_addr, info->sechdrs[info->index.pcpu].sh_size); @@ -4634,6 +4643,35 @@ const struct exception_table_entry *search_module_extables(unsigned long addr) return e; } +#ifdef CONFIG_ARCH_HAS_MC_EXTABLE +/* Given an address, look for it in the module machine check safe exception tables. */ +const struct exception_table_entry *search_module_mc_extables(unsigned long addr) +{ + const struct exception_table_entry *e = NULL; + struct module *mod; + + preempt_disable(); + mod = __module_address(addr); + if (!mod) + goto out; + + if (!mod->num_mc_exentries) + goto out; + + e = search_extable(mod->mc_extable, + mod->num_mc_exentries, + addr); +out: + preempt_enable(); + + /* + * Now, if we found one, we are running inside it now, hence + * we cannot unload the module, hence no refcnt needed. + */ + return e; +} +#endif + /* * is_module_address - is this address inside a module? * @addr: the address to check. diff --git a/scripts/sorttable.h b/scripts/sorttable.h index a2baa2fefb..5bd2f85c3a 100644 --- a/scripts/sorttable.h +++ b/scripts/sorttable.h @@ -223,6 +223,11 @@ static int do_sort(Elf_Ehdr *ehdr, unsigned int orc_num_entries = 0; #endif + Elf_Shdr *mc_extab_sec = NULL; + Elf_Rel *mc_relocs = NULL; + int mc_relocs_size = 0; + char *mc_extab_image = NULL; + shstrndx = r2(&ehdr->e_shstrndx); if (shstrndx == SHN_XINDEX) shstrndx = r(&shdr[0].sh_link); @@ -249,6 +254,18 @@ static int do_sort(Elf_Ehdr *ehdr, relocs = (void *)ehdr + _r(&s->sh_offset); relocs_size = _r(&s->sh_size); } + + if (!strcmp(secstrings + idx, "__mc_ex_table")) { + mc_extab_sec = s; + + if ((r(&s->sh_type) == SHT_REL || + r(&s->sh_type) == SHT_RELA) && + r(&s->sh_info) == i) { + mc_relocs = (void *)ehdr + _r(&s->sh_offset); + mc_relocs_size = _r(&s->sh_size); + } + } + if (r(&s->sh_type) == SHT_SYMTAB_SHNDX) symtab_shndx = (Elf32_Word *)((const char *)ehdr + _r(&s->sh_offset)); @@ -310,12 +327,18 @@ static int do_sort(Elf_Ehdr *ehdr, } extab_image = (void *)ehdr + _r(&extab_sec->sh_offset); + + if (mc_extab_sec) + mc_extab_image = (void *)ehdr + _r(&mc_extab_sec->sh_offset); + strtab = (const char *)ehdr + _r(&strtab_sec->sh_offset); symtab = (const Elf_Sym *)((const char *)ehdr + _r(&symtab_sec->sh_offset)); if (custom_sort) { custom_sort(extab_image, _r(&extab_sec->sh_size)); + if (mc_extab_image) + custom_sort(mc_extab_image, _r(&mc_extab_sec->sh_size)); } else { int num_entries = _r(&extab_sec->sh_size) / extable_ent_size; qsort(extab_image, num_entries, @@ -326,6 +349,9 @@ static int do_sort(Elf_Ehdr *ehdr, if (relocs) memset(relocs, 0, relocs_size); + if (mc_relocs) + memset(mc_relocs, 0, mc_relocs_size); + /* find the flag main_extable_sort_needed */ for (sym = (void *)ehdr + _r(&symtab_sec->sh_offset); sym < sym + _r(&symtab_sec->sh_size) / sizeof(Elf_Sym); -- Gitee From 90088220cc266397100fb4a1929fe75d4194a593 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 18 Sep 2024 11:29:17 +0800 Subject: [PATCH 100/154] arm64: add support for machine check error safe During the processing of arm64 kernel hardware memory errors(do_sea()), if the errors is consumed in the kernel, the current processing is panic. However, it is not optimal. Take uaccess for example, if the uaccess operation fails due to memory error, only the user process will be affected, kill the user process and isolate the user page with hardware memory errors is a better choice. This patch only enable machine error check framework, it add exception fixup before kernel panic in do_sea() and only limit the consumption of hardware memory errors in kernel mode triggered by user mode processes. If fixup successful, panic can be avoided. Signed-off-by: Tong Tiangen Signed-off-by: Wang Yinfeng Change-Id: Ic6097eff14e6e6b86318b5e6f495373e264bb67d --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/extable.h | 1 + arch/arm64/mm/extable.c | 12 ++++++++++++ arch/arm64/mm/fault.c | 28 +++++++++++++++++++++++++++- 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index ec6860357e..0a812a6185 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -22,6 +22,7 @@ config ARM64 select ARCH_HAS_GIGANTIC_PAGE select ARCH_HAS_KCOV select ARCH_HAS_KEEPINITRD + select ARCH_HAS_MC_EXTABLE if ACPI_APEI_GHES select ARCH_HAS_MEMBARRIER_SYNC_CORE select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE select ARCH_HAS_PTE_DEVMAP diff --git a/arch/arm64/include/asm/extable.h b/arch/arm64/include/asm/extable.h index b15eb4a3e6..35c70cc4e9 100644 --- a/arch/arm64/include/asm/extable.h +++ b/arch/arm64/include/asm/extable.h @@ -44,4 +44,5 @@ int arm64_bpf_fixup_exception(const struct exception_table_entry *ex, #endif /* !CONFIG_BPF_JIT */ extern int fixup_exception(struct pt_regs *regs); +extern int fixup_exception_mc(struct pt_regs *regs); #endif diff --git a/arch/arm64/mm/extable.c b/arch/arm64/mm/extable.c index aa00601783..9f07b8f13c 100644 --- a/arch/arm64/mm/extable.c +++ b/arch/arm64/mm/extable.c @@ -20,3 +20,15 @@ int fixup_exception(struct pt_regs *regs) regs->pc = (unsigned long)&fixup->fixup + fixup->fixup; return 1; } + +int fixup_exception_mc(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + fixup = search_mc_exception_tables(instruction_pointer(regs)); + if (!fixup) + return 0; + + regs->pc = (unsigned long)&fixup->fixup + fixup->fixup; + return 1; +} diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index d8baedd160..9c0721b281 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -619,6 +619,31 @@ static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs) return 1; /* "fault" */ } +static bool arm64_do_kernel_sea(void __user *addr, unsigned int esr, + struct pt_regs *regs, int sig, int code) +{ + if (!IS_ENABLED(CONFIG_ARCH_HAS_MC_EXTABLE)) + return false; + + if (user_mode(regs)) + return false; + + if (apei_claim_sea(regs) < 0) + return false; + + if (!fixup_exception_mc(regs)) + return false; + + if (current->flags & PF_KTHREAD) + return true; + + set_thread_esr(0, esr); + arm64_force_sig_fault(sig, code, addr, + "Uncorrected memory error on access to user memory\n"); + + return true; +} + static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) { const struct fault_info *inf; @@ -638,7 +663,8 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) siaddr = NULL; else siaddr = (void __user *)addr; - arm64_notify_die(inf->name, regs, inf->sig, inf->code, siaddr, esr); + if (!arm64_do_kernel_sea(siaddr, esr, regs, inf->sig, inf->code)) + arm64_notify_die(inf->name, regs, inf->sig, inf->code, siaddr, esr); return 0; } -- Gitee From 6f2bee9cf1989323d3c70409687606102a496e3f Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 18 Sep 2024 11:31:19 +0800 Subject: [PATCH 101/154] arm64: copy_from/to_user support machine check safe Add copy_{to, from}_user() to machine check safe. If copy fail due to hardware memory error, only the relevant processes are affected, so killing the user process and isolate the user page with hardware memory errors is a more reasonable choice than kernel panic. Signed-off-by: Tong Tiangen Signed-off-by: Wang Yinfeng Change-Id: I13889447caedb05b36f2197744b4125b837a34f1 --- arch/arm64/include/asm/alternative.h | 5 +++++ arch/arm64/include/asm/assembler.h | 7 ++++++- arch/arm64/lib/copy_from_user.S | 8 ++++---- arch/arm64/lib/copy_to_user.S | 8 ++++---- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h index 3cb3c4ab3e..0d66ad8b01 100644 --- a/arch/arm64/include/asm/alternative.h +++ b/arch/arm64/include/asm/alternative.h @@ -243,6 +243,9 @@ alternative_endif _asm_extable 8888b,\l; _asm_extable 8889b,\l; + + _asm_mc_extable 8888b,\l; + _asm_mc_extable 8889b,\l; .endm .macro uao_stp l, reg1, reg2, addr, post_inc @@ -270,6 +273,8 @@ alternative_endif alternative_endif _asm_extable 8888b,\l; + + _asm_mc_extable 8888b,\l; .endm #else .macro uao_ldp l, reg1, reg2, addr, post_inc diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 6188c9c301..0a50c6e558 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -153,7 +153,12 @@ alternative_endif #define USER(l, x...) \ 9999: x; \ - _asm_extable 9999b, l + _asm_extable 9999b, l; \ + _asm_mc_extable 9999b, l + +#define USER_MC(l, x...) \ +9999: x; \ + _asm_mc_extable 9999b, l /* * Register aliases. diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S index 957a6d092d..1c609cdae3 100644 --- a/arch/arm64/lib/copy_from_user.S +++ b/arch/arm64/lib/copy_from_user.S @@ -25,7 +25,7 @@ .endm .macro strb1 reg, ptr, val - strb \reg, [\ptr], \val + USER_MC(9998f, strb \reg, [\ptr], \val) .endm .macro ldrh1 reg, ptr, val @@ -33,7 +33,7 @@ .endm .macro strh1 reg, ptr, val - strh \reg, [\ptr], \val + USER_MC(9998f, strh \reg, [\ptr], \val) .endm .macro ldr1 reg, ptr, val @@ -41,7 +41,7 @@ .endm .macro str1 reg, ptr, val - str \reg, [\ptr], \val + USER_MC(9998f, str \reg, [\ptr], \val) .endm .macro ldp1 reg1, reg2, ptr, val @@ -49,7 +49,7 @@ .endm .macro stp1 reg1, reg2, ptr, val - stp \reg1, \reg2, [\ptr], \val + USER_MC(9998f, stp \reg1, \reg2, [\ptr], \val) .endm end .req x5 diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S index 85705350ff..486ee4658d 100644 --- a/arch/arm64/lib/copy_to_user.S +++ b/arch/arm64/lib/copy_to_user.S @@ -20,7 +20,7 @@ * x0 - bytes not copied */ .macro ldrb1 reg, ptr, val - ldrb \reg, [\ptr], \val + USER_MC(9998f, ldrb \reg, [\ptr], \val) .endm .macro strb1 reg, ptr, val @@ -28,7 +28,7 @@ .endm .macro ldrh1 reg, ptr, val - ldrh \reg, [\ptr], \val + USER_MC(9998f, ldrh \reg, [\ptr], \val) .endm .macro strh1 reg, ptr, val @@ -36,7 +36,7 @@ .endm .macro ldr1 reg, ptr, val - ldr \reg, [\ptr], \val + USER_MC(9998f, ldr \reg, [\ptr], \val) .endm .macro str1 reg, ptr, val @@ -44,7 +44,7 @@ .endm .macro ldp1 reg1, reg2, ptr, val - ldp \reg1, \reg2, [\ptr], \val + USER_MC(9998f, ldp \reg1, \reg2, [\ptr], \val) .endm .macro stp1 reg1, reg2, ptr, val -- Gitee From 5ec2ac4cc39586e5f7b7f774323688c5c3076935 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 18 Sep 2024 11:32:23 +0800 Subject: [PATCH 102/154] arm64: get/put_user support machine check safe Add {get, put}_user() to machine check safe. If get/put fail due to hardware memory error, only the relevant processes are affected, so killing the user process and isolate the user page with hardware memory errors is a more reasonable choice than kernel panic. Signed-off-by: Tong Tiangen Signed-off-by: Wang Yinfeng Change-Id: I7be48ab219855b1e15c74d1c1916c38ca1d8f69e --- arch/arm64/include/asm/uaccess.h | 37 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index feb73906bd..44e566c8c1 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -112,6 +112,11 @@ static inline unsigned long __range_ok(const void __user *addr, unsigned long si #define _ASM_MC_EXTABLE(from, to) #endif +#define _ASM_KACCESS_EXTABLE(from, to) _ASM_EXTABLE(from, to) +#define _ASM_UACCESS_EXTABLE(from, to) \ + _ASM_EXTABLE(from, to) \ + _ASM_MC_EXTABLE(from, to) + /* * User access enabling/disabling. */ @@ -263,7 +268,7 @@ static inline void __user *__uaccess_mask_ptr(const void __user *ptr) * The "__xxx_error" versions set the third argument to -EFAULT if an error * occurs, and leave it unchanged on success. */ -#define __get_user_asm(instr, alt_instr, reg, x, addr, err, feature) \ +#define __get_user_asm(instr, alt_instr, reg, x, addr, err, feature, type) \ asm volatile( \ "1:"ALTERNATIVE(instr " " reg "1, [%2]\n", \ alt_instr " " reg "1, [%2]\n", feature) \ @@ -274,11 +279,11 @@ static inline void __user *__uaccess_mask_ptr(const void __user *ptr) " mov %1, #0\n" \ " b 2b\n" \ " .previous\n" \ - _ASM_EXTABLE(1b, 3b) \ + _ASM_##type##ACCESS_EXTABLE(1b, 3b) \ : "+r" (err), "=&r" (x) \ : "r" (addr), "i" (-EFAULT)) -#define __raw_get_user(x, ptr, err) \ +#define __raw_get_user(x, ptr, err, type) \ do { \ unsigned long __gu_val; \ __chk_user_ptr(ptr); \ @@ -286,19 +291,19 @@ do { \ switch (sizeof(*(ptr))) { \ case 1: \ __get_user_asm("ldrb", "ldtrb", "%w", __gu_val, (ptr), \ - (err), ARM64_HAS_UAO); \ + (err), ARM64_HAS_UAO, type); \ break; \ case 2: \ __get_user_asm("ldrh", "ldtrh", "%w", __gu_val, (ptr), \ - (err), ARM64_HAS_UAO); \ + (err), ARM64_HAS_UAO, type); \ break; \ case 4: \ __get_user_asm("ldr", "ldtr", "%w", __gu_val, (ptr), \ - (err), ARM64_HAS_UAO); \ + (err), ARM64_HAS_UAO, type); \ break; \ case 8: \ __get_user_asm("ldr", "ldtr", "%x", __gu_val, (ptr), \ - (err), ARM64_HAS_UAO); \ + (err), ARM64_HAS_UAO, type); \ break; \ default: \ BUILD_BUG(); \ @@ -313,7 +318,7 @@ do { \ might_fault(); \ if (access_ok(__p, sizeof(*__p))) { \ __p = uaccess_mask_ptr(__p); \ - __raw_get_user((x), __p, (err)); \ + __raw_get_user((x), __p, (err), U); \ } else { \ (x) = (__force __typeof__(x))0; (err) = -EFAULT; \ } \ @@ -328,7 +333,7 @@ do { \ #define get_user __get_user -#define __put_user_asm(instr, alt_instr, reg, x, addr, err, feature) \ +#define __put_user_asm(instr, alt_instr, reg, x, addr, err, feature, type) \ asm volatile( \ "1:"ALTERNATIVE(instr " " reg "1, [%2]\n", \ alt_instr " " reg "1, [%2]\n", feature) \ @@ -338,11 +343,11 @@ do { \ "3: mov %w0, %3\n" \ " b 2b\n" \ " .previous\n" \ - _ASM_EXTABLE(1b, 3b) \ + _ASM_##type##ACCESS_EXTABLE(1b, 3b) \ : "+r" (err) \ : "r" (x), "r" (addr), "i" (-EFAULT)) -#define __raw_put_user(x, ptr, err) \ +#define __raw_put_user(x, ptr, err, type) \ do { \ __typeof__(*(ptr)) __pu_val = (x); \ __chk_user_ptr(ptr); \ @@ -350,19 +355,19 @@ do { \ switch (sizeof(*(ptr))) { \ case 1: \ __put_user_asm("strb", "sttrb", "%w", __pu_val, (ptr), \ - (err), ARM64_HAS_UAO); \ + (err), ARM64_HAS_UAO, type); \ break; \ case 2: \ __put_user_asm("strh", "sttrh", "%w", __pu_val, (ptr), \ - (err), ARM64_HAS_UAO); \ + (err), ARM64_HAS_UAO, type); \ break; \ case 4: \ __put_user_asm("str", "sttr", "%w", __pu_val, (ptr), \ - (err), ARM64_HAS_UAO); \ + (err), ARM64_HAS_UAO, type); \ break; \ case 8: \ __put_user_asm("str", "sttr", "%x", __pu_val, (ptr), \ - (err), ARM64_HAS_UAO); \ + (err), ARM64_HAS_UAO, type); \ break; \ default: \ BUILD_BUG(); \ @@ -376,7 +381,7 @@ do { \ might_fault(); \ if (access_ok(__p, sizeof(*__p))) { \ __p = uaccess_mask_ptr(__p); \ - __raw_put_user((x), __p, (err)); \ + __raw_put_user((x), __p, (err), U); \ } else { \ (err) = -EFAULT; \ } \ -- Gitee From f85b702fb388d223221a4ee25a8463f4d76e0e3a Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 18 Sep 2024 11:49:18 +0800 Subject: [PATCH 103/154] arm64: add cow to machine check safe In the cow(copy on write) processing, the data of the user process is copied, when hardware memory error is encountered during copy, only the relevant processes are affected, so killing the user process and isolate the user page with hardware memory errors is a more reasonable choice than kernel panic. Add new helper copy_page_mc() which provide a page copy implementation with machine check safe. At present, only used in cow. In future, we can expand more scenes. As long as the consequences of page copy failure are not fatal(eg: only affect user process), we can use this helper. The copy_page_mc() in copy_page_mc.S is largely borrows from copy_page() in copy_page.S and the main difference is copy_page_mc() add extable entry to every load/store insn to support machine check safe. largely to keep the patch simple. If needed those optimizations can be folded in. Signed-off-by: Tong Tiangen Signed-off-by: Wang Yinfeng Change-Id: Ic8f5ff19c637352164b2904b79eea78a3d913965 --- arch/arm64/include/asm/assembler.h | 4 ++ arch/arm64/include/asm/mte.h | 4 ++ arch/arm64/include/asm/page.h | 10 ++++ arch/arm64/lib/Makefile | 2 + arch/arm64/lib/copy_page_mc.S | 79 ++++++++++++++++++++++++++++++ arch/arm64/lib/mte.S | 19 +++++++ arch/arm64/mm/copypage.c | 36 ++++++++++++-- include/linux/highmem.h | 8 +++ mm/memory.c | 2 +- 9 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 arch/arm64/lib/copy_page_mc.S diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 0a50c6e558..7d12f340ae 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -160,6 +160,10 @@ alternative_endif 9999: x; \ _asm_mc_extable 9999b, l +#define CPY_MC(l, x...) \ +9999: x; \ + _asm_mc_extable 9999b, l + /* * Register aliases. */ diff --git a/arch/arm64/include/asm/mte.h b/arch/arm64/include/asm/mte.h index 1c99fcadb5..eeeb74fa22 100644 --- a/arch/arm64/include/asm/mte.h +++ b/arch/arm64/include/asm/mte.h @@ -37,6 +37,7 @@ void mte_free_tag_storage(char *storage); void mte_sync_tags(pte_t *ptep, pte_t pte); void mte_copy_page_tags(void *kto, const void *kfrom); +void mte_copy_page_tags_mc(void *kto, const void *kfrom); void flush_mte_state(void); void mte_thread_switch(struct task_struct *next); void mte_suspend_exit(void); @@ -56,6 +57,9 @@ static inline void mte_sync_tags(pte_t *ptep, pte_t pte) static inline void mte_copy_page_tags(void *kto, const void *kfrom) { } +static inline void mte_copy_page_tags_mc(void *kto, const void *kfrom) +{ +} static inline void flush_mte_state(void) { } diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h index 012cffc574..4d3ba27b96 100644 --- a/arch/arm64/include/asm/page.h +++ b/arch/arm64/include/asm/page.h @@ -28,6 +28,16 @@ void copy_user_highpage(struct page *to, struct page *from, void copy_highpage(struct page *to, struct page *from); #define __HAVE_ARCH_COPY_HIGHPAGE +#ifdef CONFIG_ARCH_HAS_COPY_MC +extern void copy_page_mc(void *to, const void *from); +void copy_highpage_mc(struct page *to, struct page *from); +#define __HAVE_ARCH_COPY_HIGHPAGE_MC + +void copy_user_highpage_mc(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma); +#define __HAVE_ARCH_COPY_USER_HIGHPAGE_MC +#endif + #define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \ alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr) #define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile index d31e1169d9..d35d997710 100644 --- a/arch/arm64/lib/Makefile +++ b/arch/arm64/lib/Makefile @@ -13,6 +13,8 @@ endif lib-$(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) += uaccess_flushcache.o +lib-$(CONFIG_ARCH_HAS_COPY_MC) += copy_page_mc.o + obj-$(CONFIG_CRC32) += crc32.o obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o diff --git a/arch/arm64/lib/copy_page_mc.S b/arch/arm64/lib/copy_page_mc.S new file mode 100644 index 0000000000..493d4c90dd --- /dev/null +++ b/arch/arm64/lib/copy_page_mc.S @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2012 ARM Ltd. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Copy a page from src to dest (both are page aligned) + * + * Parameters: + * x0 - dest + * x1 - src + */ +SYM_FUNC_START(copy_page_mc) +alternative_if ARM64_HAS_NO_HW_PREFETCH + // Prefetch three cache lines ahead. + prfm pldl1strm, [x1, #128] + prfm pldl1strm, [x1, #256] + prfm pldl1strm, [x1, #384] +alternative_else_nop_endif + +CPY_MC(9998f, ldp x2, x3, [x1]) +CPY_MC(9998f, ldp x4, x5, [x1, #16]) +CPY_MC(9998f, ldp x6, x7, [x1, #32]) +CPY_MC(9998f, ldp x8, x9, [x1, #48]) +CPY_MC(9998f, ldp x10, x11, [x1, #64]) +CPY_MC(9998f, ldp x12, x13, [x1, #80]) +CPY_MC(9998f, ldp x14, x15, [x1, #96]) +CPY_MC(9998f, ldp x16, x17, [x1, #112]) + + add x0, x0, #256 + add x1, x1, #128 +1: + tst x0, #(PAGE_SIZE - 1) + +alternative_if ARM64_HAS_NO_HW_PREFETCH + prfm pldl1strm, [x1, #384] +alternative_else_nop_endif + +CPY_MC(9998f, stnp x2, x3, [x0, #-256]) +CPY_MC(9998f, ldp x2, x3, [x1]) +CPY_MC(9998f, stnp x4, x5, [x0, #16 - 256]) +CPY_MC(9998f, ldp x4, x5, [x1, #16]) +CPY_MC(9998f, stnp x6, x7, [x0, #32 - 256]) +CPY_MC(9998f, ldp x6, x7, [x1, #32]) +CPY_MC(9998f, stnp x8, x9, [x0, #48 - 256]) +CPY_MC(9998f, ldp x8, x9, [x1, #48]) +CPY_MC(9998f, stnp x10, x11, [x0, #64 - 256]) +CPY_MC(9998f, ldp x10, x11, [x1, #64]) +CPY_MC(9998f, stnp x12, x13, [x0, #80 - 256]) +CPY_MC(9998f, ldp x12, x13, [x1, #80]) +CPY_MC(9998f, stnp x14, x15, [x0, #96 - 256]) +CPY_MC(9998f, ldp x14, x15, [x1, #96]) +CPY_MC(9998f, stnp x16, x17, [x0, #112 - 256]) +CPY_MC(9998f, ldp x16, x17, [x1, #112]) + + add x0, x0, #128 + add x1, x1, #128 + + b.ne 1b + +CPY_MC(9998f, stnp x2, x3, [x0, #-256]) +CPY_MC(9998f, stnp x4, x5, [x0, #16 - 256]) +CPY_MC(9998f, stnp x6, x7, [x0, #32 - 256]) +CPY_MC(9998f, stnp x8, x9, [x0, #48 - 256]) +CPY_MC(9998f, stnp x10, x11, [x0, #64 - 256]) +CPY_MC(9998f, stnp x12, x13, [x0, #80 - 256]) +CPY_MC(9998f, stnp x14, x15, [x0, #96 - 256]) +CPY_MC(9998f, stnp x16, x17, [x0, #112 - 256]) + + ret +SYM_FUNC_END(copy_page_mc) +EXPORT_SYMBOL(copy_page_mc) diff --git a/arch/arm64/lib/mte.S b/arch/arm64/lib/mte.S index 03ca6d8b86..220a66d2be 100644 --- a/arch/arm64/lib/mte.S +++ b/arch/arm64/lib/mte.S @@ -54,6 +54,25 @@ SYM_FUNC_START(mte_copy_page_tags) ret SYM_FUNC_END(mte_copy_page_tags) +/* + * Copy the tags from the source page to the destination one wiht machine check safe + * x0 - address of the destination page + * x1 - address of the source page + */ +SYM_FUNC_START(mte_copy_page_tags_mc) + mov x2, x0 + mov x3, x1 + multitag_transfer_size x5, x6 +1: +CPY_MC(2f, ldgm x4, [x3]) + stgm x4, [x2] + add x2, x2, x5 + add x3, x3, x5 + tst x2, #(PAGE_SIZE - 1) + b.ne 1b +2: ret +SYM_FUNC_END(mte_copy_page_tags_mc) + /* * Read tags from a user buffer (one tag per byte) and set the corresponding * tags at the given kernel address. Used by PTRACE_POKEMTETAGS. diff --git a/arch/arm64/mm/copypage.c b/arch/arm64/mm/copypage.c index 24913271e8..9d9532fb2d 100644 --- a/arch/arm64/mm/copypage.c +++ b/arch/arm64/mm/copypage.c @@ -14,6 +14,17 @@ #include #include +static void do_mte(struct page *to, struct page *from, void *kto, void *kfrom, bool mc) +{ + if (system_supports_mte() && test_bit(PG_mte_tagged, &from->flags)) { + set_bit(PG_mte_tagged, &to->flags); + if (mc) + mte_copy_page_tags_mc(kto, kfrom); + else + mte_copy_page_tags(kto, kfrom); + } +} + void copy_highpage(struct page *to, struct page *from) { void *kto = page_address(to); @@ -21,10 +32,7 @@ void copy_highpage(struct page *to, struct page *from) copy_page(kto, kfrom); - if (system_supports_mte() && test_bit(PG_mte_tagged, &from->flags)) { - set_bit(PG_mte_tagged, &to->flags); - mte_copy_page_tags(kto, kfrom); - } + do_mte(to, from, kto, kfrom, false); } EXPORT_SYMBOL(copy_highpage); @@ -35,3 +43,23 @@ void copy_user_highpage(struct page *to, struct page *from, flush_dcache_page(to); } EXPORT_SYMBOL_GPL(copy_user_highpage); + +#ifdef CONFIG_ARCH_HAS_COPY_MC +void copy_highpage_mc(struct page *to, struct page *from) +{ + void *kto = page_address(to); + void *kfrom = page_address(from); + + copy_page_mc(kto, kfrom); + do_mte(to, from, kto, kfrom, true); +} +EXPORT_SYMBOL(copy_highpage_mc); + +void copy_user_highpage_mc(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + copy_highpage_mc(to, from); + flush_dcache_page(to); +} +EXPORT_SYMBOL_GPL(copy_user_highpage_mc); +#endif diff --git a/include/linux/highmem.h b/include/linux/highmem.h index b25df1f8d4..cb0005934b 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -330,6 +330,14 @@ static inline void copy_user_highpage(struct page *to, struct page *from, #endif +#ifndef __HAVE_ARCH_COPY_USER_HIGHPAGE_MC +#define copy_user_highpage_mc copy_user_highpage +#endif + +#ifndef __HAVE_ARCH_COPY_HIGHPAGE_MC +#define copy_highpage_mc copy_highpage +#endif + #ifndef __HAVE_ARCH_COPY_HIGHPAGE static inline void copy_highpage(struct page *to, struct page *from) diff --git a/mm/memory.c b/mm/memory.c index 1d101aeae4..37ace14bb2 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2632,7 +2632,7 @@ static inline bool cow_user_page(struct page *dst, struct page *src, unsigned long addr = vmf->address; if (likely(src)) { - copy_user_highpage(dst, src, addr, vma); + copy_user_highpage_mc(dst, src, addr, vma); return true; } -- Gitee From 7f935c36df5af65a4c43622227dba18e3496d042 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 18 Sep 2024 11:52:32 +0800 Subject: [PATCH 104/154] arm64: introduce copy_mc_to_kernel() implementation The copy_mc_to_kernel() helper is memory copy implementation that handles source exceptions. It can be used in memory copy scenarios that tolerate hardware memory errors. Currnently, only x86 and ppc suuport this helper, after arm64 support machine check safe framework, we introduce copy_mc_to_kernel() implementation. Signed-off-by: Tong Tiangen Signed-off-by: Wang Yinfeng Change-Id: I7390bafac0da74fa454013e94f294d1dbc338212 --- arch/arm64/include/asm/string.h | 5 +++ arch/arm64/include/asm/uaccess.h | 18 ++++++++ arch/arm64/lib/Makefile | 2 +- arch/arm64/lib/memcpy_mc.S | 71 ++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/lib/memcpy_mc.S diff --git a/arch/arm64/include/asm/string.h b/arch/arm64/include/asm/string.h index b31e8e87a0..e197b49383 100644 --- a/arch/arm64/include/asm/string.h +++ b/arch/arm64/include/asm/string.h @@ -31,6 +31,10 @@ extern int memcmp(const void *, const void *, size_t); extern void *memchr(const void *, int, __kernel_size_t); #endif +#define __HAVE_ARCH_MEMCPY_MC +extern void *memcpy_mcs(void *, const void *, __kernel_size_t); +extern void *__memcpy_mcs(void *, const void *, __kernel_size_t); + #define __HAVE_ARCH_MEMCPY extern void *memcpy(void *, const void *, __kernel_size_t); extern void *__memcpy(void *, const void *, __kernel_size_t); @@ -56,6 +60,7 @@ void memcpy_flushcache(void *dst, const void *src, size_t cnt); */ #define memcpy(dst, src, len) __memcpy(dst, src, len) +#define memcpy_mcs(dst, src, len) __memcpy_mcs(dst, src, len) #define memmove(dst, src, len) __memmove(dst, src, len) #define memset(s, c, n) __memset(s, c, n) diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 44e566c8c1..7bde77253f 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -460,4 +460,22 @@ static inline int __copy_from_user_flushcache(void *dst, const void __user *src, } #endif +#ifdef CONFIG_ARCH_HAS_COPY_MC +/** + * copy_mc_to_kernel - memory copy that handles source exceptions + * + * @dst: destination address + * @src: source address + * @len: number of bytes to copy + * + * Return 0 for success, or number of bytes not copied if there was an + * exception. + */ +static inline unsigned long __must_check +copy_mc_to_kernel(void *to, const void *from, unsigned long size) +{ + return (unsigned long)memcpy_mcs(to, from, size); +} +#define copy_mc_to_kernel copy_mc_to_kernel +#endif #endif /* __ASM_UACCESS_H */ diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile index d35d997710..dbca161139 100644 --- a/arch/arm64/lib/Makefile +++ b/arch/arm64/lib/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 lib-y := clear_user.o delay.o copy_from_user.o \ copy_to_user.o copy_in_user.o copy_page.o \ - clear_page.o csum.o memchr.o memcpy.o memmove.o \ + clear_page.o csum.o memchr.o memcpy.o memcpy_mc.o memmove.o \ memset.o memcmp.o strcmp.o strncmp.o strlen.o \ strnlen.o strchr.o strrchr.o tishift.o diff --git a/arch/arm64/lib/memcpy_mc.S b/arch/arm64/lib/memcpy_mc.S new file mode 100644 index 0000000000..997190de78 --- /dev/null +++ b/arch/arm64/lib/memcpy_mc.S @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 ARM Ltd. + * Copyright (C) 2013 Linaro. + * + * This code is based on glibc cortex strings work originally authored by Linaro + * be found @ + * + * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ + * files/head:/src/aarch64/ + */ + +#include +#include +#include + +/* + * Copy a buffer from src to dest (alignment handled by the hardware) + * + * Parameters: + * x0 - dest + * x1 - src + * x2 - n + * Returns: + * x0 - dest + */ + .macro ldrb1 reg, ptr, val + CPY_MC(9998f, ldrb \reg, [\ptr], \val) + .endm + + .macro strb1 reg, ptr, val + CPY_MC(9998f, strb \reg, [\ptr], \val) + .endm + + .macro ldrh1 reg, ptr, val + CPY_MC(9998f, ldrh \reg, [\ptr], \val) + .endm + + .macro strh1 reg, ptr, val + CPY_MC(9998f, strh \reg, [\ptr], \val) + .endm + + .macro ldr1 reg, ptr, val + CPY_MC(9998f, ldr \reg, [\ptr], \val) + .endm + + .macro str1 reg, ptr, val + CPY_MC(9998f, str \reg, [\ptr], \val) + .endm + + .macro ldp1 reg1, reg2, ptr, val + CPY_MC(9998f, ldp \reg1, \reg2, [\ptr], \val) + .endm + + .macro stp1 reg1, reg2, ptr, val + CPY_MC(9998f, stp \reg1, \reg2, [\ptr], \val) + .endm + +end .req x5 +SYM_FUNC_START_ALIAS(__memcpy_mcs) +SYM_FUNC_START_WEAK_PI(memcpy_mcs) + add end, x0, x2 +#include "copy_template.S" + mov x0, #0 + ret +9998: sub x0, end, dst + ret +SYM_FUNC_END_PI(memcpy_mcs) +EXPORT_SYMBOL(memcpy_mcs) +SYM_FUNC_END_ALIAS(__memcpy_mcs) +EXPORT_SYMBOL(__memcpy_mcs) -- Gitee From e905cd12a1128c9007928e77b75b68830e43d520 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 18 Sep 2024 11:41:17 +0800 Subject: [PATCH 105/154] arm64: add dump_user_range() to machine check safe In the dump_user_range() processing, the data of the user process is dump to corefile, when hardware memory error is encountered during dump, only the relevant processes are affected, so killing the user process and isolate the user page with hardware memory errors is a more reasonable choice than kernel panic. The dump_user_range() typical usage scenarios is coredump. Coredump file writing to fs is related to the specific implementation of fs's write_iter operation. This patch only supports two typical fs write function (_copy_from_iter/iov_iter_copy_from_user_atomic) which is used by ext4/tmpfs/pipefs. Signed-off-by: Tong Tiangen Signed-off-by: Wang Yinfeng Change-Id: I9ec9ccaf765c4a4f345e9582acd41412e9c2cddf --- fs/coredump.c | 2 ++ include/linux/sched.h | 1 + lib/iov_iter.c | 12 ++++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index 9d91e831ed..1619bb11f7 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -907,7 +907,9 @@ int dump_user_range(struct coredump_params *cprm, unsigned long start, if (page) { void *kaddr = kmap(page); + current->flags |= PF_COREDUMP_MCS; stop = !dump_emit(cprm, kaddr, PAGE_SIZE); + current->flags &= ~PF_COREDUMP_MCS; kunmap(page); put_page(page); } else { diff --git a/include/linux/sched.h b/include/linux/sched.h index aa015416c5..47c9863e15 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1554,6 +1554,7 @@ extern struct pid *cad_pid; #define PF_KTHREAD 0x00200000 /* I am a kernel thread */ #define PF_RANDOMIZE 0x00400000 /* Randomize virtual address space */ #define PF_SWAPWRITE 0x00800000 /* Allowed to write to swap */ +#define PF_COREDUMP_MCS 0x01000000 /* Task coredump support machine check safe */ #define PF_NO_SETAFFINITY 0x04000000 /* Userland is not allowed to meddle with cpus_mask */ #define PF_MCE_EARLY 0x08000000 /* Early kill for mce process policy */ #define PF_MEMALLOC_NOCMA 0x10000000 /* All allocation request will have _GFP_MOVABLE cleared */ diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 6e30113303..0a4b7aa470 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -750,6 +750,14 @@ size_t _copy_mc_to_iter(const void *addr, size_t bytes, struct iov_iter *i) EXPORT_SYMBOL_GPL(_copy_mc_to_iter); #endif /* CONFIG_ARCH_HAS_COPY_MC */ +static void *memcpy_iter(void *to, const void *from, __kernel_size_t size) +{ + if (IS_ENABLED(CONFIG_ARCH_HAS_COPY_MC) && current->flags & PF_COREDUMP_MCS) + return (void *)copy_mc_to_kernel(to, from, size); + else + return memcpy(to, from, size); +} + size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) { char *to = addr; @@ -763,7 +771,7 @@ size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) copyin((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len), memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page, v.bv_offset, v.bv_len), - memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len) + memcpy_iter((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len) ) return bytes; @@ -999,7 +1007,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, copyin((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len), memcpy_from_page((p += v.bv_len) - v.bv_len, v.bv_page, v.bv_offset, v.bv_len), - memcpy((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len) + memcpy_iter((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len) ) kunmap_atomic(kaddr); return bytes; -- Gitee From b37f72fa5182b9b09e347e6f6f36c40053ec0e44 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 18 Sep 2024 11:43:41 +0800 Subject: [PATCH 106/154] arm64: add machine check safe sysctl interface Add /proc/sys/kernel/machine_check_safe_enable. Set 1(default value) to enable machine check safe support. Set 0(default) to disable machine check safe support. Signed-off-by: Tong Tiangen Signed-off-by: Wang Yinfeng Change-Id: I22b4c556813bb5c096acdbab49b4e232749f757f --- Documentation/admin-guide/sysctl/kernel.rst | 21 +++++++++++++++++++++ arch/arm64/include/asm/processor.h | 2 ++ arch/arm64/mm/fault.c | 5 +++++ kernel/sysctl.c | 11 +++++++++++ 4 files changed, 39 insertions(+) diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 6b0c7b650d..f23e08ce80 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -478,6 +478,27 @@ if leaking kernel pointer values to unprivileged users is a concern. When ``kptr_restrict`` is set to 2, kernel pointers printed using %pK will be replaced with 0s regardless of privileges. +machine_check_safe (arm64 only) +================================ + +Controls the kernel's behaviour when an hardware memory error is +encountered in the following scenarios: + += =================== +1 cow +2 copy_mc_to_kernel +3 copy_from_user +4 copy_to_user +5 get_user +6 put_user += =================== + +Correspondence between sysctl value and behavior: + += ======================= +0 Kernel panic +1 Kill related processes += ======================= modprobe ======== diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index c628d8e3a4..8e30c4562d 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -86,6 +86,8 @@ #define STACK_TOP STACK_TOP_MAX #endif /* CONFIG_COMPAT */ +extern int sysctl_machine_check_safe; + #ifndef CONFIG_ARM64_FORCE_52BIT #define arch_get_mmap_end(addr) ((addr > DEFAULT_MAP_WINDOW) ? TASK_SIZE :\ DEFAULT_MAP_WINDOW) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 9c0721b281..c650b97675 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -39,6 +39,8 @@ #include #include +int sysctl_machine_check_safe = 1; + struct fault_info { int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs); @@ -625,6 +627,9 @@ static bool arm64_do_kernel_sea(void __user *addr, unsigned int esr, if (!IS_ENABLED(CONFIG_ARCH_HAS_MC_EXTABLE)) return false; + if (!sysctl_machine_check_safe) + return false; + if (user_mode(regs)) return false; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index a45f0dd10b..0c8ac62935 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -3439,6 +3439,17 @@ static struct ctl_table debug_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE, }, +#endif +#if defined(CONFIG_ARM64) && defined(CONFIG_ARCH_HAS_COPY_MC) + { + .procname = "machine_check_safe", + .data = &sysctl_machine_check_safe, + .maxlen = sizeof(sysctl_machine_check_safe), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, #endif { } }; -- Gitee From 47becd306d7eae458db9ddaa92cd5afaffa61410 Mon Sep 17 00:00:00 2001 From: Li Jiayi Date: Wed, 5 Mar 2025 17:41:52 +0800 Subject: [PATCH 107/154] devfreq: Add phytium noc devfreq driver This adds the DEVFREQ driver for Phytium Net On Chip.It adjusts frequency for noc based on load bandwidth obtained from register. Mainline: NA Signed-off-by: Li Jiayi Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Change-Id: I3619d40cf45fd082217b4b72ccd745596184d6bb --- drivers/devfreq/Kconfig | 9 + drivers/devfreq/Makefile | 1 + drivers/devfreq/phytium_noc.c | 440 ++++++++++++++++++++++++++++++++++ 3 files changed, 450 insertions(+) create mode 100644 drivers/devfreq/phytium_noc.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 37dc40d1fc..e9a581da37 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -143,6 +143,15 @@ config ARM_RK3399_DMC_DEVFREQ It sets the frequency for the memory controller and reads the usage counts from hardware. +config ARM_PHYTIUM_NOC_DEVFREQ + tristate "ARM PHYTIUM NOC DEVFREQ Driver" + depends on ARCH_PHYTIUM || COMPILE_TEST + depends on ACPI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for Phytium Net On Chip. + It adjusts frequency for noc based on load bandwidth obtained from register. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 3ca1ad0ecb..5bc3851392 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o +obj-$(CONFIG_ARM_PHYTIUM_NOC_DEVFREQ) += phytium_noc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c new file mode 100644 index 0000000000..697e796b8b --- /dev/null +++ b/drivers/devfreq/phytium_noc.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-1.0 +/* + *phytium_noc.c - Phytium Processor noc Frequency Driver + * + *Copyright (C) 2024,Phytium Technology Co.,Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MINI_SIZE 0x400 +#define CNT_ENABLE 0x000 +#define WORK_STATE 0X004 +#define CLR_EN 0X010 +#define SNAPSHOT_EN 0X014 +#define INT_CTRL_CLR 0x024 +#define WR_NOLAST_HANDSHARK_NUM 0x44 + +#define DEBUG +#define DEVICE_TYPE 7 + +#define NOCFREQ_DRIVER_VERSION "1.0.0" + +struct phytium_nocfreq { + struct device *dev; + + struct devfreq *devfreq; + struct devfreq_dev_profile profile; + struct devfreq_simple_ondemand_data ondemand_data; + + void __iomem *reg_noc; + struct mutex lock; + + unsigned long rate, target_rate; + unsigned int freq_count; + unsigned long freq_table[]; +}; + +static u32 phytium_nocfreq_get_peak_bw(struct phytium_nocfreq *priv) +{ + /*Returns the peak number of dmu read/write commands on the axi bus.*/ + unsigned long peak_bw, bw_0, bw_1, bw_2, bw_3; + + bw_0 = readl_relaxed(priv->reg_noc + WR_NOLAST_HANDSHARK_NUM); + bw_1 = readl_relaxed(priv->reg_noc + MINI_SIZE*1 + WR_NOLAST_HANDSHARK_NUM); + bw_2 = readl_relaxed(priv->reg_noc + MINI_SIZE*2 + WR_NOLAST_HANDSHARK_NUM); + bw_3 = readl_relaxed(priv->reg_noc + MINI_SIZE*3 + WR_NOLAST_HANDSHARK_NUM); + + peak_bw = bw_0; + if (bw_1 > peak_bw) + peak_bw = bw_1; + if (bw_2 > peak_bw) + peak_bw = bw_2; + if (bw_3 > peak_bw) + peak_bw = bw_3; + + return peak_bw; +} + +static void phytium_nocfreq_restart_handshark_counters(struct phytium_nocfreq *priv) +{ + + /*clear interrupt*/ + + writel_relaxed(0x80000000, priv->reg_noc + INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*1 + INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*2 + INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*3 + INT_CTRL_CLR); + + /*clear counters*/ + writel_relaxed(0x1, priv->reg_noc + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + CLR_EN); +} + + +static int phytium_noc_set_freq(struct device *dev, unsigned long freq) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object args[4]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + unsigned long long ret; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = freq; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + args[3].type = ACPI_TYPE_INTEGER; + args[3].integer.value = 0; + + mutex_lock(&priv->lock); + status = acpi_evaluate_integer(handle, "PSCF", &arg_list, &ret); + mutex_unlock(&priv->lock); + + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PSCF method\n"); + return -EIO; + } + + if (ret) { + dev_err(dev, "Failed to set the freq to %lu\n", freq); + return -EIO; + } + dev_dbg(dev, "set target_freq = %lu khz\n", freq); + return 0; +} + +static int phytium_noc_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + unsigned long old_freq = priv->rate; + unsigned long target_rate; + struct dev_pm_opp *opp; + int ret; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + target_rate = dev_pm_opp_get_freq(opp); + dev_pm_opp_put(opp); + + if (target_rate == old_freq) + return 0; + /* + * Read back the clk rate to verify switch was correct and so that + * we can report it on all error paths. + */ + ret = phytium_noc_set_freq(dev, target_rate); + + if (ret) { + dev_warn(dev, "failed to set noc frequency: %d\n", ret); + *freq = old_freq; + } + priv->rate = target_rate; + return ret; + +} + +static int phytium_noc_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + unsigned long long ret; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + mutex_lock(&priv->lock); + status = acpi_evaluate_integer(handle, "PGCF", &arg_list, &ret); + mutex_unlock(&priv->lock); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCF method\n"); + return -EIO; + } + + if (ret < 0) { + dev_err(dev, "Failed to get the freq\n"); + return -EIO; + } + *freq = ret; + + return 0; +} + +static int phytium_noc_get_freq_info(struct device *dev, u32 flags) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + int i; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = flags; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + if (!buffer.length) { + dev_err(dev, "buffer is NULL\n"); + return -EINVAL; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + priv->freq_count = element->integer.value; + + for (i = 0; i < priv->freq_count; i++) { + element = &package->package.elements[i+2]; + priv->freq_table[i] = element->integer.value; + dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); + } + + return 0; + +} + +static int get_freq_count(struct device *dev) +{ + int freq_count = -1; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + if (!buffer.length) { + dev_err(dev, "buffer is NULL\n"); + return -EINVAL; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + freq_count = element->integer.value; + dev_dbg(dev, "freq_count = %d\n", freq_count); + + return freq_count; +} + +static int phytium_noc_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + unsigned int val; + + writel_relaxed(0x1, priv->reg_noc + SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + SNAPSHOT_EN); + + val = DIV_ROUND_CLOSEST(priv->rate * 100, priv->profile.initial_freq); + stat->busy_time = phytium_nocfreq_get_peak_bw(priv); + stat->total_time = 320000 * val; + stat->current_frequency = priv->rate; + + phytium_nocfreq_restart_handshark_counters(priv); + dev_dbg(dev, "Using %lu/%lu (%lu%%) at %lu KHz\n", + stat->busy_time, stat->total_time, + DIV_ROUND_CLOSEST(stat->busy_time * 100, stat->total_time), + stat->current_frequency); + + return 0; +} + + +static int phytium_nocfreq_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phytium_nocfreq *priv; + const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + int i, ret; + unsigned int max_state; + struct resource *mem; + + max_state = get_freq_count(dev); + + dev->init_name = "nocfreq"; + + priv = devm_kzalloc(dev, struct_size(priv, freq_table, max_state), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + platform_set_drvdata(pdev, priv); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource"); + return -EINVAL; + } + + priv->reg_noc = devm_ioremap_resource(&pdev->dev, mem); + if (!priv->reg_noc) { + dev_err(dev, "NOC region map failed\n"); + return PTR_ERR(priv->reg_noc); + } + + ret = phytium_noc_get_freq_info(dev, DEVICE_TYPE); + if (ret) { + dev_err(dev, "failed to get noc frequency info\n"); + return -EIO; + } + + priv->profile.initial_freq = priv->freq_table[0]; + priv->profile.polling_ms = 100; + priv->profile.target = phytium_noc_target; + priv->profile.get_cur_freq = phytium_noc_get_cur_freq; + priv->profile.get_dev_status = phytium_noc_get_dev_status; + priv->profile.freq_table = priv->freq_table; + priv->profile.max_state = priv->freq_count; + priv->rate = priv->freq_table[0]; + priv->ondemand_data.upthreshold = 80; + priv->ondemand_data.downdifferential = 10; + priv->profile.max_state = priv->freq_count; + + for (i = 0; i < max_state; ++i) { + ret = dev_pm_opp_add(dev, priv->freq_table[i], 0); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + goto err; + } + } + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, + gov, &priv->ondemand_data); + if (IS_ERR(priv->devfreq)) { + ret = PTR_ERR(priv->devfreq); + dev_err(dev, "failed to add devfreq device: %d\n", ret); + goto err; + } + + ret = phytium_noc_set_freq(dev, priv->profile.initial_freq); + if (ret) + dev_warn(dev, "failed to init noc frequency: %d\n", ret); + + writel_relaxed(0x02, priv->reg_noc + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*1 + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*2 + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*3 + WORK_STATE); + + writel_relaxed(0x3f, priv->reg_noc + CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*1 + CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*2 + CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*3 + CNT_ENABLE); + return 0; + +err: + dev_pm_opp_of_remove_table(dev); + kfree(priv); + return ret; +} + +static int phytium_nocfreq_remove(struct platform_device *pdev) +{ + struct phytium_nocfreq *priv = platform_get_drvdata(pdev); + unsigned long initial_freq = priv->profile.initial_freq; + struct device *dev = &pdev->dev; + int ret; + + writel_relaxed(0x0, priv->reg_noc + CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*1 + CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*2 + CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*3 + CNT_ENABLE); + + writel_relaxed(0x1, priv->reg_noc + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + CLR_EN); + + ret = phytium_noc_set_freq(dev, initial_freq); + if (ret) + dev_warn(dev, "failed to restore NOC frequency: %d\n", ret); + + iounmap(priv->reg_noc); + + if (!priv->devfreq) + return 0; + + dev_pm_opp_of_remove_table(dev); + + kfree(priv); + return 0; +} + +static const struct acpi_device_id phytium_noc_acpi_ids[] = { + {"PHYT0047"}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, phytium_noc_acpi_ids); + +static struct platform_driver phytium_nocfreq_driver = { + .probe = phytium_nocfreq_probe, + .remove = phytium_nocfreq_remove, + .driver = { + .name = "phytium_nocfreq", + .acpi_match_table = ACPI_PTR(phytium_noc_acpi_ids), + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(phytium_nocfreq_driver); + +MODULE_DESCRIPTION("Phytium NOC Controller frequency driver"); +MODULE_AUTHOR("Li Jiayi "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(NOCFREQ_DRIVER_VERSION); -- Gitee From 5fcd8f8aa35c69476ffabdaa61e1209c2b7e161d Mon Sep 17 00:00:00 2001 From: Li Jiayi Date: Wed, 5 Mar 2025 17:48:51 +0800 Subject: [PATCH 108/154] devfreq: Add phytium dmu devfreq driver This adds the DEVFREQ driver for Phytium DDR Memory Unit.It adjusts frequency for dmu based on load bandwidth obtained from register. Mainline: NA Signed-off-by: Li Jiayi Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Change-Id: Id92c3b6e8df9790477e861c173ab51f193735d4e --- drivers/devfreq/Kconfig | 9 + drivers/devfreq/Makefile | 1 + drivers/devfreq/phytium_dmu.c | 373 ++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100644 drivers/devfreq/phytium_dmu.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index e9a581da37..0aa77f3b78 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -152,6 +152,15 @@ config ARM_PHYTIUM_NOC_DEVFREQ This adds the DEVFREQ driver for Phytium Net On Chip. It adjusts frequency for noc based on load bandwidth obtained from register. +config ARM_PHYTIUM_DMU_DEVFREQ + tristate "ARM PHYTIUM DMU DEVFREQ Driver" + depends on ARCH_PHYTIUM || COMPILE_TEST + depends on ACPI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for Phytium DDR Memory Unit. + It adjusts frequency for dmu based on load bandwidth obtained from register. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 5bc3851392..f625ae6a4a 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o obj-$(CONFIG_ARM_PHYTIUM_NOC_DEVFREQ) += phytium_noc.o +obj-$(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) += phytium_dmu.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c new file mode 100644 index 0000000000..c0a281b9dc --- /dev/null +++ b/drivers/devfreq/phytium_dmu.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-1.0 +/* + *phytium_dmu.c - Phytium Processor dmu Frequency Driver + * + *Copyright (C) 2024,Phytium Technology Co.,Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#define DEVICE_TYPE 9 //DMU ID + +#define UPDATE_INTERVAL_MS 10 + +#define DMUFREQ_DRIVER_VERSION "1.0.0" + +struct phytium_dmufreq { + struct device *dev; + + struct devfreq *devfreq; + struct devfreq_dev_profile profile; + struct devfreq_simple_ondemand_data ondemand_data; + + unsigned long rate, target_rate; + unsigned long bandwidth; + + struct timer_list sampling; + struct work_struct work; + + unsigned int freq_count; + unsigned long freq_table[]; +}; + +static ktime_t stop; + +static int phytium_dmu_set_freq(struct device *dev, unsigned long freq) +{ + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object args[4]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + unsigned long long ret; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = freq; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + args[3].type = ACPI_TYPE_INTEGER; + args[3].integer.value = 0; + + status = acpi_evaluate_integer(handle, "PSCF", &arg_list, &ret); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PSCF method\n"); + return -EIO; + } + + return 0; +} + +static int phytium_dmu_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + unsigned long old_freq = priv->rate; + unsigned long target_rate; + struct dev_pm_opp *opp; + int ret; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + target_rate = dev_pm_opp_get_freq(opp); + + dev_pm_opp_put(opp); + + if (target_rate == old_freq) + return 0; + + dev_dbg(dev, "target_rate = %lu\n", target_rate); + /* + * Read back the clk rate to verify switch was correct and so that + * we can report it on all error paths. + */ + ret = phytium_dmu_set_freq(dev, target_rate); + if (ret) { + dev_warn(dev, "failed to set DRAM frequency: %lu\n", target_rate); + return ret; + } + priv->rate = target_rate; + + return ret; + +} + +static int phytium_dmu_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + + *freq = priv->rate; + + return 0; +} + +static int phytium_read_perf_counter(struct device *dev) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *package, *elements; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + + status = acpi_evaluate_object(handle, "PDMU", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + + package = buffer.pointer; + + elements = package->package.elements; + + return elements[0].integer.value + elements[1].integer.value; +} + +static void sampling_timer_callback(struct timer_list *t) +{ + struct phytium_dmufreq *priv = from_timer(priv, t, sampling); + + schedule_work(&priv->work); +} + +static void sampling_work_handle(struct work_struct *work) +{ + struct phytium_dmufreq *priv = container_of(work, struct phytium_dmufreq, work); + struct device *dev = priv->dev; + static unsigned long load_counter; + static int count; + unsigned long current_load; + + current_load = phytium_read_perf_counter(dev); + + load_counter += current_load; + count += 1; + + if (ktime_after(ktime_get(), stop)) { + priv->bandwidth = (load_counter / count)/2; + load_counter = 0; + count = 0; + stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); + } else + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); +} + +static int phytium_dmu_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + + stat->busy_time = priv->bandwidth; + stat->total_time = (75000000 * priv->rate) / priv->freq_table[0]; + dev_dbg(dev, "busy_time = %lu, total_time = %lu\n", + stat->busy_time, stat->total_time); + + stat->current_frequency = priv->rate; + + return 0; +} + +static int phytium_dmu_get_freq_info(struct device *dev) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + int i; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + priv->freq_count = element->integer.value; + + for (i = 0; i < priv->freq_count; i++) { + element = &package->package.elements[i+2]; + priv->freq_table[i] = element->integer.value; + dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); + } + + return 0; + +} + +static int get_freq_count(struct device *dev) +{ + int freq_count = -1; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method, status = %d\n", status); + return -EIO; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + freq_count = element->integer.value; + dev_dbg(dev, "freq_count = %d\n", freq_count); + + return freq_count; +} + +static int phytium_dmufreq_probe(struct platform_device *pdev) +{ + struct phytium_dmufreq *priv; + struct device *dev = &pdev->dev; + const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + int i, ret; + unsigned int max_state = get_freq_count(dev); + + if (max_state <= 0) + return max_state; + + dev->init_name = "dmufreq"; + + priv = kzalloc(sizeof(struct phytium_dmufreq) + + max_state * sizeof(unsigned long), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + ret = phytium_dmu_get_freq_info(dev); + if (ret) { + dev_err(dev, "failed to get ddr frequency info\n"); + return -EIO; + } + + priv->profile.initial_freq = priv->freq_table[0]; + priv->profile.polling_ms = 100; + priv->profile.target = phytium_dmu_target; + priv->profile.get_cur_freq = phytium_dmu_get_cur_freq; + priv->profile.get_dev_status = phytium_dmu_get_dev_status; + priv->profile.freq_table = priv->freq_table; + priv->rate = priv->profile.initial_freq; + priv->profile.max_state = priv->freq_count; + priv->ondemand_data.upthreshold = 80; + priv->ondemand_data.downdifferential = 10; + + for (i = 0; i < max_state; ++i) { + ret = dev_pm_opp_add(dev, priv->freq_table[i], 0); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + goto err; + } + } + + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, + gov, &priv->ondemand_data); + if (IS_ERR(priv->devfreq)) { + ret = PTR_ERR(priv->devfreq); + dev_err(dev, "failed to add devfreq device: %d\n", ret); + goto err; + } + + INIT_WORK(&priv->work, sampling_work_handle); + timer_setup(&priv->sampling, sampling_timer_callback, 0); + stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); + + priv->dev = dev; + + return 0; + +err: + dev_pm_opp_of_remove_table(dev); + return ret; +} + +static int phytium_dmufreq_remove(struct platform_device *pdev) +{ + struct phytium_dmufreq *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + if (!priv->devfreq) + return 0; + + flush_work(&priv->work); + del_timer_sync(&priv->sampling); + + dev_pm_opp_of_remove_table(dev); + + kfree(priv); + + return 0; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_dmufreq_acpi_ids[] = { + {"PHYT0063"}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, phytium_dmufreq_acpi_ids); +#else +#define phytium_dmu_acpi_ids NULL +#endif + +static struct platform_driver phytium_dmufreq_driver = { + .probe = phytium_dmufreq_probe, + .remove = phytium_dmufreq_remove, + .driver = { + .name = "phytium_dmufreq", + .acpi_match_table = ACPI_PTR(phytium_dmufreq_acpi_ids), + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(phytium_dmufreq_driver); + +MODULE_DESCRIPTION("Phytium DDR Memory Unit frequency driver"); +MODULE_AUTHOR("Li Jiayi "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DMUFREQ_DRIVER_VERSION); -- Gitee From 90fe7f9217dadd4203a031f93f0c658c607af737 Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Fri, 28 Feb 2025 11:26:40 +0800 Subject: [PATCH 109/154] hda: phytium: Put off creating runtime inquiry node HDA driver will use schedule work to register codec driver used by input_register_device function. There will be a conflict if sysfs_create_group is operated at the same time. So put off creating runtime inquiry node to avoid this conflict. Mainline: NA Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I9b6d5711a874cbd3e15eb2b19595b69285fbc129 --- sound/pci/hda/hda_phytium.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/pci/hda/hda_phytium.c b/sound/pci/hda/hda_phytium.c index 1fe49515cc..e5089a75e7 100644 --- a/sound/pci/hda/hda_phytium.c +++ b/sound/pci/hda/hda_phytium.c @@ -1055,18 +1055,11 @@ static int hda_ft_probe(struct platform_device *pdev) if (schedule_probe) schedule_work(&hda->probe_work); - if (sysfs_create_group(&hda->dev->kobj, &hda_ft_runtime_status_group)) { - dev_warn(&pdev->dev, "failed create sysfs\n"); - goto err_sysfs; - } - set_bit(dev, probed_devs); if (chip->disabled) complete_all(&hda->probe_wait); return 0; -err_sysfs: - sysfs_remove_group(&hda->dev->kobj, &hda_ft_runtime_status_group); out_free: snd_card_free(card); return err; @@ -1116,8 +1109,15 @@ static int azx_probe_continue(struct azx *chip) if (azx_has_pm_runtime(chip)) pm_runtime_put_noidle(hddev); + + if (sysfs_create_group(&hda->dev->kobj, &hda_ft_runtime_status_group)) { + dev_warn(hda->dev, "failed create sysfs\n"); + goto err_sysfs; + } return err; +err_sysfs: + sysfs_remove_group(&hda->dev->kobj, &hda_ft_runtime_status_group); out_free: if (bus->irq >= 0) { free_irq(bus->irq, (void *)chip); -- Gitee From fa8aa6069021a34505990d051a82875eb080c908 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Tue, 4 Mar 2025 10:20:05 +0800 Subject: [PATCH 110/154] i2c: phytium: Support MCTP over I2C MCTP protocol is a transmission protocol used for device management and control,which can be transmitted via I2C link.When the I2C controller is configured as master-slave switching mode,it works in slave mode by default,and switches to master mode when it wants to take the initiaitve to communicate externally,and when the master mode communication is completed,it switches to the slave mode communication immediately to ensure the completeness of a communication transaction. Mainline: NA Signed-off-by: Wu Jinyong Signed-off-by: Wang Yinfeng Change-Id: If3acb0b647c5fc3f925e0e0852783a818e8c2fc9 --- drivers/i2c/busses/i2c-phytium-common.c | 2 +- drivers/i2c/busses/i2c-phytium-core.h | 19 ++-- drivers/i2c/busses/i2c-phytium-mix.c | 108 +++++++++++++--------- drivers/i2c/busses/i2c-phytium-pci.c | 2 +- drivers/i2c/busses/i2c-phytium-platform.c | 3 +- 5 files changed, 75 insertions(+), 59 deletions(-) diff --git a/drivers/i2c/busses/i2c-phytium-common.c b/drivers/i2c/busses/i2c-phytium-common.c index b008e6a10b..809aba7974 100644 --- a/drivers/i2c/busses/i2c-phytium-common.c +++ b/drivers/i2c/busses/i2c-phytium-common.c @@ -117,7 +117,7 @@ EXPORT_SYMBOL_GPL(i2c_phytium_prepare_clk); int i2c_phytium_check_bus_not_busy(struct phytium_i2c_dev *dev) { - if (dev->slave_state != SLAVE_STATE_IDLE) + if (dev->slave_status != SLAVE_STATE_IDLE) return -EAGAIN; if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) return -ETIMEDOUT; diff --git a/drivers/i2c/busses/i2c-phytium-core.h b/drivers/i2c/busses/i2c-phytium-core.h index cd37d3ea68..ad21611636 100644 --- a/drivers/i2c/busses/i2c-phytium-core.h +++ b/drivers/i2c/busses/i2c-phytium-core.h @@ -10,7 +10,7 @@ #include #include -#define I2C_PHYTIUM_DRV_VERSION "1.0.0" +#define I2C_PHYTIUM_DRV_VERSION "1.0.1" #define IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \ I2C_FUNC_SMBUS_BYTE | \ @@ -140,13 +140,13 @@ #define PHYTIUM_IC_SLAVE 1 #if IS_ENABLED(CONFIG_I2C_SLAVE) -enum i2c_slave_state { - SLAVE_STATE_IDLE, - SLAVE_STATE_RECV, - SLAVE_STATE_SEND, - SLAVE_STATE_REQUEST, - SLAVE_STATE_RESPONSE -}; +/* Slave status */ +#define SLAVE_STATE_IDLE 0x0 +#define SLAVE_WRITE_IN_PROGRESS BIT(0) +#define SLAVE_READ_IN_PROGRESS BIT(1) +#define SLAVE_STATE_ACTIVE BIT(2) + +#define REQ_MIN_LEN 6 #endif #define ABRT_7B_ADDR_NOACK 0 #define ABRT_10ADDR1_NOACK 1 @@ -198,7 +198,8 @@ struct phytium_i2c_dev { u32 capability; #if IS_ENABLED(CONFIG_I2C_SLAVE) - enum i2c_slave_state slave_state; + u32 slave_status; + struct completion slave_complete; #endif spinlock_t i2c_lock; struct i2c_client *slave; diff --git a/drivers/i2c/busses/i2c-phytium-mix.c b/drivers/i2c/busses/i2c-phytium-mix.c index 748042c9fe..4a157decea 100644 --- a/drivers/i2c/busses/i2c-phytium-mix.c +++ b/drivers/i2c/busses/i2c-phytium-mix.c @@ -24,6 +24,9 @@ #define I2C_QUICK_CMD_BIT_SET BIT(13) #define DEFAULT_TIMEOUT (DEFAULT_CLOCK_FREQUENCY / 1000 * 35) +static int i2c_phytium_init_master(struct phytium_i2c_dev *dev); +static void i2c_phytium_change_mode(int target_mode, struct phytium_i2c_dev *dev); + static int i2c_phytium_recover_controller(struct phytium_i2c_dev *dev) { unsigned long flags; @@ -31,14 +34,20 @@ static int i2c_phytium_recover_controller(struct phytium_i2c_dev *dev) spin_lock_irqsave(&dev->i2c_lock, flags); reset_control_reset(dev->rst); + + if (!dev->slave) { + i2c_phytium_init_master(dev); #if IS_ENABLED(CONFIG_I2C_SLAVE) - dev->slave_state = SLAVE_STATE_IDLE; - dev->status = STATUS_IDLE; - if (dev->slave) + } else { + dev->slave_status = SLAVE_STATE_IDLE; phytium_writel(dev, dev->slave->addr, IC_SAR); + i2c_phytium_change_mode(PHYTIUM_IC_SLAVE, dev); #endif + } + dev->status = STATUS_IDLE; spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; } @@ -131,7 +140,6 @@ static int i2c_phytium_init_master(struct phytium_i2c_dev *dev) static void i2c_phytium_change_mode(int target_mode, struct phytium_i2c_dev *dev) { if (target_mode == PHYTIUM_IC_MASTER) { - dev->disable_int(dev); dev->disable(dev); i2c_phytium_init_master(dev); } else { @@ -371,31 +379,41 @@ static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], ret = pm_runtime_get_sync(dev->dev); if (ret < 0) { dev_err(dev->dev, "pm runtime get sync err.\n"); - goto pm_exit; + goto done; } spin_lock_irqsave(&dev->i2c_lock, flags); - reinit_completion(&dev->cmd_complete); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave && dev->slave_status != SLAVE_STATE_IDLE) { + reinit_completion(&dev->slave_complete); + spin_unlock_irqrestore(&dev->i2c_lock, flags); + + /* Waiting for slave to complete. */ + if (!wait_for_completion_timeout(&dev->slave_complete, + adapter->timeout)) { + dev_err(dev->dev, "Slave is timeout, recover\n"); + i2c_phytium_recover_controller(dev); + ret = -ETIMEDOUT; + goto done; + } + spin_lock_irqsave(&dev->i2c_lock, flags); + } + + if (dev->mode == PHYTIUM_IC_SLAVE) + i2c_phytium_change_mode(PHYTIUM_IC_MASTER, dev); +#endif + + reinit_completion(&dev->cmd_complete); dev->msgs = msgs; dev->msgs_num = num; dev->cmd_err = 0; dev->msg_write_idx = 0; dev->msg_read_idx = 0; dev->msg_err = 0; - dev->status = STATUS_IDLE; dev->abort_source = 0; dev->rx_outstanding = 0; - ret = i2c_phytium_check_bus_not_busy(dev); - if (ret < 0) - goto done; - -#if IS_ENABLED(CONFIG_I2C_SLAVE) - if (dev->slave_cfg) - i2c_phytium_change_mode(PHYTIUM_IC_MASTER, dev); -#endif - /* Start the transfers */ i2c_phytium_xfer_init(dev); spin_unlock_irqrestore(&dev->i2c_lock, flags); @@ -404,15 +422,19 @@ static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], adapter->timeout)) { dev_err(dev->dev, "controller timed out\n"); i2c_phytium_recover_controller(dev); - spin_lock_irqsave(&dev->i2c_lock, flags); - if (!dev->slave_cfg) - i2c_phytium_init_master(dev); ret = -ETIMEDOUT; goto done; } - spin_lock_irqsave(&dev->i2c_lock, flags); - __i2c_phytium_disable_nowait(dev); + if (!dev->slave) + __i2c_phytium_disable_nowait(dev); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* Do nothing. */ + if (dev->slave && dev->msgs->len >= REQ_MIN_LEN) { + reinit_completion(&dev->slave_complete); + wait_for_completion_timeout(&dev->slave_complete, adapter->timeout); + } +#endif if (dev->msg_err) { ret = dev->msg_err; goto done; @@ -425,25 +447,13 @@ static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], /* We have got an error */ if (dev->cmd_err == IC_ERR_TX_ABRT) { - spin_unlock_irqrestore(&dev->i2c_lock, flags); ret = i2c_phytium_handle_tx_abort(dev); - spin_lock_irqsave(&dev->i2c_lock, flags); goto done; } ret = -EIO; done: -#if IS_ENABLED(CONFIG_I2C_SLAVE) - if (dev->slave_cfg) - i2c_phytium_change_mode(PHYTIUM_IC_SLAVE, dev); - dev->status = STATUS_IDLE; - dev->slave_state = SLAVE_STATE_IDLE; -#endif - spin_unlock_irqrestore(&dev->i2c_lock, flags); - -pm_exit: - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); @@ -483,7 +493,7 @@ static int i2c_phytium_reg_slave(struct i2c_client *slave) dev->msg_write_idx = 0; dev->msg_read_idx = 0; dev->msg_err = 0; - dev->status = STATUS_IDLE; + dev->slave_status = SLAVE_STATE_IDLE; dev->abort_source = 0; dev->rx_outstanding = 0; @@ -603,8 +613,7 @@ static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) phytium_readl(dev, IC_CLR_TX_ABRT); phytium_writel(dev, 0, IC_INTR_MASK); - complete(&dev->cmd_complete); - return 0; + goto abort; } } @@ -617,6 +626,10 @@ static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) abort: if ((stat & IC_INTR_TX_ABRT) || (stat & IC_INTR_STOP_DET) || dev->msg_err) { complete(&dev->cmd_complete); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave) + i2c_phytium_change_mode(PHYTIUM_IC_SLAVE, dev); +#endif } else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { /* Workaround to trigger pending interrupt */ stat = phytium_readl(dev, IC_INTR_MASK); @@ -644,11 +657,10 @@ static int i2c_phytium_irq_handler_slave(struct phytium_i2c_dev *dev) stat = i2c_phytium_read_clear_intrbits(dev); if (stat & IC_INTR_RX_FULL) { - if (dev->status != STATUS_WRITE_IN_PROGRESS) { - dev->status = STATUS_WRITE_IN_PROGRESS; + if (dev->slave_status != SLAVE_WRITE_IN_PROGRESS) { + dev->slave_status = SLAVE_WRITE_IN_PROGRESS; i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val); - dev->slave_state = SLAVE_STATE_RECV; } do { val = phytium_readl(dev, IC_DATA_CMD); @@ -662,13 +674,11 @@ static int i2c_phytium_irq_handler_slave(struct phytium_i2c_dev *dev) if (slave_activity) { phytium_readl(dev, IC_CLR_RD_REQ); - if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + if (!(dev->slave_status & SLAVE_READ_IN_PROGRESS)) { i2c_slave_event(dev->slave, I2C_SLAVE_READ_REQUESTED, &val); - dev->status |= STATUS_READ_IN_PROGRESS; - dev->status &= ~STATUS_WRITE_IN_PROGRESS; - dev->slave_state = SLAVE_STATE_SEND; - + dev->slave_status |= SLAVE_READ_IN_PROGRESS; + dev->slave_status &= ~SLAVE_WRITE_IN_PROGRESS; } else { i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &val); @@ -680,8 +690,8 @@ static int i2c_phytium_irq_handler_slave(struct phytium_i2c_dev *dev) if (stat & IC_INTR_STOP_DET) { i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); #if IS_ENABLED(CONFIG_I2C_SLAVE) - dev->status = STATUS_IDLE; - dev->slave_state = SLAVE_STATE_IDLE; + dev->slave_status = SLAVE_STATE_IDLE; + complete(&dev->slave_complete); #endif } @@ -697,6 +707,8 @@ static irqreturn_t i2c_phytium_isr(int this_irq, void *dev_id) #if IS_ENABLED(CONFIG_I2C_SLAVE) if (dev->mode == PHYTIUM_IC_SLAVE) { + if (dev->slave_status == SLAVE_STATE_IDLE) + dev->slave_status = SLAVE_STATE_ACTIVE; i2c_phytium_irq_handler_slave(dev); spin_unlock(&dev->i2c_lock); return IRQ_HANDLED; @@ -829,6 +841,10 @@ int i2c_phytium_probe(struct phytium_i2c_dev *dev) "Phytium I2C Slave Adapter"); dev->init = i2c_phytium_init_slave; } +#if IS_ENABLED(CONFIG_I2C_SLAVE) + init_completion(&dev->slave_complete); + dev->slave_status = SLAVE_STATE_IDLE; +#endif dev->disable = i2c_phytium_disable; dev->disable_int = i2c_phytium_disable_int; diff --git a/drivers/i2c/busses/i2c-phytium-pci.c b/drivers/i2c/busses/i2c-phytium-pci.c index 11db371264..17a0137e4c 100644 --- a/drivers/i2c/busses/i2c-phytium-pci.c +++ b/drivers/i2c/busses/i2c-phytium-pci.c @@ -178,7 +178,7 @@ static int i2c_phytium_pci_probe(struct pci_dev *pdev, dev->flags |= controller->flags; #if IS_ENABLED(CONFIG_I2C_SLAVE) - dev->slave_state = SLAVE_STATE_IDLE; + dev->slave_status = SLAVE_STATE_IDLE; #endif spin_lock_init(&dev->i2c_lock); dev->functionality = controller->functionality | IC_DEFAULT_FUNCTIONALITY; diff --git a/drivers/i2c/busses/i2c-phytium-platform.c b/drivers/i2c/busses/i2c-phytium-platform.c index f33aeb32cb..2dfe919fdd 100644 --- a/drivers/i2c/busses/i2c-phytium-platform.c +++ b/drivers/i2c/busses/i2c-phytium-platform.c @@ -136,7 +136,6 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) 0, 100000, 400000, 1000000, 3400000 }; - irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -154,7 +153,7 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) dev->irq = irq; dev->first_time_init_master = false; #if IS_ENABLED(CONFIG_I2C_SLAVE) - dev->slave_state = SLAVE_STATE_IDLE; + dev->slave_status = SLAVE_STATE_IDLE; #endif spin_lock_init(&dev->i2c_lock); platform_set_drvdata(pdev, dev); -- Gitee From 2129d3b16d37623bcbe6b1ef8600cbe075b4ff90 Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Tue, 21 Jan 2025 17:50:12 +0800 Subject: [PATCH 111/154] i2s: phytium: Add playback only in pmdk_dai There should be only one stream of dp-i2s, which is used for palyback. Add playback only in pmdk_dai to avoid the "Only one simultaneous stream supported!" warning when loading driver. Mainline: Open-Source Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I37d8d7b7a79fd45115f4f51852652d9263b29369 --- sound/soc/phytium/pmdk_dp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/phytium/pmdk_dp.c b/sound/soc/phytium/pmdk_dp.c index 24fa6e893f..1dbd6b6663 100755 --- a/sound/soc/phytium/pmdk_dp.c +++ b/sound/soc/phytium/pmdk_dp.c @@ -120,6 +120,7 @@ static struct snd_soc_dai_link pmdk_dai0 = { .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp0_init, SND_SOC_DAILINK_REG(pmdk_dp0_dai), + .playback_only = 1, }; static struct snd_soc_dai_link pmdk_dai1 = { @@ -128,6 +129,7 @@ static struct snd_soc_dai_link pmdk_dai1 = { .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp1_init, SND_SOC_DAILINK_REG(pmdk_dp1_dai), + .playback_only = 1, }; static struct snd_soc_dai_link pmdk_dai2 = { @@ -136,6 +138,7 @@ static struct snd_soc_dai_link pmdk_dai2 = { .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp2_init, SND_SOC_DAILINK_REG(pmdk_dp2_dai), + .playback_only = 1, }; static struct snd_soc_card pmdk = { -- Gitee From bcc95b096900b5f3692d744d02573fe46c5eae56 Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Thu, 13 Feb 2025 17:43:54 +0800 Subject: [PATCH 112/154] i2s: phytium: Avoid enable gpio in dp-i2s driver Use insert flag to avoid enable gpio in dp-i2s driver when resumed from s3/s4. Mainline: Open-Source Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I95cffa63ed2f7b948e1386a7af3a4d139fefed78 --- sound/soc/phytium/phytium-i2s-v2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c index ab260b95c9..5f5398f6fb 100644 --- a/sound/soc/phytium/phytium-i2s-v2.c +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -527,7 +527,8 @@ static int phyt_pcm_resume(struct snd_soc_component *component) } } } - phyt_i2s_enable_gpio(priv); + if (priv->insert >= 0) + phyt_i2s_enable_gpio(priv); error: return ret; } -- Gitee From 618f4bd9845bb680389fae80e1be8bd95f26586b Mon Sep 17 00:00:00 2001 From: Dai Jingtao Date: Wed, 12 Feb 2025 10:19:10 +0800 Subject: [PATCH 113/154] i2s: phytium: Add nonatomic in i2s machine driver Add nonatomic flag in i2s machine driver so that spin_lock will not be used in pcm_ioctl. And in that case, usleep and mutex_lock can be used in i2s driver. Mainline: Open-Source Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng Change-Id: I04bd3583b3977eb7be0bd59dcd14014f27e038fe --- sound/soc/phytium/phytium-i2s-v2.c | 5 +---- sound/soc/phytium/phytium-machine-v2.c | 1 + sound/soc/phytium/pmdk_dp.c | 3 +++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c index 5f5398f6fb..145685f06e 100644 --- a/sound/soc/phytium/phytium-i2s-v2.c +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -405,10 +405,7 @@ int phyt_i2s_msg_set_cmd(struct phytium_i2s *priv, struct phyti2s_cmd *msg) while ((ans_msg->complete == PHYTI2S_COMPLETE_NONE || ans_msg->complete == PHYTI2S_COMPLETE_GOING) && timeout) { - if (preempt_count() != 0) - udelay(500); - else - usleep_range(500, 1000); + usleep_range(500, 1000); timeout--; } diff --git a/sound/soc/phytium/phytium-machine-v2.c b/sound/soc/phytium/phytium-machine-v2.c index cf29c368b2..f072520e87 100644 --- a/sound/soc/phytium/phytium-machine-v2.c +++ b/sound/soc/phytium/phytium-machine-v2.c @@ -51,6 +51,7 @@ static struct snd_soc_dai_link phyt_machine_dai[] = { .stream_name = "PHYTIUM HIFT V2", .dai_fmt = PMDK_DAI_FMT, SND_SOC_DAILINK_REG(phyt_machine), + .nonatomic = true, }, }; diff --git a/sound/soc/phytium/pmdk_dp.c b/sound/soc/phytium/pmdk_dp.c index 1dbd6b6663..01e6fe5f67 100755 --- a/sound/soc/phytium/pmdk_dp.c +++ b/sound/soc/phytium/pmdk_dp.c @@ -120,6 +120,7 @@ static struct snd_soc_dai_link pmdk_dai0 = { .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp0_init, SND_SOC_DAILINK_REG(pmdk_dp0_dai), + .nonatomic = true, .playback_only = 1, }; @@ -129,6 +130,7 @@ static struct snd_soc_dai_link pmdk_dai1 = { .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp1_init, SND_SOC_DAILINK_REG(pmdk_dp1_dai), + .nonatomic = true, .playback_only = 1, }; @@ -138,6 +140,7 @@ static struct snd_soc_dai_link pmdk_dai2 = { .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp2_init, SND_SOC_DAILINK_REG(pmdk_dp2_dai), + .nonatomic = true, .playback_only = 1, }; -- Gitee From 96f1bb6fe93e83b09d7aa526b37127f4feee039d Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Sat, 8 Feb 2025 06:22:42 +0000 Subject: [PATCH 114/154] codec-v2: phytium: Enable interrupt and set interrupt mask If the interrupt mask for sound control are not being set, and the interrupt from the AP to the controller is not being enabled, will prevent real-time sound adjustment and it means that the adjusting the volume during playback will not cause any change to the sound. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: Ia8d1d350119bf84566865ea0c0bcce8c2e7ea38b --- sound/soc/codecs/phytium-codec-v2.c | 8 +++++++- sound/soc/codecs/phytium-codec-v2.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/phytium-codec-v2.c b/sound/soc/codecs/phytium-codec-v2.c index 3a3e063e26..664839c1d9 100644 --- a/sound/soc/codecs/phytium-codec-v2.c +++ b/sound/soc/codecs/phytium-codec-v2.c @@ -189,8 +189,11 @@ static int phyt_pm_cmd(struct snd_soc_component *component, msg->cmd_subid = cmd; msg->complete = 0; msg->cmd_para.phytcodec_reg.cnt = 0; - if (cmd == PHYTCODEC_MSG_CMD_SET_RESUME) + if (cmd == PHYTCODEC_MSG_CMD_SET_RESUME) { memcpy(msg->cmd_para.phytcodec_reg.regs, priv->regs, REG_SH_LEN); + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_MASK, 0x0); + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_ENABLE, 0x1); + } ret = phyt_codec_msg_set_cmd(priv); if (ret < 0) { dev_err(priv->dev, "set cmd_subid 0x%x failed\n", cmd); @@ -685,6 +688,9 @@ static int phyt_codec_probe(struct platform_device *pdev) phyt_dai.playback.channels_max = phyt_get_channels(priv); phyt_dai.capture.channels_max = phyt_dai.playback.channels_max; + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_MASK, 0x0); + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_ENABLE, 0x1); + ret = devm_snd_soc_register_component(dev, &phyt_component_driver, &phyt_dai, 1); if (ret != 0) { diff --git a/sound/soc/codecs/phytium-codec-v2.h b/sound/soc/codecs/phytium-codec-v2.h index 2dff1a28ee..23b643fd92 100644 --- a/sound/soc/codecs/phytium-codec-v2.h +++ b/sound/soc/codecs/phytium-codec-v2.h @@ -34,6 +34,8 @@ #define PHYTIUM_CODEC_ADC_ENABLE 0X524 #define PHYTIUM_CODEC_DAC_ENABLE 0x528 #define PHYTIUM_CODEC_INT_STATUS 0x560 +#define PHYTIUM_CODEC_INT_MASK 0x564 +#define PHYTIUM_CODEC_INT_ENABLE 0x590 #define PIPE_NUM 11 #define REG_MAX 0x52c -- Gitee From 7548fc0b72c26f9b388148f309dbdfa207078890 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Mon, 17 Feb 2025 18:52:49 +0800 Subject: [PATCH 115/154] codec-v2:phytium: Improve debug function support This patch add some new debugging methods for codec, such as "get" "set" "dump". users can read or write codec registers. these allow us to abtain the current registers status of the codec, which helps in troubleshooting issues. Mainline: Open-Source Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: I2d8cb1653ba2fa352a551567a608dd1b9182b4ac --- sound/soc/codecs/phytium-codec-v2.c | 293 +++++++++++++++++++--------- sound/soc/codecs/phytium-codec-v2.h | 10 + 2 files changed, 207 insertions(+), 96 deletions(-) diff --git a/sound/soc/codecs/phytium-codec-v2.c b/sound/soc/codecs/phytium-codec-v2.c index 664839c1d9..8ecc6e50ca 100644 --- a/sound/soc/codecs/phytium-codec-v2.c +++ b/sound/soc/codecs/phytium-codec-v2.c @@ -32,7 +32,7 @@ #include "phytium-codec-v2.h" -#define PHYT_CODEC_V2_VERSION "1.0.0" +#define PHYT_CODEC_V2_VERSION "1.1.0" #define PHYTIUM_RATES (SNDRV_PCM_RATE_192000 | \ SNDRV_PCM_RATE_96000 | \ SNDRV_PCM_RATE_88200 | \ @@ -136,24 +136,30 @@ int phyt_codec_msg_set_cmd(struct phytium_codec *priv) if (timeout == 0) { dev_err(priv->dev, "failed to receive msg, timeout\n"); - ret = -EINVAL; - } else if (ans_msg->complete >= PHYTCODEC_COMPLETE_GENERIC_ERROR) { - dev_err(priv->dev, "receive msg; generic_error, error code:%d\n", - ans_msg->complete); - ret = -EINVAL; + return -EINVAL; } else if (ans_msg->complete == PHYTCODEC_COMPLETE_SUCCESS) { dev_dbg(priv->dev, "receive msg successfully\n"); + if (ans_msg->status != 0) { + phyt_codec_show_status(ans_msg->status); + dev_err(priv->dev, "controller status error code:%d\n", + ans_msg->status); + return -EINVAL; + } + } else if (ans_msg->complete != PHYTCODEC_COMPLETE_SUCCESS) { + dev_err(priv->dev, "receive msg; error code:%d\n", + ans_msg->complete); + ret = -EINVAL; + } else { + dev_err(priv->dev, "unkonwn error"); + ret = -EINVAL; } - if (ans_msg->complete != PHYTCODEC_COMPLETE_SUCCESS) - phyt_codec_show_status(ans_msg->status); return ret; } -static int phyt_cmd(struct snd_soc_component *component, +static int phyt_set_cmd(struct phytium_codec *priv, unsigned int cmd) { - struct phytium_codec *priv = snd_soc_component_get_drvdata(component); struct phytcodec_cmd *msg = priv->sharemem_base; int ret = 0; @@ -172,14 +178,13 @@ static int phyt_cmd(struct snd_soc_component *component, return ret; } -static int phyt_pm_cmd(struct snd_soc_component *component, +static int phyt_pm_cmd(struct phytium_codec *priv, unsigned int cmd) { - struct phytium_codec *priv = snd_soc_component_get_drvdata(component); struct phytcodec_cmd *msg = priv->sharemem_base; uint16_t total_regs_len; uint8_t *regs; - int ret = 0; + int ret = 0, i; memset(msg, 0, sizeof(struct phytcodec_cmd)); @@ -202,7 +207,7 @@ static int phyt_pm_cmd(struct snd_soc_component *component, } total_regs_len = msg->cmd_para.phytcodec_reg.total_regs_len; - if (cmd == PHYTCODEC_MSG_CMD_SET_SUSPEND) { + if (cmd == PHYTCODEC_MSG_CMD_SET_SUSPEND || cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) { regs = kmalloc(total_regs_len, GFP_KERNEL); priv->regs = regs; while (total_regs_len > REG_SH_LEN * msg->cmd_para.phytcodec_reg.cnt) { @@ -218,6 +223,12 @@ static int phyt_pm_cmd(struct snd_soc_component *component, } memcpy(regs, msg->cmd_para.phytcodec_reg.regs, total_regs_len - REG_SH_LEN * (msg->cmd_para.phytcodec_reg.cnt - 1)); + if (cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) { + dev_dbg(priv->dev, "all codec registers:\n"); + for (i = 0; i < total_regs_len; i++) + dev_dbg(priv->dev, "0x%02x-0x%02x\n", i, priv->regs[i]); + kfree(priv->regs); + } } else if (cmd == PHYTCODEC_MSG_CMD_SET_RESUME) { regs = priv->regs; while (total_regs_len > REG_SH_LEN * msg->cmd_para.phytcodec_reg.cnt) { @@ -237,51 +248,51 @@ static int phyt_pm_cmd(struct snd_soc_component *component, return ret; } -static int phyt_show_registers(struct phytium_codec *priv) +static int phyt_get_cmd(struct phytium_codec *priv, unsigned int cmd) { struct phytcodec_cmd *msg = priv->sharemem_base; - int ret = 0, i; + int ret = 0; msg->reserved = 0; msg->seq = 0; msg->cmd_id = PHYTCODEC_MSG_CMD_GET; - msg->cmd_subid = 0; + msg->cmd_subid = cmd; msg->complete = 0; ret = phyt_codec_msg_set_cmd(priv); if (ret < 0) { - dev_err(priv->dev, "failed to get codec registers\n"); + dev_err(priv->dev, "get cmd_subid 0x%x failed\n", cmd); ret = -EINVAL; - goto error; - } else { - dev_dbg(priv->dev, "show codec registers\n"); - for (i = 0; i < msg->len && i < 56; i++) { - dev_dbg(priv->dev, "%d ", msg->cmd_para.para[i]); - if (i % 16 == 0) - dev_dbg(priv->dev, "\n"); - } } -error: + return ret; } static int phyt_probe(struct snd_soc_component *component) { - return phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_PROBE); + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + + return phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_PROBE); } static void phyt_remove(struct snd_soc_component *component) { - phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_REMOVE); + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + + phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_REMOVE); } static int phyt_suspend(struct snd_soc_component *component) { - return phyt_pm_cmd(component, PHYTCODEC_MSG_CMD_SET_SUSPEND); + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + + return phyt_pm_cmd(priv, PHYTCODEC_MSG_CMD_SET_SUSPEND); } static int phyt_resume(struct snd_soc_component *component) { - return phyt_pm_cmd(component, PHYTCODEC_MSG_CMD_SET_RESUME); + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + + return phyt_pm_cmd(priv, PHYTCODEC_MSG_CMD_SET_RESUME); } static int phyt_set_bias_level(struct snd_soc_component *component, @@ -296,22 +307,22 @@ static int phyt_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_ON: - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_ON); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_BIAS_ON); break; case SND_SOC_BIAS_PREPARE: - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_PREPARE); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_BIAS_PREPARE); break; case SND_SOC_BIAS_STANDBY: if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_STANDBY); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_BIAS_STANDBY); else - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_STANDBY); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_BIAS_STANDBY); break; case SND_SOC_BIAS_OFF: - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_BIAS_OFF); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_BIAS_OFF); break; } @@ -346,9 +357,9 @@ static int phyt_mute(struct snd_soc_dai *dai, int mute, int direction) memset(msg, 0, sizeof(struct phytcodec_cmd)); msg->cmd_para.para[0] = (uint8_t)direction; if (mute) - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_MUTE); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_MUTE); else - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_UNMUTE); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_UNMUTE); return ret; } @@ -358,11 +369,12 @@ static int phyt_startup(struct snd_pcm_substream *substream, { int ret; struct snd_soc_component *component = dai->component; + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_STARTUP); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_STARTUP); else - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_STARTUP_RC); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_STARTUP_RC); return ret; } @@ -372,11 +384,12 @@ static void phyt_shutdown(struct snd_pcm_substream *substream, { int ret; struct snd_soc_component *component = dai->component; + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_SHUTDOWN); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_SHUTDOWN); else - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_SHUTDOWN_RC); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_SHUTDOWN_RC); } static int phyt_hw_params(struct snd_pcm_substream *substream, @@ -423,6 +436,7 @@ static int phyt_set_dai_fmt(struct snd_soc_dai *codec_dai, { int ret; struct snd_soc_component *component = codec_dai->component; + struct phytium_codec *priv = snd_soc_component_get_drvdata(component); if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) return -EINVAL; @@ -433,7 +447,7 @@ static int phyt_set_dai_fmt(struct snd_soc_dai *codec_dai, if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) return -EINVAL; - ret = phyt_cmd(component, PHYTCODEC_MSG_CMD_SET_DAI_FMT); + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_DAI_FMT); return ret; } @@ -529,11 +543,41 @@ static void phyt_timer_handle(struct timer_list *t) mod_timer(&priv->timer, jiffies + msecs_to_jiffies(2000)); } +static int phyt_get_one_reg(struct phytium_codec *priv, uint8_t arg1, uint8_t arg2) +{ + struct phytcodec_cmd *msg = priv->sharemem_base; + int ret = 0; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); + msg->cmd_para.rw_data.addr = arg1; + msg->cmd_para.rw_data.reg = arg2; + ret = phyt_get_cmd(priv, PHYTCODEC_MSG_CMD_GET_ONE_REG); + dev_dbg(priv->dev, "val: 0x%x\n", msg->cmd_para.rw_data.val); + + return ret; +} + +static int phyt_set_one_reg(struct phytium_codec *priv, uint8_t arg1, uint8_t arg2, uint16_t arg3) +{ + struct phytcodec_cmd *msg = priv->sharemem_base; + int ret = 0; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); + msg->cmd_para.rw_data.addr = arg1; + msg->cmd_para.rw_data.reg = arg2; + msg->cmd_para.rw_data.val = arg3; + ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_ONE_REG); + + return ret; +} + static ssize_t debug_show(struct device *dev, struct device_attribute *da, char *buf) { struct phytium_codec *priv = dev_get_drvdata(dev); ssize_t ret; u32 reg; + dev_info(dev, "Usage: echo [args...] > debug\n"); + dev_info(dev, "Usage: echo help 1 > debug for more details"); reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); ret = sprintf(buf, "%x\n", reg); @@ -542,60 +586,126 @@ static ssize_t debug_show(struct device *dev, struct device_attribute *da, char } static ssize_t debug_store(struct device *dev, struct device_attribute *da, - const char *buf, size_t size) + const char *buf, size_t size) { struct phytium_codec *priv = dev_get_drvdata(dev); - u8 loc, dis_en, status = 0; - char *p; - char *token; + char *arg1_str = NULL, *arg2_str = NULL, *arg3_str = NULL; + uint8_t arg1 = 0, arg2 = 0; + uint16_t arg3 = 0; + char *cmd_buffer, *cmd; long value; u32 reg; + int status; - dev_info(dev, "first number is debug/alive/register, the second number is disable/enable"); - dev_info(dev, "echo 2 1 > debug, print all codec register"); + cmd_buffer = kmalloc(size + 1, GFP_KERNEL); + if (!cmd_buffer) + goto error; + strscpy(cmd_buffer, buf, size + 1); - p = kmalloc(size, GFP_KERNEL); - strscpy(p, buf, sizeof(p)); - token = strsep(&p, " "); - if (!token) - return -EINVAL; - status = kstrtol(token, 0, &value); - if (status) - return status; - loc = (u8)value; + cmd = strsep(&cmd_buffer, " "); + if (!cmd) { + dev_err(dev, "Invalid command argument\n"); + goto error; + } - token = strsep(&p, " "); - if (!token) - return -EINVAL; - status = kstrtol(token, 0, &value); - if (status) - return status; - dis_en = value; + arg1_str = strsep(&cmd_buffer, " "); + if (arg1_str) { + status = kstrtoul(arg1_str, 0, &value); + if (status) { + dev_err(dev, "Invalid value for arg1: %s\n", arg1_str); + goto error; + } + arg1 = (uint8_t)value; + } - reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); - if (loc == 1) { - if (dis_en == 1) { - priv->alive_enabled = true; - reg |= BIT(loc); - } else if (dis_en == 0) { - priv->alive_enabled = false; - reg &= ~BIT(loc); + arg2_str = strsep(&cmd_buffer, " "); + if (arg2_str) { + status = kstrtoul(arg2_str, 0, &value); + if (status) { + dev_err(dev, "Invalid value for arg2: %s\n", arg2_str); + goto error; } - } else if (loc == 0) { - if (dis_en == 1) { - priv->debug_enabled = true; - reg |= BIT(loc); - } else if (dis_en == 0) { - priv->debug_enabled = false; - reg &= ~BIT(loc); + arg2 = (uint8_t)value; + } + + arg3_str = strsep(&cmd_buffer, " "); + if (arg3_str) { + status = kstrtou16(arg3_str, 0, &arg3); + if (status) { + dev_err(dev, "Invalid value for arg3: %s\n", arg3_str); + goto error; } - } else if (loc == 2) - if (dis_en == 1) - phyt_show_registers(priv); - phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG, reg); - kfree(p); + } + + if (strcmp(cmd, "dbg") == 0) { + if (!arg1_str || !arg2_str) { + dev_err(dev, "debug command requires two arguments\n"); + goto error; + } + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG); + if (arg1 == 1) { + if (arg2 == 1) { + priv->alive_enabled = true; + reg |= BIT(arg1); + } else if (arg2 == 0) { + priv->alive_enabled = false; + reg &= ~BIT(arg1); + } else { + dev_err(dev, "arg2 should be 0 or 1 for dbg command\n"); + goto error; + } + } else if (arg1 == 0) { + if (arg2 == 1) { + priv->debug_enabled = true; + reg |= BIT(arg1); + } else if (arg2 == 0) { + priv->debug_enabled = false; + reg &= ~BIT(arg1); + } else { + dev_err(dev, "arg2 should be 0 or 1 for dbg command\n"); + goto error; + } + } else { + dev_err(dev, "arg1 should be 0 or 1 for dbg command\n"); + goto error; + } + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_DEBUG, reg); + } else if (strcmp(cmd, "get") == 0) { + if (!arg1_str || !arg2_str) { + dev_err(dev, "get command requires two arguments\n"); + goto error; + } + phyt_get_one_reg(priv, arg1, arg2); + } else if (strcmp(cmd, "set") == 0) { + if (!arg1_str || !arg2_str || !arg3_str) { + dev_err(dev, "set command requires three arguments\n"); + goto error; + } + phyt_set_one_reg(priv, arg1, arg2, arg3); + } else if (strcmp(cmd, "dump") == 0) { + if (!arg1_str) { + dev_err(dev, "dump command requires one argument\n"); + goto error; + } + phyt_pm_cmd(priv, PHYTCODEC_MSG_CMD_GET_ALL_REGS); + } else if (strcmp(cmd, "help") == 0) { + dev_info(dev, "Available commands:\n" + "dump all regs: echo \"dump\" > debug\n" + "dbg: echo \"dbg 0 1\" > debug\n" + "heartbeat: echo \"dbg 1 1\" > debug\n" + "read a reg: echo \"get [addr] [reg]\" > debug\n" + "write a reg: echo \"set [addr] [reg] [val]\" > debug\n"); + } else { + dev_err(dev, "Unknown command: %s\n", cmd); + goto error; + } + kfree(cmd_buffer); return size; + +error: + kfree(cmd_buffer); + return -EINVAL; } static DEVICE_ATTR_RW(debug); @@ -616,18 +726,9 @@ static int phyt_get_channels(struct phytium_codec *priv) uint8_t channels; memset(msg, 0, sizeof(struct phytcodec_cmd)); - msg->reserved = 0; - msg->seq = 0; - msg->cmd_id = PHYTCODEC_MSG_CMD_GET; - msg->cmd_subid = PHYTCODEC_MSG_CMD_GET_CHANNELS; - msg->complete = 0; - - ret = phyt_codec_msg_set_cmd(priv); - if (ret < 0) { - dev_err(priv->dev, "failed to get codec channels\n"); - return -EINVAL; - } + ret = phyt_get_cmd(priv, PHYTCODEC_MSG_CMD_GET_CHANNELS); channels = msg->cmd_para.para[0] * 2; + return channels; } diff --git a/sound/soc/codecs/phytium-codec-v2.h b/sound/soc/codecs/phytium-codec-v2.h index 23b643fd92..1284f287a8 100644 --- a/sound/soc/codecs/phytium-codec-v2.h +++ b/sound/soc/codecs/phytium-codec-v2.h @@ -56,6 +56,8 @@ enum phytcodec_msg_cmd_id { enum phytcodec_get_subid { PHYTCODEC_MSG_CMD_GET_CHANNELS = 0, + PHYTCODEC_MSG_CMD_GET_ONE_REG, + PHYTCODEC_MSG_CMD_GET_ALL_REGS, }; enum phytcodec_set_subid { @@ -74,6 +76,7 @@ enum phytcodec_set_subid { PHYTCODEC_MSG_CMD_SET_BIAS_STANDBY, PHYTCODEC_MSG_CMD_SET_SHUTDOWN, PHYTCODEC_MSG_CMD_SET_SHUTDOWN_RC, + PHYTCODEC_MSG_CMD_SET_ONE_REG, }; enum phytcodec_complete { @@ -86,6 +89,12 @@ enum phytcodec_complete { PHYTCODEC_COMPLETE_INVALID_PARAMETERS, }; +struct phytcodec_rw_data { + uint8_t addr; + uint8_t reg; + uint16_t val; +}; + struct phytcodec_reg { uint16_t total_regs_len; uint8_t one_reg_len; @@ -104,6 +113,7 @@ struct phytcodec_cmd { union { uint8_t para[56]; struct phytcodec_reg phytcodec_reg; + struct phytcodec_rw_data rw_data; } cmd_para; }; -- Gitee From 65de20d17524aae1ac7f0efa20840e1fd20f9107 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 17 Mar 2025 11:41:26 +0800 Subject: [PATCH 116/154] net/phytmac: Slove left-shift out of bound issue When the value of the bd_prefetch is equal to 0, subtracting 1 and shifting left will cause the overflow issue. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I010b1b8a8fc66731b63cd0a995ad44ab9b6b79f1 --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index c7c560c7da..76c4335b0b 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.37" +#define PHYTMAC_DRIVER_VERSION "1.0.38" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 8bbf0fcd2e..43e5dc52f4 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -368,10 +368,13 @@ static int phytmac_v2_get_feature_all(struct phytmac *pdata) pdata->dma_addr_width = 32; pdata->dma_data_width = para.dma_data_width; pdata->max_rx_fs = para.max_rx_fs; - pdata->tx_bd_prefetch = (2 << (para.tx_bd_prefetch - 1)) * - sizeof(struct phytmac_dma_desc); - pdata->rx_bd_prefetch = (2 << (para.rx_bd_prefetch - 1)) * - sizeof(struct phytmac_dma_desc); + + if (para.tx_bd_prefetch) + pdata->tx_bd_prefetch = (2 << (para.tx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); + if (para.rx_bd_prefetch) + pdata->rx_bd_prefetch = (2 << (para.rx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); if (netif_msg_hw(pdata)) { netdev_info(pdata->ndev, "feature qnum=%d, daw=%d, dbw=%d, rxfs=%d, rxbd=%d, txbd=%d\n", -- Gitee From 6d0f434edc1f2833255d44aec218be9231d92f71 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 17 Mar 2025 11:42:35 +0800 Subject: [PATCH 117/154] net/phytmac: Manage WOL on MAC if PHY supports WOL feature Don't manage WoL on MAC, if PHY sets wol fails, So modify if statement to handle abnormal scenarios correctly. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Icefbf6c1ae6789b4898e213e148825f4b6114396 --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_ethtool.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 76c4335b0b..d930daa078 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.38" +#define PHYTMAC_DRIVER_VERSION "1.0.39" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 1f0b3c9e4b..2f2fb24bfc 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -113,7 +113,8 @@ static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) ret = phylink_ethtool_set_wol(pdata->phylink, wol); - if (!ret || ret != -EOPNOTSUPP) + /* Don't manage WoL on MAC, if PHY set_wol() fails */ + if (ret && ret != -EOPNOTSUPP) return ret; pdata->wol = 0; -- Gitee From 938d28a6f21eb9ca1cc7f4d7bb7fa2280d68d8d0 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 17 Mar 2025 11:43:31 +0800 Subject: [PATCH 118/154] net/phytmac: Fixed the PTP test failure issue The bit of RX_TS_VALID should be retained in the process of zero RX address description, which indicates a valid timestamp in the BD entry. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ibf600463d28be6579d3704c29c8274996a760f9f --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v1.c | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index d930daa078..ee7b6d6efa 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.39" +#define PHYTMAC_DRIVER_VERSION "1.0.40" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index defe187fb1..a110b4b114 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -995,7 +995,7 @@ static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { desc->desc2 = 0; - desc->desc0 = PHYTMAC_BIT(RX_USED); + desc->desc0 = (desc->desc0 & PHYTMAC_BIT(RX_TS_VALID)) | PHYTMAC_BIT(RX_USED); return 0; } diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 43e5dc52f4..6d0d75bf80 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -1044,7 +1044,7 @@ static unsigned int phytmac_v2_rx_map_desc(struct phytmac_queue *queue, u32 inde static unsigned int phytmac_v2_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { desc->desc2 = 0; - desc->desc0 = PHYTMAC_BIT(RXUSED); + desc->desc0 = (desc->desc0 & PHYTMAC_BIT(RXTSVALID)) | PHYTMAC_BIT(RXUSED); return 0; } -- Gitee From 48299d2ef44fe62f7e77263f3d6a833dcf9589ca Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 17 Mar 2025 11:51:02 +0800 Subject: [PATCH 119/154] net/phytmac: Cancels the power-on/off capability The MAC register is powered on by default in the firmware and the driver cancels the power-on and power-off capability. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I407198ed1e631335abcd983caa5a701b9f437efa --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_platform.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index ee7b6d6efa..4c1b47aabc 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.40" +#define PHYTMAC_DRIVER_VERSION "1.0.41" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 12df94aa67..540159f8fc 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -30,7 +30,6 @@ static const struct phytmac_config phytium_2p0_config = { .hw_if = &phytmac_2p0_hw, .caps = PHYTMAC_CAPS_TAILPTR | PHYTMAC_CAPS_RXPTR - | PHYTMAC_CAPS_PWCTRL | PHYTMAC_CAPS_LSO | PHYTMAC_CAPS_MSG | PHYTMAC_CAPS_JUMBO, -- Gitee From c810ae4ec77c3173ec788ddf7c4449b98ad68225 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 17 Mar 2025 11:52:24 +0800 Subject: [PATCH 120/154] net/phytmac: Exit probe when MDIO times out Exit early to avoid system stuck due to MDIO timeout. We have added a callback function for mdio_idle and improved its return value for judgment. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I7b2de4a29beb5760f404f133a7dd58c315ed0938 --- drivers/net/ethernet/phytium/phytmac.h | 3 ++- drivers/net/ethernet/phytium/phytmac_main.c | 6 ++++++ drivers/net/ethernet/phytium/phytmac_v1.c | 5 ++++- drivers/net/ethernet/phytium/phytmac_v2.c | 5 ++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 4c1b47aabc..9fae1afa72 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.41" +#define PHYTMAC_DRIVER_VERSION "1.0.42" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -572,6 +572,7 @@ struct phytmac_hw_if { /* mido ops */ int (*enable_mdio_control)(struct phytmac *pdata, int enable); + int (*mdio_idle)(struct phytmac *pdata); int (*mdio_read)(struct phytmac *pdata, int mii_id, int regnum, int is_c45); int (*mdio_write)(struct phytmac *pdata, int mii_id, int regnum, int is_c45, u16 data); diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 0a0862ffb6..13e077ca1a 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2033,6 +2033,12 @@ int phytmac_mdio_register(struct phytmac *pdata) goto err_out; } + if (hw_if->mdio_idle) { + ret = hw_if->mdio_idle(pdata); + if (ret) + goto free_mdio; + } + pdata->mii_bus->name = "phytmac_mii_bus"; pdata->mii_bus->read = &phytmac_mdio_read; pdata->mii_bus->write = &phytmac_mdio_write; diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index a110b4b114..ef131ee58b 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -557,7 +557,7 @@ static int phytmac_set_wake(struct phytmac *pdata, int wake) return 0; } -static void phytmac_mdio_idle(struct phytmac *pdata) +static int phytmac_mdio_idle(struct phytmac *pdata) { u32 val; int ret; @@ -567,6 +567,8 @@ static void phytmac_mdio_idle(struct phytmac *pdata) 1, PHYTMAC_MDIO_TIMEOUT); if (ret) netdev_err(pdata->ndev, "mdio wait for idle time out!"); + + return ret; } static int phytmac_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, int is_c45) @@ -1408,6 +1410,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { .get_stats = phytmac_get_hw_stats, .set_mac_address = phytmac_set_mac_addr, .get_mac_address = phytmac_get_mac_addr, + .mdio_idle = phytmac_mdio_idle, .mdio_read = phytmac_mdio_data_read, .mdio_write = phytmac_mdio_data_write, .poweron = phytmac_powerup_hw, diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 6d0d75bf80..30d8c2eac6 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -439,7 +439,7 @@ static void phytmac_v2_get_hw_stats(struct phytmac *pdata) } } -static void phytmac_v2_mdio_idle(struct phytmac *pdata) +static int phytmac_v2_mdio_idle(struct phytmac *pdata) { u32 val; int ret; @@ -449,6 +449,8 @@ static void phytmac_v2_mdio_idle(struct phytmac *pdata) 1, PHYTMAC_MDIO_TIMEOUT); if (ret) netdev_err(pdata->ndev, "mdio wait for idle time out!"); + + return ret; } static int phytmac_v2_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, int is_c45) @@ -1326,6 +1328,7 @@ struct phytmac_hw_if phytmac_2p0_hw = { .get_stats = phytmac_v2_get_hw_stats, .set_mac_address = phytmac_v2_set_mac_addr, .get_mac_address = phytmac_v2_get_mac_addr, + .mdio_idle = phytmac_v2_mdio_idle, .mdio_read = phytmac_v2_mdio_data_read, .mdio_write = phytmac_v2_mdio_data_write, .poweron = phytmac_v2_powerup_hw, -- Gitee From 0f3258fad6d2cab620e609cffae09ec65e85595c Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 28 Mar 2025 15:50:09 +0800 Subject: [PATCH 121/154] net/phytmac: Clear RX descriptor address after the skb construction If the skb build failed, the descriptor address has been cleared, and the current descriptor will be considered invalid in the next round of processing. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I91261f1db9b762aee3996166cb22f2960a82be1f --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_main.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 9fae1afa72..2fadf01bd5 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.42" +#define PHYTMAC_DRIVER_VERSION "1.0.43" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 13e077ca1a..79ec6cf1ad 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -955,8 +955,6 @@ static struct sk_buff *phytmac_rx_xdp_single(struct phytmac_queue *queue, len = hw_if->get_rx_pkt_len(pdata, desc); rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); - hw_if->zero_rx_desc_addr(desc); - xdp.data = page_address(rx_buffer->page) + rx_buffer->page_offset; xdp.data_meta = xdp.data; xdp.data_hard_start = xdp.data - PHYTMAC_SKB_PAD; @@ -978,6 +976,7 @@ static struct sk_buff *phytmac_rx_xdp_single(struct phytmac_queue *queue, } else { rx_buffer->pagecnt_bias++; } + hw_if->zero_rx_desc_addr(desc); phytmac_put_rx_buffer(queue, rx_buffer); pdata->ndev->stats.rx_bytes += len; queue->stats.rx_bytes += len; @@ -998,7 +997,6 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy len = hw_if->get_rx_pkt_len(pdata, desc); rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); - hw_if->zero_rx_desc_addr(desc); skb = phytmac_build_skb(rx_buffer, len); if (unlikely(!skb)) { @@ -1010,6 +1008,7 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy return NULL; } + hw_if->zero_rx_desc_addr(desc); phytmac_put_rx_buffer(queue, rx_buffer); skb->protocol = eth_type_trans(skb, pdata->ndev); @@ -1045,7 +1044,6 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, desc = phytmac_get_rx_desc(queue, first_frag); rx_buffer = phytmac_get_rx_buffer(queue, first_frag, frag_len); - hw_if->zero_rx_desc_addr(desc); skb = phytmac_build_skb(rx_buffer, frag_len); if (unlikely(!skb)) { @@ -1056,6 +1054,7 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, return NULL; } + hw_if->zero_rx_desc_addr(desc); phytmac_put_rx_buffer(queue, rx_buffer); for (frag = first_frag + 1; ; frag++) { -- Gitee From 5f3ba2263ec416fbda0ab18a4ba2ac1fc6b590f8 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 28 Mar 2025 15:52:22 +0800 Subject: [PATCH 122/154] net/phytmac: Enable the tail pointer by the driver It is need to enable the RX/TX tail pointer to ensure the NIC works properly. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I32e4af993fec27d6215bbcc7a18dcd458c374a2e --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 8 ++++++++ drivers/net/ethernet/phytium/phytmac_v2.h | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 2fadf01bd5..a6113c5f2f 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.43" +#define PHYTMAC_DRIVER_VERSION "1.0.44" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 30d8c2eac6..6e0b1c8cd9 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -191,8 +191,16 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) u16 cmd_id, cmd_subid; struct phytmac_dma_info dma; struct phytmac_eth_info eth; + u32 ptrconfig = 0; u8 mdc; + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + ptrconfig |= PHYTMAC_BIT(TXTAIL_EN); + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + ptrconfig |= PHYTMAC_BIT(RXTAIL_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR_ENABLE, ptrconfig); + if (pdata->mii_bus) { cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 25e120e5b9..c9e159b1eb 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -18,6 +18,7 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_RX_MSG_TAIL 0x00c #define PHYTMAC_MSG_IMR 0x020 #define PHYTMAC_MSG_ISR 0x02c +#define PHYTMAC_TAILPTR_ENABLE 0x038 #define PHYTMAC_SIZE 0x0048 #define PHYTMAC_NETWORK_STATUS 0x0240 @@ -45,6 +46,12 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_MSG_COMPLETE_INDEX 0 #define PHYTMAC_MSG_COMPLETE_WIDTH 1 +/* Bitfields in PHYTMAC_TAILPTR_ENABLE */ +#define PHYTMAC_TXTAIL_EN_INDEX 0 /* Enable tx tail */ +#define PHYTMAC_TXTAIL_EN_WIDTH 1 +#define PHYTMAC_RXTAIL_EN_INDEX 16 /* Enable rx tail */ +#define PHYTMAC_RXTAIL_EN_WIDTH 1 + /* Bitfields in PHYTMAC_SIZE */ #define PHYTMAC_MEM_SIZE_INDEX 0 #define PHYTMAC_MEM_SIZE_WIDTH 4 -- Gitee From 263e1a91bd0674556baecdf2fc8b206d710cc0c7 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Fri, 21 Feb 2025 14:46:48 +0800 Subject: [PATCH 123/154] i2s-v2: phytium: Add log funciton support to improve debug This patch adds support for logging functionality. After enabling the debug feature via debugfs. it reads the information from the debug registers to abtain DDR address and log size. And then we can read the log and debug. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: Ic4f602e4ffaf59d82193a9f9621eb8b7e01f6e77 --- sound/soc/phytium/phytium-i2s-v2.c | 58 ++++++++++++++++++++++++++++-- sound/soc/phytium/phytium-i2s-v2.h | 9 +++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c index 145685f06e..888f6937a2 100644 --- a/sound/soc/phytium/phytium-i2s-v2.c +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -896,6 +896,51 @@ static void phyt_i2s_timer_handler(struct timer_list *timer) mod_timer(&priv->timer, jiffies + msecs_to_jiffies(2000)); } +void phyt_i2s_show_log(struct phytium_i2s *priv) +{ + u32 reg, len; + u8 *plog; + int i = 0; + + if (!priv->log_addr) + return; + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + + plog = priv->log_addr; + if (reg & LOG_MASK) { + len = strnlen((char *)priv->log_addr, 8192); + dev_info(priv->dev, "log len :%d, addr:0x%llx, size:%d\n", len, + (u64)priv->log_addr, priv->log_size); + if (len > LOG_LINE_MAX_LEN) { + for (i = 0; i < len; i += LOG_LINE_MAX_LEN) + dev_info(priv->dev, "(DEV)%.*s\n", LOG_LINE_MAX_LEN, &plog[i]); + } else { + dev_info(priv->dev, "(DDR)%.*s\n", LOG_LINE_MAX_LEN, &plog[i]); + } + for (i = 0; i < priv->log_size; i++) + plog[i] = 0; + } + reg &= ~LOG_MASK; + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG, reg); +} + +int phyt_i2s_alloc_log_mem(struct phytium_i2s *priv) +{ + u32 reg; + u64 phy_addr; + + reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + phy_addr = ((reg & ADDR_MASK) >> ADDR_LOW_SHIFT) << ADDR_SHIFT; + + priv->log_size = ((reg & LOG_SIZE_MASK) >> LOG_SIZE_LOW_SHIFT) * 1024; + priv->log_addr = devm_ioremap_wc(priv->dev, phy_addr, priv->log_size); + if (IS_ERR(priv->log_addr)) { + dev_err(priv->dev, "log_addr alloc failed\n"); + return -ENOMEM; + } + return 0; +} + static ssize_t phyt_i2s_debug_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -946,10 +991,12 @@ static ssize_t phyt_i2s_debug_store(struct device *dev, dis_en = value; if (loc == 1) { - if (dis_en) + if (dis_en) { phyt_i2s_enable_debug(priv); - else + phyt_i2s_show_log(priv); + } else { phyt_i2s_disable_debug(priv); + } } else if (loc == 0) { if (dis_en) phyt_i2s_enable_heartbeat(priv); @@ -1032,6 +1079,12 @@ static int phyt_i2s_probe(struct platform_device *pdev) goto failed_ioremap_res2; } + ret = phyt_i2s_alloc_log_mem(priv); + if (ret != 0) { + dev_err(&pdev->dev, "failed to alloc log mem\n"); + goto failed_alloc_log_mem; + } + status = readl(priv->dma_reg_base + PHYTIUM_DMA_STS); if (status & DMA_TX_DONE) writel(DMA_TX_DONE, priv->dma_reg_base + PHYTIUM_DMA_STS); @@ -1115,6 +1168,7 @@ static int phyt_i2s_probe(struct platform_device *pdev) failed_request_irq: failed_disable_gpioint: failed_enable_gpio: +failed_alloc_log_mem: failed_ioremap_res2: failed_ioremap_res1: failed_ioremap_res0: diff --git a/sound/soc/phytium/phytium-i2s-v2.h b/sound/soc/phytium/phytium-i2s-v2.h index 12122b9945..ba86ba97a5 100644 --- a/sound/soc/phytium/phytium-i2s-v2.h +++ b/sound/soc/phytium/phytium-i2s-v2.h @@ -45,6 +45,8 @@ struct phytium_i2s { uint32_t sample_rate; int insert; struct mutex sharemem_mutex; + void __iomem *log_addr; + u32 log_size; }; struct pdata_pd230x_mfd { @@ -142,6 +144,13 @@ struct phyti2s_cmd { #define DEBUG_ENABLE (1 << 0) #define HEART_ENABLE (1 << 1) #define HEARTBEAT (1 << 2) + #define LOG_MASK (1 << 3) + #define LOG_SIZE_LOW_SHIFT 4 + #define LOG_SIZE_MASK GENMASK(7, 4) + #define ADDR_LOW_SHIFT 8 + #define ADDR_MASK GENMASK(27, 8) + #define ADDR_SHIFT 12 + #define LOG_LINE_MAX_LEN 400 /* DMA register */ #define PHYTIUM_DMA_CTL 0x0000 -- Gitee From a6c9f7609dd82f1b74b8488b9141acb75d4efdd9 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Wed, 26 Feb 2025 17:01:43 +0800 Subject: [PATCH 124/154] codec-v2: phytium: Bugfix the NULL pointer issue in S3 When entering suspend mode, the controller mistakenly releases the memory space for codec registers due to an incorrect if statement. Upon resuming, it releases the same memory space again. which will cause the NULL pointer issue. So modify if statement in suspend, and add a new function to print codec registers. Mainline: Open-Source Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: Ib002be01922c2e6e2dbde67eedd42fa76a2aa623 --- sound/soc/codecs/phytium-codec-v2.c | 86 +++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/sound/soc/codecs/phytium-codec-v2.c b/sound/soc/codecs/phytium-codec-v2.c index 8ecc6e50ca..b0b8615623 100644 --- a/sound/soc/codecs/phytium-codec-v2.c +++ b/sound/soc/codecs/phytium-codec-v2.c @@ -184,7 +184,7 @@ static int phyt_pm_cmd(struct phytium_codec *priv, struct phytcodec_cmd *msg = priv->sharemem_base; uint16_t total_regs_len; uint8_t *regs; - int ret = 0, i; + int ret = 0, i = 0, cnt = 1; memset(msg, 0, sizeof(struct phytcodec_cmd)); @@ -194,6 +194,7 @@ static int phyt_pm_cmd(struct phytium_codec *priv, msg->cmd_subid = cmd; msg->complete = 0; msg->cmd_para.phytcodec_reg.cnt = 0; + if (cmd == PHYTCODEC_MSG_CMD_SET_RESUME) { memcpy(msg->cmd_para.phytcodec_reg.regs, priv->regs, REG_SH_LEN); phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_MASK, 0x0); @@ -202,15 +203,24 @@ static int phyt_pm_cmd(struct phytium_codec *priv, ret = phyt_codec_msg_set_cmd(priv); if (ret < 0) { dev_err(priv->dev, "set cmd_subid 0x%x failed\n", cmd); - ret = -EINVAL; - goto error; + return -EINVAL; } total_regs_len = msg->cmd_para.phytcodec_reg.total_regs_len; + if (total_regs_len % REG_SH_LEN == 0) + cnt = total_regs_len / REG_SH_LEN; + else + cnt = total_regs_len / REG_SH_LEN + 1; - if (cmd == PHYTCODEC_MSG_CMD_SET_SUSPEND || cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) { + if (cmd == PHYTCODEC_MSG_CMD_SET_SUSPEND) { regs = kmalloc(total_regs_len, GFP_KERNEL); priv->regs = regs; - while (total_regs_len > REG_SH_LEN * msg->cmd_para.phytcodec_reg.cnt) { + + for (i = 1; i < cnt; i++) { + if (msg->cmd_para.phytcodec_reg.cnt != i) { + dev_err(priv->dev, "error phytcodec_reg.cnt\n"); + ret = -EINVAL; + goto error; + } memcpy(regs, msg->cmd_para.phytcodec_reg.regs, REG_SH_LEN); regs += REG_SH_LEN; msg->complete = 0; @@ -223,15 +233,14 @@ static int phyt_pm_cmd(struct phytium_codec *priv, } memcpy(regs, msg->cmd_para.phytcodec_reg.regs, total_regs_len - REG_SH_LEN * (msg->cmd_para.phytcodec_reg.cnt - 1)); - if (cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) { - dev_dbg(priv->dev, "all codec registers:\n"); - for (i = 0; i < total_regs_len; i++) - dev_dbg(priv->dev, "0x%02x-0x%02x\n", i, priv->regs[i]); - kfree(priv->regs); - } } else if (cmd == PHYTCODEC_MSG_CMD_SET_RESUME) { regs = priv->regs; - while (total_regs_len > REG_SH_LEN * msg->cmd_para.phytcodec_reg.cnt) { + for (i = 1; i < cnt; i++) { + if (msg->cmd_para.phytcodec_reg.cnt != i) { + dev_err(priv->dev, "error phytcodec_reg.cnt\n"); + ret = -EINVAL; + goto error; + } regs += REG_SH_LEN; memcpy(msg->cmd_para.phytcodec_reg.regs, regs, REG_SH_LEN); msg->complete = 0; @@ -243,28 +252,73 @@ static int phyt_pm_cmd(struct phytium_codec *priv, } } kfree(priv->regs); + priv->regs = NULL; } + return ret; + error: + kfree(priv->regs); + priv->regs = NULL; return ret; } static int phyt_get_cmd(struct phytium_codec *priv, unsigned int cmd) { struct phytcodec_cmd *msg = priv->sharemem_base; - int ret = 0; + int ret = 0, i = 0, cnt = 1; + uint16_t total_regs_len; + uint8_t *regs; msg->reserved = 0; msg->seq = 0; msg->cmd_id = PHYTCODEC_MSG_CMD_GET; msg->cmd_subid = cmd; msg->complete = 0; + if (cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) + msg->cmd_para.phytcodec_reg.cnt = 0; ret = phyt_codec_msg_set_cmd(priv); if (ret < 0) { dev_err(priv->dev, "get cmd_subid 0x%x failed\n", cmd); - ret = -EINVAL; + return -EINVAL; } + total_regs_len = msg->cmd_para.phytcodec_reg.total_regs_len; + if (cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) { + if (total_regs_len % REG_SH_LEN == 0) + cnt = total_regs_len / REG_SH_LEN; + else + cnt = total_regs_len / REG_SH_LEN + 1; + regs = kmalloc(total_regs_len, GFP_KERNEL); + priv->regs = regs; + for (i = 1; i < cnt; i++) { + if (msg->cmd_para.phytcodec_reg.cnt != i) { + dev_err(priv->dev, "error phytcodec_reg.cnt\n"); + ret = -EINVAL; + goto error; + } + memcpy(regs, msg->cmd_para.phytcodec_reg.regs, REG_SH_LEN); + regs += REG_SH_LEN; + msg->complete = 0; + ret = phyt_codec_msg_set_cmd(priv); + if (ret < 0) { + dev_err(priv->dev, "set cmd_subid 0x%x failed\n", cmd); + ret = -EINVAL; + goto error; + } + } + memcpy(regs, msg->cmd_para.phytcodec_reg.regs, + total_regs_len - REG_SH_LEN * (msg->cmd_para.phytcodec_reg.cnt - 1)); + for (i = 0; i < total_regs_len; i++) + dev_info(priv->dev, "0x%02x-0x%02x\n", i, priv->regs[i]); + + kfree(priv->regs); + priv->regs = NULL; + } return ret; +error: + kfree(priv->regs); + priv->regs = NULL; + return -EINVAL; } static int phyt_probe(struct snd_soc_component *component) @@ -552,7 +606,7 @@ static int phyt_get_one_reg(struct phytium_codec *priv, uint8_t arg1, uint8_t ar msg->cmd_para.rw_data.addr = arg1; msg->cmd_para.rw_data.reg = arg2; ret = phyt_get_cmd(priv, PHYTCODEC_MSG_CMD_GET_ONE_REG); - dev_dbg(priv->dev, "val: 0x%x\n", msg->cmd_para.rw_data.val); + dev_info(priv->dev, "val: 0x%x\n", msg->cmd_para.rw_data.val); return ret; } @@ -687,7 +741,7 @@ static ssize_t debug_store(struct device *dev, struct device_attribute *da, dev_err(dev, "dump command requires one argument\n"); goto error; } - phyt_pm_cmd(priv, PHYTCODEC_MSG_CMD_GET_ALL_REGS); + phyt_get_cmd(priv, PHYTCODEC_MSG_CMD_GET_ALL_REGS); } else if (strcmp(cmd, "help") == 0) { dev_info(dev, "Available commands:\n" "dump all regs: echo \"dump\" > debug\n" -- Gitee From a1a29ab1243f6565d6d4760e5578aa7c326f9931 Mon Sep 17 00:00:00 2001 From: Tang Jiemin Date: Thu, 27 Mar 2025 15:21:12 +0800 Subject: [PATCH 125/154] mmu_notifiers: rename invalidate_range notifier There are two main use cases for mmu notifiers. One is by KVM which uses mmu_notifier_invalidate_range_start()/end() to manage a software TLB. The other is to manage hardware TLBs which need to use the invalidate_range() callback because HW can establish new TLB entries at any time. Hence using start/end() can lead to memory corruption as these callbacks happen too soon/late during page unmap. mmu notifier users should therefore either use the start()/end() callbacks or the invalidate_range() callbacks. To make this usage clearer rename the invalidate_range() callback to arch_invalidate_secondary_tlbs() and update documention. Dduptext module need to take advantage of the mmu_nofitier callback, We backport and rework it from commit 1af5a8109904 ("mmu_notifiers: rename invalidate_range notifier") Mainline: Open-Source Signed-off-by: Alistair Popple Suggested-by: Jason Gunthorpe Acked-by: Catalin Marinas Reviewed-by: Jason Gunthorpe Cc: Andrew Donnellan Cc: Chaitanya Kumar Borah Cc: Frederic Barrat Cc: Jason Gunthorpe Cc: John Hubbard Cc: Kevin Tian Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Nicolin Chen Cc: Robin Murphy Cc: Sean Christopherson Cc: SeongJae Park Cc: Tvrtko Ursulin Cc: Will Deacon Cc: Zhi Wang Signed-off-by: Andrew Morton Backported-by: Tang Jiemin Signed-off-by: Wang Yinfeng Change-Id: Ic2de734982eefbaa0dd9931cb316d303f4d13c30 --- arch/arm64/include/asm/tlbflush.h | 9 ++++++++ include/linux/mmu_notifier.h | 36 +++++++++++++++++++++++++++++++ mm/mmu_notifier.c | 18 ++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index 36f02892e1..2f363651e7 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -252,6 +253,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm) __tlbi(aside1is, asid); __tlbi_user(aside1is, asid); dsb(ish); + mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL); } static inline void flush_tlb_page_nosync(struct vm_area_struct *vma, @@ -263,6 +265,8 @@ static inline void flush_tlb_page_nosync(struct vm_area_struct *vma, addr = __TLBI_VADDR(uaddr, ASID(vma->vm_mm)); __tlbi(vale1is, addr); __tlbi_user(vale1is, addr); + mmu_notifier_arch_invalidate_secondary_tlbs(vma->vm_mm, uaddr & PAGE_MASK, + (uaddr & PAGE_MASK) + PAGE_SIZE); } static inline void flush_tlb_page(struct vm_area_struct *vma, @@ -286,6 +290,10 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, int num = 0; int scale = 0; unsigned long asid, addr, pages; + unsigned long ustart, uend; + + ustart = start; + uend = end; start = round_down(start, stride); end = round_up(end, stride); @@ -358,6 +366,7 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, scale++; } dsb(ish); + mmu_notifier_arch_invalidate_secondary_tlbs(vma->vm_mm, ustart, uend); } static inline void flush_tlb_range(struct vm_area_struct *vma, diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h index 1a6a9eb6d3..2485c53892 100644 --- a/include/linux/mmu_notifier.h +++ b/include/linux/mmu_notifier.h @@ -202,6 +202,26 @@ struct mmu_notifier_ops { struct mm_struct *mm, unsigned long start, unsigned long end); + /* + * arch_invalidate_secondary_tlbs() is used to manage a non-CPU TLB + * which shares page-tables with the CPU. The + * invalidate_range_start()/end() callbacks should not be implemented as + * arch_invalidate_secondary_tlbs() already catches the points in time when + * an external TLB needs to be flushed. + * + * This requires arch_invalidate_secondary_tlbs() to be called while + * holding the ptl spin-lock and therefore this callback is not allowed + * to sleep. + * + * This is called by architecture code whenever invalidating a TLB + * entry. It is assumed that any secondary TLB has the same rules for + * when invalidations are required. If this is not the case architecture + * code will need to call this explicitly when required for secondary + * TLB invalidation. + */ + void (*arch_invalidate_secondary_tlbs)(struct mmu_notifier *mn, struct mm_struct *mm, + unsigned long start, unsigned long end); + /* * These callbacks are used with the get/put interface to manage the @@ -394,6 +414,9 @@ extern void __mmu_notifier_invalidate_range_end(struct mmu_notifier_range *r, bool only_end); extern void __mmu_notifier_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end); +extern void __mmu_notifier_arch_invalidate_secondary_tlbs(struct mm_struct *mm, + unsigned long start, unsigned long end); + extern bool mmu_notifier_range_update_to_read_only(const struct mmu_notifier_range *range); @@ -493,6 +516,14 @@ static inline void mmu_notifier_invalidate_range(struct mm_struct *mm, __mmu_notifier_invalidate_range(mm, start, end); } +static inline void mmu_notifier_arch_invalidate_secondary_tlbs(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, end); +} + + static inline void mmu_notifier_subscriptions_init(struct mm_struct *mm) { mm->notifier_subscriptions = NULL; @@ -718,6 +749,11 @@ static inline void mmu_notifier_invalidate_range(struct mm_struct *mm, { } +static inline void mmu_notifier_arch_invalidate_secondary_tlbs(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ +} + static inline void mmu_notifier_subscriptions_init(struct mm_struct *mm) { } diff --git a/mm/mmu_notifier.c b/mm/mmu_notifier.c index 9165ca619c..d82782c342 100644 --- a/mm/mmu_notifier.c +++ b/mm/mmu_notifier.c @@ -621,6 +621,24 @@ void __mmu_notifier_invalidate_range(struct mm_struct *mm, srcu_read_unlock(&srcu, id); } +void __mmu_notifier_arch_invalidate_secondary_tlbs(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + struct mmu_notifier *subscription; + int id; + + id = srcu_read_lock(&srcu); + hlist_for_each_entry_rcu(subscription, + &mm->notifier_subscriptions->list, hlist, + srcu_read_lock_held(&srcu)) { + if (subscription->ops->arch_invalidate_secondary_tlbs) + subscription->ops->arch_invalidate_secondary_tlbs(subscription, mm, + start, end); + } + srcu_read_unlock(&srcu, id); +} + + /* * Same as mmu_notifier_register but here the caller must hold the mmap_lock in * write mode. A NULL mn signals the notifier is being registered for itree -- Gitee From 153c7d0dcb4163d252401f5003c4f08333baf6b2 Mon Sep 17 00:00:00 2001 From: Lai Xueyu Date: Fri, 26 Jul 2024 10:58:51 +0800 Subject: [PATCH 126/154] mmc: phytium: Add driver version information for mmc This patch add driver version information, which will be used to synchronize and manage driver patches in the future. Mainline: Open-Source Signed-off-by: Lai Xueyu Signed-off-by: Wang Yinfeng Change-Id: I975522cb4fa2a6f4b94610dd41892757acc9019c --- drivers/mmc/host/phytium-mci-pci.c | 1 + drivers/mmc/host/phytium-mci-plat.c | 1 + drivers/mmc/host/phytium-mci.c | 1 + drivers/mmc/host/phytium-mci.h | 2 ++ drivers/mmc/host/phytium-sdci.c | 3 +++ 5 files changed, 8 insertions(+) diff --git a/drivers/mmc/host/phytium-mci-pci.c b/drivers/mmc/host/phytium-mci-pci.c index bd193bbd0c..3b1b42b64e 100644 --- a/drivers/mmc/host/phytium-mci-pci.c +++ b/drivers/mmc/host/phytium-mci-pci.c @@ -173,3 +173,4 @@ module_pci_driver(phytium_mci_pci_driver); MODULE_DESCRIPTION("Phytium Multimedia Card Interface PCI driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Cheng Quan "); +MODULE_VERSION(PHYTIUM_MMC_DRIVER_VERSION); diff --git a/drivers/mmc/host/phytium-mci-plat.c b/drivers/mmc/host/phytium-mci-plat.c index de47a4fe5f..a19280a63d 100644 --- a/drivers/mmc/host/phytium-mci-plat.c +++ b/drivers/mmc/host/phytium-mci-plat.c @@ -193,3 +193,4 @@ module_platform_driver(phytium_mci_driver); MODULE_DESCRIPTION("Phytium Multimedia Card Interface driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Cheng Quan "); +MODULE_VERSION(PHYTIUM_MMC_DRIVER_VERSION); diff --git a/drivers/mmc/host/phytium-mci.c b/drivers/mmc/host/phytium-mci.c index 4bcdbfae27..aa0211b023 100644 --- a/drivers/mmc/host/phytium-mci.c +++ b/drivers/mmc/host/phytium-mci.c @@ -1628,3 +1628,4 @@ EXPORT_SYMBOL(phytium_mci_common_probe); MODULE_DESCRIPTION("Phytium Multimedia Card Interface driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Cheng Quan "); +MODULE_VERSION(PHYTIUM_MMC_DRIVER_VERSION); diff --git a/drivers/mmc/host/phytium-mci.h b/drivers/mmc/host/phytium-mci.h index 9d3e86a4d5..5bf6df4540 100644 --- a/drivers/mmc/host/phytium-mci.h +++ b/drivers/mmc/host/phytium-mci.h @@ -19,6 +19,8 @@ /*------------------------------------------------------*/ /* Common Definition */ /*------------------------------------------------------*/ +#define PHYTIUM_MMC_DRIVER_VERSION "1.0.0" + #define MAX_BD_NUM 128 #define SD_BLOCK_SIZE 512 diff --git a/drivers/mmc/host/phytium-sdci.c b/drivers/mmc/host/phytium-sdci.c index b4ca402c1d..45e859aecd 100755 --- a/drivers/mmc/host/phytium-sdci.c +++ b/drivers/mmc/host/phytium-sdci.c @@ -34,6 +34,8 @@ #include "phytium-sdci.h" +#define PHYTIUM_SDCI_DRIVER_VERSION "1.0.0" + 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 | @@ -1431,3 +1433,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Cheng Quan "); MODULE_AUTHOR("Chen Baozi "); MODULE_DESCRIPTION("Phytium SD Card Interface driver"); +MODULE_VERSION(PHYTIUM_SDCI_DRIVER_VERSION); -- Gitee From 9111410681c1474b4b21784c7e9c4b4d577a9557 Mon Sep 17 00:00:00 2001 From: YuDa Date: Tue, 11 Mar 2025 03:49:20 +0000 Subject: [PATCH 127/154] drm/phytium: Enable dither feature for DRM driver Enable the dither function to alleviate the contour stripe phenomenon when connectiong to a 6bpc monitor. Mainline: NA Signed-off-by: Yu Da Signed-off-by: Wang Yinfeng Change-Id: I80aeeaaf85e13686c2060a8ef1e0abf778f9ec27 --- drivers/gpu/drm/phytium/phytium_crtc.c | 6 ++++++ drivers/gpu/drm/phytium/phytium_reg.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/gpu/drm/phytium/phytium_crtc.c b/drivers/gpu/drm/phytium/phytium_crtc.c index 2e932418d9..16f68a4dee 100644 --- a/drivers/gpu/drm/phytium/phytium_crtc.c +++ b/drivers/gpu/drm/phytium/phytium_crtc.c @@ -600,6 +600,12 @@ phytium_crtc_atomic_enable(struct drm_crtc *crtc, else phytium_crtc_gamma_init(crtc); + /* enable dither*/ + DRM_DEBUG_KMS("Enable dither on DC-%d\n", phys_pipe); + phytium_writel_reg(priv, DITHER_TABLE_LOW, group_offset, DC_DITHER_TABLE_LOW); + phytium_writel_reg(priv, DITHER_TABLE_HIGH, group_offset, DC_DITHER_TABLE_HIGH); + phytium_writel_reg(priv, ENABLE, group_offset, DC_DITHER_CONFIG); + phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG); drm_crtc_vblank_on(crtc); } diff --git a/drivers/gpu/drm/phytium/phytium_reg.h b/drivers/gpu/drm/phytium/phytium_reg.h index f5d4a6945c..14a4810906 100644 --- a/drivers/gpu/drm/phytium/phytium_reg.h +++ b/drivers/gpu/drm/phytium/phytium_reg.h @@ -29,6 +29,12 @@ #define PANEL_DATAENABLE_ENABLE (1<<0) #define PANEL_DATA_ENABLE (1<<4) #define PANEL_CLOCK_ENABLE (1<<8) +#define DC_DITHER_CONFIG 0X1410 + #define ENABLE 0x80000000 +#define DC_DITHER_TABLE_LOW 0x1420 + #define DITHER_TABLE_LOW 0x7B48F3C0 +#define DC_DITHER_TABLE_HIGH 0X1428 + #define DITHER_TABLE_HIGH 0x596AD1E2 #define PHYTIUM_DC_HDISPLAY 0x1430 #define HDISPLAY_END_SHIFT 0 #define HDISPLAY_END_MASK 0x7fff -- Gitee From d63bf7fa0097efb44adb0e02f6a6c2bf02848e53 Mon Sep 17 00:00:00 2001 From: Huangjie Date: Mon, 28 Apr 2025 17:53:12 +0800 Subject: [PATCH 128/154] Revert "drivers: pwm: phytium pwm driver add ACPI support" This reverts commit 78e286c5b33ec547b4b024d0482e13913a24bd5b. --- drivers/pwm/pwm-phytium.c | 44 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/drivers/pwm/pwm-phytium.c b/drivers/pwm/pwm-phytium.c index 1eaf292952..b2544d7919 100644 --- a/drivers/pwm/pwm-phytium.c +++ b/drivers/pwm/pwm-phytium.c @@ -18,7 +18,6 @@ #include #include #include -#include #define REG_TCNT 0x00 #define REG_TCTRL 0x04 @@ -98,7 +97,6 @@ struct phytium_pwm_chip { struct phytium_pwm_param parameter[MAX_PARAMETER]; unsigned int num_parameters; - unsigned long clk_rate; struct clk *base_clk; }; @@ -185,7 +183,7 @@ static void pwm_phytium_set_periodns(struct pwm_chip *chip, unsigned int periodn int div = our_chip->state.div; u64 cycles; - cycles = our_chip->clk_rate; + cycles = clk_get_rate(our_chip->base_clk); cycles *= (periodns / (div + 1)); do_div(cycles, NSEC_PER_SEC); @@ -203,7 +201,7 @@ static void pwm_phytium_set_duty(struct pwm_chip *chip, unsigned int duty, int n int div = our_chip->state.div; u64 cycles; - cycles = our_chip->clk_rate; + cycles = clk_get_rate(our_chip->base_clk); cycles *= (duty / (div + 1)); do_div(cycles, NSEC_PER_SEC); @@ -221,7 +219,7 @@ static int pwm_phytium_set_dbcly(struct pwm_chip *chip, unsigned int updbcly, un u64 dbcly, cycles, upcycles, dwcycles; reg = readl(our_chip->base + REG_TPERIOD); - cycles = our_chip->clk_rate; + cycles = clk_get_rate(our_chip->base_clk); dbcly &= 0x0; if (updbcly) { upcycles = cycles * updbcly; @@ -399,18 +397,19 @@ static int phytium_pwm_set_parameter(struct phytium_pwm_chip *priv) } static int pwm_phytium_probe_parameter(struct phytium_pwm_chip *priv, - struct fwnode_handle *np) + struct device_node *np) { int nb, ret, array_size; unsigned int i; - array_size = fwnode_property_read_u32_array(np, "phytium,db", NULL, 0); - nb = array_size / (sizeof(struct phytium_pwm_param) / sizeof(u32)); + nb = of_property_count_elems_of_size(np, "phytium,db", + sizeof(struct phytium_pwm_param)); if (nb <= 0 || nb > MAX_PARAMETER) return -EINVAL; priv->num_parameters = nb; - ret = fwnode_property_read_u32_array(np, "phytium,db", + array_size = nb * sizeof(struct phytium_pwm_param) / sizeof(u32); + ret = of_property_read_u32_array(np, "phytium,db", (u32 *)priv->parameter, array_size); if (ret) return ret; @@ -428,7 +427,7 @@ static int pwm_phytium_probe_parameter(struct phytium_pwm_chip *priv, static int pwm_phytium_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct fwnode_handle *np = dev_fwnode(dev); + struct device_node *np = dev->of_node; struct phytium_pwm_chip *chip; struct resource *res; int ret; @@ -444,9 +443,16 @@ static int pwm_phytium_probe(struct platform_device *pdev) chip->chip.npwm = PWM_NUM; chip->inverter_mask = BIT(PWM_NUM) - 1; - if (dev->of_node) { + if (pdev->dev.of_node) { chip->chip.of_xlate = of_pwm_xlate_with_flags; chip->chip.of_pwm_n_cells = 3; + } else { + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data specified\n"); + return -EINVAL; + } + memcpy(&chip->variant, + pdev->dev.platform_data, sizeof(chip->variant)); } ret = pwm_phytium_probe_parameter(chip, np); if (ret) { @@ -462,7 +468,6 @@ static int pwm_phytium_probe(struct platform_device *pdev) dev_err(dev, "failed to get base_addr\n"); return PTR_ERR(chip->base); } - if (dev->of_node) { chip->base_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(chip->base_clk)) { dev_err(dev, "failed to get clk\n"); @@ -474,11 +479,7 @@ static int pwm_phytium_probe(struct platform_device *pdev) dev_err(dev, "failed to enable clk\n"); return ret; } - chip->clk_rate = clk_get_rate(chip->base_clk); - } else if (has_acpi_companion(dev)){ - if(fwnode_property_read_u32(dev_fwnode(dev),"clock-frequency", (u32 *)&(chip->clk_rate) ) <0) - chip->clk_rate = 50000000; - } + platform_set_drvdata(pdev, chip); ret = pwmchip_add(&chip->chip); @@ -537,14 +538,6 @@ static int pwm_phytium_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(phytium_pwm_dev_pm_ops, pwm_phytium_suspend, pwm_phytium_resume); -#ifdef CONFIG_ACPI -static const struct acpi_device_id phytium_pwm_acpi_ids[] = { - { "PHYT0029", 0 }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(acpi, phytium_pwm_acpi_ids); -#endif - static const struct of_device_id phytium_pwm_matches[] = { { .compatible = "phytium,pwm" }, {}, @@ -556,7 +549,6 @@ static struct platform_driver pwm_phytium_driver = { .name = "phytium-pwm", .pm = &phytium_pwm_dev_pm_ops, .of_match_table = phytium_pwm_matches, - .acpi_match_table = ACPI_PTR(phytium_pwm_acpi_ids), }, .probe = pwm_phytium_probe, .remove = pwm_phytium_remove, -- Gitee From db04bd3ce1c2ab6ffb5637aee6b55ddad05e6c13 Mon Sep 17 00:00:00 2001 From: Chen Zhenhua Date: Tue, 26 Sep 2023 15:09:04 +0800 Subject: [PATCH 129/154] pwm: phytium: Add ACPI support for pwm driver This patch used to support acpi and set the phytium ACPI ID to PHYT0029. Mainline: Open-Source Signed-off-by: Chen Zhenhua Signed-off-by: Wang Yinfeng Change-Id: I6fcecae4fd7da8330a23f099f48adcea0d41cba0 --- drivers/pwm/pwm-phytium.c | 96 +++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/drivers/pwm/pwm-phytium.c b/drivers/pwm/pwm-phytium.c index b2544d7919..dc0c685b2d 100644 --- a/drivers/pwm/pwm-phytium.c +++ b/drivers/pwm/pwm-phytium.c @@ -18,6 +18,7 @@ #include #include #include +#include #define REG_TCNT 0x00 #define REG_TCTRL 0x04 @@ -183,7 +184,10 @@ static void pwm_phytium_set_periodns(struct pwm_chip *chip, unsigned int periodn int div = our_chip->state.div; u64 cycles; - cycles = clk_get_rate(our_chip->base_clk); + if (has_acpi_companion(chip->dev)) + device_property_read_u64(chip->dev, "clock-frequency", &cycles); + else + cycles = clk_get_rate(our_chip->base_clk); cycles *= (periodns / (div + 1)); do_div(cycles, NSEC_PER_SEC); @@ -201,7 +205,10 @@ static void pwm_phytium_set_duty(struct pwm_chip *chip, unsigned int duty, int n int div = our_chip->state.div; u64 cycles; - cycles = clk_get_rate(our_chip->base_clk); + if (has_acpi_companion(chip->dev)) + device_property_read_u64(chip->dev, "clock-frequency", &cycles); + else + cycles = clk_get_rate(our_chip->base_clk); cycles *= (duty / (div + 1)); do_div(cycles, NSEC_PER_SEC); @@ -219,7 +226,10 @@ static int pwm_phytium_set_dbcly(struct pwm_chip *chip, unsigned int updbcly, un u64 dbcly, cycles, upcycles, dwcycles; reg = readl(our_chip->base + REG_TPERIOD); - cycles = clk_get_rate(our_chip->base_clk); + if (has_acpi_companion(chip->dev)) + device_property_read_u64(chip->dev, "clock-frequency", &cycles); + else + cycles = clk_get_rate(our_chip->base_clk); dbcly &= 0x0; if (updbcly) { upcycles = cycles * updbcly; @@ -402,17 +412,27 @@ static int pwm_phytium_probe_parameter(struct phytium_pwm_chip *priv, int nb, ret, array_size; unsigned int i; - nb = of_property_count_elems_of_size(np, "phytium,db", + if (has_acpi_companion(priv->chip.dev)) { + priv->num_parameters = 1; + array_size = sizeof(struct phytium_pwm_param) / sizeof(u32); + ret = fwnode_property_read_u32_array(dev_fwnode(priv->chip.dev), + "phytium,db", (u32 *)priv->parameter, + array_size); + if (ret < 0) + return ret; + } else { + nb = of_property_count_elems_of_size(np, "phytium,db", sizeof(struct phytium_pwm_param)); - if (nb <= 0 || nb > MAX_PARAMETER) - return -EINVAL; - - priv->num_parameters = nb; - array_size = nb * sizeof(struct phytium_pwm_param) / sizeof(u32); - ret = of_property_read_u32_array(np, "phytium,db", - (u32 *)priv->parameter, array_size); - if (ret) - return ret; + if (nb <= 0 || nb > MAX_PARAMETER) + return -EINVAL; + + priv->num_parameters = nb; + array_size = nb * sizeof(struct phytium_pwm_param) / sizeof(u32); + ret = of_property_read_u32_array(np, "phytium,db", + (u32 *)priv->parameter, array_size); + if (ret) + return ret; + } for (i = 0; i < priv->num_parameters; i++) { if (priv->parameter[i].cntmod > 1 || @@ -446,14 +466,8 @@ static int pwm_phytium_probe(struct platform_device *pdev) if (pdev->dev.of_node) { chip->chip.of_xlate = of_pwm_xlate_with_flags; chip->chip.of_pwm_n_cells = 3; - } else { - if (!pdev->dev.platform_data) { - dev_err(&pdev->dev, "no platform data specified\n"); - return -EINVAL; - } - memcpy(&chip->variant, - pdev->dev.platform_data, sizeof(chip->variant)); } + ret = pwm_phytium_probe_parameter(chip, np); if (ret) { dev_err(dev, "failed to set parameter\n"); @@ -464,20 +478,25 @@ static int pwm_phytium_probe(struct platform_device *pdev) chip->base1 = devm_ioremap_resource(&pdev->dev, res); chip->base = (chip->base1 + 0x400); - if (IS_ERR(chip->base)) { - dev_err(dev, "failed to get base_addr\n"); - return PTR_ERR(chip->base); - } - chip->base_clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(chip->base_clk)) { - dev_err(dev, "failed to get clk\n"); - return PTR_ERR(chip->base_clk); - } + if (!has_acpi_companion(&pdev->dev)) { + if (IS_ERR(chip->base)) { + dev_err(dev, "failed to get base_addr\n"); + return PTR_ERR(chip->base); + } - ret = clk_prepare_enable(chip->base_clk); - if (ret < 0) { - dev_err(dev, "failed to enable clk\n"); - return ret; + if (pdev->dev.of_node) { + chip->base_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(chip->base_clk)) { + dev_err(dev, "failed to get clk\n"); + return PTR_ERR(chip->base_clk); + } + + ret = clk_prepare_enable(chip->base_clk); + if (ret < 0) { + dev_err(dev, "failed to enable clk\n"); + return ret; + } + } } platform_set_drvdata(pdev, chip); @@ -544,11 +563,22 @@ static const struct of_device_id phytium_pwm_matches[] = { }; MODULE_DEVICE_TABLE(of, phytium_pwm_matches); +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_pwm_acpi_matches[] = { + { "PHYT0029", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, phytium_pwm_acpi_matches); +#endif + static struct platform_driver pwm_phytium_driver = { .driver = { .name = "phytium-pwm", .pm = &phytium_pwm_dev_pm_ops, .of_match_table = phytium_pwm_matches, +#ifdef CONFIG_ACPI + .acpi_match_table = phytium_pwm_acpi_matches, +#endif }, .probe = pwm_phytium_probe, .remove = pwm_phytium_remove, -- Gitee From 9227cce51d28eb002bfb0e3801127e3c17039c32 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Thu, 27 Mar 2025 17:52:47 +0800 Subject: [PATCH 130/154] spi: phytium: Dynamically get the clock in the ACPI table Originally, the SPI driver wrote the controller clock as a fixed value, but now it is modified to obtain dynamiclly. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: I67bf00bd04765544508945d73b53ad10e4f09a49 --- drivers/spi/spi-phytium-plat.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-phytium-plat.c b/drivers/spi/spi-phytium-plat.c index 57593cd0e8..e88c62ce51 100644 --- a/drivers/spi/spi-phytium-plat.c +++ b/drivers/spi/spi-phytium-plat.c @@ -26,15 +26,19 @@ #define DRIVER_NAME "phytium_spi" +#define SPI_PHYTIUM_DEFAULT_CLK_RATE 50000000 + static int phytium_spi_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct phytium_spi *fts; struct resource *mem; int ret; int num_cs; int cs_gpio; - int global_cs; + int global_cs = 0; int i; + u32 clk_rate = SPI_PHYTIUM_DEFAULT_CLK_RATE; fts = devm_kzalloc(&pdev->dev, sizeof(struct phytium_spi), GFP_KERNEL); @@ -71,7 +75,9 @@ static int phytium_spi_probe(struct platform_device *pdev) fts->max_freq = clk_get_rate(fts->clk); } else if (has_acpi_companion(&pdev->dev)) { - fts->max_freq = 48000000; + fts->max_freq = clk_rate; + if (!fwnode_property_read_u32(dev->fwnode, "spi-clock", &clk_rate)) + fts->max_freq = clk_rate; } fts->bus_num = pdev->id; -- Gitee From 7a9590d868850ca65d153a9bb7a28b73943a0120 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Thu, 27 Mar 2025 18:18:16 +0800 Subject: [PATCH 131/154] spi: phytium: Add the driver version description After adding the driver version description, you can use the modinfo command to view the driver version information. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: Icbc6d29e3ea54095ff30a820b833b1b95af9fe30 --- drivers/spi/spi-phytium-pci.c | 3 +++ drivers/spi/spi-phytium-plat.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/spi/spi-phytium-pci.c b/drivers/spi/spi-phytium-pci.c index 5bc6863427..e1ff699168 100644 --- a/drivers/spi/spi-phytium-pci.c +++ b/drivers/spi/spi-phytium-pci.c @@ -23,6 +23,7 @@ #include "spi-phytium.h" #define DRIVER_NAME "phytium_spi_pci" +#define DRIVER_VERSION "1.0.0" static int phytium_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) @@ -104,6 +105,7 @@ static const struct pci_device_id phytium_device_pci_tbl[] = { { PCI_VDEVICE(PHYTIUM, 0xdc2c) }, {}, }; +MODULE_DEVICE_TABLE(pci, phytium_device_pci_tbl); static struct pci_driver phytium_spi_pci_driver = { .name = DRIVER_NAME, @@ -120,3 +122,4 @@ module_pci_driver(phytium_spi_pci_driver); MODULE_AUTHOR("Yiqun Zhang "); MODULE_DESCRIPTION("PCI Driver for Phytium SPI controller core"); MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/spi/spi-phytium-plat.c b/drivers/spi/spi-phytium-plat.c index e88c62ce51..11479fcfe0 100644 --- a/drivers/spi/spi-phytium-plat.c +++ b/drivers/spi/spi-phytium-plat.c @@ -25,6 +25,7 @@ #include "spi-phytium.h" #define DRIVER_NAME "phytium_spi" +#define DRIVER_VERSION "1.0.0" #define SPI_PHYTIUM_DEFAULT_CLK_RATE 50000000 @@ -208,3 +209,4 @@ module_platform_driver(phytium_spi_driver); MODULE_AUTHOR("Yiqun Zhang "); MODULE_DESCRIPTION("Platform Driver for Phytium SPI controller core"); MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRIVER_VERSION); -- Gitee From b23e441d1ddd44133eae3a181992512b57091951 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Wed, 2 Apr 2025 15:51:42 +0800 Subject: [PATCH 132/154] qspi: phytium: Capacity register can't be configured without flash node If the device tree or ACPI table does not describe the flash node, the capacity register should not be configured for the QSPI controller driver. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: I03f2342bd46dc11d8ecbd86d43f7e3d3b97c5242 --- drivers/spi/spi-phytium-qspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-phytium-qspi.c b/drivers/spi/spi-phytium-qspi.c index 6350d7c0cd..b9ee1d455d 100755 --- a/drivers/spi/spi-phytium-qspi.c +++ b/drivers/spi/spi-phytium-qspi.c @@ -755,7 +755,7 @@ static int phytium_qspi_probe(struct platform_device *pdev) goto probe_setup_failed; } - if (!qspi->nodirmap) { + if (!qspi->nodirmap && qspi->fnum != 0) { /* * The controller supports direct mapping access only if all * flashes are of same size. -- Gitee From acdd56506fb95977fc64a5c21abc726d22a99631 Mon Sep 17 00:00:00 2001 From: Huang Shaobo Date: Thu, 6 Mar 2025 15:21:02 +0800 Subject: [PATCH 133/154] mm/init: Add KASAN quirk for phytium ps23064 SoC Enabling KASAN in the Phytium ps23064 SoC will result in a reduction in linear mapping space, leading to a failure and panic in allocating percpu variables. This patch avoid this problem throuth removing high physical addresses, but it should be note that device should not be attach to die 5-7. Mainline: NA Signed-off-by: Huang Shaobo Signed-off-by: Wang Yinfeng Change-Id: I3fbd33044d97c1739ac1b4889b79d39435e33d6a --- arch/arm64/mm/init.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 80cc79760e..fa2fcf8eb7 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -320,6 +320,24 @@ static void __init fdt_enforce_memory_region(void) memblock_cap_memory_range(reg.base, reg.size); } +#define SOCID_PS23064 0x8 +#define MIDR_PS23064 0x700F8620 +#define SYS_AIDR_EL1 sys_reg(3, 1, 0, 0, 7) + +static inline bool is_phytium_ps23064(void) +{ + return read_sysreg_s(SYS_AIDR_EL1) == SOCID_PS23064 && + read_cpuid_id() == MIDR_PS23064; +} + + +#define PS23064_MAX_ADDR 0x510783f00000 +static inline void phytium_ps23064_quirk(void) +{ + pr_warn("Enable Phytium S5000C-128 Core quirk\n"); + memblock_remove(PS23064_MAX_ADDR, (1ULL << PHYS_MASK_SHIFT) - PS23064_MAX_ADDR); +} + void __init arm64_memblock_init(void) { const s64 linear_region_size = BIT(vabits_actual - 1); @@ -330,6 +348,8 @@ void __init arm64_memblock_init(void) /* Remove memory above our supported physical address size */ memblock_remove(1ULL << PHYS_MASK_SHIFT, ULLONG_MAX); + if (IS_ENABLED(CONFIG_KASAN) && is_phytium_ps23064()) + phytium_ps23064_quirk(); /* * Select a suitable value for the base of physical memory. */ -- Gitee From b10f41f6d16cdc852bb8320d4c80816f6132ff8f Mon Sep 17 00:00:00 2001 From: Tang Jiemin Date: Tue, 22 Apr 2025 16:11:17 +0800 Subject: [PATCH 134/154] mmu_notifiers: Add a new callback in __flush_tlb_range It is necessary to obtain the accurate address for Duptext tools in the __flush_tlb_range function. However, the address of flush_tlb_mm is from 0 to -1, so we have added the callback with the accurate address here. Mainline: Open-Source Signed-off-by: Tang Jiemin Signed-off-by: Wang Yinfeng Change-Id: Ieb9fd0fb0545a97bd079efb19cfd8c1709dca39d --- arch/arm64/include/asm/tlbflush.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index 2f363651e7..bdcefa9701 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -309,6 +309,9 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, (end - start) >= (MAX_TLBI_OPS * stride)) || pages >= MAX_TLBI_RANGE_PAGES) { flush_tlb_mm(vma->vm_mm); +#ifdef CONFIG_ARCH_PHYTIUM + mmu_notifier_arch_invalidate_secondary_tlbs(vma->vm_mm, ustart, uend); +#endif return; } -- Gitee From 60eeb57cb56038ca604e78110f58b4b3c434356c Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 22 Apr 2025 19:12:45 +0800 Subject: [PATCH 135/154] net/phytmac: Update hardware identifiers and the driver name Change the driver name to the same as the module name. We can use "ethtool -i DEV" to view the hardware identifier and driver name. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I70745c6e7ed5331e203fbadf77650d806938be27 --- drivers/net/ethernet/phytium/phytmac.h | 6 ++++-- drivers/net/ethernet/phytium/phytmac_ethtool.c | 9 ++++++--- drivers/net/ethernet/phytium/phytmac_main.c | 10 ++++++++++ drivers/net/ethernet/phytium/phytmac_pci.c | 4 ++-- drivers/net/ethernet/phytium/phytmac_platform.c | 2 +- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index a6113c5f2f..2ffbeb1c77 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -14,9 +14,10 @@ #include #include -#define PHYTMAC_DRV_NAME "phytium-mac" +#define PHYTMAC_PCI_DRV_NAME "phytmac_pci" +#define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.44" +#define PHYTMAC_DRIVER_VERSION "1.0.45" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -511,6 +512,7 @@ struct phytmac { spinlock_t rx_fs_lock; unsigned int max_rx_fs; u32 version; + char fw_version[32]; }; /* phytmac_desc_unused - calculate if we have unused descriptors */ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 2f2fb24bfc..2cc224c96a 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -503,13 +503,16 @@ static void phytmac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo { struct phytmac *pdata = netdev_priv(ndev); - strncpy(drvinfo->driver, PHYTMAC_DRV_NAME, sizeof(drvinfo->driver)); strncpy(drvinfo->version, PHYTMAC_DRIVER_VERSION, sizeof(drvinfo->version)); + strscpy(drvinfo->fw_version, pdata->fw_version, sizeof(drvinfo->fw_version)); - if (pdata->platdev) + if (pdata->platdev) { + strscpy(drvinfo->driver, PHYTMAC_PLAT_DRV_NAME, sizeof(drvinfo->driver)); strncpy(drvinfo->bus_info, pdata->platdev->name, sizeof(drvinfo->bus_info)); - else if (pdata->pcidev) + } else if (pdata->pcidev) { + strscpy(drvinfo->driver, PHYTMAC_PCI_DRV_NAME, sizeof(drvinfo->driver)); strncpy(drvinfo->bus_info, pci_name(pdata->pcidev), sizeof(drvinfo->bus_info)); + } } static const struct ethtool_ops phytmac_ethtool_ops = { diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 79ec6cf1ad..e056ac2b22 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2683,6 +2683,16 @@ void phytmac_default_config(struct phytmac *pdata) ndev->max_mtu = ETH_DATA_LEN; ndev->features = ndev->hw_features; + + switch (pdata->version) { + case VERSION_V3: + strscpy(pdata->fw_version, "MAC_FTM300", sizeof(pdata->fw_version)); + break; + + default: + strscpy(pdata->fw_version, "", sizeof(pdata->fw_version)); + break; + } } static void phytmac_ncsi_handler(struct ncsi_dev *nd) diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index 62766c49d1..fac5d717ad 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -74,7 +74,7 @@ static int phytmac_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i /* Obtain the mmio areas for the device */ bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); - ret = pcim_iomap_regions(pdev, bar_mask, PHYTMAC_DRV_NAME); + ret = pcim_iomap_regions(pdev, bar_mask, PHYTMAC_PCI_DRV_NAME); if (ret) { dev_err(dev, "pcim_iomap_regions failed\n"); goto err_pci_enable; @@ -305,7 +305,7 @@ static const struct dev_pm_ops phytmac_pci_pm_ops = { }; static struct pci_driver phytmac_driver = { - .name = PHYTMAC_DRV_NAME, + .name = PHYTMAC_PCI_DRV_NAME, .id_table = phytmac_pci_table, .probe = phytmac_pci_probe, .remove = phytmac_pci_remove, diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 540159f8fc..2f2170cf07 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -356,7 +356,7 @@ static struct platform_driver phytmac_driver = { .probe = phytmac_plat_probe, .remove = phytmac_plat_remove, .driver = { - .name = PHYTMAC_DRV_NAME, + .name = PHYTMAC_PLAT_DRV_NAME, .of_match_table = of_match_ptr(phytmac_dt_ids), .acpi_match_table = phytmac_acpi_ids, .pm = &phytmac_plat_pm_ops, -- Gitee From b600d11301360608f222e58f9073448e1f2ff3cc Mon Sep 17 00:00:00 2001 From: Wang Min Date: Tue, 22 Apr 2025 09:36:49 +0800 Subject: [PATCH 136/154] media: phytium-jpeg: reduce the latency introduced by IRQ threads When OS is under heavy load, there is a high probability that the IRQ thread will not get invoked immediately. In the scenario, scheduling IRQ threads generate latency. Therefore, the legacy IRQ method is used to mitigate this latency. Mainline: Open-Source Signed-off-by: Wang Min Signed-off-by: Wang Yinfeng Change-Id: Ie9a6f60640afe92b48374a38ccfacd640bd3719b --- drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c index 2e81aaf16f..f1d600a928 100644 --- a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c +++ b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c @@ -1092,8 +1092,8 @@ static int phytium_jpeg_parser_timer31_irq(struct phytium_jpeg_dev *jpeg_dev) return -ENODEV; } - ret = devm_request_threaded_irq(dev, irq, NULL, phytium_jpeg_timer31_irq, - IRQF_ONESHOT, PHYTIUM_JPEG_NAME, jpeg_dev); + ret = devm_request_irq(dev, irq, phytium_jpeg_timer31_irq, + IRQF_TIMER, PHYTIUM_JPEG_NAME, jpeg_dev); if (ret < 0) dev_err(dev, "Failed to request timer31 IRQ %d\n", irq); @@ -1136,8 +1136,8 @@ static int phytium_jpeg_parser_timer30_irq(struct phytium_jpeg_dev *jpeg_dev) return -ENODEV; } - ret = devm_request_threaded_irq(dev, irq, NULL, phytium_jpeg_timer30_irq, - IRQF_ONESHOT, PHYTIUM_JPEG_NAME, jpeg_dev); + ret = devm_request_irq(dev, irq, phytium_jpeg_timer30_irq, + IRQF_TIMER, PHYTIUM_JPEG_NAME, jpeg_dev); if (ret < 0) dev_err(dev, "Failed to request timer30 IRQ %d\n", irq); -- Gitee From d378590c145726201fb7ee8f8ddab88f4821026e Mon Sep 17 00:00:00 2001 From: Li Mingzhe Date: Wed, 2 Apr 2025 09:29:12 +0800 Subject: [PATCH 137/154] devfreq: Phytium: Update some new functions for DMU driver This patch modifies and adds the following functions: 1). On account of DMU and DDR PMU drivers operate PMU registers at the same time, which will result in conflict. So the register operation of se in dmufreq is transferred to the upper driver. 2). The notification chain of dmufreq to DDR PMU is added in order to suspend dmufreq's register action and maintain the rate at the current frequency when the PMU driver is loaded. 3). Add suspend and resume features. Mainline: Open-Source Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Change-Id: Iaee1415a791fe2e0fee7bcbd8e5c4730e5aae27e --- drivers/devfreq/phytium_dmu.c | 333 +++++++++++++++++++++++++++++++--- 1 file changed, 311 insertions(+), 22 deletions(-) diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index c0a281b9dc..5b3c633425 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #define DEBUG @@ -25,6 +27,20 @@ #define UPDATE_INTERVAL_MS 10 +#define DMU_PMU_STRIDE 0x80000 + +#define AXI_MONITOR2_L 0x084 +#define AXI_MONITOR3_L 0x08c +#define AXI_MONITOR_EN 0X01c +#define TIMER_START 0X000 +#define TIMER_STOP 0X004 +#define CLEAR_EVENT 0X008 + +#define MCU_STRIDE 0x00080000 +/* PMU notifier event */ +#define DDR_PMU_NOTICE_START 0x0 +#define DDR_PMU_NOTICE_STOP 0x1 + #define DMUFREQ_DRIVER_VERSION "1.0.0" struct phytium_dmufreq { @@ -36,14 +52,72 @@ struct phytium_dmufreq { unsigned long rate, target_rate; unsigned long bandwidth; + int max_count; + int cnt; + + void __iomem **base; + + unsigned long *read_bw; + unsigned long *write_bw; struct timer_list sampling; struct work_struct work; + struct notifier_block nb; + + /*dmu to pmu operation status identification 0: not operable, 1: operable*/ + bool pmu_active; + + unsigned long last_bust_time; + unsigned int freq_count; unsigned long freq_table[]; }; +struct acpi_result { + int status; + unsigned long long value; +}; + +static inline void dmu_write32(struct phytium_dmufreq *priv, int dmu, + unsigned long offest, unsigned long value) +{ + writel_relaxed(value, priv->base[dmu] + offest); +} + +static inline unsigned long dmu_read32(struct phytium_dmufreq *priv, int dmu, + unsigned long offest) +{ + return readl_relaxed(priv->base[dmu] + offest); +} + +#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) +BLOCKING_NOTIFIER_HEAD(dmu_pmu_notifier_chain); +EXPORT_SYMBOL(dmu_pmu_notifier_chain); + +static int dmu_pmu_notifier_call(struct notifier_block *nb, unsigned long event, void *data) +{ + struct phytium_dmufreq *priv = container_of(nb, struct phytium_dmufreq, nb); + struct device *dev = priv->dev; + + switch (event) { + case DDR_PMU_NOTICE_START: + priv->pmu_active = false; + dev_dbg(dev, "DDR PMU START: Stopping monitoring\n"); + break; + case DDR_PMU_NOTICE_STOP: + priv->cnt = 0; + priv->pmu_active = true; + dev_dbg(dev, "DDR PMU STOP: Resuming monitoring\n"); + break; + default: + break; + } + + return NOTIFY_OK; +} +#endif + static ktime_t stop; static int phytium_dmu_set_freq(struct device *dev, unsigned long freq) @@ -119,24 +193,105 @@ static int phytium_dmu_get_cur_freq(struct device *dev, unsigned long *freq) return 0; } -static int phytium_read_perf_counter(struct device *dev) +struct acpi_result phytium_current_enabled_channels(struct device *dev) { - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *package, *elements; acpi_handle handle = ACPI_HANDLE(dev); acpi_status status; + unsigned long long enabled_channels; + struct acpi_result result; - status = acpi_evaluate_object(handle, "PDMU", NULL, &buffer); + status = acpi_evaluate_integer(handle, "CHAN", NULL, &enabled_channels); if (ACPI_FAILURE(status)) { - dev_err(dev, "No PGCL method\n"); - return -EIO; + dev_err(dev, "Failed to evaluate CHAN method: ACPI status 0x%x\n", status); + result.status = -EIO; + result.value = 0; + return result; } + dev_dbg(dev, "enabled_channels = %lld\n", enabled_channels); + result.status = 0; + result.value = enabled_channels; + return result; +} - package = buffer.pointer; +struct acpi_result phytium_controller_bit_width(struct device *dev) +{ + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + unsigned long long single_bit_width; + struct acpi_result result; + + status = acpi_evaluate_integer(handle, "BITW", NULL, &single_bit_width); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Failed to evaluate BITW method: ACPI status 0x%x\n", status); + result.status = -EIO; + result.value = 0; + return result; + } + dev_dbg(dev, "single_bit_width = %lld(MB/s)\n", single_bit_width); + result.status = 0; + result.value = single_bit_width; + return result; +} + +struct acpi_result phytium_dmufreq_state(struct device *dev) +{ + struct acpi_result result; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + unsigned long long dmufreq_state; + + status = acpi_evaluate_integer(handle, "STAT", NULL, &dmufreq_state); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Failed to evaluate STAT method: ACPI status 0x%x\n", status); + result.status = -EIO; + result.value = 0; + return result; + } + dev_dbg(dev, "dmufreq_state = %lld\n", dmufreq_state); + result.status = 0; + result.value = dmufreq_state; + return result; +} - elements = package->package.elements; +struct acpi_result phytium_read_threshold_value(struct device *dev) +{ + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + unsigned long long single_threshold_value; + struct acpi_result result; - return elements[0].integer.value + elements[1].integer.value; + status = acpi_evaluate_integer(handle, "BAND", NULL, &single_threshold_value); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Failed to evaluate BAND method: ACPI status 0x%x\n", status); + result.status = -EIO; + result.value = 0; + return result; + } + dev_dbg(dev, "single_threshold_value = %llu\n", single_threshold_value); + result.status = 0; + result.value = single_threshold_value; + return result; +} + +static u64 phytium_dmufreq_get_real_bw(struct phytium_dmufreq *priv) +{ + unsigned long peak_bw = 0; + unsigned long sum_peak_bw = 0; + int i; + + for (i = 0; i < priv->max_count; i++) { + priv->read_bw[i] = dmu_read32(priv, i, AXI_MONITOR2_L); + priv->write_bw[i] = dmu_read32(priv, i, AXI_MONITOR3_L); + + /*clear the counter(only pmu_reg active)*/ + dmu_write32(priv, i, CLEAR_EVENT, 0x1); + dmu_write32(priv, i, TIMER_START, 0x1); + sum_peak_bw = priv->read_bw[i] + priv->write_bw[i]; + if (sum_peak_bw > peak_bw) + peak_bw = sum_peak_bw; + } + dev_dbg(priv->dev, "peak_bw = %lu\n", peak_bw); + return peak_bw; } static void sampling_timer_callback(struct timer_list *t) @@ -149,18 +304,30 @@ static void sampling_timer_callback(struct timer_list *t) static void sampling_work_handle(struct work_struct *work) { struct phytium_dmufreq *priv = container_of(work, struct phytium_dmufreq, work); - struct device *dev = priv->dev; static unsigned long load_counter; static int count; unsigned long current_load; + int i; - current_load = phytium_read_perf_counter(dev); - - load_counter += current_load; - count += 1; + /*if the pmu_reg is not active, return the last busy time(pmu_reg not work)*/ + if (!priv->pmu_active) { + priv->bandwidth = priv->last_bust_time; + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); + return; + } + if (priv->cnt > 0) { + for (i = 0; i < priv->max_count ; i++) { + dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); + dmu_write32(priv, i, TIMER_STOP, 0x1); + } + current_load = phytium_dmufreq_get_real_bw(priv); + load_counter += current_load; + count += 1; + } + priv->cnt = 1; if (ktime_after(ktime_get(), stop)) { - priv->bandwidth = (load_counter / count)/2; + priv->bandwidth = div64_u64(load_counter, count); load_counter = 0; count = 0; stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); @@ -173,11 +340,22 @@ static int phytium_dmu_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) { struct phytium_dmufreq *priv = dev_get_drvdata(dev); + struct acpi_result result; + unsigned long long single_threshold_value; + + result = phytium_read_threshold_value(dev); + if (result.status) { + dev_err(dev, "Failed to get threshold value\n"); + return -EINVAL; + } + single_threshold_value = result.value; + single_threshold_value = (single_threshold_value * 1024 * 1024) / 100; stat->busy_time = priv->bandwidth; - stat->total_time = (75000000 * priv->rate) / priv->freq_table[0]; - dev_dbg(dev, "busy_time = %lu, total_time = %lu\n", - stat->busy_time, stat->total_time); + stat->total_time = (single_threshold_value * priv->rate) / priv->freq_table[0]; + priv->last_bust_time = priv->bandwidth; + dev_dbg(dev, "busy_time = %lu, total_time = %lu,single_threshold_value = %llu\n", + stat->busy_time, stat->total_time, single_threshold_value); stat->current_frequency = priv->rate; @@ -260,6 +438,49 @@ static int get_freq_count(struct device *dev) return freq_count; } +static __maybe_unused int phytium_dmufreq_suspend(struct device *dev) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(dev, "DMU is being suspended\n"); + + del_timer_sync(&priv->sampling); + flush_work(&priv->work); + + ret = devfreq_suspend_device(priv->devfreq); + if (ret < 0) { + dev_err(dev, "failed to suspend the devfreq devices\n"); + return ret; + } + + return 0; +} + +static __maybe_unused int phytium_dmufreq_resume(struct device *dev) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(dev, "DMU is being resumed\n"); + + ret = devfreq_resume_device(priv->devfreq); + if (ret < 0) { + dev_err(dev, "failed to resume the devfreq devices\n"); + return ret; + } + + if (!timer_pending(&priv->sampling)) + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); + else + dev_warn(dev, "Sampling timer already active ,skipping reinitialization\n"); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(phytium_dmufreq_pm, phytium_dmufreq_suspend, + phytium_dmufreq_resume); + static int phytium_dmufreq_probe(struct platform_device *pdev) { struct phytium_dmufreq *priv; @@ -267,25 +488,67 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; int i, ret; unsigned int max_state = get_freq_count(dev); + struct acpi_result result; + struct resource *res; + + result = phytium_dmufreq_state(dev); + if (result.value == 0) { + dev_err(dev, "DMUFREQ is not enabled\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, struct_size(priv, freq_table, max_state), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + result = phytium_current_enabled_channels(dev); + if (result.status) { + dev_err(dev, "Failed to get enabled channels\n"); + return -EINVAL; + } + + priv->max_count = result.value; if (max_state <= 0) return max_state; dev->init_name = "dmufreq"; - priv = kzalloc(sizeof(struct phytium_dmufreq) + - max_state * sizeof(unsigned long), GFP_KERNEL); - if (!priv) + priv->base = devm_kcalloc(dev, priv->max_count, sizeof(void __iomem *), GFP_KERNEL); + priv->read_bw = devm_kcalloc(dev, priv->max_count, sizeof(unsigned long), GFP_KERNEL); + priv->write_bw = devm_kcalloc(dev, priv->max_count, sizeof(unsigned long), GFP_KERNEL); + if (!priv->base || !priv->read_bw || !priv->write_bw) { + dev_err(dev, "failed to allocate memory\n"); return -ENOMEM; - + } platform_set_drvdata(pdev, priv); +#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) + /* Register the notifier */ + priv->nb.notifier_call = dmu_pmu_notifier_call; + ret = blocking_notifier_chain_register(&dmu_pmu_notifier_chain, &priv->nb); + if (ret) { + dev_err(dev, "Failed to register notifier\n"); + return ret; + } +#endif + + /* Get the base address of the DMU PMU */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + for (i = 0; i < priv->max_count; i++) { + priv->base[i] = ioremap(res->start + i * DMU_PMU_STRIDE, resource_size(res)); + if (!priv->base[i]) + return -ENOMEM; + } + ret = phytium_dmu_get_freq_info(dev); if (ret) { dev_err(dev, "failed to get ddr frequency info\n"); return -EIO; } + priv->pmu_active = true; + priv->cnt = 1; priv->profile.initial_freq = priv->freq_table[0]; priv->profile.polling_ms = 100; priv->profile.target = phytium_dmu_target; @@ -313,6 +576,15 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) goto err; } + /*Enable PMU*/ + if (priv->pmu_active) { + for (i = 0; i < priv->max_count; i++) { + dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); + dmu_write32(priv, i, CLEAR_EVENT, 0x1); + dmu_write32(priv, i, TIMER_START, 0x1); + } + } + INIT_WORK(&priv->work, sampling_work_handle); timer_setup(&priv->sampling, sampling_timer_callback, 0); stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); @@ -331,6 +603,16 @@ static int phytium_dmufreq_remove(struct platform_device *pdev) { struct phytium_dmufreq *priv = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + int i; + + for (i = 0; i < priv->max_count; i++) { + dmu_write32(priv, i, AXI_MONITOR_EN, 0x0); + dmu_write32(priv, i, TIMER_STOP, 0x1); + } +#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) + /*Unregister the notifier*/ + blocking_notifier_chain_unregister(&dmu_pmu_notifier_chain, &priv->nb); +#endif if (!priv->devfreq) return 0; @@ -356,11 +638,18 @@ MODULE_DEVICE_TABLE(acpi, phytium_dmufreq_acpi_ids); #define phytium_dmu_acpi_ids NULL #endif +#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) +struct notifier_block nb = { + .notifier_call = dmu_pmu_notifier_call, +}; +#endif + static struct platform_driver phytium_dmufreq_driver = { .probe = phytium_dmufreq_probe, .remove = phytium_dmufreq_remove, .driver = { .name = "phytium_dmufreq", + .pm = &phytium_dmufreq_pm, .acpi_match_table = ACPI_PTR(phytium_dmufreq_acpi_ids), .suppress_bind_attrs = true, }, -- Gitee From 4514b5c0282fb2c94f5695a6a2768a3abd266624 Mon Sep 17 00:00:00 2001 From: Tan Rui Date: Thu, 10 Apr 2025 11:01:47 +0800 Subject: [PATCH 138/154] drivers/perf: phytium: Add DDR PMU Support for PD2408 SoCs Add DDR controller performance monitoring unit (PMU) Support for Phytium PD2408 SoCs. Mainline: Open-Source Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng Change-Id: Ic389f966c67de932d8185df209c7dfb00e0bbe7d --- drivers/perf/phytium/Kconfig | 8 + drivers/perf/phytium/Makefile | 1 + drivers/perf/phytium/phytium_dmu_pmu_pd2408.c | 707 ++++++++++++++++++ 3 files changed, 716 insertions(+) create mode 100644 drivers/perf/phytium/phytium_dmu_pmu_pd2408.c diff --git a/drivers/perf/phytium/Kconfig b/drivers/perf/phytium/Kconfig index e9dabba722..048b5d54ce 100644 --- a/drivers/perf/phytium/Kconfig +++ b/drivers/perf/phytium/Kconfig @@ -7,6 +7,14 @@ menuconfig PHYTIUM_PMU if PHYTIUM_PMU +config PHYT_DMU_PMU_PD2408 + tristate "Phytium PD2408 SoC DMU PMU driver" + depends on (ARCH_PHYTIUM && ACPI && ARM_PHYTIUM_DMU_DEVFREQ) || COMPILE_TEST || (ARCH_PHYTIUM && ACPI && !ARM_PHYTIUM_DMU_DEVFREQ) + default m + help + Provides support for Phytium PD2408 SoC DDR memory Controller + performance monitoring unit (PMU). + config PHYT_DDR_PMU tristate "Phytium SoC DDR PMU driver" depends on (ARCH_PHYTIUM && ACPI) || COMPILE_TEST diff --git a/drivers/perf/phytium/Makefile b/drivers/perf/phytium/Makefile index af37afc692..1f9f1e5bf4 100644 --- a/drivers/perf/phytium/Makefile +++ b/drivers/perf/phytium/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 # +obj-$(CONFIG_PHYT_DMU_PMU_PD2408) += phytium_dmu_pmu_pd2408.o obj-$(CONFIG_PHYT_DDR_PMU) += phytium_ddr_pmu.o obj-$(CONFIG_PHYT_PCIE_PMU) += phytium_pcie_pmu.o diff --git a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c new file mode 100644 index 0000000000..d07b0a8869 --- /dev/null +++ b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium PD2408 DMU performance monitoring unit support + * + * Copyright (c) 2025, 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 + +#undef pr_fmt +#define pr_fmt(fmt) "pd2408_dmu_pmu: " fmt + +#define DMU_PERF_DRIVER_VERSION "1.0.0" + +#define DMU_PMU_TIMER_START 0x0 +#define DMU_PMU_TIMER_STOP 0x4 +#define DMU_PMU_CLEAR_EVENT 0x8 +#define DMU_PMU_SET_TIMER_L 0xc +#define DMU_PMU_SET_TIMER_H 0x10 +#define DMU_PMU_AXI_MONITOR_EN 0x1c +#define DMU_PMU_TIMER_INT_CLEAR 0x2c +#define DMU_PMU_AXI_MONITOR_INT_CLEAR 0x30 +#define DMU_PMU_TIMER_INT_STA 0x40 +#define DMU_PMU_AXI_MONITOR_INT_STA 0x44 +#define DMU_PMU_TIMER_INT_MASK 0x54 +#define DMU_PMU_AXI_MONITOR_INT_MASK 0x58 + +#define DMU_PMU_EVENT_CYCLES 0x208 + +#define DMU_PMU_EVENT_AXI_READ_CMD_CNT 0x074 +#define DMU_PMU_EVENT_AXI_WRITE_CMD_CNT 0x07c +#define DMU_PMU_EVENT_AXI_READ_FLUX_CNT 0x084 +#define DMU_PMU_EVENT_AXI_WRITE_FLUX_CNT 0x08c + +#define DMU_PMU_MAX_COUNTERS 5 +#define DMU_PMU_MAX_COUNTERS_TIMER 1 +#define DMU_PMU_MAX_COUNTERS_AXI 4 + +#define ALL_EVENT_CLEAR_BIT 0x1 +#define DMU_PMU_TIMER_OPT_BIT 0x1 +#define AXI_MONITOR_OPT_BIT 0x01010101 + +#define DMU_PMU_NOTICE_START 0x0 +#define DMU_PMU_NOTICE_STOP 0x1 + +#define to_pd2408_dmu_pmu(p) (container_of(p, struct pd2408_dmu_pmu, pmu)) + +#define GET_DMU_EVENTID(hwc) (hwc->config_base & 0x7) +#define EVENT_VALID(idx) ((idx >= 0) && (idx < DMU_PMU_MAX_COUNTERS)) + +static int pd2408_dmu_pmu_hp_state; +int used_event; + +struct pd2408_dmu_pmu_hwevents { + struct perf_event *hw_events[DMU_PMU_MAX_COUNTERS]; + DECLARE_BITMAP(used_event_mask, DMU_PMU_MAX_COUNTERS); +}; + +struct pd2408_dmu_pmu { + struct device *dev; + void __iomem *base; + struct pmu pmu; + struct pd2408_dmu_pmu_hwevents pmu_events; + struct hlist_node node; + int on_cpu; + int irq; + u32 soc_version; + u32 dmu_id; + bool used_flag; +}; + +static const u32 dmu_counter_reg_offset[] = { + DMU_PMU_EVENT_CYCLES, + DMU_PMU_EVENT_AXI_WRITE_FLUX_CNT, DMU_PMU_EVENT_AXI_READ_FLUX_CNT, + DMU_PMU_EVENT_AXI_WRITE_CMD_CNT, DMU_PMU_EVENT_AXI_READ_CMD_CNT +}; + +ssize_t pd2408_dmu_pmu_format_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + + return sprintf(buf, "%s\n", (char *)eattr->var); +} + +ssize_t pd2408_dmu_pmu_event_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + + return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var); +} + +static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pd2408_dmu_pmu *dmu_pmu = + to_pd2408_dmu_pmu(dev_get_drvdata(dev)); + + return cpumap_print_to_pagebuf(true, buf, cpumask_of(dmu_pmu->on_cpu)); +} + +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ + { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ + .attr.attr) + +#define PHYTIUM_DMU_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, pd2408_dmu_pmu_format_sysfs_show, \ + (void *)_config) + +#define PHYTIUM_DMU_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, pd2408_dmu_pmu_event_sysfs_show, \ + (unsigned long)_config) + +static struct attribute *pd2408_dmu_pmu_format_attr[] = { + PHYTIUM_DMU_PMU_FORMAT_ATTR(event, "config:0-2"), + NULL, +}; + +static const struct attribute_group pd2408_dmu_pmu_format_group = { + .name = "format", + .attrs = pd2408_dmu_pmu_format_attr, +}; + +static struct attribute *pd2408_dmu_pmu_events_attr[] = { + PHYTIUM_DMU_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_flux, 0x01), + PHYTIUM_DMU_PMU_EVENT_ATTR(axi_read_flux, 0x02), + PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_cmd, 0x03), + PHYTIUM_DMU_PMU_EVENT_ATTR(axi_read_cmd, 0x04), + NULL, +}; + +static const struct attribute_group pd2408_dmu_pmu_events_group = { + .name = "events", + .attrs = pd2408_dmu_pmu_events_attr, +}; + +static DEVICE_ATTR_RO(cpumask); + +static struct attribute *pd2408_dmu_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group pd2408_dmu_pmu_cpumask_attr_group = { + .attrs = pd2408_dmu_pmu_cpumask_attrs, +}; + +static const struct attribute_group *pd2408_dmu_pmu_attr_groups[] = { + &pd2408_dmu_pmu_format_group, + &pd2408_dmu_pmu_events_group, + &pd2408_dmu_pmu_cpumask_attr_group, + NULL, +}; + +#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) +extern struct blocking_notifier_head dmu_pmu_notifier_chain; + +void pd2408_dmu_pmu_notifier_chain_trigger(struct pd2408_dmu_pmu *dmu_pmu, int event) +{ + static bool start_flag; + + if ((event == DMU_PMU_NOTICE_START) && (start_flag == false)) { + blocking_notifier_call_chain(&dmu_pmu_notifier_chain, event, NULL); + start_flag = true; + dmu_pmu->used_flag = true; + } else if ((event == DMU_PMU_NOTICE_STOP) && (start_flag == true)) { + blocking_notifier_call_chain(&dmu_pmu_notifier_chain, event, NULL); + start_flag = false; + dmu_pmu->used_flag = false; + } +} +#endif + +static u64 pd2408_dmu_pmu_read_counter(struct pd2408_dmu_pmu *dmu_pmu, + struct hw_perf_event *hwc) +{ + u32 val32_l, val32_h, idx, counter_offset; + u64 val64; + + idx = GET_DMU_EVENTID(hwc); + counter_offset = dmu_counter_reg_offset[idx]; + + if (!EVENT_VALID(idx)) { + dev_err(dmu_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + val32_l = readl(dmu_pmu->base + counter_offset); + val32_h = readl(dmu_pmu->base + counter_offset + 4); + val64 = (u64)val32_h << 32 | (u64)val32_l; + + return val64; +} + +static void pd2408_dmu_pmu_clear_all_counters(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(BIT(0), dmu_pmu->base + DMU_PMU_CLEAR_EVENT); +} + +static void pd2408_dmu_pmu_disable_axi_cmd_events(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(0x0, dmu_pmu->base + DMU_PMU_AXI_MONITOR_EN); +} + +static void pd2408_dmu_pmu_mask_all_irq(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(DMU_PMU_TIMER_OPT_BIT, dmu_pmu->base + DMU_PMU_TIMER_INT_MASK); + writel(AXI_MONITOR_OPT_BIT, dmu_pmu->base + DMU_PMU_AXI_MONITOR_INT_MASK); +} + +static void pd2408_dmu_pmu_start_all_counters(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(BIT(0), dmu_pmu->base + DMU_PMU_TIMER_START); +} + +static void pd2408_dmu_pmu_stop_all_counters(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(BIT(0), dmu_pmu->base + DMU_PMU_TIMER_STOP); +} + +static void pd2408_dmu_pmu_reset_timer(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(0xFFFFFFFF, dmu_pmu->base + DMU_PMU_SET_TIMER_L); + writel(0xFFFFFFFF, dmu_pmu->base + DMU_PMU_SET_TIMER_H); +} + +static void pd2408_dmu_pmu_enable_events(struct pd2408_dmu_pmu *dmu_pmu, int idx) +{ + u8 en_bit; + u32 en_offset, irq_offset, val; + + if (idx == 0) { + en_bit = 0; + en_offset = 0; + irq_offset = DMU_PMU_TIMER_INT_MASK; + } else { + en_bit = (idx - 1) * 8; + en_offset = DMU_PMU_AXI_MONITOR_EN; + irq_offset = DMU_PMU_AXI_MONITOR_INT_MASK; + } + + if (en_offset) { + val = readl(dmu_pmu->base + en_offset); + val |= BIT(en_bit); + writel(val, dmu_pmu->base + en_offset); + } + + val = readl(dmu_pmu->base + irq_offset); + val &= ~BIT(en_bit); + writel(val, dmu_pmu->base + irq_offset); +} + +static int pd2408_dmu_pmu_mark_event(struct perf_event *event) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + unsigned long *used_mask = dmu_pmu->pmu_events.used_event_mask; + + int idx = GET_DMU_EVENTID(hwc); + + if (test_bit(idx, used_mask)) + return -EAGAIN; + + set_bit(idx, used_mask); + + return idx; +} + +static void pd2408_dmu_pmu_unmark_event(struct pd2408_dmu_pmu *dmu_pmu, + int idx) +{ + if (!EVENT_VALID(idx)) { + dev_err(dmu_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + clear_bit(idx, dmu_pmu->pmu_events.used_event_mask); +} + +int pd2408_dmu_pmu_event_init(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + struct pd2408_dmu_pmu *dmu_pmu; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) + return -EOPNOTSUPP; + + dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + + if (event->cpu < 0) { + dev_warn(dmu_pmu->dev, "Can't provide per-task data!\n"); + return -EINVAL; + } + + if (event->attr.config > DMU_PMU_MAX_COUNTERS) + return -EINVAL; + + if (dmu_pmu->on_cpu == -1) + return -EINVAL; + + hwc->idx = -1; + hwc->config_base = event->attr.config; + + event->cpu = dmu_pmu->on_cpu; + used_event = 0; + + return 0; +} + +void pd2408_dmu_pmu_event_update(struct perf_event *event) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u64 delta; + + pd2408_dmu_pmu_stop_all_counters(dmu_pmu); + delta = pd2408_dmu_pmu_read_counter(dmu_pmu, hwc); + local64_add(delta, &event->count); + +} + +void pd2408_dmu_pmu_event_start(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state = 0; + perf_event_update_userpage(event); +} + +void pd2408_dmu_pmu_event_stop(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state |= PERF_HES_STOPPED; + + if (flags & PERF_EF_UPDATE) + pd2408_dmu_pmu_event_update(event); +} + +int pd2408_dmu_pmu_event_add(struct perf_event *event, int flags) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx; + + hwc->state |= PERF_HES_STOPPED; + + idx = pd2408_dmu_pmu_mark_event(event); + if (!EVENT_VALID(idx)) { + dev_err(dmu_pmu->dev, "Unsupported event index:%d!\n", idx); + return idx; + } +#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) + pd2408_dmu_pmu_notifier_chain_trigger(dmu_pmu, DMU_PMU_NOTICE_START); +#endif + event->hw.idx = idx; + dmu_pmu->pmu_events.hw_events[idx] = event; + + pd2408_dmu_pmu_enable_events(dmu_pmu, idx); + used_event += 1; + return 0; +} + +void pd2408_dmu_pmu_event_del(struct perf_event *event, int flags) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + + used_event -= 1; + pd2408_dmu_pmu_event_stop(event, PERF_EF_UPDATE); + + pd2408_dmu_pmu_unmark_event(dmu_pmu, hwc->idx); + + perf_event_update_userpage(event); + dmu_pmu->pmu_events.hw_events[hwc->idx] = NULL; +#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) + if (used_event == 0) + pd2408_dmu_pmu_notifier_chain_trigger(dmu_pmu, DMU_PMU_NOTICE_STOP); +#endif + +} + +void pd2408_dmu_pmu_enable(struct pmu *pmu) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(pmu); + int event_added; + + event_added = bitmap_weight(dmu_pmu->pmu_events.used_event_mask, + DMU_PMU_MAX_COUNTERS); + + if (event_added) { + pd2408_dmu_pmu_stop_all_counters(dmu_pmu); + pd2408_dmu_pmu_clear_all_counters(dmu_pmu); + pd2408_dmu_pmu_reset_timer(dmu_pmu); + pd2408_dmu_pmu_start_all_counters(dmu_pmu); + } +} + +void pd2408_dmu_pmu_disable(struct pmu *pmu) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(pmu); + int event_added; + + event_added = bitmap_weight(dmu_pmu->pmu_events.used_event_mask, + DMU_PMU_MAX_COUNTERS); + if (event_added && dmu_pmu->used_flag) { + pd2408_dmu_pmu_mask_all_irq(dmu_pmu); + pd2408_dmu_pmu_disable_axi_cmd_events(dmu_pmu); + } +} + +static const struct acpi_device_id pd2408_dmu_pmu_acpi_match[] = { + { "PHYT0069", }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, pd2408_dmu_pmu_acpi_match); + +static irqreturn_t pd2408_dmu_pmu_overflow_handler(int irq, void *dev_id) +{ + struct pd2408_dmu_pmu *dmu_pmu = dev_id; + struct perf_event *event; + int idx; + unsigned long *used_mask = dmu_pmu->pmu_events.used_event_mask; + u32 timer_int_sta, axi_int_sta; + + timer_int_sta = readl(dmu_pmu->base + DMU_PMU_TIMER_INT_STA); + axi_int_sta = readl(dmu_pmu->base + DMU_PMU_AXI_MONITOR_INT_STA); + + if ((timer_int_sta + axi_int_sta) == 0) + return IRQ_NONE; + + if (timer_int_sta) + writel(0x1, dmu_pmu->base + DMU_PMU_TIMER_INT_CLEAR); + + if (axi_int_sta) + writel(axi_int_sta, dmu_pmu->base + DMU_PMU_AXI_MONITOR_INT_CLEAR); + + if (!dmu_pmu->used_flag) { + pd2408_dmu_pmu_mask_all_irq(dmu_pmu); + return IRQ_HANDLED; + } + + for_each_set_bit(idx, used_mask, DMU_PMU_MAX_COUNTERS) { + event = dmu_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + pd2408_dmu_pmu_event_update(event); + } + writel(ALL_EVENT_CLEAR_BIT, dmu_pmu->base + DMU_PMU_CLEAR_EVENT); + pd2408_dmu_pmu_start_all_counters(dmu_pmu); + + return IRQ_HANDLED; +} + +static int pd2408_dmu_pmu_init_irq(struct pd2408_dmu_pmu *dmu_pmu, + struct platform_device *pdev) +{ + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, + pd2408_dmu_pmu_overflow_handler, + IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_SHARED, + dev_name(&pdev->dev), dmu_pmu); + if (ret < 0) { + dev_err(&pdev->dev, "Fail to request IRQ:%d ret:%d\n", irq, + ret); + return ret; + } + + dmu_pmu->irq = irq; + + return 0; +} + +static int pd2408_dmu_pmu_init_data(struct platform_device *pdev, + struct pd2408_dmu_pmu *dmu_pmu) +{ + struct resource *res; + + if (device_property_read_u32(&pdev->dev, "phytium,ddr-id", + &dmu_pmu->dmu_id)) { + dev_err(&pdev->dev, "Can not read phytium,ddr-id!\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmu_pmu->base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(dmu_pmu->base)) { + dev_err(&pdev->dev, + "ioremap failed for dmu_pmu base resource\n"); + return PTR_ERR(dmu_pmu->base); + } + + dmu_pmu->used_flag = true; + pd2408_dmu_pmu_mask_all_irq(dmu_pmu); + + return 0; +} + +static int pd2408_dmu_pmu_dev_probe(struct platform_device *pdev, + struct pd2408_dmu_pmu *dmu_pmu) +{ + int ret; + + ret = pd2408_dmu_pmu_init_data(pdev, dmu_pmu); + if (ret) + return ret; + + ret = pd2408_dmu_pmu_init_irq(dmu_pmu, pdev); + if (ret) + return ret; + + dmu_pmu->dev = &pdev->dev; + dmu_pmu->on_cpu = -1; + + return 0; +} + +static int pd2408_dmu_pmu_probe(struct platform_device *pdev) +{ + struct pd2408_dmu_pmu *dmu_pmu; + char *name; + int ret; + + dmu_pmu = devm_kzalloc(&pdev->dev, sizeof(*dmu_pmu), GFP_KERNEL); + if (!dmu_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, dmu_pmu); + + ret = pd2408_dmu_pmu_dev_probe(pdev, dmu_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance(pd2408_dmu_pmu_hp_state, + &dmu_pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); + return ret; + } + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt_dmu%u_pmu", dmu_pmu->dmu_id); + dmu_pmu->pmu = (struct pmu) { + .name = name, + .module = THIS_MODULE, + .task_ctx_nr = perf_invalid_context, + .event_init = pd2408_dmu_pmu_event_init, + .pmu_enable = pd2408_dmu_pmu_enable, + .pmu_disable = pd2408_dmu_pmu_disable, + .add = pd2408_dmu_pmu_event_add, + .del = pd2408_dmu_pmu_event_del, + .start = pd2408_dmu_pmu_event_start, + .stop = pd2408_dmu_pmu_event_stop, + .read = pd2408_dmu_pmu_event_update, + .attr_groups = pd2408_dmu_pmu_attr_groups, + }; + + ret = perf_pmu_register(&dmu_pmu->pmu, name, -1); + if (ret) { + dev_err(dmu_pmu->dev, "DMU PMU register failed!\n"); + cpuhp_state_remove_instance_nocalls(pd2408_dmu_pmu_hp_state, + &dmu_pmu->node); + } + + pr_info("die_dmu%d_pmu on cpu%d.\n", dmu_pmu->dmu_id, dmu_pmu->on_cpu); + + return ret; +} + +static int pd2408_dmu_pmu_remove(struct platform_device *pdev) +{ + struct pd2408_dmu_pmu *dmu_pmu = platform_get_drvdata(pdev); + + pd2408_dmu_pmu_mask_all_irq(dmu_pmu); + perf_pmu_unregister(&dmu_pmu->pmu); + cpuhp_state_remove_instance_nocalls(pd2408_dmu_pmu_hp_state, + &dmu_pmu->node); + + return 0; +} + +static struct platform_driver pd2408_dmu_pmu_driver = { + .driver = { + .name = "pd2408_dmu_pmu", + .acpi_match_table = ACPI_PTR(pd2408_dmu_pmu_acpi_match), + .suppress_bind_attrs = true, + }, + .probe = pd2408_dmu_pmu_probe, + .remove = pd2408_dmu_pmu_remove, +}; + +int pd2408_dmu_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct pd2408_dmu_pmu *dmu_pmu = + hlist_entry_safe(node, struct pd2408_dmu_pmu, node); + + if (dmu_pmu->on_cpu != -1) { + if (!cpumask_test_cpu(dmu_pmu->on_cpu, cpu_online_mask)) { + perf_pmu_migrate_context(&dmu_pmu->pmu, dmu_pmu->on_cpu, cpu); + dmu_pmu->on_cpu = cpu; + WARN_ON(irq_set_affinity_hint(dmu_pmu->irq, cpumask_of(cpu))); + } + return 0; + } + + dmu_pmu->on_cpu = cpu; + WARN_ON(irq_set_affinity_hint(dmu_pmu->irq, cpumask_of(cpu))); + + return 0; +} + +int pd2408_dmu_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct pd2408_dmu_pmu *dmu_pmu = + hlist_entry_safe(node, struct pd2408_dmu_pmu, node); + unsigned int target; + + if (dmu_pmu->on_cpu != cpu) + return 0; + + target = cpumask_last(cpu_online_mask); + + if (target >= nr_cpu_ids) { + dev_err(dmu_pmu->dev, "offline cpu%d with no target to migrate.\n", + cpu); + return 0; + } + + perf_pmu_migrate_context(&dmu_pmu->pmu, cpu, target); + WARN_ON(irq_set_affinity_hint(dmu_pmu->irq, cpumask_of(target))); + dmu_pmu->on_cpu = target; + + return 0; +} + +static int __init pd2408_dmu_pmu_module_init(void) +{ + int ret; + + pd2408_dmu_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/phytium/dmupmu:online", + pd2408_dmu_pmu_online_cpu, pd2408_dmu_pmu_offline_cpu); + if (pd2408_dmu_pmu_hp_state < 0) { + pr_err("DMU PMU: setup hotplug, pd2408_dmu_pmu_hp_state = %d\n", + pd2408_dmu_pmu_hp_state); + return pd2408_dmu_pmu_hp_state; + } + + ret = platform_driver_register(&pd2408_dmu_pmu_driver); + if (ret) + cpuhp_remove_multi_state( + pd2408_dmu_pmu_hp_state); + + return ret; +} +module_init(pd2408_dmu_pmu_module_init); + +static void __exit pd2408_dmu_pmu_module_exit(void) +{ + platform_driver_unregister(&pd2408_dmu_pmu_driver); + cpuhp_remove_multi_state(pd2408_dmu_pmu_hp_state); +} +module_exit(pd2408_dmu_pmu_module_exit); + +MODULE_DESCRIPTION("Phytium DMU PMU driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DMU_PERF_DRIVER_VERSION); +MODULE_AUTHOR("Hu Xianghua "); +MODULE_AUTHOR("Tan Rui "); -- Gitee From 222e853ed15ce0dbb19671bca2fe2987ec3a60d2 Mon Sep 17 00:00:00 2001 From: Tan Rui Date: Wed, 23 Apr 2025 15:06:32 +0800 Subject: [PATCH 139/154] drivers/perf: phytium: Modify the name of PMU cycles event Add a prefix before the name of the cycles event. Mainline: Open-Source Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng Change-Id: I504e54e99f8345c0f5d1d6606ad3ca6e9922e46b --- drivers/perf/phytium/phytium_ddr_pmu.c | 4 ++-- drivers/perf/phytium/phytium_dmu_pmu_pd2408.c | 4 ++-- drivers/perf/phytium/phytium_pcie_pmu.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 4c3475ddf9..2a28aa0ae2 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -32,7 +32,7 @@ #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt #define PHYTIUM_DDR_MAX_COUNTERS 8 -#define DDR_PERF_DRIVER_VERSION "1.3.0" +#define DDR_PERF_DRIVER_VERSION "1.3.1" #define DDR_START_TIMER 0x000 #define DDR_STOP_TIMER 0x004 @@ -153,7 +153,7 @@ static const struct attribute_group phytium_ddr_pmu_format_group = { }; static struct attribute *phytium_ddr_pmu_events_attr[] = { - PHYTIUM_DDR_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_DDR_PMU_EVENT_ATTR(ddr_cycles, 0x00), PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq, 0x01), PHYTIUM_DDR_PMU_EVENT_ATTR(rxdat, 0x02), PHYTIUM_DDR_PMU_EVENT_ATTR(txdat, 0x03), diff --git a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c index d07b0a8869..8c68a0fd96 100644 --- a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c +++ b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c @@ -31,7 +31,7 @@ #undef pr_fmt #define pr_fmt(fmt) "pd2408_dmu_pmu: " fmt -#define DMU_PERF_DRIVER_VERSION "1.0.0" +#define DMU_PERF_DRIVER_VERSION "1.0.1" #define DMU_PMU_TIMER_START 0x0 #define DMU_PMU_TIMER_STOP 0x4 @@ -151,7 +151,7 @@ static const struct attribute_group pd2408_dmu_pmu_format_group = { }; static struct attribute *pd2408_dmu_pmu_events_attr[] = { - PHYTIUM_DMU_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_DMU_PMU_EVENT_ATTR(dmu_axi_cycles, 0x00), PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_flux, 0x01), PHYTIUM_DMU_PMU_EVENT_ATTR(axi_read_flux, 0x02), PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_cmd, 0x03), diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index 7f3294d168..8f67636b0b 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -31,7 +31,7 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_pcie_pmu: " fmt -#define PCIE_PERF_DRIVER_VERSION "1.3.0" +#define PCIE_PERF_DRIVER_VERSION "1.3.1" #define PHYTIUM_PCIE_MAX_COUNTERS 18 @@ -175,7 +175,7 @@ static const struct attribute_group phytium_pcie_pmu_format_group = { }; static struct attribute *phytium_pcie_pmu_events_attr[] = { - PHYTIUM_PCIE_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_PCIE_PMU_EVENT_ATTR(pcie_cycles, 0x00), PHYTIUM_PCIE_PMU_EVENT_ATTR(aw, 0x01), PHYTIUM_PCIE_PMU_EVENT_ATTR(w_last, 0x02), PHYTIUM_PCIE_PMU_EVENT_ATTR(b, 0x03), -- Gitee From 7c42268e51a380b11344650d75a7abafe31c094e Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Thu, 16 Nov 2023 08:56:09 +0800 Subject: [PATCH 140/154] PM: hibernate: Enforce ordering during image compression/decompression An S4 (suspend to disk) test on the LoongArch 3A6000 platform sometimes fails with the following error messaged in the dmesg log: Invalid LZO compressed length That happens because when compressing/decompressing the image, the synchronization between the control thread and the compress/decompress/crc thread is based on a relaxed ordering interface, which is unreliable, and the following situation may occur: CPU 0 CPU 1 save_image_lzo lzo_compress_threadfn atomic_set(&d->stop, 1); atomic_read(&data[thr].stop) data[thr].cmp = data[thr].cmp_len; WRITE data[thr].cmp_len Then CPU0 gets a stale cmp_len and writes it to disk. During resume from S4, wrong cmp_len is loaded. To maintain data consistency between the two threads, use the acquire/release variants of atomic set and read operations. Fixes: 081a9d043c98 ("PM / Hibernate: Improve performance of LZO/plain hibernation, checksum image") Cc: All applicable Signed-off-by: Hongchen Zhang Co-developed-by: Weihao Li Signed-off-by: Weihao Li [ rjw: Subject rewrite and changelog edits ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman Signed-off-by: Huang Shaobo Change-Id: I828b5ed873841521c7356f95e095acb2b70d1408 --- kernel/power/swap.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 25e7cb96bb..b288aba804 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -603,11 +603,11 @@ static int crc32_threadfn(void *data) unsigned i; while (1) { - wait_event(d->go, atomic_read(&d->ready) || + wait_event(d->go, atomic_read_acquire(&d->ready) || kthread_should_stop()); if (kthread_should_stop()) { d->thr = NULL; - atomic_set(&d->stop, 1); + atomic_set_release(&d->stop, 1); wake_up(&d->done); break; } @@ -616,7 +616,7 @@ static int crc32_threadfn(void *data) for (i = 0; i < d->run_threads; i++) *d->crc32 = crc32_le(*d->crc32, d->unc[i], *d->unc_len[i]); - atomic_set(&d->stop, 1); + atomic_set_release(&d->stop, 1); wake_up(&d->done); } return 0; @@ -646,12 +646,12 @@ static int lzo_compress_threadfn(void *data) struct cmp_data *d = data; while (1) { - wait_event(d->go, atomic_read(&d->ready) || + wait_event(d->go, atomic_read_acquire(&d->ready) || kthread_should_stop()); if (kthread_should_stop()) { d->thr = NULL; d->ret = -1; - atomic_set(&d->stop, 1); + atomic_set_release(&d->stop, 1); wake_up(&d->done); break; } @@ -660,7 +660,7 @@ static int lzo_compress_threadfn(void *data) d->ret = lzo1x_1_compress(d->unc, d->unc_len, d->cmp + LZO_HEADER, &d->cmp_len, d->wrk); - atomic_set(&d->stop, 1); + atomic_set_release(&d->stop, 1); wake_up(&d->done); } return 0; @@ -798,7 +798,7 @@ static int save_image_lzo(struct swap_map_handle *handle, data[thr].unc_len = off; - atomic_set(&data[thr].ready, 1); + atomic_set_release(&data[thr].ready, 1); wake_up(&data[thr].go); } @@ -806,12 +806,12 @@ static int save_image_lzo(struct swap_map_handle *handle, break; crc->run_threads = thr; - atomic_set(&crc->ready, 1); + atomic_set_release(&crc->ready, 1); wake_up(&crc->go); for (run_threads = thr, thr = 0; thr < run_threads; thr++) { wait_event(data[thr].done, - atomic_read(&data[thr].stop)); + atomic_read_acquire(&data[thr].stop)); atomic_set(&data[thr].stop, 0); ret = data[thr].ret; @@ -850,7 +850,7 @@ static int save_image_lzo(struct swap_map_handle *handle, } } - wait_event(crc->done, atomic_read(&crc->stop)); + wait_event(crc->done, atomic_read_acquire(&crc->stop)); atomic_set(&crc->stop, 0); } @@ -1132,12 +1132,12 @@ static int lzo_decompress_threadfn(void *data) struct dec_data *d = data; while (1) { - wait_event(d->go, atomic_read(&d->ready) || + wait_event(d->go, atomic_read_acquire(&d->ready) || kthread_should_stop()); if (kthread_should_stop()) { d->thr = NULL; d->ret = -1; - atomic_set(&d->stop, 1); + atomic_set_release(&d->stop, 1); wake_up(&d->done); break; } @@ -1150,7 +1150,7 @@ static int lzo_decompress_threadfn(void *data) flush_icache_range((unsigned long)d->unc, (unsigned long)d->unc + d->unc_len); - atomic_set(&d->stop, 1); + atomic_set_release(&d->stop, 1); wake_up(&d->done); } return 0; @@ -1338,7 +1338,7 @@ static int load_image_lzo(struct swap_map_handle *handle, } if (crc->run_threads) { - wait_event(crc->done, atomic_read(&crc->stop)); + wait_event(crc->done, atomic_read_acquire(&crc->stop)); atomic_set(&crc->stop, 0); crc->run_threads = 0; } @@ -1374,7 +1374,7 @@ static int load_image_lzo(struct swap_map_handle *handle, pg = 0; } - atomic_set(&data[thr].ready, 1); + atomic_set_release(&data[thr].ready, 1); wake_up(&data[thr].go); } @@ -1393,7 +1393,7 @@ static int load_image_lzo(struct swap_map_handle *handle, for (run_threads = thr, thr = 0; thr < run_threads; thr++) { wait_event(data[thr].done, - atomic_read(&data[thr].stop)); + atomic_read_acquire(&data[thr].stop)); atomic_set(&data[thr].stop, 0); ret = data[thr].ret; @@ -1424,7 +1424,7 @@ static int load_image_lzo(struct swap_map_handle *handle, ret = snapshot_write_next(snapshot); if (ret <= 0) { crc->run_threads = thr + 1; - atomic_set(&crc->ready, 1); + atomic_set_release(&crc->ready, 1); wake_up(&crc->go); goto out_finish; } @@ -1432,13 +1432,13 @@ static int load_image_lzo(struct swap_map_handle *handle, } crc->run_threads = thr; - atomic_set(&crc->ready, 1); + atomic_set_release(&crc->ready, 1); wake_up(&crc->go); } out_finish: if (crc->run_threads) { - wait_event(crc->done, atomic_read(&crc->stop)); + wait_event(crc->done, atomic_read_acquire(&crc->stop)); atomic_set(&crc->stop, 0); } stop = ktime_get(); -- Gitee From 56065b869a9686b536d0605886cf53617a3f0e66 Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Thu, 24 Apr 2025 14:01:50 +0800 Subject: [PATCH 141/154] i2s-v2: phytium: Avoid using mutex in i2s interrupt In interrupt context, funcitons that may cause blocking are not allowed. However, in the i2s interrupt handler, the function snd_pcm_period_elapsed attempted to use a mutex due to nonatomic, which will result in a call trace error. To resolve this, another thread is used to execute the function. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: I0828ca2278e8fbaf927e1e5097500c3d90a15735 --- sound/soc/phytium/phytium-i2s-v2.c | 24 +++++++++++++++++++++--- sound/soc/phytium/phytium-i2s-v2.h | 2 ++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c index 888f6937a2..fd3eaa01e3 100644 --- a/sound/soc/phytium/phytium-i2s-v2.c +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -30,7 +30,7 @@ #include #include "phytium-i2s-v2.h" -#define PHYT_I2S_V2_VERSION "1.0.0" +#define PHYT_I2S_V2_VERSION "1.0.1" static struct snd_soc_jack hs_jack; @@ -791,6 +791,22 @@ static irqreturn_t phyt_i2s_gpio_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static void phyt_i2s_playback_elapsed_work(struct work_struct *work) +{ + struct phytium_i2s *priv = container_of(work, struct phytium_i2s, + i2s_playback_elapsed_work.work); + + snd_pcm_period_elapsed(priv->substream_playback); +} + +static void phyt_i2s_capture_elapsed_work(struct work_struct *work) +{ + struct phytium_i2s *priv = container_of(work, struct phytium_i2s, + i2s_capture_elapsed_work.work); + + snd_pcm_period_elapsed(priv->substream_capture); +} + static irqreturn_t phyt_i2s_interrupt(int irq, void *dev_id) { struct phytium_i2s *priv = dev_id; @@ -801,13 +817,13 @@ static irqreturn_t phyt_i2s_interrupt(int irq, void *dev_id) status = readl(priv->dma_reg_base + PHYTIUM_DMA_STS); if (status & DMA_TX_DONE) { - snd_pcm_period_elapsed(priv->substream_playback); + queue_delayed_work(system_wq, &priv->i2s_playback_elapsed_work, 0); writel(DMA_TX_DONE, priv->dma_reg_base + PHYTIUM_DMA_STS); ret = IRQ_HANDLED; } if (status & DMA_RX_DONE) { - snd_pcm_period_elapsed(priv->substream_capture); + queue_delayed_work(system_wq, &priv->i2s_capture_elapsed_work, 0); writel(DMA_RX_DONE, priv->dma_reg_base + PHYTIUM_DMA_STS); ret = IRQ_HANDLED; } @@ -1151,6 +1167,8 @@ static int phyt_i2s_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&priv->i2s_playback_stop_work, i2s_interrupt_playback_stop_work); INIT_DELAYED_WORK(&priv->i2s_capture_stop_work, i2s_interrupt_capture_stop_work); + INIT_DELAYED_WORK(&priv->i2s_playback_elapsed_work, phyt_i2s_playback_elapsed_work); + INIT_DELAYED_WORK(&priv->i2s_capture_elapsed_work, phyt_i2s_capture_elapsed_work); mutex_init(&priv->sharemem_mutex); if (sysfs_create_group(&priv->dev->kobj, &phyt_i2s_device_group)) diff --git a/sound/soc/phytium/phytium-i2s-v2.h b/sound/soc/phytium/phytium-i2s-v2.h index ba86ba97a5..ea72a56d5f 100644 --- a/sound/soc/phytium/phytium-i2s-v2.h +++ b/sound/soc/phytium/phytium-i2s-v2.h @@ -34,6 +34,8 @@ struct phytium_i2s { struct phytium_pcm_config pcm_config[2]; struct delayed_work i2s_playback_stop_work; struct delayed_work i2s_capture_stop_work; + struct delayed_work i2s_playback_elapsed_work; + struct delayed_work i2s_capture_elapsed_work; struct delayed_work phyt_i2s_gpio_work; struct phyti2s_cmd *msg; int interrupt; -- Gitee From 6205967233a87b30c05065e58c2bfea1eb362dac Mon Sep 17 00:00:00 2001 From: Zhou Zheng Date: Thu, 24 Apr 2025 14:03:51 +0800 Subject: [PATCH 142/154] i2s-v2: phytium: Slove freezing issue when reloading driver Reloading driver after headphone plugging or unpluging, the interrupt is triggered before initializing the delayed work, which will lead to the func attribute of timer in the delayed work to be NULL. To solve it, swap the order of interrupt registration and delayed work initialization and enable gpio interrupt after initializing gpio jack. Mainline: NA Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng Change-Id: I48070578430f8e3bb52025c7707f18236f12dd1f --- sound/soc/phytium/phytium-i2s-v2.c | 59 +++++++++++++++--------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c index fd3eaa01e3..1e0a335f03 100644 --- a/sound/soc/phytium/phytium-i2s-v2.c +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -30,7 +30,7 @@ #include #include "phytium-i2s-v2.h" -#define PHYT_I2S_V2_VERSION "1.0.1" +#define PHYT_I2S_V2_VERSION "1.0.2" static struct snd_soc_jack hs_jack; @@ -117,25 +117,6 @@ static void phyt_pcm_free(struct snd_soc_component *component, snd_pcm_lib_preallocate_free_for_all(pcm); } -static int phyt_pcm_component_probe(struct snd_soc_component *component) -{ - struct phytium_i2s *priv = snd_soc_component_get_drvdata(component); - struct snd_soc_card *card = component->card; - int ret; - - if (priv->insert < 0) - return 0; - - ret = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET, - &hs_jack, hs_jack_pins, - ARRAY_SIZE(hs_jack_pins)); - if (ret < 0) { - dev_err(component->dev, "Cannot create jack\n"); - return ret; - } - return 0; -} - static int phyt_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -530,6 +511,30 @@ static int phyt_pcm_resume(struct snd_soc_component *component) return ret; } +static int phyt_pcm_component_probe(struct snd_soc_component *component) +{ + struct phytium_i2s *priv = snd_soc_component_get_drvdata(component); + struct snd_soc_card *card = component->card; + int ret; + + if (priv->insert < 0) + return 0; + + ret = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET, + &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); + if (ret < 0) { + dev_err(component->dev, "Cannot create jack\n"); + return ret; + } + ret = phyt_i2s_enable_gpio(priv); + if (ret < 0) { + dev_err(component->dev, "failed to enable gpio\n"); + return ret; + } + return 0; +} + static const struct snd_soc_component_driver phytium_i2s_component = { .name = "phytium-i2s", .pcm_construct = phyt_pcm_new, @@ -756,10 +761,12 @@ static void phyt_i2s_gpio_jack_work(struct work_struct *work) int ret = 0; if (priv->insert == 1) { - snd_soc_jack_report(&hs_jack, HEADPHONE_DISABLE, SND_JACK_HEADSET); + if (hs_jack.jack) + snd_soc_jack_report(&hs_jack, HEADPHONE_DISABLE, SND_JACK_HEADSET); priv->insert = 0; } else { - snd_soc_jack_report(&hs_jack, HEADPHONE_ENABLE, SND_JACK_HEADSET); + if (hs_jack.jack) + snd_soc_jack_report(&hs_jack, HEADPHONE_ENABLE, SND_JACK_HEADSET); priv->insert = 1; } @@ -1119,6 +1126,7 @@ static int phyt_i2s_probe(struct platform_device *pdev) gpio_irq = platform_get_irq_optional(pdev, 1); priv->insert = -1; if (gpio_irq > 0) { + INIT_DELAYED_WORK(&priv->phyt_i2s_gpio_work, phyt_i2s_gpio_jack_work); phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_GPIO_PORTA_EOI, BIT(0)); ret = phyt_i2s_disable_gpioint(priv); if (ret < 0) { @@ -1132,12 +1140,6 @@ static int phyt_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to request gpio irq\n"); goto failed_request_irq; } - ret = phyt_i2s_enable_gpio(priv); - if (ret < 0) { - dev_err(&pdev->dev, "failed to enable gpio\n"); - goto failed_enable_gpio; - } - INIT_DELAYED_WORK(&priv->phyt_i2s_gpio_work, phyt_i2s_gpio_jack_work); } if (pdev->dev.of_node) { @@ -1185,7 +1187,6 @@ static int phyt_i2s_probe(struct platform_device *pdev) failed_get_dai_name: failed_request_irq: failed_disable_gpioint: -failed_enable_gpio: failed_alloc_log_mem: failed_ioremap_res2: failed_ioremap_res1: -- Gitee From 88d34ce27680d064fa7e7e0018d8f8513256d56e Mon Sep 17 00:00:00 2001 From: Huangjie Date: Tue, 29 Apr 2025 09:40:30 +0800 Subject: [PATCH 143/154] arm64: phytium_defconfig: select new phytium driver Signed-off-by: Huangjie --- arch/arm64/configs/phytium_defconfig | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm64/configs/phytium_defconfig b/arch/arm64/configs/phytium_defconfig index 68335764bb..8fd7c4bb1c 100644 --- a/arch/arm64/configs/phytium_defconfig +++ b/arch/arm64/configs/phytium_defconfig @@ -360,7 +360,8 @@ CONFIG_SERIAL_8250_DW=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -CONFIG_SERIAL_PHYTIUM_PCI=m +CONFIG_SERIAL_PHYTIUM_PCI=y +CONFIG_SERIAL_PHYTIUM_UART_V2=y CONFIG_SERIAL_XILINX_PS_UART=y CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y CONFIG_SERIAL_FSL_LPUART=y @@ -384,6 +385,7 @@ CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_I2C_GPIO=m CONFIG_I2C_PHYTIUM_PCI=y CONFIG_I2C_PHYTIUM_PLATFORM=y +CONFIG_I2C_V2_PLATFORM=y CONFIG_I3C=y CONFIG_PHYTIUM_I3C_MASTER=m CONFIG_SPI=y @@ -391,6 +393,7 @@ CONFIG_SPI_BITBANG=m CONFIG_SPI_PHYTIUM_PLAT=y CONFIG_SPI_PHYTIUM_PCI=y CONFIG_SPI_PHYTIUM_QSPI=y +CONFIG_SPI_PHYTIUM_PLAT_V2=m CONFIG_SPI_SPIDEV=y CONFIG_SPMI=y CONFIG_PINCTRL=y @@ -527,6 +530,8 @@ CONFIG_SND_SOC_FSL_SAI=m CONFIG_SND_SOC_PHYTIUM_I2S=y CONFIG_SND_PMDK_ES8388=y CONFIG_SND_PMDK_ES8336=y +CONFIG_SND_SOC_PHYTIUM_I2S_V2=y +CONFIG_SND_SOC_PHYTIUM_MACHINE_V2=y CONFIG_SND_PMDK_DP=y CONFIG_SND_SOC_AK4613=m CONFIG_SND_SOC_CROS_EC_CODEC=m @@ -536,6 +541,7 @@ CONFIG_SND_SOC_ES7241=m CONFIG_SND_SOC_MAX98357A=m CONFIG_SND_SOC_MAX98927=m CONFIG_SND_SOC_PCM3168A_I2C=m +CONFIG_SND_SOC_PHYTIUM_CODEC_V2=y CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m CONFIG_SND_SOC_SPDIF=m CONFIG_SND_SOC_TAS571X=m @@ -687,6 +693,8 @@ CONFIG_SOC_BRCMSTB=y CONFIG_SOC_TI=y CONFIG_DEVFREQ_GOV_PERFORMANCE=y CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_ARM_PHYTIUM_NOC_DEVFREQ=y +CONFIG_ARM_PHYTIUM_DMU_DEVFREQ=y CONFIG_EXTCON_PTN5150=m CONFIG_EXTCON_USB_GPIO=y CONFIG_EXTCON_USBC_CROS_EC=y -- Gitee From eb5a779364840e888fbc7a404799d524165e7997 Mon Sep 17 00:00:00 2001 From: Huangjie Date: Tue, 29 Apr 2025 10:29:18 +0800 Subject: [PATCH 144/154] Revert "drivers: misc: remove phytium lpc snoop driver" This reverts commit f791bd2e6a5960ef8eb632c94f03086c0d88f949. --- drivers/misc/Kconfig | 8 + drivers/misc/Makefile | 1 + drivers/misc/phytium-lpc-snoop.c | 326 +++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100755 drivers/misc/phytium-lpc-snoop.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index fafa8b0d80..750db66efe 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -112,6 +112,14 @@ config PHANTOM If you choose to build module, its name will be phantom. If unsure, say N here. +config PHYTIUM_LPC_SNOOP + tristate "Phytium HOST LPC snoop support" + depends on ARCH_PHYTIUM && REGMAP && MFD_SYSCON + help + Provides a driver to control the LPC snoop interface which + allows the BMC to listen on and save the data written by + the host to an arbitrary LPC I/O port. + config INTEL_MID_PTI tristate "Parallel Trace Interface for MIPI P1149.7 cJTAG standard" depends on PCI && TTY && (X86_INTEL_MID || COMPILE_TEST) diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d23231e733..4a240ad6cb 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_LKDTM) += lkdtm/ obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o +obj-$(CONFIG_PHYTIUM_LPC_SNOOP) += phytium-lpc-snoop.o obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o diff --git a/drivers/misc/phytium-lpc-snoop.c b/drivers/misc/phytium-lpc-snoop.c new file mode 100755 index 0000000000..fc1369943a --- /dev/null +++ b/drivers/misc/phytium-lpc-snoop.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2023, Phytium Phytium Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Provides a simple driver to control the PHYTIUM LPC snoop interface which + * allows the BMC to listen on and save the data written by + * the host to an arbitrary LPC I/O port. + * + * Typically used by the BMC to "watch" host boot progress via port + * 0x80 writes made by the BIOS during the boot process. + * + * Derived from drivers/misc/aspeed-lpc-snoop.c + * Copyright 2017 Google Inc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "phytium-lpc-snoop" + +#define NUM_SNOOP_CHANNELS 2 +#define SNOOP_FIFO_SIZE 2048 + +#define snp_enable_reg 0x150 +#define snp_enable_reg_snp1_en BIT(0) +#define snp_enable_reg_snp1_int_en BIT(1) +#define snp_enable_reg_snp2_en BIT(2) +#define snp_enable_reg_snp2_int_en BIT(3) + +#define snp_status_reg 0x154 +#define snp_status_reg_snp1_int BIT(0) +#define snp_status_reg_snp2_int BIT(1) + +#define snp_addr_reg 0x158 +#define snp_addr_reg_snp1_addr GENMASK(15, 0) +#define snp_addr_reg_snp1_shift 0 +#define snp_addr_reg_snp2_addr GENMASK(31, 16) +#define snp_addr_reg_snp2_shift 16 + +#define snp_data_reg 0x15c +#define snp_data_reg_snp1_data_reg GENMASK(7, 0) +#define snp_data_reg_snp1_shift 0 +#define snp_data_reg_snp2_data_reg GENMASK(15, 8) +#define snp_data_reg_snp2_shift 8 + +struct phytium_lpc_snoop_channel { + struct kfifo fifo; + wait_queue_head_t wq; + struct miscdevice miscdev; +}; + +struct phytium_lpc_snoop { + struct regmap *regmap; + int irq; + struct phytium_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; +}; + +static struct phytium_lpc_snoop_channel *snoop_file_to_chan(struct file *file) +{ + return container_of(file->private_data, + struct phytium_lpc_snoop_channel, + miscdev); +} + +static ssize_t snoop_file_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct phytium_lpc_snoop_channel *chan = snoop_file_to_chan(file); + unsigned int copied; + int ret = 0; + + if (kfifo_is_empty(&chan->fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(chan->wq, + !kfifo_is_empty(&chan->fifo)); + if (ret == -ERESTARTSYS) + return -EINTR; + } + ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); + + return ret ? ret : copied; +} + +static unsigned int snoop_file_poll(struct file *file, + struct poll_table_struct *pt) +{ + struct phytium_lpc_snoop_channel *chan = snoop_file_to_chan(file); + + poll_wait(file, &chan->wq, pt); + return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0; +} + +static const struct file_operations snoop_fops = { + .owner = THIS_MODULE, + .read = snoop_file_read, + .poll = snoop_file_poll, + .llseek = noop_llseek, +}; + +/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ +static void put_fifo_with_discard(struct phytium_lpc_snoop_channel *chan, u8 val) +{ + if (!kfifo_initialized(&chan->fifo)) + return; + if (kfifo_is_full(&chan->fifo)) + kfifo_skip(&chan->fifo); + kfifo_put(&chan->fifo, val); + wake_up_interruptible(&chan->wq); +} + +static irqreturn_t phytium_lpc_snoop_irq(int irq, void *arg) +{ + struct phytium_lpc_snoop *lpc_snoop = arg; + u32 reg, data; + + if (regmap_read(lpc_snoop->regmap, snp_status_reg, ®)) + return IRQ_NONE; + + /* Check if one of the snoop channels is interrupting */ + reg &= (snp_status_reg_snp1_int | snp_status_reg_snp2_int); + if (!reg) + return IRQ_NONE; + + /* Ack pending IRQs */ + regmap_write(lpc_snoop->regmap, snp_status_reg, reg); + + /* Read and save most recent snoop'ed data byte to FIFO */ + regmap_read(lpc_snoop->regmap, snp_data_reg, &data); + + if (reg & snp_status_reg_snp1_int) { + u8 val = (data & snp_data_reg_snp1_data_reg) >> snp_data_reg_snp1_shift; + + put_fifo_with_discard(&lpc_snoop->chan[0], val); + } + if (reg & snp_status_reg_snp2_int) { + u8 val = (data & snp_data_reg_snp2_data_reg) >> snp_data_reg_snp2_shift; + + put_fifo_with_discard(&lpc_snoop->chan[1], val); + } + + return IRQ_HANDLED; +} + +static int phytium_lpc_snoop_config_irq(struct phytium_lpc_snoop *lpc_snoop, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + lpc_snoop->irq = platform_get_irq(pdev, 0); + if (!lpc_snoop->irq) + return -ENODEV; + + rc = devm_request_irq(dev, lpc_snoop->irq, + phytium_lpc_snoop_irq, IRQF_SHARED, + DEVICE_NAME, lpc_snoop); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", lpc_snoop->irq); + lpc_snoop->irq = 0; + return rc; + } + + return 0; +} + +static int phytium_lpc_enable_snoop(struct phytium_lpc_snoop *lpc_snoop, + struct device *dev, + int channel, u16 lpc_port) +{ + int rc = 0; + u32 snp_enable_reg_en, snp_addr_reg_mask, snp_addr_reg_shift; + init_waitqueue_head(&lpc_snoop->chan[channel].wq); + /* Create FIFO datastructure */ + rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo, + SNOOP_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + + lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; + lpc_snoop->chan[channel].miscdev.name = + devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); + lpc_snoop->chan[channel].miscdev.fops = &snoop_fops; + lpc_snoop->chan[channel].miscdev.parent = dev; + rc = misc_register(&lpc_snoop->chan[channel].miscdev); + if (rc) + return rc; + + /* Enable LPC snoop channel at requested port */ + switch (channel) { + case 0: + snp_enable_reg_en = snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en; + snp_addr_reg_mask = snp_addr_reg_snp1_addr; + snp_addr_reg_shift = snp_addr_reg_snp1_shift; + break; + case 1: + snp_enable_reg_en = snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en; + snp_addr_reg_mask = snp_addr_reg_snp2_addr; + snp_addr_reg_shift = snp_addr_reg_snp2_shift; + break; + default: + return -EINVAL; + } + + regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, snp_enable_reg_en, snp_enable_reg_en); + regmap_update_bits(lpc_snoop->regmap, snp_addr_reg, snp_addr_reg_mask, + lpc_port << snp_addr_reg_shift); + return rc; +} + +static void phytium_lpc_disable_snoop(struct phytium_lpc_snoop *lpc_snoop, + int channel) +{ + switch (channel) { + case 0: + regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, + snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en, + 0); + break; + case 1: + regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, + snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en, + 0); + break; + default: + return; + } + + kfifo_free(&lpc_snoop->chan[channel].fifo); + misc_deregister(&lpc_snoop->chan[channel].miscdev); +} + +static int phytium_lpc_snoop_probe(struct platform_device *pdev) +{ + struct phytium_lpc_snoop *lpc_snoop; + struct device *dev; + u32 port; + int rc; + + dev = &pdev->dev; + + lpc_snoop = devm_kzalloc(dev, sizeof(*lpc_snoop), GFP_KERNEL); + if (!lpc_snoop) + return -ENOMEM; + + lpc_snoop->regmap = syscon_node_to_regmap( + pdev->dev.parent->of_node); + if (IS_ERR(lpc_snoop->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + dev_set_drvdata(&pdev->dev, lpc_snoop); + + rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); + if (rc) { + dev_err(dev, "no snoop ports configured\n"); + return -ENODEV; + } + + rc = phytium_lpc_snoop_config_irq(lpc_snoop, pdev); + if (rc) + return rc; + + rc = phytium_lpc_enable_snoop(lpc_snoop, dev, 0, port); + if (rc) + return rc; + + /* Configuration of 2nd snoop channel port is optional */ + if (of_property_read_u32_index(dev->of_node, "snoop-ports", + 1, &port) == 0) { + rc = phytium_lpc_enable_snoop(lpc_snoop, dev, 1, port); + if (rc) + phytium_lpc_disable_snoop(lpc_snoop, 0); + } + + return rc; +} + +static int phytium_lpc_snoop_remove(struct platform_device *pdev) +{ + struct phytium_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); + + /* Disable both snoop channels */ + phytium_lpc_disable_snoop(lpc_snoop, 0); + phytium_lpc_disable_snoop(lpc_snoop, 1); + + return 0; +} + + +static const struct of_device_id phytium_lpc_snoop_match[] = { + { .compatible = "phytium,lpc-snoop"}, + { }, + }; + +static struct platform_driver phytium_lpc_snoop_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = phytium_lpc_snoop_match, + }, + .probe = phytium_lpc_snoop_probe, + .remove = phytium_lpc_snoop_remove, +}; + +module_platform_driver(phytium_lpc_snoop_driver); + +MODULE_DEVICE_TABLE(of, phytium_lpc_snoop_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lan Hengyu lanhengyu1395@phytium.com.cn"); +MODULE_DESCRIPTION("Driver to control Phytium LPC snoop functionality"); -- Gitee From 6d4bb54f31cfcc607943c51a21b02e860e24755b Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Thu, 27 Mar 2025 16:19:26 +0800 Subject: [PATCH 145/154] snoop: phytium: Add the driver of phytium snoop controller This patch adds the driver of phytium snoop controller in non-functional modifications. Mainline: NA Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: Idf4223827cb280772efa1894a1632a2435677aa5 --- drivers/misc/Kconfig | 16 +- drivers/misc/Makefile | 2 +- ...ytium-lpc-snoop.c => phytium-snoop-ctrl.c} | 655 +++++++++--------- 3 files changed, 339 insertions(+), 334 deletions(-) rename drivers/misc/{phytium-lpc-snoop.c => phytium-snoop-ctrl.c} (56%) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 750db66efe..1ccdff966d 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -112,13 +112,15 @@ config PHANTOM If you choose to build module, its name will be phantom. If unsure, say N here. -config PHYTIUM_LPC_SNOOP - tristate "Phytium HOST LPC snoop support" - depends on ARCH_PHYTIUM && REGMAP && MFD_SYSCON - help - Provides a driver to control the LPC snoop interface which - allows the BMC to listen on and save the data written by - the host to an arbitrary LPC I/O port. +config PHYTIUM_SNOOP_CTRL + tristate "Phytium snoop controller support" + depends on ARCH_PHYTIUM && REGMAP && MFD_SYSCON + default n + help + Say Y here if you want BMC to snoop 80h port. + Provides a driver to control the phytium snoop interface which + allows the BMC to listen on and save the data written by the + host to an arbitrary I/O port. config INTEL_MID_PTI tristate "Parallel Trace Interface for MIPI P1149.7 cJTAG standard" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 4a240ad6cb..7813f5dde9 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -17,7 +17,7 @@ obj-$(CONFIG_LKDTM) += lkdtm/ obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o -obj-$(CONFIG_PHYTIUM_LPC_SNOOP) += phytium-lpc-snoop.o +obj-$(CONFIG_PHYTIUM_SNOOP_CTRL) += phytium-snoop-ctrl.o obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o diff --git a/drivers/misc/phytium-lpc-snoop.c b/drivers/misc/phytium-snoop-ctrl.c similarity index 56% rename from drivers/misc/phytium-lpc-snoop.c rename to drivers/misc/phytium-snoop-ctrl.c index fc1369943a..aedb15bbfa 100755 --- a/drivers/misc/phytium-lpc-snoop.c +++ b/drivers/misc/phytium-snoop-ctrl.c @@ -1,326 +1,329 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2019-2023, Phytium Phytium Technology Co., Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Provides a simple driver to control the PHYTIUM LPC snoop interface which - * allows the BMC to listen on and save the data written by - * the host to an arbitrary LPC I/O port. - * - * Typically used by the BMC to "watch" host boot progress via port - * 0x80 writes made by the BIOS during the boot process. - * - * Derived from drivers/misc/aspeed-lpc-snoop.c - * Copyright 2017 Google Inc - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEVICE_NAME "phytium-lpc-snoop" - -#define NUM_SNOOP_CHANNELS 2 -#define SNOOP_FIFO_SIZE 2048 - -#define snp_enable_reg 0x150 -#define snp_enable_reg_snp1_en BIT(0) -#define snp_enable_reg_snp1_int_en BIT(1) -#define snp_enable_reg_snp2_en BIT(2) -#define snp_enable_reg_snp2_int_en BIT(3) - -#define snp_status_reg 0x154 -#define snp_status_reg_snp1_int BIT(0) -#define snp_status_reg_snp2_int BIT(1) - -#define snp_addr_reg 0x158 -#define snp_addr_reg_snp1_addr GENMASK(15, 0) -#define snp_addr_reg_snp1_shift 0 -#define snp_addr_reg_snp2_addr GENMASK(31, 16) -#define snp_addr_reg_snp2_shift 16 - -#define snp_data_reg 0x15c -#define snp_data_reg_snp1_data_reg GENMASK(7, 0) -#define snp_data_reg_snp1_shift 0 -#define snp_data_reg_snp2_data_reg GENMASK(15, 8) -#define snp_data_reg_snp2_shift 8 - -struct phytium_lpc_snoop_channel { - struct kfifo fifo; - wait_queue_head_t wq; - struct miscdevice miscdev; -}; - -struct phytium_lpc_snoop { - struct regmap *regmap; - int irq; - struct phytium_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; -}; - -static struct phytium_lpc_snoop_channel *snoop_file_to_chan(struct file *file) -{ - return container_of(file->private_data, - struct phytium_lpc_snoop_channel, - miscdev); -} - -static ssize_t snoop_file_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct phytium_lpc_snoop_channel *chan = snoop_file_to_chan(file); - unsigned int copied; - int ret = 0; - - if (kfifo_is_empty(&chan->fifo)) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - ret = wait_event_interruptible(chan->wq, - !kfifo_is_empty(&chan->fifo)); - if (ret == -ERESTARTSYS) - return -EINTR; - } - ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); - - return ret ? ret : copied; -} - -static unsigned int snoop_file_poll(struct file *file, - struct poll_table_struct *pt) -{ - struct phytium_lpc_snoop_channel *chan = snoop_file_to_chan(file); - - poll_wait(file, &chan->wq, pt); - return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0; -} - -static const struct file_operations snoop_fops = { - .owner = THIS_MODULE, - .read = snoop_file_read, - .poll = snoop_file_poll, - .llseek = noop_llseek, -}; - -/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ -static void put_fifo_with_discard(struct phytium_lpc_snoop_channel *chan, u8 val) -{ - if (!kfifo_initialized(&chan->fifo)) - return; - if (kfifo_is_full(&chan->fifo)) - kfifo_skip(&chan->fifo); - kfifo_put(&chan->fifo, val); - wake_up_interruptible(&chan->wq); -} - -static irqreturn_t phytium_lpc_snoop_irq(int irq, void *arg) -{ - struct phytium_lpc_snoop *lpc_snoop = arg; - u32 reg, data; - - if (regmap_read(lpc_snoop->regmap, snp_status_reg, ®)) - return IRQ_NONE; - - /* Check if one of the snoop channels is interrupting */ - reg &= (snp_status_reg_snp1_int | snp_status_reg_snp2_int); - if (!reg) - return IRQ_NONE; - - /* Ack pending IRQs */ - regmap_write(lpc_snoop->regmap, snp_status_reg, reg); - - /* Read and save most recent snoop'ed data byte to FIFO */ - regmap_read(lpc_snoop->regmap, snp_data_reg, &data); - - if (reg & snp_status_reg_snp1_int) { - u8 val = (data & snp_data_reg_snp1_data_reg) >> snp_data_reg_snp1_shift; - - put_fifo_with_discard(&lpc_snoop->chan[0], val); - } - if (reg & snp_status_reg_snp2_int) { - u8 val = (data & snp_data_reg_snp2_data_reg) >> snp_data_reg_snp2_shift; - - put_fifo_with_discard(&lpc_snoop->chan[1], val); - } - - return IRQ_HANDLED; -} - -static int phytium_lpc_snoop_config_irq(struct phytium_lpc_snoop *lpc_snoop, - struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - int rc; - - lpc_snoop->irq = platform_get_irq(pdev, 0); - if (!lpc_snoop->irq) - return -ENODEV; - - rc = devm_request_irq(dev, lpc_snoop->irq, - phytium_lpc_snoop_irq, IRQF_SHARED, - DEVICE_NAME, lpc_snoop); - if (rc < 0) { - dev_warn(dev, "Unable to request IRQ %d\n", lpc_snoop->irq); - lpc_snoop->irq = 0; - return rc; - } - - return 0; -} - -static int phytium_lpc_enable_snoop(struct phytium_lpc_snoop *lpc_snoop, - struct device *dev, - int channel, u16 lpc_port) -{ - int rc = 0; - u32 snp_enable_reg_en, snp_addr_reg_mask, snp_addr_reg_shift; - init_waitqueue_head(&lpc_snoop->chan[channel].wq); - /* Create FIFO datastructure */ - rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo, - SNOOP_FIFO_SIZE, GFP_KERNEL); - if (rc) - return rc; - - lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; - lpc_snoop->chan[channel].miscdev.name = - devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); - lpc_snoop->chan[channel].miscdev.fops = &snoop_fops; - lpc_snoop->chan[channel].miscdev.parent = dev; - rc = misc_register(&lpc_snoop->chan[channel].miscdev); - if (rc) - return rc; - - /* Enable LPC snoop channel at requested port */ - switch (channel) { - case 0: - snp_enable_reg_en = snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en; - snp_addr_reg_mask = snp_addr_reg_snp1_addr; - snp_addr_reg_shift = snp_addr_reg_snp1_shift; - break; - case 1: - snp_enable_reg_en = snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en; - snp_addr_reg_mask = snp_addr_reg_snp2_addr; - snp_addr_reg_shift = snp_addr_reg_snp2_shift; - break; - default: - return -EINVAL; - } - - regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, snp_enable_reg_en, snp_enable_reg_en); - regmap_update_bits(lpc_snoop->regmap, snp_addr_reg, snp_addr_reg_mask, - lpc_port << snp_addr_reg_shift); - return rc; -} - -static void phytium_lpc_disable_snoop(struct phytium_lpc_snoop *lpc_snoop, - int channel) -{ - switch (channel) { - case 0: - regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, - snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en, - 0); - break; - case 1: - regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, - snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en, - 0); - break; - default: - return; - } - - kfifo_free(&lpc_snoop->chan[channel].fifo); - misc_deregister(&lpc_snoop->chan[channel].miscdev); -} - -static int phytium_lpc_snoop_probe(struct platform_device *pdev) -{ - struct phytium_lpc_snoop *lpc_snoop; - struct device *dev; - u32 port; - int rc; - - dev = &pdev->dev; - - lpc_snoop = devm_kzalloc(dev, sizeof(*lpc_snoop), GFP_KERNEL); - if (!lpc_snoop) - return -ENOMEM; - - lpc_snoop->regmap = syscon_node_to_regmap( - pdev->dev.parent->of_node); - if (IS_ERR(lpc_snoop->regmap)) { - dev_err(dev, "Couldn't get regmap\n"); - return -ENODEV; - } - - dev_set_drvdata(&pdev->dev, lpc_snoop); - - rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); - if (rc) { - dev_err(dev, "no snoop ports configured\n"); - return -ENODEV; - } - - rc = phytium_lpc_snoop_config_irq(lpc_snoop, pdev); - if (rc) - return rc; - - rc = phytium_lpc_enable_snoop(lpc_snoop, dev, 0, port); - if (rc) - return rc; - - /* Configuration of 2nd snoop channel port is optional */ - if (of_property_read_u32_index(dev->of_node, "snoop-ports", - 1, &port) == 0) { - rc = phytium_lpc_enable_snoop(lpc_snoop, dev, 1, port); - if (rc) - phytium_lpc_disable_snoop(lpc_snoop, 0); - } - - return rc; -} - -static int phytium_lpc_snoop_remove(struct platform_device *pdev) -{ - struct phytium_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); - - /* Disable both snoop channels */ - phytium_lpc_disable_snoop(lpc_snoop, 0); - phytium_lpc_disable_snoop(lpc_snoop, 1); - - return 0; -} - - -static const struct of_device_id phytium_lpc_snoop_match[] = { - { .compatible = "phytium,lpc-snoop"}, - { }, - }; - -static struct platform_driver phytium_lpc_snoop_driver = { - .driver = { - .name = DEVICE_NAME, - .of_match_table = phytium_lpc_snoop_match, - }, - .probe = phytium_lpc_snoop_probe, - .remove = phytium_lpc_snoop_remove, -}; - -module_platform_driver(phytium_lpc_snoop_driver); - -MODULE_DEVICE_TABLE(of, phytium_lpc_snoop_match); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Lan Hengyu lanhengyu1395@phytium.com.cn"); -MODULE_DESCRIPTION("Driver to control Phytium LPC snoop functionality"); +// SPDX-License-Identifier: GPL-2.0 +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Provides a simple driver to control the PHYTIUM snoop ctrl interface which + * allows the BMC to listen on and save the data written by + * the host to an arbitrary I/O port. + * + * Typically used by the BMC to "watch" host boot progress via port + * 0x80 writes made by the BIOS during the boot process. + * + * Copyright (c) 2019-2023, Phytium Technology Co., Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "phytium-snoop-ctrl" + +#define NUM_SNOOP_CHANNELS 2 +#define SNOOP_FIFO_SIZE 2048 + +#define snp_enable_reg 0x150 +#define snp_enable_reg_snp1_en BIT(0) +#define snp_enable_reg_snp1_int_en BIT(1) +#define snp_enable_reg_snp2_en BIT(2) +#define snp_enable_reg_snp2_int_en BIT(3) + +#define snp_status_reg 0x154 +#define snp_status_reg_snp1_int BIT(0) +#define snp_status_reg_snp2_int BIT(1) + +#define snp_addr_reg 0x158 +#define snp_addr_reg_snp1_addr GENMASK(15, 0) +#define snp_addr_reg_snp1_shift 0 +#define snp_addr_reg_snp2_addr GENMASK(31, 16) +#define snp_addr_reg_snp2_shift 16 + +#define snp_data_reg 0x15c +#define snp_data_reg_snp1_data_reg GENMASK(7, 0) +#define snp_data_reg_snp1_shift 0 +#define snp_data_reg_snp2_data_reg GENMASK(15, 8) +#define snp_data_reg_snp2_shift 8 + +#define SNOOP_DRIVER_VERSION "1.1.1" + +struct phytium_snoop_ctrl_channel { + struct kfifo fifo; + wait_queue_head_t wq; + struct miscdevice miscdev; +}; + +struct phytium_snoop_ctrl { + struct regmap *regmap; + int irq; + struct phytium_snoop_ctrl_channel chan[NUM_SNOOP_CHANNELS]; +}; + +static struct phytium_snoop_ctrl_channel *snoop_file_to_chan(struct file *file) +{ + return container_of(file->private_data, + struct phytium_snoop_ctrl_channel, + miscdev); +} + +static ssize_t snoop_file_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct phytium_snoop_ctrl_channel *chan = snoop_file_to_chan(file); + unsigned int copied; + int ret = 0; + + if (kfifo_is_empty(&chan->fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(chan->wq, + !kfifo_is_empty(&chan->fifo)); + if (ret == -ERESTARTSYS) + return -EINTR; + } + ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); + + return ret ? ret : copied; +} + +static unsigned int snoop_file_poll(struct file *file, + struct poll_table_struct *pt) +{ + struct phytium_snoop_ctrl_channel *chan = snoop_file_to_chan(file); + + poll_wait(file, &chan->wq, pt); + return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0; +} + +static const struct file_operations snoop_fops = { + .owner = THIS_MODULE, + .read = snoop_file_read, + .poll = snoop_file_poll, + .llseek = noop_llseek, +}; + +/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ +static void put_fifo_with_discard(struct phytium_snoop_ctrl_channel *chan, u8 val) +{ + if (!kfifo_initialized(&chan->fifo)) + return; + if (kfifo_is_full(&chan->fifo)) + kfifo_skip(&chan->fifo); + kfifo_put(&chan->fifo, val); + wake_up_interruptible(&chan->wq); +} + +static irqreturn_t phytium_snoop_ctrl_irq(int irq, void *arg) +{ + struct phytium_snoop_ctrl *snoop_ctrl = arg; + u32 reg, data; + + if (regmap_read(snoop_ctrl->regmap, snp_status_reg, ®)) + return IRQ_NONE; + + /* Check if one of the snoop channels is interrupting */ + reg &= (snp_status_reg_snp1_int | snp_status_reg_snp2_int); + if (!reg) + return IRQ_NONE; + + /* Ack pending IRQs */ + regmap_write(snoop_ctrl->regmap, snp_status_reg, reg); + + /* Read and save most recent snoop'ed data byte to FIFO */ + regmap_read(snoop_ctrl->regmap, snp_data_reg, &data); + + if (reg & snp_status_reg_snp1_int) { + u8 val = (data & snp_data_reg_snp1_data_reg) >> snp_data_reg_snp1_shift; + + put_fifo_with_discard(&snoop_ctrl->chan[0], val); + } + if (reg & snp_status_reg_snp2_int) { + u8 val = (data & snp_data_reg_snp2_data_reg) >> snp_data_reg_snp2_shift; + + put_fifo_with_discard(&snoop_ctrl->chan[1], val); + } + + return IRQ_HANDLED; +} + +static int phytium_snoop_ctrl_config_irq(struct phytium_snoop_ctrl *snoop_ctrl, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + snoop_ctrl->irq = platform_get_irq(pdev, 0); + if (!snoop_ctrl->irq) + return -ENODEV; + + rc = devm_request_irq(dev, snoop_ctrl->irq, + phytium_snoop_ctrl_irq, IRQF_SHARED, + DEVICE_NAME, snoop_ctrl); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", snoop_ctrl->irq); + snoop_ctrl->irq = 0; + return rc; + } + + return 0; +} + +static int phytium_enable_snoop(struct phytium_snoop_ctrl *snoop_ctrl, + struct device *dev, + int channel, u16 snoop_port) +{ + int rc = 0; + u32 snp_enable_reg_en, snp_addr_reg_mask, snp_addr_reg_shift; + + init_waitqueue_head(&snoop_ctrl->chan[channel].wq); + /* Create FIFO datastructure */ + rc = kfifo_alloc(&snoop_ctrl->chan[channel].fifo, + SNOOP_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + + snoop_ctrl->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; + snoop_ctrl->chan[channel].miscdev.name = + devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); + snoop_ctrl->chan[channel].miscdev.fops = &snoop_fops; + snoop_ctrl->chan[channel].miscdev.parent = dev; + rc = misc_register(&snoop_ctrl->chan[channel].miscdev); + if (rc) + return rc; + + /* Enable snoop channel at requested port */ + switch (channel) { + case 0: + snp_enable_reg_en = snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en; + snp_addr_reg_mask = snp_addr_reg_snp1_addr; + snp_addr_reg_shift = snp_addr_reg_snp1_shift; + break; + case 1: + snp_enable_reg_en = snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en; + snp_addr_reg_mask = snp_addr_reg_snp2_addr; + snp_addr_reg_shift = snp_addr_reg_snp2_shift; + break; + default: + return -EINVAL; + } + + regmap_update_bits(snoop_ctrl->regmap, + snp_enable_reg, snp_enable_reg_en, snp_enable_reg_en); + regmap_update_bits(snoop_ctrl->regmap, snp_addr_reg, snp_addr_reg_mask, + snoop_port << snp_addr_reg_shift); + return rc; +} + +static void phytium_disable_snoop(struct phytium_snoop_ctrl *snoop_ctrl, + int channel) +{ + switch (channel) { + case 0: + regmap_update_bits(snoop_ctrl->regmap, snp_enable_reg, + snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en, + 0); + break; + case 1: + regmap_update_bits(snoop_ctrl->regmap, snp_enable_reg, + snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en, + 0); + break; + default: + return; + } + + kfifo_free(&snoop_ctrl->chan[channel].fifo); + misc_deregister(&snoop_ctrl->chan[channel].miscdev); +} + +static int phytium_snoop_ctrl_probe(struct platform_device *pdev) +{ + struct phytium_snoop_ctrl *snoop_ctrl; + struct device *dev; + u32 port; + int rc; + + dev = &pdev->dev; + + snoop_ctrl = devm_kzalloc(dev, sizeof(*snoop_ctrl), GFP_KERNEL); + if (!snoop_ctrl) + return -ENOMEM; + + snoop_ctrl->regmap = syscon_node_to_regmap( + pdev->dev.of_node); + if (IS_ERR(snoop_ctrl->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + dev_set_drvdata(&pdev->dev, snoop_ctrl); + + rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); + if (rc) { + dev_err(dev, "no snoop ports configured\n"); + return -ENODEV; + } + + rc = phytium_snoop_ctrl_config_irq(snoop_ctrl, pdev); + if (rc) + return rc; + + rc = phytium_enable_snoop(snoop_ctrl, dev, 0, port); + if (rc) + return rc; + + /* Configuration of 2nd snoop channel port is optional */ + if (of_property_read_u32_index(dev->of_node, "snoop-ports", + 1, &port) == 0) { + rc = phytium_enable_snoop(snoop_ctrl, dev, 1, port); + if (rc) + phytium_disable_snoop(snoop_ctrl, 0); + } + + return rc; +} + +static int phytium_snoop_ctrl_remove(struct platform_device *pdev) +{ + struct phytium_snoop_ctrl *snoop_ctrl = dev_get_drvdata(&pdev->dev); + + /* Disable both snoop channels */ + phytium_disable_snoop(snoop_ctrl, 0); + phytium_disable_snoop(snoop_ctrl, 1); + + return 0; +} + + +static const struct of_device_id phytium_snoop_ctrl_match[] = { + { .compatible = "phytium,snoop-ctrl"}, + { }, +}; + +static struct platform_driver phytium_snoop_ctrl_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = phytium_snoop_ctrl_match, + }, + .probe = phytium_snoop_ctrl_probe, + .remove = phytium_snoop_ctrl_remove, +}; + +module_platform_driver(phytium_snoop_ctrl_driver); + +MODULE_DEVICE_TABLE(of, phytium_snoop_ctrl_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lan Hengyu lanhengyu1395@phytium.com.cn"); +MODULE_DESCRIPTION("Driver to control Phytium snoop controller functionality"); +MODULE_VERSION(SNOOP_DRIVER_VERSION); -- Gitee From 93ac3541f9f6cf625a369b4e9ecc70aae3a731ed Mon Sep 17 00:00:00 2001 From: Xiao Cong Date: Tue, 8 Apr 2025 11:20:14 +0800 Subject: [PATCH 146/154] NVME: Fall back to the simple suspend/resume pm interface When enable ASPM, NVMe devices will use Native PCI Power Management to go to any of the supported low-power states(D0-D3), which need special power domain design for the hardware plantforms. So falling back to the simple suspend/resume power management interface. Mainline: NA Signed-off-by: Xiao Cong Signed-off-by: Wang Yinfeng Change-Id: Iac046c3bc71b0380990e75aabb7af99f6158656a --- drivers/nvme/host/pci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 970a1b374a..561edeb101 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2893,6 +2893,12 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev, "platform quirk: setting simple suspend\n"); quirks |= NVME_QUIRK_SIMPLE_SUSPEND; } + +#ifdef CONFIG_ARCH_PHYTIUM + if (read_cpuid_implementor() == ARM_CPU_IMP_PHYTIUM) + quirks |= NVME_QUIRK_SIMPLE_SUSPEND; +#endif + ret = nvme_init_ctrl(&dev->ctrl, &pdev->dev, &nvme_pci_ctrl_ops, quirks); if (ret) -- Gitee From 6e94b984d5ffb5b81f7c19e0592576c08380132d Mon Sep 17 00:00:00 2001 From: zuoqian Date: Tue, 6 May 2025 10:39:32 +0800 Subject: [PATCH 147/154] Revert "net: phytmac: Bugfix udp stopped issue in half deplux mode" This reverts commit 78e35aac97e1f0eb001b4e17d1c26a5e47c5a57e. --- drivers/net/ethernet/phytium/phytmac_v1.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index ef131ee58b..58c81d424d 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -986,7 +986,6 @@ static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count while (count) { desc = phytmac_get_rx_desc(queue, index); desc->desc0 &= ~PHYTMAC_BIT(RX_USED); - dma_wmb(); index--; count--; } -- Gitee From bb3eedbf77c8799ad9a7443f80c8c41aaee98aca Mon Sep 17 00:00:00 2001 From: zuoqian Date: Tue, 6 May 2025 10:41:57 +0800 Subject: [PATCH 148/154] Revert "net: phytium: Bugfix mac wrong DMA address access issue" This reverts commit e1d59dd169e02ce19a0b5273ae20f95576acbdd3. --- drivers/net/ethernet/phytium/phytmac.h | 2 -- drivers/net/ethernet/phytium/phytmac_main.c | 15 ++++++-------- drivers/net/ethernet/phytium/phytmac_v1.c | 22 +++++++-------------- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 2ffbeb1c77..79a360460f 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -42,7 +42,6 @@ #define MIN_TX_RING_SIZE 64 #define MIN_RX_RING_SIZE 64 #define DEFAULT_TX_DESC_MIN_FREE 64 -#define DEFAULT_RX_DESC_MIN_FREE 64 #define MEMORY_SIZE 4096 #define MHU_SIZE 0x20 @@ -588,7 +587,6 @@ struct phytmac_hw_if { struct packet_info *packet); void (*init_rx_map)(struct phytmac_queue *queue, u32 index); unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); - unsigned int (*rx_clean)(struct phytmac_queue *queue, u32 cleaned_count); void (*transmit)(struct phytmac_queue *queue); void (*update_rx_tail)(struct phytmac_queue *queue); int (*tx_complete)(const struct phytmac_dma_desc *desc); diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index e056ac2b22..f97abb027a 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1133,17 +1133,12 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) struct phytmac_hw_if *hw_if = pdata->hw_if; unsigned int index, space; struct phytmac_rx_buffer *rx_buf_info; - unsigned int rx_unclean = 0; space = CIRC_SPACE(queue->rx_head, queue->rx_tail, pdata->rx_ring_size); - if (space < DEFAULT_RX_DESC_MIN_FREE) - return; - - index = queue->rx_head & (pdata->rx_ring_size - 1); - while (space > 0) { + index = queue->rx_head & (pdata->rx_ring_size - 1); rx_buf_info = &queue->rx_buffer_info[index]; if (!phytmac_alloc_mapped_page(pdata, rx_buf_info)) @@ -1156,8 +1151,10 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) hw_if->rx_map(queue, index, rx_buf_info->addr + rx_buf_info->page_offset); - index = (index + 1) & (pdata->rx_ring_size - 1); - rx_unclean++; + queue->rx_head++; + if (queue->rx_head >= pdata->rx_ring_size) + queue->rx_head &= (pdata->rx_ring_size - 1); + space--; } @@ -1320,7 +1317,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) if (!hw_if->tx_complete(desc)) break; - /* Process all buffers of the current transmitted frame */ + /* Process all buffers of the current transmitted frame */ for (;; head++) { tx_skb = phytmac_get_tx_skb(queue, head); if (tx_skb->type == PHYTMAC_TYPE_SKB) { diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 58c81d424d..095fb70b51 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -973,21 +973,14 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, addr |= PHYTMAC_BIT(RX_WRAP); desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); - desc->desc0 = lower_32_bits(addr) | PHYTMAC_BIT(RX_USED); - } - return 0; -} - -static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count) -{ - struct phytmac_dma_desc *desc; - u32 index = queue->rx_head + count - 1; - - while (count) { - desc = phytmac_get_rx_desc(queue, index); + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 = lower_32_bits(addr); + } else { + desc->desc1 = 0; + /* Make newly descriptor to hardware */ + dma_wmb(); desc->desc0 &= ~PHYTMAC_BIT(RX_USED); - index--; - count--; } return 0; @@ -1447,7 +1440,6 @@ struct phytmac_hw_if phytmac_1p0_hw = { .get_rx_pkt_len = phytmac_rx_pkt_len, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, - .rx_clean = phytmac_rx_clean_desc, .rx_checksum = phytmac_rx_checksum, .rx_single_buffer = phytmac_rx_single_buffer, .rx_pkt_start = phytmac_rx_sof, -- Gitee From a9bdad80a567d81a9d9686b83fdf885ed4e908c9 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Wed, 7 May 2025 16:29:24 +0800 Subject: [PATCH 149/154] arm64: phytium_defconfig: enable some configs Signed-off-by: liutianyu1250 --- arch/arm64/configs/phytium_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/phytium_defconfig b/arch/arm64/configs/phytium_defconfig index 8fd7c4bb1c..3bdf0ebc55 100644 --- a/arch/arm64/configs/phytium_defconfig +++ b/arch/arm64/configs/phytium_defconfig @@ -231,6 +231,7 @@ CONFIG_NVME_TARGET_LOOP=y CONFIG_NVME_TARGET_FC=y CONFIG_NVME_TARGET_FCLOOP=y CONFIG_NVME_TARGET_TCP=y +CONFIG_PHYTIUM_SNOOP_CTRL=y CONFIG_SRAM=y CONFIG_PCI_ENDPOINT_TEST=m CONFIG_EEPROM_AT24=m -- Gitee From d82e67b2e21bf14864a68b5a4a1c166a3f834426 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Sat, 11 May 2024 15:00:50 +0800 Subject: [PATCH 150/154] net: macb: phytium: add old of-compatible support If only update kernel, without dtb, macb will be not probe because compatible name changed, any way add old name here. Signed-off-by: liutianyu1250 --- drivers/net/ethernet/cadence/macb_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index d0aaa43c18..9c5e769927 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -5264,6 +5264,7 @@ static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,zynqmp-gem", .data = &zynqmp_config}, { .compatible = "cdns,zynq-gem", .data = &zynq_config }, { .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config }, + { .compatible = "cdns,phytium-gem", .data = &phytium_gem1p0_config }, /* old version */ { .compatible = "cdns,phytium-gem-1.0", .data = &phytium_gem1p0_config }, { .compatible = "cdns,phytium-gem-2.0", .data = &phytium_gem2p0_config }, { /* sentinel */ } -- Gitee From f2565e2e5975564821fb86ccdc50b91a0c04ca5a Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Mon, 12 May 2025 15:25:42 +0800 Subject: [PATCH 151/154] arm64: phytium_defconfig: disable hotplug_pci Signed-off-by: liutianyu1250 --- arch/arm64/configs/phytium_defconfig | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm64/configs/phytium_defconfig b/arch/arm64/configs/phytium_defconfig index 3bdf0ebc55..072baaed4b 100644 --- a/arch/arm64/configs/phytium_defconfig +++ b/arch/arm64/configs/phytium_defconfig @@ -176,11 +176,8 @@ CONFIG_NFC_NCI=m CONFIG_NFC_S3FWRN5_I2C=m CONFIG_PCI=y CONFIG_PCIEPORTBUS=y -CONFIG_HOTPLUG_PCI_PCIE=y CONFIG_PCI_IOV=y CONFIG_PCI_PASID=y -CONFIG_HOTPLUG_PCI=y -CONFIG_HOTPLUG_PCI_ACPI=y CONFIG_PCI_HOST_GENERIC=y CONFIG_PCI_XGENE=y CONFIG_PCIE_ALTERA=y -- Gitee From 855998ae9171f449776a6f89b50844064ef14144 Mon Sep 17 00:00:00 2001 From: Huangjie Date: Sun, 27 Apr 2025 11:24:46 +0800 Subject: [PATCH 152/154] drivers: edac/phytium: update pe220x SoC ras error table Signed-off-by: Huangjie --- drivers/edac/phytium_edac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/edac/phytium_edac.c b/drivers/edac/phytium_edac.c index 2d9b2dda19..9201d0d703 100644 --- a/drivers/edac/phytium_edac.c +++ b/drivers/edac/phytium_edac.c @@ -101,9 +101,9 @@ static const struct ras_error_info pe220x_ras_soc_error[] = { { 37, UNCORRECTED_ERROR, "nINTERRIRQ_clust1" }, { 38, UNCORRECTED_ERROR, "nEXTERRIRQ_clust2" }, { 39, UNCORRECTED_ERROR, "nINTERRIRQ_clust2" }, - { 40, UNCORRECTED_ERROR, "ams_ame0_ras_err" }, - { 41, UNCORRECTED_ERROR, "ams_ame1_ras_err" }, - { 42, UNCORRECTED_ERROR, "ams_amer_ras_err" }, + { 40, UNCORRECTED_ERROR, "ras_err_amu0" }, + { 41, UNCORRECTED_ERROR, "ras_err_amu1" }, + { 42, UNCORRECTED_ERROR, "ras_err_ame0" }, { 43, UNCORRECTED_ERROR, "ras_err_ame1" }, }; -- Gitee From dff2948a1fd0c7b6f6eace68a42268f3013735bb Mon Sep 17 00:00:00 2001 From: Huangjie Date: Tue, 29 Apr 2025 09:07:57 +0800 Subject: [PATCH 153/154] dt-bindings: gdma: fix spelling errors Signed-off-by: Huangjie --- Documentation/devicetree/bindings/dma/phytium,gdma.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/dma/phytium,gdma.yaml b/Documentation/devicetree/bindings/dma/phytium,gdma.yaml index b5c70dba56..0aefb13731 100644 --- a/Documentation/devicetree/bindings/dma/phytium,gdma.yaml +++ b/Documentation/devicetree/bindings/dma/phytium,gdma.yaml @@ -37,7 +37,7 @@ properties: max-outstanding: minimum: 1 maximum: 64 - description: set interrupmax-outstandingts according to the programming manual + description: set max-outstanding according to the programming manual required: - compatible -- Gitee From 3f2ab4e9402f4a150c7dc3df77dd0c3b24cb1132 Mon Sep 17 00:00:00 2001 From: Huangjie Date: Fri, 9 May 2025 17:40:04 +0800 Subject: [PATCH 154/154] drivers: gpio/phytium: add interrupt affinity interface for px210 Signed-off-by: Huangjie --- drivers/gpio/gpio-phytium-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-phytium-pci.c b/drivers/gpio/gpio-phytium-pci.c index 0bec73380f..0f94fb5b5e 100644 --- a/drivers/gpio/gpio-phytium-pci.c +++ b/drivers/gpio/gpio-phytium-pci.c @@ -64,6 +64,7 @@ static int phytium_gpio_pci_probe(struct pci_dev *pdev, const struct pci_device_ gpio->irq_chip.irq_set_type = phytium_gpio_irq_set_type; gpio->irq_chip.irq_enable = phytium_gpio_irq_enable; gpio->irq_chip.irq_disable = phytium_gpio_irq_disable; + gpio->irq_chip.irq_set_affinity = phytium_gpio_irq_set_affinity; raw_spin_lock_init(&gpio->lock); -- Gitee