From nobody Fri Dec 19 19:18:43 2025 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by smtp.subspace.kernel.org (Postfix) with ESMTP id B25A51A08A4 for ; Mon, 30 Dec 2024 09:31:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=114.242.206.163 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735551121; cv=none; b=sbOUvhqHRtRwxPspkBOlpunWnvAUIr5kE8M+UVBlhgEPq9LuUNjlf9fnoe/wFX7avDJ5P/f1GuRId0tMmeSdW1XW6x2Zy5U9TMdq45NJxp+GGAi43jEfS10lIWcMjRkrXnZiQlOvRVRb/ahanTU9fLG5TJaepjnipdROkUl7XdI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735551121; c=relaxed/simple; bh=jKCb/+TVBHCzx/8FChJ08th/JGE4Yj7WCLCJO17VOvA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PA/aV9aUbw89PEuVcNs/QDcTYjI+gLzByFd+Vvkbn4PNphoQh/z5IkYbRIO0KxBcaIHtqIGBMAgDbw1yyrDg2ob/wUubSc8x72pP2xuyT4kbs8/6Equ5v2Mv6Pu+enjDujiHI/iY8ZzSpnllFxu1Qa1ng/cY+zMc+Fs8gyjTX9g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=loongson.cn; spf=pass smtp.mailfrom=loongson.cn; arc=none smtp.client-ip=114.242.206.163 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=loongson.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=loongson.cn Received: from loongson.cn (unknown [223.64.68.63]) by gateway (Coremail) with SMTP id _____8CxieCGaHJnfM9bAA--.48975S3; Mon, 30 Dec 2024 17:31:50 +0800 (CST) Received: from localhost.localdomain (unknown [223.64.68.63]) by front1 (Coremail) with SMTP id qMiowMCxbcd5aHJnyL4NAA--.3877S2; Mon, 30 Dec 2024 17:31:38 +0800 (CST) From: Binbin Zhou To: Binbin Zhou , Huacai Chen , Lee Jones , Corey Minyard , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter Cc: Huacai Chen , linux-kernel@vger.kernel.org, openipmi-developer@lists.sourceforge.net, dri-devel@lists.freedesktop.org, Xuerui Wang , loongarch@lists.linux.dev, Binbin Zhou , Chong Qiao Subject: [PATCH v1 4/4] drm/ls2kbmc: Add Loongson-2K BMC reset function support Date: Mon, 30 Dec 2024 17:31:36 +0800 Message-ID: X-Mailer: git-send-email 2.43.5 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: qMiowMCxbcd5aHJnyL4NAA--.3877S2 X-CM-SenderInfo: p2kr3uplqex0o6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj93XoWxKw4UZrWDJrW3AF1DZr4xAFc_yoWfuF1rpF W5Aa4rtrWUtr1YgrZrX3WUCwsxKwn0qay7GFyIq3Zaya1avryDZFyrtF1fZF1fJrWkAr13 X3ySkFW7uFyDA3gCm3ZEXasCq-sJn29KB7ZKAUJUUUUD529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUBvb4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r1Y6r17M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Ar0_tr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Cr0_Gr1UM28EF7xvwVC2z280aVAFwI0_Gr1j6F4UJwA2z4x0Y4vEx4A2jsIEc7CjxVAF wI0_Gr1j6F4UJwAaw2AFwI0_JF0_Jw1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2 xF0cIa020Ex4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_ Wrv_ZF1lYx0Ex4A2jsIE14v26F4j6r4UJwAm72CE4IkC6x0Yz7v_Jr0_Gr1lF7xvr2IYc2 Ij64vIr41lc7CjxVAaw2AFwI0_Jw0_GFyl42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Y z7v_Jr0_Gr1l4IxYO2xFxVAFwI0_JF0_Jw1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x 8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r4a6rW5MIIYrxkI7VAKI48JMIIF0xvE 2Ix0cI8IcVAFwI0_Ar0_tr1lIxAIcVC0I7IYx2IY6xkF7I0E14v26F4j6r4UJwCI42IY6x AIw20EY4v20xvaj40_Jr0_JF4lIxAIcVC2z280aVAFwI0_Cr0_Gr1UMIIF0xvEx4A2jsIE c7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVjvjDU0xZFpf9x07j2XdUUUUUU= Content-Type: text/plain; charset="utf-8" Since the display is a sub-function of the Loongson-2K BMC, when the BMC reset, the entire BMC PCIe is disconnected, including the display which is interrupted. Not only do you need to save/restore the relevant PCIe registers throughout the reset process, but you also need to re-push the display to the monitor at the end. Co-developed-by: Chong Qiao Signed-off-by: Chong Qiao Signed-off-by: Binbin Zhou --- drivers/gpu/drm/tiny/ls2kbmc.c | 284 ++++++++++++++++++++++++++++++++- 1 file changed, 283 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tiny/ls2kbmc.c b/drivers/gpu/drm/tiny/ls2kbmc.c index 909d6c687193..4b440f20cb4d 100644 --- a/drivers/gpu/drm/tiny/ls2kbmc.c +++ b/drivers/gpu/drm/tiny/ls2kbmc.c @@ -8,10 +8,12 @@ */ =20 #include +#include #include #include #include #include +#include =20 #include #include @@ -32,9 +34,27 @@ #include #include =20 +#define BMC_RESET_DELAY (60 * HZ) +#define BMC_RESET_WAIT 10000 + +static const u32 index[] =3D { 0x4, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, + 0x30, 0x3c, 0x54, 0x58, 0x78, 0x7c, 0x80 }; +static const u32 cindex[] =3D { 0x4, 0x10, 0x3c }; + +struct ls2kbmc_pci_data { + u32 d80c; + u32 d71c; + u32 data[14]; + u32 cdata[3]; +}; + struct ls2kbmc_pdata { struct pci_dev *pdev; + struct drm_device *ddev; + struct work_struct bmc_work; + unsigned long reset_time; struct simplefb_platform_data pd; + struct ls2kbmc_pci_data pci_data; }; =20 /* @@ -583,6 +603,265 @@ static struct ls2kbmc_device *ls2kbmc_device_create(s= truct drm_driver *drv, return sdev; } =20 +static bool ls2kbmc_bar0_addr_is_set(struct pci_dev *ppdev) +{ + u32 addr; + + pci_read_config_dword(ppdev, PCI_BASE_ADDRESS_0, &addr); + addr &=3D PCI_BASE_ADDRESS_MEM_MASK; + + return addr ? true : false; +} + +static void ls2kbmc_save_pci_data(struct ls2kbmc_pdata *priv) +{ + struct pci_dev *pdev =3D priv->pdev; + struct pci_dev *parent =3D pdev->bus->self; + int i; + + for (i =3D 0; i < ARRAY_SIZE(index); i++) + pci_read_config_dword(parent, index[i], &priv->pci_data.data[i]); + + for (i =3D 0; i < ARRAY_SIZE(cindex); i++) + pci_read_config_dword(pdev, cindex[i], &priv->pci_data.cdata[i]); + + pci_read_config_dword(parent, 0x80c, &priv->pci_data.d80c); + priv->pci_data.d80c =3D (priv->pci_data.d80c & ~(3 << 17)) | (1 << 17); + + pci_read_config_dword(parent, 0x71c, &priv->pci_data.d71c); + priv->pci_data.d71c |=3D 1 << 26; +} + +static bool ls2kbmc_check_pcie_connected(struct pci_dev *parent, struct dr= m_device *dev) +{ + void __iomem *mmio; + int sts, timeout =3D 10000; + + mmio =3D pci_iomap(parent, 0, 0x100); + if (!mmio) + return false; + + writel(readl(mmio) | 0x8, mmio); + while (timeout) { + sts =3D readl(mmio + 0xc); + if ((sts & 0x11) =3D=3D 0x11) + break; + mdelay(1); + timeout--; + } + + pci_iounmap(parent, mmio); + + if (!timeout) { + drm_err(dev, "pcie train failed status=3D0x%x\n", sts); + return false; + } + + return true; +} + +static int ls2kbmc_recove_pci_data(void *data) +{ + struct ls2kbmc_pdata *priv =3D data; + struct pci_dev *pdev =3D priv->pdev; + struct drm_device *dev =3D priv->ddev; + struct pci_dev *parent =3D pdev->bus->self; + u32 i, timeout, retry =3D 0; + bool ready; + + pci_write_config_dword(parent, PCI_BASE_ADDRESS_2, 0); + pci_write_config_dword(parent, PCI_BASE_ADDRESS_3, 0); + pci_write_config_dword(parent, PCI_BASE_ADDRESS_4, 0); + + timeout =3D 10000; + while (timeout) { + ready =3D ls2kbmc_bar0_addr_is_set(parent); + if (!ready) + break; + mdelay(1); + timeout--; + }; + + if (!timeout) + drm_warn(dev, "bar not clear 0\n"); + +retrain: + for (i =3D 0; i < ARRAY_SIZE(index); i++) + pci_write_config_dword(parent, index[i], priv->pci_data.data[i]); + + pci_write_config_dword(parent, 0x80c, priv->pci_data.d80c); + pci_write_config_dword(parent, 0x71c, priv->pci_data.d71c); + + /* Check if the pcie is connected */ + ready =3D ls2kbmc_check_pcie_connected(parent, dev); + if (!ready) + return ready; + + for (i =3D 0; i < ARRAY_SIZE(cindex); i++) + pci_write_config_dword(pdev, cindex[i], priv->pci_data.cdata[i]); + + drm_info(dev, "pcie recovered done\n"); + + if (!retry) { + /*wait u-boot ddr config */ + mdelay(BMC_RESET_WAIT); + ready =3D ls2kbmc_bar0_addr_is_set(parent); + if (!ready) { + retry =3D 1; + goto retrain; + } + } + + return 0; +} + +static int ls2kbmc_push_drm_mode(struct drm_device *dev) +{ + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(dev); + struct drm_crtc *crtc =3D &sdev->crtc; + struct drm_plane *plane =3D crtc->primary; + struct drm_connector *connector =3D &sdev->connector; + struct drm_framebuffer *fb =3D NULL; + struct drm_mode_set set; + struct drm_modeset_acquire_ctx ctx; + int ret; + + mutex_lock(&dev->mode_config.mutex); + connector->funcs->fill_modes(connector, 4096, 4096); + mutex_unlock(&dev->mode_config.mutex); + + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, + DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret); + + if (plane->state) + fb =3D plane->state->fb; + else + fb =3D plane->fb; + + if (!fb) { + drm_dbg(dev, "CRTC doesn't have current FB\n"); + ret =3D -EINVAL; + goto out; + } + + drm_framebuffer_get(fb); + + set.crtc =3D crtc; + set.x =3D 0; + set.y =3D 0; + set.mode =3D &sdev->mode; + set.connectors =3D &connector; + set.num_connectors =3D 1; + set.fb =3D fb; + + ret =3D crtc->funcs->set_config(&set, &ctx); + +out: + DRM_MODESET_LOCK_ALL_END(dev, ctx, ret); + + return ret; +} + +static void ls2kbmc_events_fn(struct work_struct *work) +{ + struct ls2kbmc_pdata *priv =3D container_of(work, struct ls2kbmc_pdata, b= mc_work); + + /* + * The pcie is lost when the BMC resets, + * at which point access to the pcie from other CPUs + * is suspended to prevent a crash. + */ + stop_machine(ls2kbmc_recove_pci_data, priv, NULL); + + drm_info(priv->ddev, "redraw console\n"); + + /* We need to re-push the display due to previous pcie loss. */ + ls2kbmc_push_drm_mode(priv->ddev); +} + +static irqreturn_t ls2kbmc_interrupt(int irq, void *arg) +{ + struct ls2kbmc_pdata *priv =3D arg; + + if (system_state !=3D SYSTEM_RUNNING) + return IRQ_HANDLED; + + /* skip interrupt in BMC_RESET_DELAY */ + if (time_after(jiffies, priv->reset_time + BMC_RESET_DELAY)) + schedule_work(&priv->bmc_work); + + priv->reset_time =3D jiffies; + + return IRQ_HANDLED; +} + +#define BMC_RESET_GPIO 14 +#define LOONGSON_GPIO_REG_BASE 0x1fe00500 +#define LOONGSON_GPIO_REG_SIZE 0x18 +#define LOONGSON_GPIO_OEN 0x0 +#define LOONGSON_GPIO_FUNC 0x4 +#define LOONGSON_GPIO_INTPOL 0x10 +#define LOONGSON_GPIO_INTEN 0x14 + +/* The gpio interrupt is a watchdog interrupt that is triggered when the B= MC resets. */ +static int ls2kbmc_gpio_reset_handler(struct ls2kbmc_pdata *priv) +{ + int irq, ret =3D 0; + int gsi =3D 16 + (BMC_RESET_GPIO & 7); + void __iomem *gpio_base; + + /* Since Loongson-3A hardware does not support GPIO interrupt cascade, + * chip->gpio_to_irq() cannot be implemented, + * here acpi_register_gsi() is used to get gpio irq. + */ + irq =3D acpi_register_gsi(NULL, gsi, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW= ); + if (irq < 0) + return irq; + + gpio_base =3D ioremap(LOONGSON_GPIO_REG_BASE, LOONGSON_GPIO_REG_SIZE); + if (!gpio_base) { + acpi_unregister_gsi(gsi); + return PTR_ERR(gpio_base); + } + + writel(readl(gpio_base + LOONGSON_GPIO_OEN) | BIT(BMC_RESET_GPIO), + gpio_base + LOONGSON_GPIO_OEN); + writel(readl(gpio_base + LOONGSON_GPIO_FUNC) & ~BIT(BMC_RESET_GPIO), + gpio_base + LOONGSON_GPIO_FUNC); + writel(readl(gpio_base + LOONGSON_GPIO_INTPOL) & ~BIT(BMC_RESET_GPIO), + gpio_base + LOONGSON_GPIO_INTPOL); + writel(readl(gpio_base + LOONGSON_GPIO_INTEN) | BIT(BMC_RESET_GPIO), + gpio_base + LOONGSON_GPIO_INTEN); + + ret =3D request_irq(irq, ls2kbmc_interrupt, IRQF_SHARED | IRQF_TRIGGER_FA= LLING, + "ls2kbmc gpio", priv); + + acpi_unregister_gsi(gsi); + iounmap(gpio_base); + + return ret; +} + +static int ls2kbmc_pdata_initial(struct platform_device *pdev, struct ls2k= bmc_pdata *priv) +{ + int ret; + + priv->pdev =3D *(struct pci_dev **)dev_get_platdata(&pdev->dev); + + ls2kbmc_save_pci_data(priv); + + INIT_WORK(&priv->bmc_work, ls2kbmc_events_fn); + + ret =3D request_irq(priv->pdev->irq, ls2kbmc_interrupt, + IRQF_SHARED | IRQF_TRIGGER_RISING, "ls2kbmc pcie", priv); + if (ret) { + pr_err("request_irq(%d) failed\n", priv->pdev->irq); + return ret; + } + + return ls2kbmc_gpio_reset_handler(priv); +} + /* * Platform driver */ @@ -598,12 +877,15 @@ static int ls2kbmc_probe(struct platform_device *pdev) if (IS_ERR(priv)) return -ENOMEM; =20 - priv->pdev =3D *(struct pci_dev **)dev_get_platdata(&pdev->dev); + ret =3D ls2kbmc_pdata_initial(pdev, priv); + if (ret) + return ret; =20 sdev =3D ls2kbmc_device_create(&ls2kbmc_driver, pdev, priv); if (IS_ERR(sdev)) return PTR_ERR(sdev); dev =3D &sdev->dev; + priv->ddev =3D &sdev->dev; =20 ret =3D drm_dev_register(dev, 0); if (ret) --=20 2.43.5