From nobody Fri Dec 19 19:07:37 2025 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9FDA21A23B1 for ; Mon, 30 Dec 2024 09:31:22 +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=1735551086; cv=none; b=ByFGp+6uUO3Josla7LWXlYUV+upYlSdp1cjhBxlBmj+wTq+Jj159Ct5wu8zX+ppuHREQdcjD81iTqsjICTvgVNZKaj/X9G7s5isWqekoZh8UoMF+miRmSY8jWHzqkau6PHRJo3mvIgpZ1qY9ysQ97eRDhrzgdGWccxedyHSFlb8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735551086; c=relaxed/simple; bh=Q7LzPmcoeSwIbMinzQKXPqJsaFsmJP9+QsXs8KW/x4E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kUKtPkYmNKfLqJPqVRXg5iiRNzpmpHxNWTFbCm+hdI407Ovi//HXwwatDKfawi/TXcy0yGqZVyOwb/GAK7n6Lcc8HepsiBxBdlaKWDyTlyXzQoXq+PrCFOfIW92M4YuujsW9Q+FgLezDw55pyvpYD0LTPk0OZB+OAskLf8POHic= 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 _____8CxPuNoaHJnEM9bAA--.48440S3; Mon, 30 Dec 2024 17:31:20 +0800 (CST) Received: from localhost.localdomain (unknown [223.64.68.63]) by front1 (Coremail) with SMTP id qMiowMCxncVkaHJnp74NAA--.4295S3; Mon, 30 Dec 2024 17:31:18 +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 1/4] mfd: ls2kbmc: Introduce Loongson-2K BMC MFD Core driver Date: Mon, 30 Dec 2024 17:31:08 +0800 Message-ID: <074e91224f0e48861e11954dc80c92de322906a2.1735550269.git.zhoubinbin@loongson.cn> 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: qMiowMCxncVkaHJnp74NAA--.4295S3 X-CM-SenderInfo: p2kr3uplqex0o6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj93XoW3XrykWrWDAw1kJr4xKFWDJrc_yoW7uw43pF n3Jay5CF4kXF47GrsxWr1UCFsxua9Yya4jqrsrJ34avayay34UZ348tF9xZryDGryq9ry7 tFWYyw1jkFs8JabCm3ZEXasCq-sJn29KB7ZKAUJUUUUD529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUBIb4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r126r13M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Gr0_Xr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr0_Cr1l84ACjcxK6I8E87Iv67AKxVW8Jr0_Cr1UM28EF7xvwVC2z280aVCY1x0267AK xVW8Jr0_Cr1UM2kKe7AKxVWUAVWUtwAS0I0E0xvYzxvE52x082IY62kv0487Mc804VCY07 AIYIkI8VC2zVCFFI0UMc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AKxVWU tVWrXwAv7VC2z280aVAFwI0_Gr0_Cr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0Y48IcxkI7V AKI48JMxkF7I0En4kS14v26r1q6r43MxAIw28IcxkI7VAKI48JMxC20s026xCaFVCjc4AY 6r1j6r4UMxCIbckI1I0E14v26r126r1DMI8I3I0E5I8CrVAFwI0_Jr0_Jr4lx2IqxVCjr7 xvwVAFwI0_JrI_JrWlx4CE17CEb7AF67AKxVW8ZVWrXwCIc40Y0x0EwIxGrwCI42IY6xII jxv20xvE14v26r4j6ryUMIIF0xvE2Ix0cI8IcVCY1x0267AKxVW8JVWxJwCI42IY6xAIw2 0EY4v20xvaj40_Jr0_JF4lIxAIcVC2z280aVAFwI0_Gr0_Cr1lIxAIcVC2z280aVCY1x02 67AKxVW8JVW8JrUvcSsGvfC2KfnxnUUI43ZEXa7IU8BuWPUUUUU== Content-Type: text/plain; charset="utf-8" The Loongson-2K Board Management Controller provides an PCIe interface to the host to access the feature implemented in the BMC. The BMC is assembled on a server similar to the server machine with Loongson-3C6000 CPUs. It supports multiple sub-devices like DRM. Co-developed-by: Chong Qiao Signed-off-by: Chong Qiao Signed-off-by: Binbin Zhou --- drivers/mfd/Kconfig | 15 ++++ drivers/mfd/Makefile | 2 + drivers/mfd/ls2kbmc-mfd.c | 145 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 drivers/mfd/ls2kbmc-mfd.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ae23b317a64e..97d9d52b7e8a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2414,5 +2414,20 @@ config MFD_RSMU_SPI Additional drivers must be enabled in order to use the functionality of the device. =20 +config MFD_LS2K_BMC + tristate "Loongson-2K Board Management Controller Support" + depends on LOONGARCH + select MFD_CORE + help + Say yes here to add support for the Loongson-2K BMC + which is a Board Management Controller connected to the PCIe bus. + The device supports multiple sub-devices like DRM. + This driver provides common support for accessing the devices; + additional drivers must be enabled in order to use the + functionality of the BMC device. + + This driver can also be built as a module. If so the module + will be called ls2kbmc-mfd. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e057d6d6faef..3faec9638303 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -290,3 +290,5 @@ obj-$(CONFIG_MFD_ATC260X_I2C) +=3D atc260x-i2c.o =20 obj-$(CONFIG_MFD_RSMU_I2C) +=3D rsmu_i2c.o rsmu_core.o obj-$(CONFIG_MFD_RSMU_SPI) +=3D rsmu_spi.o rsmu_core.o + +obj-$(CONFIG_MFD_LS2K_BMC) +=3D ls2kbmc-mfd.o diff --git a/drivers/mfd/ls2kbmc-mfd.c b/drivers/mfd/ls2kbmc-mfd.c new file mode 100644 index 000000000000..2912112c41c8 --- /dev/null +++ b/drivers/mfd/ls2kbmc-mfd.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Loongson-2K Board Management Controller (BMC) MFD Core Driver. + * + * Copyright (C) 2024 Loongson Technology Corporation Limited. + * + * Originally written by Chong Qiao + * Rewritten for mainline by Binbin Zhou + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct resource ls2k_display_resources[] =3D { + { + .name =3D "ls2kbmc-simplebuf-res", + .start =3D SZ_16M + SZ_2M, + .end =3D SZ_16M + SZ_2M + SZ_4M - 1, + .flags =3D IORESOURCE_MEM, + }, +}; + +static struct resource ls2k_ipmi0_resources[] =3D { + { + .name =3D "ipmi-res0", + .start =3D SZ_16M + 0x00f00000, + .end =3D SZ_16M + 0x00f00000 + 0x1c - 1, + .flags =3D IORESOURCE_MEM, + }, +}; + +static struct resource ls2k_ipmi1_resources[] =3D { + { + .name =3D "ipmi-res1", + .start =3D SZ_16M + 0x00f00000 + 0x1c, + .end =3D SZ_16M + 0x00f00000 + 0x1c * 2 - 1, + .flags =3D IORESOURCE_MEM, + }, +}; + +static struct resource ls2k_ipmi2_resources[] =3D { + { + .name =3D "ipmi-res2", + .start =3D SZ_16M + 0x00f00000 + 0x1c * 2, + .end =3D SZ_16M + 0x00f00000 + 0x1c * 3 - 1, + .flags =3D IORESOURCE_MEM, + }, +}; + +static struct resource ls2k_ipmi3_resources[] =3D { + { + .name =3D "ipmi-res3", + .start =3D SZ_16M + 0x00f00000 + 0x1c * 3, + .end =3D SZ_16M + 0x00f00000 + 0x1c * 4 - 1, + .flags =3D IORESOURCE_MEM, + }, +}; + +static struct resource ls2k_ipmi4_resources[] =3D { + { + .name =3D "ipmi-res4", + .start =3D SZ_16M + 0x00f00000 + 0x1c * 4, + .end =3D SZ_16M + 0x00f00000 + 0x1c * 5 - 1, + .flags =3D IORESOURCE_MEM, + }, +}; + +static struct mfd_cell ls2k_bmc_cells[] =3D { + { + .name =3D "ls2kbmc-framebuffer", + .num_resources =3D ARRAY_SIZE(ls2k_display_resources), + .resources =3D ls2k_display_resources, + }, + { + .name =3D "ls2k-ipmi-si", + .num_resources =3D ARRAY_SIZE(ls2k_ipmi0_resources), + .resources =3D ls2k_ipmi0_resources, + }, + { + .name =3D "ls2k-ipmi-si", + .num_resources =3D ARRAY_SIZE(ls2k_ipmi1_resources), + .resources =3D ls2k_ipmi1_resources, + }, + { + .name =3D "ls2k-ipmi-si", + .num_resources =3D ARRAY_SIZE(ls2k_ipmi2_resources), + .resources =3D ls2k_ipmi2_resources, + }, + { + .name =3D "ls2k-ipmi-si", + .num_resources =3D ARRAY_SIZE(ls2k_ipmi3_resources), + .resources =3D ls2k_ipmi3_resources, + }, + { + .name =3D "ls2k-ipmi-si", + .num_resources =3D ARRAY_SIZE(ls2k_ipmi4_resources), + .resources =3D ls2k_ipmi4_resources, + }, +}; + +static int ls2k_bmc_probe(struct pci_dev *dev, const struct pci_device_id = *id) +{ + int ret =3D 0; + + ret =3D pci_enable_device(dev); + if (ret) + return ret; + + ls2k_bmc_cells[0].platform_data =3D &dev; + ls2k_bmc_cells[0].pdata_size =3D sizeof(dev); + + return devm_mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO, + ls2k_bmc_cells, ARRAY_SIZE(ls2k_bmc_cells), + &dev->resource[0], 0, NULL); +} + +static void ls2k_bmc_remove(struct pci_dev *dev) +{ + pci_disable_device(dev); +} + +static struct pci_device_id ls2k_bmc_devices[] =3D { + { PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x1a05) }, + { } +}; +MODULE_DEVICE_TABLE(pci, ls2k_bmc_devices); + +static struct pci_driver ls2k_bmc_driver =3D { + .name =3D "ls2k-bmc", + .id_table =3D ls2k_bmc_devices, + .probe =3D ls2k_bmc_probe, + .remove =3D ls2k_bmc_remove, +}; + +module_pci_driver(ls2k_bmc_driver); + +MODULE_DESCRIPTION("Loongson-2K BMC driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); --=20 2.43.5 From nobody Fri Dec 19 19:07:37 2025 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by smtp.subspace.kernel.org (Postfix) with ESMTP id CA2941A256B for ; Mon, 30 Dec 2024 09:31:37 +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=1735551099; cv=none; b=Ue7a+gYyp9zbe4J5o8VWBcERUCaJlnKONJJJofFeGcqKVgCfFfitKp+9qRvi5nMO4CNN//WMWhrIRbuh9rM2dakiNSpxBd9WaTMd+raWdf/6gMDp0SU1pBCmLQZLTTKjz2bZuNNCwtoisYHFmkpU1B7UHi59clYFW1Lio/iW0kk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735551099; c=relaxed/simple; bh=Msv0l4BrNFwtmUlpgJuSYwD3cDeYqqYSA5dJVfi/jiw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kwDl/lFIGk0OcOVbGhRy0gfYdiY6OeAg+ApSm93Lf84PucX0Vpn5+gLWJYAfqvUBo7FLzErnTqVP9fMmjESYqHAb0jWiW74vbi9WGgZBMoUQ5bBXQV2Y9vu+sHpl7HbX7vXxCWZVa0Uf6yyLZsn6a4TvJEJ3j2rE5XuNxzjsLw0= 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 _____8CxyuF0aHJnQs9bAA--.52326S3; Mon, 30 Dec 2024 17:31:32 +0800 (CST) Received: from localhost.localdomain (unknown [223.64.68.63]) by front1 (Coremail) with SMTP id qMiowMCxncVkaHJnp74NAA--.4295S4; Mon, 30 Dec 2024 17:31:19 +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 2/4] ipmi: Add Loongson-2K BMC support Date: Mon, 30 Dec 2024 17:31:09 +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: qMiowMCxncVkaHJnp74NAA--.4295S4 X-CM-SenderInfo: p2kr3uplqex0o6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj93XoW3XFyUJF17Ar45KFW7Ary8CrX_yoW3tw4Upa 15K34kCw48tF47K3srJrykZFWrJr93Wa45tFW7X3yfurW3try0grn2yFy3ZF9rKFyDKF13 tFZ8Ar43WFW7A3gCm3ZEXasCq-sJn29KB7ZKAUJUUUUD529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUBab4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r126r13M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Ar0_tr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Cr0_Gr1UM28EF7xvwVC2z280aVAFwI0_Gr1j6F4UJwA2z4x0Y4vEx4A2jsIEc7CjxVAF wI0_Gr1j6F4UJwAaw2AFwI0_JF0_Jw1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2 xF0cIa020Ex4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_ Wrv_ZF1lYx0Ex4A2jsIE14v26r4j6F4UMcvjeVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x 0EwIxGrwCY1x0262kKe7AKxVWUtVW8ZwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkE bVWUJVW8JwCFI7km07C267AKxVWUAVWUtwC20s026c02F40E14v26r1j6r18MI8I3I0E74 80Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_GFv_WrylIxkGc2Ij64vIr41lIxAIcVC0 I7IYx2IY67AKxVW5JVW7JwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAIcVCF04 k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r4j6F4UMIIF0xvEx4A2jsIEc7Cj xVAFwI0_Gr0_Gr1UYxBIdaVFxhVjvjDU0xZFpf9x07j-b1bUUUUU= Content-Type: text/plain; charset="utf-8" This patch adds Loongson-2K BMC IPMI support. According to the existing design, we use software simulation to implement the KCS interface registers: Stauts/Command/Data_Out/Data_In. Also since both host side and BMC side read and write kcs status, I use fifo pointer to ensure data consistency. Therefore I made the whole IPMI driver independent. Co-developed-by: Chong Qiao Signed-off-by: Chong Qiao Signed-off-by: Binbin Zhou --- drivers/char/ipmi/Makefile | 1 + drivers/char/ipmi/ipmi_si.h | 8 + drivers/char/ipmi/ipmi_si_intf.c | 3 + drivers/char/ipmi/ipmi_si_ls2k.c | 250 +++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 drivers/char/ipmi/ipmi_si_ls2k.c diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index e0944547c9d0..614be45863b4 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -7,6 +7,7 @@ ipmi_si-y :=3D ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o = ipmi_bt_sm.o \ ipmi_si_hotmod.o ipmi_si_hardcode.o ipmi_si_platform.o \ ipmi_si_mem_io.o ipmi_si-$(CONFIG_HAS_IOPORT) +=3D ipmi_si_port_io.o +ipmi_si-$(CONFIG_LOONGARCH) +=3D ipmi_si_ls2k.o ipmi_si-$(CONFIG_PCI) +=3D ipmi_si_pci.o ipmi_si-$(CONFIG_PARISC) +=3D ipmi_si_parisc.o =20 diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h index a7ead2a4c753..0a4af352a42c 100644 --- a/drivers/char/ipmi/ipmi_si.h +++ b/drivers/char/ipmi/ipmi_si.h @@ -101,6 +101,14 @@ static inline void ipmi_si_parisc_init(void) { } static inline void ipmi_si_parisc_shutdown(void) { } #endif =20 +#ifdef CONFIG_LOONGARCH +void ipmi_si_ls2k_init(void); +void ipmi_si_ls2k_shutdown(void); +#else +static inline void ipmi_si_ls2k_init(void) { } +static inline void ipmi_si_ls2k_shutdown(void) { } +#endif + int ipmi_si_port_setup(struct si_sm_io *io); int ipmi_si_mem_setup(struct si_sm_io *io); =20 diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_i= ntf.c index eea23a3b966e..7227bc61be79 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2108,6 +2108,7 @@ static int __init init_ipmi_si(void) ipmi_si_pci_init(); =20 ipmi_si_parisc_init(); + ipmi_si_ls2k_init(); =20 /* We prefer devices with interrupts, but in the case of a machine with multiple BMCs we assume that there will be several instances @@ -2292,6 +2293,8 @@ static void cleanup_ipmi_si(void) =20 ipmi_si_platform_shutdown(); =20 + ipmi_si_ls2k_shutdown(); + mutex_lock(&smi_infos_lock); list_for_each_entry_safe(e, tmp_e, &smi_infos, link) cleanup_one_si(e); diff --git a/drivers/char/ipmi/ipmi_si_ls2k.c b/drivers/char/ipmi/ipmi_si_l= s2k.c new file mode 100644 index 000000000000..cb31bb989fca --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_ls2k.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Loongson-2K BMC IPMI + * + * Copyright (C) 2024 Loongson Technology Corporation Limited. + * + * Originally written by Chong Qiao + * Rewritten for mainline by Binbin Zhou + */ + +#include +#include +#include + +#include "ipmi_si.h" + +#define LS2K_KCS_STS_OBF BIT(0) +#define LS2K_KCS_STS_IBF BIT(1) +#define LS2K_KCS_STS_SMS_ATN BIT(2) +#define LS2K_KCS_STS_CMD BIT(3) + +#define LS2K_KCS_DATA_MASK (LS2K_KCS_STS_OBF | LS2K_KCS_STS_IBF | LS2K_KCS= _STS_CMD) + +/* Read and write fifo pointers for data consistency. */ +struct ls2k_fifo_flag { + u8 ibfh; + u8 ibft; + u8 obfh; + u8 obft; +}; + +struct ls2k_kcs_reg { + u8 status; + u8 data_out; + s16 data_in; + s16 cmd; +}; + +struct ls2k_kcs_data { + struct ls2k_fifo_flag fifo; + struct ls2k_kcs_reg reg; + u8 cmd_data; + u8 version; + u32 write_req; + u32 write_ack; + u32 reserved[2]; +}; + +static void ls2k_set_obf(struct ls2k_kcs_data *ik, u8 sts) +{ + ik->reg.status =3D (ik->reg.status & ~LS2K_KCS_STS_OBF) | (sts & BIT(0)); +} + +static void ls2k_set_ibf(struct ls2k_kcs_data *ik, u8 sts) +{ + ik->reg.status =3D (ik->reg.status & ~LS2K_KCS_STS_IBF) | ((sts & BIT(0))= << 1); +} + +static u8 ls2k_get_ibf(struct ls2k_kcs_data *ik) +{ + return (ik->reg.status >> 1) & BIT(0); +} + +static unsigned char intf_sim_inb_v0(struct ls2k_kcs_data *ik, + unsigned int offset) +{ + u32 inb =3D 0; + + switch (offset & BIT(0)) { + case 0: + inb =3D ik->reg.data_out; + ls2k_set_obf(ik, 0); + break; + case 1: + inb =3D ik->reg.status; + break; + } + + return inb; +} + +static unsigned char intf_sim_inb_v1(struct ls2k_kcs_data *ik, + unsigned int offset) +{ + u32 inb =3D 0; + int cmd; + bool obf, ibf; + + obf =3D ik->fifo.obfh !=3D ik->fifo.obft; + ibf =3D ik->fifo.ibfh !=3D ik->fifo.ibft; + cmd =3D ik->cmd_data; + + switch (offset & BIT(0)) { + case 0: + inb =3D ik->reg.data_out; + ik->fifo.obft =3D ik->fifo.obfh; + break; + case 1: + inb =3D ik->reg.status & ~LS2K_KCS_DATA_MASK; + inb |=3D obf | (ibf << 1) | (cmd << 3); + break; + } + + return inb; +} + +static unsigned char ls2k_mem_inb(const struct si_sm_io *io, + unsigned int offset) +{ + struct ls2k_kcs_data *ik =3D io->addr; + int inb =3D 0; + + if (ik->version =3D=3D 0) + inb =3D intf_sim_inb_v0(ik, offset); + else if (ik->version =3D=3D 1) + inb =3D intf_sim_inb_v1(ik, offset); + + return inb; +} + +static void intf_sim_outb_v0(struct ls2k_kcs_data *ik, unsigned int offset, + unsigned char val) +{ + if (ls2k_get_ibf(ik)) + return; + + switch (offset & BIT(0)) { + case 0: + ik->reg.data_in =3D val; + ik->reg.status &=3D ~LS2K_KCS_STS_CMD; + break; + + case 1: + ik->reg.cmd =3D val; + ik->reg.status |=3D LS2K_KCS_STS_CMD; + break; + } + + ls2k_set_ibf(ik, 1); + ik->write_req++; +} + +static void intf_sim_outb_v1(struct ls2k_kcs_data *ik, unsigned int offset, + unsigned char val) +{ + if (ik->fifo.ibfh !=3D ik->fifo.ibft) + return; + + switch (offset & BIT(0)) { + case 0: + ik->reg.data_in =3D val; + ik->cmd_data =3D 0; + break; + + case 1: + ik->reg.cmd =3D val; + ik->cmd_data =3D 1; + break; + } + + ik->fifo.ibfh =3D !ik->fifo.ibft; + ik->write_req++; +} + +static void ls2k_mem_outb(const struct si_sm_io *io, unsigned int offset, + unsigned char val) +{ + struct ls2k_kcs_data *ik =3D io->addr; + + if (ik->version =3D=3D 0) + intf_sim_outb_v0(ik, offset, val); + else if (ik->version =3D=3D 1) + intf_sim_outb_v1(ik, offset, val); +} + +static void ls2k_mem_cleanup(struct si_sm_io *io) +{ + if (io->addr) + iounmap(io->addr); +} + +static int ipmi_ls2k_sim_setup(struct si_sm_io *io) +{ + io->addr =3D ioremap(io->addr_data, io->regspacing); + if (!io->addr) + return -EIO; + + io->inputb =3D ls2k_mem_inb; + io->outputb =3D ls2k_mem_outb; + io->io_cleanup =3D ls2k_mem_cleanup; + + return 0; +} + +static int ipmi_ls2k_probe(struct platform_device *pdev) +{ + struct si_sm_io io; + + dev_info(&pdev->dev, "probing via ls2k platform"); + memset(&io, 0, sizeof(io)); + + io.addr_source =3D SI_PLATFORM; + io.si_type =3D SI_KCS; + io.addr_space =3D IPMI_MEM_ADDR_SPACE; + io.io_setup =3D ipmi_ls2k_sim_setup; + io.addr_data =3D pdev->resource[0].start; + io.regspacing =3D pdev->resource[0].end - pdev->resource[0].start + 1; + io.regsize =3D DEFAULT_REGSIZE; + io.regshift =3D 0; + io.dev =3D &pdev->dev; + io.irq =3D 0; + if (io.irq) + io.irq_setup =3D ipmi_std_irq_setup; + + dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n", + &pdev->resource[0], io.regsize, io.regspacing, io.irq); + + return ipmi_si_add_smi(&io); +} + +static void ipmi_ls2k_remove(struct platform_device *pdev) +{ + ipmi_si_remove_by_dev(&pdev->dev); +} + +struct platform_driver ipmi_ls2k_platform_driver =3D { + .driver =3D { + .name =3D "ls2k-ipmi-si", + }, + .probe =3D ipmi_ls2k_probe, + .remove =3D ipmi_ls2k_remove, +}; + +static bool platform_registered; +void ipmi_si_ls2k_init(void) +{ + int rv; + + rv =3D platform_driver_register(&ipmi_ls2k_platform_driver); + if (rv) + pr_err("Unable to register driver: %d\n", rv); + else + platform_registered =3D true; +} + +void ipmi_si_ls2k_shutdown(void) +{ + if (platform_registered) + platform_driver_unregister(&ipmi_ls2k_platform_driver); +} --=20 2.43.5 From nobody Fri Dec 19 19:07:37 2025 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 25F441A23A9 for ; Mon, 30 Dec 2024 09:31:33 +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=1735551096; cv=none; b=cMB3bcjqJllzR/7JCcpq2dEZqA0XEN49+LfjrTp1ROXuKbYISdHF0fj82LP2b8qLfInp06TfrGbA6psG9oMsspI84LN/JIaxPCCfk6xurYw0UHa0r5+d6HmWg/340FSmXisZzEkMnWm7fFLWMbXvbzTafiuqEibCgKQAnqjI3JI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735551096; c=relaxed/simple; bh=wdeTuKnWdVlxsgYdcXaCluV0Rql2njun8x8xNjQo49g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=twgR17KFDA6wkpGB8BvAOOJQkwe/IFjM1vl2/OHcNFRr0xwopjIWWNTI1Bq2CiDlPiSqOQGbSgHdAGJMINLoa925HmyKq2UoRPdJa85nPwzcNwQYKU+s4GpWzON4GhJrDt/zmSqG5JdvStiA3oOmREcQqUB3oNk20exZn6aDWK0= 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 _____8AxLOJ0aHJnQM9bAA--.48422S3; Mon, 30 Dec 2024 17:31:32 +0800 (CST) Received: from localhost.localdomain (unknown [223.64.68.63]) by front1 (Coremail) with SMTP id qMiowMCxncVkaHJnp74NAA--.4295S5; Mon, 30 Dec 2024 17:31:21 +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 3/4] drm/ls2kbmc: Add support for Loongson-2K BMC display Date: Mon, 30 Dec 2024 17:31:10 +0800 Message-ID: <4d62076ee560b35c653a9fbced8d03ef3bdb3005.1735550269.git.zhoubinbin@loongson.cn> 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: qMiowMCxncVkaHJnp74NAA--.4295S5 X-CM-SenderInfo: p2kr3uplqex0o6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj9fXoWfWr4kurykGw4xAr48Xry7Jwc_yoW8uF48Xo Z7XF13Xw48Gw1Fq39xGr1ktanavayvga93G345ur4vva1DWw1YyFW7Ww1YvFWxZr1rtr4D Zr1qvF1xJF4xJan5l-sFpf9Il3svdjkaLaAFLSUrUUUU5b8apTn2vfkv8UJUUUU8wcxFpf 9Il3svdxBIdaVrn0xqx4xG64xvF2IEw4CE5I8CrVC2j2Jv73VFW2AGmfu7bjvjm3AaLaJ3 UjIYCTnIWjp_UUUYZ7kC6x804xWl14x267AKxVWUJVW8JwAFc2x0x2IEx4CE42xK8VAvwI 8IcIk0rVWrJVCq3wAFIxvE14AKwVWUAVWUZwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xG Y2AK021l84ACjcxK6xIIjxv20xvE14v26F1j6w1UM28EF7xvwVC0I7IYx2IY6xkF7I0E14 v26F4j6r4UJwA2z4x0Y4vEx4A2jsIE14v26r4UJVWxJr1l84ACjcxK6I8E87Iv6xkF7I0E 14v26r4UJVWxJr1ln4kS14v26r126r1DM2AIxVAIcxkEcVAq07x20xvEncxIr21l57IF6x kI12xvs2x26I8E6xACxx1l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj6xIIjxv20xvE14v2 6rWY6Fy7McIj6I8E87Iv67AKxVW8JVWxJwAm72CE4IkC6x0Yz7v_Jr0_Gr1lF7xvr2IYc2 Ij64vIr41lc7CjxVAaw2AFwI0_Jw0_GFyl42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Y z7v_Jr0_Gr1l4IxYO2xFxVAFwI0_JF0_Jw1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x 8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r4a6rW5MIIYrxkI7VAKI48JMIIF0xvE 2Ix0cI8IcVAFwI0_Xr0_Ar1lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4j6F4UMIIF0xvE42 xK8VAvwI8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVW8JVWxJwCI42IY6I8E87Iv6xkF 7I0E14v26r4j6r4UJbIYCTnIWIevJa73UjIFyTuYvjxUsDDJDUUUU Content-Type: text/plain; charset="utf-8" Adds a driver for the Loongson-2K BMC display as a sub-function of the BMC device. Display-related scan output buffers, sizes, and display formats are provided through the Loongson-2K BMC MFD driver. Co-developed-by: Chong Qiao Signed-off-by: Chong Qiao Signed-off-by: Binbin Zhou --- drivers/gpu/drm/tiny/Kconfig | 18 + drivers/gpu/drm/tiny/Makefile | 1 + drivers/gpu/drm/tiny/ls2kbmc.c | 636 +++++++++++++++++++++++++++++++++ 3 files changed, 655 insertions(+) create mode 100644 drivers/gpu/drm/tiny/ls2kbmc.c diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index 94cbdb1337c0..5412f639a964 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -171,6 +171,24 @@ config TINYDRM_ILI9486 =20 If M is selected the module will be called ili9486. =20 +config TINYDRM_LS2KBMC + tristate "DRM support for Loongson-2K BMC display" + depends on DRM && MMU + depends on MFD_LS2K_BMC + select APERTURE_HELPERS + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + help + DRM driver for the Loongson-2K BMC display. + + This driver assumes that the display hardware has been initialized + by the Loongson-2K BMC. Since the Loongson-2K BMC does not support + resolution detection now, the scan buffer, size and display format + are fixed and provided by the BMC. + + If M is selected the module will be called ls2kbmc. + config TINYDRM_MI0283QT tristate "DRM support for MI0283QT" depends on DRM && SPI diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index 4aaf56f8707d..fa4e1646db77 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_TINYDRM_ILI9163) +=3D ili9163.o obj-$(CONFIG_TINYDRM_ILI9225) +=3D ili9225.o obj-$(CONFIG_TINYDRM_ILI9341) +=3D ili9341.o obj-$(CONFIG_TINYDRM_ILI9486) +=3D ili9486.o +obj-$(CONFIG_TINYDRM_LS2KBMC) +=3D ls2kbmc.o obj-$(CONFIG_TINYDRM_MI0283QT) +=3D mi0283qt.o obj-$(CONFIG_TINYDRM_REPAPER) +=3D repaper.o obj-$(CONFIG_TINYDRM_SHARP_MEMORY) +=3D sharp-memory.o diff --git a/drivers/gpu/drm/tiny/ls2kbmc.c b/drivers/gpu/drm/tiny/ls2kbmc.c new file mode 100644 index 000000000000..909d6c687193 --- /dev/null +++ b/drivers/gpu/drm/tiny/ls2kbmc.c @@ -0,0 +1,636 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DRM driver for Loongson-2K BMC display + * + * Copyright (C) 2024 Loongson Technology Corporation Limited. + * + * Based on simpledrm + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ls2kbmc_pdata { + struct pci_dev *pdev; + struct simplefb_platform_data pd; +}; + +/* + * Helpers for simplefb_platform_data + */ + +static int +simplefb_get_validated_int(struct drm_device *dev, const char *name, + u32 value) +{ + if (value > INT_MAX) { + drm_err(dev, "simplefb: invalid framebuffer %s of %u\n", + name, value); + return -EINVAL; + } + return (int)value; +} + +static int +simplefb_get_validated_int0(struct drm_device *dev, const char *name, + u32 value) +{ + if (!value) { + drm_err(dev, "simplefb: invalid framebuffer %s of %u\n", + name, value); + return -EINVAL; + } + return simplefb_get_validated_int(dev, name, value); +} + +static const struct drm_format_info * +simplefb_get_validated_format(struct drm_device *dev, const char *format_n= ame) +{ + static const struct simplefb_format formats[] =3D SIMPLEFB_FORMATS; + const struct simplefb_format *fmt =3D formats; + const struct simplefb_format *end =3D fmt + ARRAY_SIZE(formats); + const struct drm_format_info *info; + + if (!format_name) { + drm_err(dev, "simplefb: missing framebuffer format\n"); + return ERR_PTR(-EINVAL); + } + + while (fmt < end) { + if (!strcmp(format_name, fmt->name)) { + info =3D drm_format_info(fmt->fourcc); + if (!info) + return ERR_PTR(-EINVAL); + return info; + } + ++fmt; + } + + drm_err(dev, "simplefb: unknown framebuffer format %s\n", + format_name); + + return ERR_PTR(-EINVAL); +} + +static int +simplefb_get_width_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_int0(dev, "width", pd->width); +} + +static int +simplefb_get_height_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_int0(dev, "height", pd->height); +} + +static int +simplefb_get_stride_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_int(dev, "stride", pd->stride); +} + +static const struct drm_format_info * +simplefb_get_format_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_format(dev, pd->format); +} + +/* + * ls2kbmc Framebuffer device + */ + +struct ls2kbmc_device { + struct drm_device dev; + + /* simplefb settings */ + struct drm_display_mode mode; + const struct drm_format_info *format; + unsigned int pitch; + + /* memory management */ + struct iosys_map screen_base; + + /* modesetting */ + u32 formats[8]; + struct drm_plane primary_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; +}; + +static struct ls2kbmc_device *ls2kbmc_device_of_dev(struct drm_device *dev) +{ + return container_of(dev, struct ls2kbmc_device, dev); +} + +/* + * Modesetting + */ + +static const u64 ls2kbmc_primary_plane_format_modifiers[] =3D { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static int ls2kbmc_primary_plane_helper_atomic_check(struct drm_plane *pla= ne, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state =3D drm_atomic_get_new_plane_stat= e(state, plane); + struct drm_shadow_plane_state *new_shadow_plane_state =3D + to_drm_shadow_plane_state(new_plane_state); + struct drm_framebuffer *new_fb =3D new_plane_state->fb; + struct drm_crtc *new_crtc =3D new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state =3D NULL; + struct drm_device *dev =3D plane->dev; + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(dev); + int ret; + + if (new_crtc) + new_crtc_state =3D drm_atomic_get_new_crtc_state(state, new_crtc); + + ret =3D drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_sta= te, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + else if (!new_plane_state->visible) + return 0; + + if (new_fb->format !=3D sdev->format) { + void *buf; + + /* format conversion necessary; reserve buffer */ + buf =3D drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_st= ate, + sdev->pitch, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + return 0; +} + +static void ls2kbmc_primary_plane_helper_atomic_update(struct drm_plane *p= lane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state =3D drm_atomic_get_new_plane_state(st= ate, plane); + struct drm_plane_state *old_plane_state =3D drm_atomic_get_old_plane_stat= e(state, plane); + struct drm_shadow_plane_state *shadow_plane_state =3D to_drm_shadow_plane= _state(plane_state); + struct drm_framebuffer *fb =3D plane_state->fb; + struct drm_device *dev =3D plane->dev; + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(dev); + struct drm_atomic_helper_damage_iter iter; + struct drm_rect damage; + int ret, idx; + + ret =3D drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); + if (ret) + return; + + if (!drm_dev_enter(dev, &idx)) + goto out_drm_gem_fb_end_cpu_access; + + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + struct drm_rect dst_clip =3D plane_state->dst; + struct iosys_map dst =3D sdev->screen_base; + + if (!drm_rect_intersect(&dst_clip, &damage)) + continue; + + iosys_map_incr(&dst, drm_fb_clip_offset(sdev->pitch, sdev->format, &dst_= clip)); + drm_fb_blit(&dst, &sdev->pitch, sdev->format->format, shadow_plane_state= ->data, + fb, &damage, &shadow_plane_state->fmtcnv_state); + } + + drm_dev_exit(idx); +out_drm_gem_fb_end_cpu_access: + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); +} + +static void ls2kbmc_primary_plane_helper_atomic_disable(struct drm_plane *= plane, + struct drm_atomic_state *state) +{ + struct drm_device *dev =3D plane->dev; + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(dev); + int idx; + + if (!drm_dev_enter(dev, &idx)) + return; + + /* Clear screen to black if disabled */ + iosys_map_memset(&sdev->screen_base, 0, 0, sdev->pitch * sdev->mode.vdisp= lay); + + drm_dev_exit(idx); +} + +static int ls2kbmc_primary_plane_helper_get_scanout_buffer(struct drm_plan= e *plane, + struct drm_scanout_buffer *sb) +{ + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(plane->dev); + + sb->width =3D sdev->mode.hdisplay; + sb->height =3D sdev->mode.vdisplay; + sb->format =3D sdev->format; + sb->pitch[0] =3D sdev->pitch; + sb->map[0] =3D sdev->screen_base; + + return 0; +} + +static const struct drm_plane_helper_funcs ls2kbmc_primary_plane_helper_fu= ncs =3D { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check =3D ls2kbmc_primary_plane_helper_atomic_check, + .atomic_update =3D ls2kbmc_primary_plane_helper_atomic_update, + .atomic_disable =3D ls2kbmc_primary_plane_helper_atomic_disable, + .get_scanout_buffer =3D ls2kbmc_primary_plane_helper_get_scanout_buffer, +}; + +static const struct drm_plane_funcs ls2kbmc_primary_plane_funcs =3D { + .update_plane =3D drm_atomic_helper_update_plane, + .disable_plane =3D drm_atomic_helper_disable_plane, + .destroy =3D drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, +}; + +static enum drm_mode_status ls2kbmc_crtc_helper_mode_valid(struct drm_crtc= *crtc, + const struct drm_display_mode *mode) +{ + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(crtc->dev); + + return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sdev->mode); +} + +/* + * The CRTC is always enabled. Screen updates are performed by + * the primary plane's atomic_update function. Disabling clears + * the screen in the primary plane's atomic_disable function. + */ +static const struct drm_crtc_helper_funcs ls2kbmc_crtc_helper_funcs =3D { + .mode_valid =3D ls2kbmc_crtc_helper_mode_valid, + .atomic_check =3D drm_crtc_helper_atomic_check, +}; + +static const struct drm_crtc_funcs ls2kbmc_crtc_funcs =3D { + .reset =3D drm_atomic_helper_crtc_reset, + .destroy =3D drm_crtc_cleanup, + .set_config =3D drm_atomic_helper_set_config, + .page_flip =3D drm_atomic_helper_page_flip, + .atomic_duplicate_state =3D drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state =3D drm_atomic_helper_crtc_destroy_state, +}; + +static const struct drm_encoder_funcs ls2kbmc_encoder_funcs =3D { + .destroy =3D drm_encoder_cleanup, +}; + +static int ls2kbmc_connector_helper_get_modes(struct drm_connector *connec= tor) +{ + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(connector->dev); + + return drm_connector_helper_get_modes_fixed(connector, &sdev->mode); +} + +static const struct drm_connector_helper_funcs ls2kbmc_connector_helper_fu= ncs =3D { + .get_modes =3D ls2kbmc_connector_helper_get_modes, +}; + +static const struct drm_connector_funcs ls2kbmc_connector_funcs =3D { + .reset =3D drm_atomic_helper_connector_reset, + .fill_modes =3D drm_helper_probe_single_connector_modes, + .destroy =3D drm_connector_cleanup, + .atomic_duplicate_state =3D drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state =3D drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_mode_config_funcs ls2kbmc_mode_config_funcs =3D { + .fb_create =3D drm_gem_fb_create_with_dirty, + .atomic_check =3D drm_atomic_helper_check, + .atomic_commit =3D drm_atomic_helper_commit, +}; + +/* + * Init / Cleanup + */ + +static struct drm_display_mode ls2kbmc_mode(unsigned int width, unsigned i= nt height, + unsigned int width_mm, unsigned int height_mm) +{ + const struct drm_display_mode mode =3D { + DRM_MODE_INIT(60, width, height, width_mm, height_mm) + }; + + return mode; +} + +/* + * DRM driver + */ + +DEFINE_DRM_GEM_FOPS(ls2kbmc_fops); + +static struct drm_driver ls2kbmc_driver =3D { + DRM_GEM_SHMEM_DRIVER_OPS, + DRM_FBDEV_SHMEM_DRIVER_OPS, + .name =3D "simpledrm", + .desc =3D "DRM driver for Loongson-2K BMC", + .date =3D "20241211", + .major =3D 1, + .minor =3D 0, + .driver_features =3D DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, + .fops =3D &ls2kbmc_fops, +}; + +/* + * Currently the Loongson-2K0500 BMC hardware does not have an i2c interfa= ce to + * adapt to the resolution. + * We set the resolution by presetting "video=3D1280x1024-16@2M" to the bm= c memory. + */ +static int ls2kbmc_get_video_mode(struct pci_dev *pdev, struct simplefb_pl= atform_data *pd) +{ + char *mode; + int depth, ret; + + /* The pci mem bar last 16M is used to store the string. */ + mode =3D devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0) + SZ_16M, S= Z_16M); + if (!mode) + return -ENOMEM; + + /*env at last 16M's beginning, first env is video */ + if (!strncmp(mode, "video=3D", 6)) + mode =3D mode + 6; + + ret =3D kstrtoint(strsep(&mode, "x"), 10, &pd->width); + if (ret) + return ret; + + ret =3D kstrtoint(strsep(&mode, "-"), 10, &pd->height); + if (ret) + return ret; + + ret =3D kstrtoint(strsep(&mode, "@"), 10, &depth); + if (ret) + return ret; + + pd->stride =3D pd->width * depth / 8; + pd->format =3D depth =3D=3D 32 ? "a8r8g8b8" : "r5g6b5"; + + return 0; +} + +static struct ls2kbmc_device *ls2kbmc_device_create(struct drm_driver *drv, + struct platform_device *pdev, + struct ls2kbmc_pdata *priv) +{ + struct pci_dev *ppdev =3D priv->pdev; + struct simplefb_platform_data *pd =3D &priv->pd; + struct ls2kbmc_device *sdev; + struct drm_device *dev; + int width, height, stride; + int width_mm =3D 0, height_mm =3D 0; + const struct drm_format_info *format; + struct resource *res, *mem =3D NULL; + struct drm_plane *primary_plane; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + unsigned long max_width, max_height; + void __iomem *screen_base; + size_t nformats; + int ret; + + sdev =3D devm_drm_dev_alloc(&pdev->dev, drv, struct ls2kbmc_device, dev); + if (IS_ERR(sdev)) + return ERR_CAST(sdev); + dev =3D &sdev->dev; + platform_set_drvdata(pdev, sdev); + + ret =3D ls2kbmc_get_video_mode(ppdev, pd); + if (ret) { + drm_err(dev, "no simplefb configuration found\n"); + return ERR_PTR(ret); + } + + width =3D simplefb_get_width_pd(dev, pd); + if (width < 0) + return ERR_PTR(width); + + height =3D simplefb_get_height_pd(dev, pd); + if (height < 0) + return ERR_PTR(height); + + stride =3D simplefb_get_stride_pd(dev, pd); + if (stride < 0) + return ERR_PTR(stride); + + if (!stride) { + stride =3D drm_format_info_min_pitch(format, 0, width); + if (drm_WARN_ON(dev, !stride)) + return ERR_PTR(-EINVAL); + } + + format =3D simplefb_get_format_pd(dev, pd); + if (IS_ERR(format)) + return ERR_CAST(format); + + /* + * Assume a monitor resolution of 96 dpi if physical dimensions + * are not specified to get a somewhat reasonable screen size. + */ + if (!width_mm) + width_mm =3D DRM_MODE_RES_MM(width, 96ul); + if (!height_mm) + height_mm =3D DRM_MODE_RES_MM(height, 96ul); + + sdev->mode =3D ls2kbmc_mode(width, height, width_mm, height_mm); + sdev->format =3D format; + sdev->pitch =3D stride; + + drm_dbg(dev, "display mode=3D{" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sdev->m= ode)); + drm_dbg(dev, "framebuffer format=3D%p4cc, size=3D%dx%d, stride=3D%d byte\= n", + &format->format, width, height, stride); + + /* + * Memory management + */ + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return ERR_PTR(-EINVAL); + + ret =3D aperture_remove_conflicting_pci_devices(ppdev, ls2kbmc_driver.nam= e); + if (ret) { + drm_err(dev, "could not acquire memory range %pr: %d\n", res, ret); + return ERR_PTR(ret); + } + + drm_dbg(dev, "using I/O memory framebuffer at %pr\n", res); + + mem =3D devm_request_mem_region(&ppdev->dev, res->start, resource_size(re= s), + drv->name); + if (!mem) { + /* + * We cannot make this fatal. Sometimes this comes from magic + * spaces our resource handlers simply don't know about. Use + * the I/O-memory resource as-is and try to map that instead. + */ + drm_warn(dev, "could not acquire memory region %pr\n", res); + mem =3D res; + } + + screen_base =3D devm_ioremap_wc(&ppdev->dev, mem->start, resource_size(me= m)); + if (!screen_base) + return ERR_PTR(-ENOMEM); + + iosys_map_set_vaddr_iomem(&sdev->screen_base, screen_base); + + /* + * Modesetting + */ + + ret =3D drmm_mode_config_init(dev); + if (ret) + return ERR_PTR(ret); + + max_width =3D max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH); + max_height =3D max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT); + + dev->mode_config.min_width =3D width; + dev->mode_config.max_width =3D max_width; + dev->mode_config.min_height =3D height; + dev->mode_config.max_height =3D max_height; + dev->mode_config.preferred_depth =3D format->depth; + dev->mode_config.funcs =3D &ls2kbmc_mode_config_funcs; + + /* Primary plane */ + + nformats =3D drm_fb_build_fourcc_list(dev, &format->format, 1, + sdev->formats, ARRAY_SIZE(sdev->formats)); + + primary_plane =3D &sdev->primary_plane; + ret =3D drm_universal_plane_init(dev, primary_plane, 0, &ls2kbmc_primary_= plane_funcs, + sdev->formats, nformats, + ls2kbmc_primary_plane_format_modifiers, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ERR_PTR(ret); + drm_plane_helper_add(primary_plane, &ls2kbmc_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary_plane); + + /* CRTC */ + + crtc =3D &sdev->crtc; + ret =3D drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, + &ls2kbmc_crtc_funcs, NULL); + if (ret) + return ERR_PTR(ret); + drm_crtc_helper_add(crtc, &ls2kbmc_crtc_helper_funcs); + + /* Encoder */ + + encoder =3D &sdev->encoder; + ret =3D drm_encoder_init(dev, encoder, &ls2kbmc_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); + if (ret) + return ERR_PTR(ret); + encoder->possible_crtcs =3D drm_crtc_mask(crtc); + + /* Connector */ + + connector =3D &sdev->connector; + ret =3D drm_connector_init(dev, connector, &ls2kbmc_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) + return ERR_PTR(ret); + drm_connector_helper_add(connector, &ls2kbmc_connector_helper_funcs); + drm_connector_set_panel_orientation_with_quirk(connector, + DRM_MODE_PANEL_ORIENTATION_UNKNOWN, + width, height); + + ret =3D drm_connector_attach_encoder(connector, encoder); + if (ret) + return ERR_PTR(ret); + + drm_mode_config_reset(dev); + + return sdev; +} + +/* + * Platform driver + */ + +static int ls2kbmc_probe(struct platform_device *pdev) +{ + struct ls2kbmc_device *sdev; + struct ls2kbmc_pdata *priv; + struct drm_device *dev; + int ret; + + priv =3D devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (IS_ERR(priv)) + return -ENOMEM; + + priv->pdev =3D *(struct pci_dev **)dev_get_platdata(&pdev->dev); + + sdev =3D ls2kbmc_device_create(&ls2kbmc_driver, pdev, priv); + if (IS_ERR(sdev)) + return PTR_ERR(sdev); + dev =3D &sdev->dev; + + ret =3D drm_dev_register(dev, 0); + if (ret) + return ret; + + drm_client_setup(dev, sdev->format); + + return 0; +} + +static void ls2kbmc_remove(struct platform_device *pdev) +{ + struct ls2kbmc_device *sdev =3D platform_get_drvdata(pdev); + struct drm_device *dev =3D &sdev->dev; + + drm_dev_unplug(dev); +} + +static struct platform_driver ls2kbmc_platform_driver =3D { + .driver =3D { + .name =3D "ls2kbmc-framebuffer", + }, + .probe =3D ls2kbmc_probe, + .remove =3D ls2kbmc_remove, +}; + +module_platform_driver(ls2kbmc_platform_driver); + +MODULE_DESCRIPTION("DRM driver for Loongson-2K BMC"); +MODULE_LICENSE("GPL"); --=20 2.43.5 From nobody Fri Dec 19 19:07:37 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