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 CFA8D189916 for ; Thu, 15 May 2025 02:32:41 +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=1747276365; cv=none; b=tQ3ADoGX8XF8tWl0dO9dUPGpQvxRqWNqENrb+y2CPGUg5l6/URDGk42h6ztia1MG0Lbj53V4bMz9LuaKPVPsNugQwEvfwR7yc+y+0PxLzXx/2eyuHY+9emct9Cex1WgdaqzswwUCy7WV8KeSj9gcidsG0n9qP4aLKZM91aCkc6w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747276365; c=relaxed/simple; bh=84v9gibVD3U7yDeP5uBGKM8oyZVBjPUiJjlCHZfE55M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eFnVwzdgn+JDHjQZG8FLafO46i21jdsx78TIrzGuNPtHXajNfNZmdpOkng0p/OfBNDLCqFDjK/Fzl4sV5z92kCvgMJ7TwUpmz0No4epIwzEHsRzKbB/1CuPgEuFD6o4QSB6iZNEpcwflSo/QoF+NJtjW+geeUz58Oi4XdFZ6ea8= 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.186]) by gateway (Coremail) with SMTP id _____8AxaeFHUiVoTonoAA--.6818S3; Thu, 15 May 2025 10:32:39 +0800 (CST) Received: from localhost.localdomain (unknown [223.64.68.186]) by front1 (Coremail) with SMTP id qMiowMCxbsVDUiVo1PnTAA--.19913S3; Thu, 15 May 2025 10:32:38 +0800 (CST) From: Binbin Zhou To: Binbin Zhou , Huacai Chen , Lee Jones , Corey Minyard Cc: Huacai Chen , Xuerui Wang , loongarch@lists.linux.dev, linux-kernel@vger.kernel.org, openipmi-developer@lists.sourceforge.net, Binbin Zhou , Chong Qiao Subject: [PATCH v2 1/3] mfd: ls2kbmc: Introduce Loongson-2K BMC MFD Core driver Date: Thu, 15 May 2025 10:32:24 +0800 Message-ID: <778675bfe1040cd1bf4d281dc5c5f20edc4145c1.1747276047.git.zhoubinbin@loongson.cn> X-Mailer: git-send-email 2.47.1 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: qMiowMCxbsVDUiVo1PnTAA--.19913S3 X-CM-SenderInfo: p2kr3uplqex0o6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj93XoW3JryfWFW5ur4kKw15Kw1fXwc_yoWxtFy3p3 WxAay5GF4DJF17Wa93ur1UCFW3ua9aq3y5tay3JwnIya97Aa4kXw1ktFyavF9rJFyvgry2 qF98Xr4UCan8JFcCm3ZEXasCq-sJn29KB7ZKAUJUUUU7529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUB0b4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r126r13M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Xr0_Ar1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr0_Cr1l84ACjcxK6I8E87Iv67AKxVW0oVCq3wA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_ GcCE3s1ln4kS14v26r1Y6r17M2AIxVAIcxkEcVAq07x20xvEncxIr21l57IF6xkI12xvs2 x26I8E6xACxx1l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj6xIIjxv20xvE14v26r1q6rW5 McIj6I8E87Iv67AKxVWUJVW8JwAm72CE4IkC6x0Yz7v_Jr0_Gr1lF7xvr2IYc2Ij64vIr4 1lc7CjxVAaw2AFwI0_JF0_Jw1l42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_ Gr1l4IxYO2xFxVAFwI0_Jrv_JF1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x8GjcxK67 AKxVWUGVWUWwC2zVAF1VAY17CE14v26r1q6r43MIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8I cVAFwI0_JFI_Gr1lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4j6F4UMIIF0xvE42xK8VAvwI 8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVWUJVW8JwCI42IY6I8E87Iv6xkF7I0E14v2 6r4j6r4UJbIYCTnIWIevJa73UjIFyTuYvjxUc-VyUUUUU 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 | 13 ++++ drivers/mfd/Makefile | 2 + drivers/mfd/ls2kbmc-mfd.c | 156 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 drivers/mfd/ls2kbmc-mfd.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 22b936310039..04e40085441d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2422,5 +2422,18 @@ config MFD_UPBOARD_FPGA To compile this driver as a module, choose M here: the module will be called upboard-fpga. =20 +config MFD_LS2K_BMC + tristate "Loongson-2K Board Management Controller Support" + depends on LOONGARCH + default y if 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. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 948cbdf42a18..18960ea13b64 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -290,3 +290,5 @@ obj-$(CONFIG_MFD_RSMU_I2C) +=3D rsmu_i2c.o rsmu_core.o obj-$(CONFIG_MFD_RSMU_SPI) +=3D rsmu_spi.o rsmu_core.o =20 obj-$(CONFIG_MFD_UPBOARD_FPGA) +=3D upboard-fpga.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..b309f6132c24 --- /dev/null +++ b/drivers/mfd/ls2kbmc-mfd.c @@ -0,0 +1,156 @@ +// 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 +#include +#include + +#define LS2K_DISPLAY_RES_START (SZ_16M + SZ_2M) +#define LS2K_IPMI_RES_SIZE 0x1c +#define LS2K_IPMI0_RES_START (SZ_16M + 0xf00000) +#define LS2K_IPMI1_RES_START (LS2K_IPMI0_RES_START + LS2K_IPMI_RES_SIZE) +#define LS2K_IPMI2_RES_START (LS2K_IPMI1_RES_START + LS2K_IPMI_RES_SIZE) +#define LS2K_IPMI3_RES_START (LS2K_IPMI2_RES_START + LS2K_IPMI_RES_SIZE) +#define LS2K_IPMI4_RES_START (LS2K_IPMI3_RES_START + LS2K_IPMI_RES_SIZE) + +static struct resource ls2k_display_resources[] =3D { + DEFINE_RES_MEM_NAMED(LS2K_DISPLAY_RES_START, SZ_4M, "simpledrm-res"), +}; + +static struct resource ls2k_ipmi0_resources[] =3D { + DEFINE_RES_MEM_NAMED(LS2K_IPMI0_RES_START, LS2K_IPMI_RES_SIZE, "ipmi0-res= "), +}; + +static struct resource ls2k_ipmi1_resources[] =3D { + DEFINE_RES_MEM_NAMED(LS2K_IPMI1_RES_START, LS2K_IPMI_RES_SIZE, "ipmi1-res= "), +}; + +static struct resource ls2k_ipmi2_resources[] =3D { + DEFINE_RES_MEM_NAMED(LS2K_IPMI2_RES_START, LS2K_IPMI_RES_SIZE, "ipmi2-res= "), +}; + +static struct resource ls2k_ipmi3_resources[] =3D { + DEFINE_RES_MEM_NAMED(LS2K_IPMI3_RES_START, LS2K_IPMI_RES_SIZE, "ipmi3-res= "), +}; + +static struct resource ls2k_ipmi4_resources[] =3D { + DEFINE_RES_MEM_NAMED(LS2K_IPMI4_RES_START, LS2K_IPMI_RES_SIZE, "ipmi4-res= "), +}; + +static struct mfd_cell ls2k_bmc_cells[] =3D { + MFD_CELL_RES("simple-framebuffer", ls2k_display_resources), + MFD_CELL_RES("ls2k-ipmi-si", ls2k_ipmi0_resources), + MFD_CELL_RES("ls2k-ipmi-si", ls2k_ipmi1_resources), + MFD_CELL_RES("ls2k-ipmi-si", ls2k_ipmi2_resources), + MFD_CELL_RES("ls2k-ipmi-si", ls2k_ipmi3_resources), + MFD_CELL_RES("ls2k-ipmi-si", ls2k_ipmi4_resources), +}; + +/* + * 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 ls2k_bmc_get_video_mode(struct pci_dev *pdev, struct simplefb_p= latform_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=3D" */ + 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 int ls2k_bmc_probe(struct pci_dev *dev, const struct pci_device_id = *id) +{ + int ret =3D 0; + resource_size_t base; + struct simplefb_platform_data pd; + + ret =3D pci_enable_device(dev); + if (ret) + return ret; + + ret =3D ls2k_bmc_get_video_mode(dev, &pd); + if (ret) + goto disable_pci; + + ls2k_bmc_cells[0].platform_data =3D &pd; + ls2k_bmc_cells[0].pdata_size =3D sizeof(pd); + base =3D dev->resource[0].start + LS2K_DISPLAY_RES_START; + + /* Remove conflicting efifb device */ + ret =3D aperture_remove_conflicting_devices(base, SZ_4M, "simple-framebuf= fer"); + if (ret) { + dev_err(&dev->dev, "Remove firmware framebuffers failed: %d\n", ret); + goto disable_pci; + } + + return devm_mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO, + ls2k_bmc_cells, ARRAY_SIZE(ls2k_bmc_cells), + &dev->resource[0], 0, NULL); + +disable_pci: + pci_disable_device(dev); + return ret; +} + +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.47.1 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 02DE2289E01 for ; Thu, 15 May 2025 02:32:44 +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=1747276367; cv=none; b=UL6I5MxwSFypygEEe6BSkjNo8iaEfLDK/LF6PG826j/uAOTei11oR3dkTCjpnh4SypJsAhVe6WjLxY819Z2vgvPlStMwPk/tJ0DnDKJIm22X7+Niq9ID3/Kr9B2aK/UtCU959v2u0H6fIEcI1ggMqTupLjd7D6NQxHgYqeJZ8Yw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747276367; c=relaxed/simple; bh=qBVaGRdrBFEzaE+GKQRR3UnCjwA49VZve8qVB5U/bc0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cDXXHGvgPhxtY5Sn8azv9bLJ7PI+UYNOEt5d90fsyYvR2sMD7AeBRGq/ncGe9bebsO/H3FjbhaHhOc0yO0O2YU/XCPHsCJNr6UpufoTgU85y13NvnafqiGOWN3Yf1AtnQywh0GuDTj5cWZ0+GoZIGjK/K+JiRocyDSEO9hqqMxQ= 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.186]) by gateway (Coremail) with SMTP id _____8DxQK9KUiVoYInoAA--.15312S3; Thu, 15 May 2025 10:32:42 +0800 (CST) Received: from localhost.localdomain (unknown [223.64.68.186]) by front1 (Coremail) with SMTP id qMiowMCxbsVDUiVo1PnTAA--.19913S4; Thu, 15 May 2025 10:32:39 +0800 (CST) From: Binbin Zhou To: Binbin Zhou , Huacai Chen , Lee Jones , Corey Minyard Cc: Huacai Chen , Xuerui Wang , loongarch@lists.linux.dev, linux-kernel@vger.kernel.org, openipmi-developer@lists.sourceforge.net, Binbin Zhou , Chong Qiao Subject: [PATCH v2 2/3] ipmi: Add Loongson-2K BMC support Date: Thu, 15 May 2025 10:32:25 +0800 Message-ID: <0963b8274bfe25a21f56da9fcba05830fb43408b.1747276047.git.zhoubinbin@loongson.cn> X-Mailer: git-send-email 2.47.1 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: qMiowMCxbsVDUiVo1PnTAA--.19913S4 X-CM-SenderInfo: p2kr3uplqex0o6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj93XoW3XFyUJF17Ar4rJF4xur18CrX_yoW3tr1xpa 15K34kCw48tF47K3srJrykuFWrJr93WFy5tFW3X3yfuFW3try0grn2yFy3ZF9rKFyqgF13 tFZ8Ar43GFW7A3gCm3ZEXasCq-sJn29KB7ZKAUJUUUU7529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUB0b4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r126r13M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Ar0_tr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr0_Cr1l84ACjcxK6I8E87Iv67AKxVW0oVCq3wA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_ GcCE3s1ln4kS14v26r1Y6r17M2AIxVAIcxkEcVAq07x20xvEncxIr21l57IF6xkI12xvs2 x26I8E6xACxx1l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj6xIIjxv20xvE14v26r1q6rW5 McIj6I8E87Iv67AKxVW8JVWxJwAm72CE4IkC6x0Yz7v_Jr0_Gr1lF7xvr2IYc2Ij64vIr4 1lc7CjxVAaw2AFwI0_JF0_Jw1l42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_ Gr1l4IxYO2xFxVAFwI0_Jrv_JF1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x8GjcxK67 AKxVWUGVWUWwC2zVAF1VAY17CE14v26r1q6r43MIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8I cVAFwI0_Xr0_Ar1lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4j6F4UMIIF0xvE42xK8VAvwI 8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVW8JVWxJwCI42IY6I8E87Iv6xkF7I0E14v2 6r4j6r4UJbIYCTnIWIevJa73UjIFyTuYvjxU4ApnDUUUU 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 | 7 + drivers/char/ipmi/ipmi_si_intf.c | 3 + drivers/char/ipmi/ipmi_si_ls2k.c | 250 +++++++++++++++++++++++++++++++ 4 files changed, 261 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..5eb3494f5f39 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -8,6 +8,7 @@ ipmi_si-y :=3D ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o = ipmi_bt_sm.o \ ipmi_si_mem_io.o ipmi_si-$(CONFIG_HAS_IOPORT) +=3D ipmi_si_port_io.o ipmi_si-$(CONFIG_PCI) +=3D ipmi_si_pci.o +ipmi_si-$(CONFIG_LOONGARCH) +=3D ipmi_si_ls2k.o ipmi_si-$(CONFIG_PARISC) +=3D ipmi_si_parisc.o =20 obj-$(CONFIG_IPMI_HANDLER) +=3D ipmi_msghandler.o diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h index a7ead2a4c753..71f1d4e1272c 100644 --- a/drivers/char/ipmi/ipmi_si.h +++ b/drivers/char/ipmi/ipmi_si.h @@ -93,6 +93,13 @@ void ipmi_si_pci_shutdown(void); static inline void ipmi_si_pci_init(void) { } static inline void ipmi_si_pci_shutdown(void) { } #endif +#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 #ifdef CONFIG_PARISC void ipmi_si_parisc_init(void); void ipmi_si_parisc_shutdown(void); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_i= ntf.c index 12b0b77eb1cc..323da77698ea 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2107,6 +2107,7 @@ static int __init init_ipmi_si(void) =20 ipmi_si_pci_init(); =20 + ipmi_si_ls2k_init(); ipmi_si_parisc_init(); =20 /* We prefer devices with interrupts, but in the case of a machine @@ -2288,6 +2289,8 @@ static void cleanup_ipmi_si(void) =20 ipmi_si_pci_shutdown(); =20 + ipmi_si_ls2k_shutdown(); + ipmi_si_parisc_shutdown(); =20 ipmi_si_platform_shutdown(); 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.47.1 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 0442828C859 for ; Thu, 15 May 2025 02:32:44 +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=1747276367; cv=none; b=DPg2C/SpNV0g88cDZ5fDkObYNTtZGVrUqHZeag7APjweyDQYQLJAqT8spjnHS5Ad1fTq2RO0JOgLCnOtWofJhgUozGFRTrvmlMQX7ee51aFGtkAdVH1auimxN7QI1fnnD0UUvqVThCltrGvvk/1ttFyeM08tUypxhqDJACcXxaE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747276367; c=relaxed/simple; bh=PL70VEBxzVJWg2rlrdYmTfDvsEGifEJirBP/LA1hqlI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jnRsND9e9ZLY1rCYCjXzDDd4qFNE7hEFnXxEfhKB8tJ+vbzGE0Jlp8BF+vEIayyAfd0tsRvw199Fsrwn5lwsjuwdDp4rEV/QLq7WP5eUoYl10A+Le03RLEgfUxZCWqHNQbBcOfW/KJPqTjKADEL1RCs3UMXuLW4+NrrnfF4PEIY= 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.186]) by gateway (Coremail) with SMTP id _____8BxXWtLUiVoYYnoAA--.50398S3; Thu, 15 May 2025 10:32:43 +0800 (CST) Received: from localhost.localdomain (unknown [223.64.68.186]) by front1 (Coremail) with SMTP id qMiowMCxbsVDUiVo1PnTAA--.19913S5; Thu, 15 May 2025 10:32:41 +0800 (CST) From: Binbin Zhou To: Binbin Zhou , Huacai Chen , Lee Jones , Corey Minyard Cc: Huacai Chen , Xuerui Wang , loongarch@lists.linux.dev, linux-kernel@vger.kernel.org, openipmi-developer@lists.sourceforge.net, Binbin Zhou , Chong Qiao Subject: [PATCH v2 3/3] mfd: ls2kbmc: Add Loongson-2K BMC reset function support Date: Thu, 15 May 2025 10:32:26 +0800 Message-ID: X-Mailer: git-send-email 2.47.1 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: qMiowMCxbsVDUiVo1PnTAA--.19913S5 X-CM-SenderInfo: p2kr3uplqex0o6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj93XoWxKw48CF4UCF1xAw1UGw1xWFX_yoWfArWfpa y5AFy5tr4ktr1YvFZrJ3WUur4agrs0qa47Ka4Ig3Zaqa12y34kXFy8KF1aqF9xGFWkJr13 X3yFkF47CF9rWagCm3ZEXasCq-sJn29KB7ZKAUJUUUU7529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUB0b4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r126r13M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Ar0_tr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr0_Cr1l84ACjcxK6I8E87Iv67AKxVW0oVCq3wA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_ GcCE3s1ln4kS14v26r1Y6r17M2AIxVAIcxkEcVAq07x20xvEncxIr21l57IF6xkI12xvs2 x26I8E6xACxx1l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj6xIIjxv20xvE14v26r1q6rW5 McIj6I8E87Iv67AKxVW8JVWxJwAm72CE4IkC6x0Yz7v_Jr0_Gr1lF7xvr2IYc2Ij64vIr4 1lc7CjxVAaw2AFwI0_JF0_Jw1l42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_ Gr1l4IxYO2xFxVAFwI0_Jrv_JF1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x8GjcxK67 AKxVWUGVWUWwC2zVAF1VAY17CE14v26r1q6r43MIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8I cVAFwI0_Xr0_Ar1lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4j6F4UMIIF0xvE42xK8VAvwI 8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVW8JVWxJwCI42IY6I8E87Iv6xkF7I0E14v2 6r4j6r4UJbIYCTnIWIevJa73UjIFyTuYvjxU4ApnDUUUU 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/mfd/ls2kbmc-mfd.c | 242 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) diff --git a/drivers/mfd/ls2kbmc-mfd.c b/drivers/mfd/ls2kbmc-mfd.c index b309f6132c24..4d35a13b3da5 100644 --- a/drivers/mfd/ls2kbmc-mfd.c +++ b/drivers/mfd/ls2kbmc-mfd.c @@ -9,8 +9,11 @@ */ =20 #include +#include #include #include +#include +#include #include #include #include @@ -18,6 +21,8 @@ #include #include #include +#include +#include =20 #define LS2K_DISPLAY_RES_START (SZ_16M + SZ_2M) #define LS2K_IPMI_RES_SIZE 0x1c @@ -27,6 +32,9 @@ #define LS2K_IPMI3_RES_START (LS2K_IPMI2_RES_START + LS2K_IPMI_RES_SIZE) #define LS2K_IPMI4_RES_START (LS2K_IPMI3_RES_START + LS2K_IPMI_RES_SIZE) =20 +#define LS2K_BMC_RESET_DELAY (60 * HZ) +#define LS2K_BMC_RESET_WAIT (10 * HZ) + static struct resource ls2k_display_resources[] =3D { DEFINE_RES_MEM_NAMED(LS2K_DISPLAY_RES_START, SZ_4M, "simpledrm-res"), }; @@ -60,6 +68,227 @@ static struct mfd_cell ls2k_bmc_cells[] =3D { MFD_CELL_RES("ls2k-ipmi-si", ls2k_ipmi4_resources), }; =20 +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 ls2k_bmc_pci_data { + u32 d80c; + u32 d71c; + u32 data[14]; + u32 cdata[3]; +}; + +struct ls2k_bmc_pdata { + struct device *dev; + struct work_struct reset_work; + unsigned long reset_time; + struct ls2k_bmc_pci_data pci_data; +}; + +static bool ls2k_bmc_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 bool ls2k_bmc_check_pcie_connected(struct pci_dev *parent, + struct ls2k_bmc_pdata *priv) +{ + void __iomem *mmio; + int sts, ret; + + mmio =3D pci_iomap(parent, 0, 0x100); + if (!mmio) + return false; + + writel(readl(mmio) | 0x8, mmio); + + ret =3D readl_poll_timeout_atomic(mmio + 0xc, sts, (sts & 0x11) =3D=3D 0x= 11, + 1000, 1000000); + if (ret) { + pci_iounmap(parent, mmio); + dev_err(priv->dev, "PCIE train failed status=3D0x%x\n", sts); + return false; + } + + pci_iounmap(parent, mmio); + return true; +} + +static int ls2k_bmc_recover_pci_data(void *data) +{ + struct ls2k_bmc_pdata *priv =3D data; + struct pci_dev *pdev =3D to_pci_dev(priv->dev); + struct pci_dev *parent =3D pdev->bus->self; + bool ready, dirty; + u32 i; + + 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); + + for (i =3D 2000; i > 0 ; i--) { + dirty =3D ls2k_bmc_bar0_addr_is_set(parent); + if (!dirty) + break; + mdelay(1); + }; + + if (i =3D=3D 0) + dev_warn(priv->dev, "The PCI Bar is not cleared.\n"); + + 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 ls2k_bmc_check_pcie_connected(parent, priv); + if (!ready) + return ready; + + dev_dbg(priv->dev, "The PCIE recovered done.\n"); + + /* Waiting for u-boot ddr config ready */ + mdelay(jiffies_to_msecs(LS2K_BMC_RESET_WAIT)); + ready =3D ls2k_bmc_bar0_addr_is_set(parent); + 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]); + + return 0; +} + +static void ls2k_bmc_events_fn(struct work_struct *work) +{ + struct ls2k_bmc_pdata *priv =3D container_of(work, struct ls2k_bmc_pdata,= reset_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(ls2k_bmc_recover_pci_data, priv, NULL); + +#ifdef CONFIG_VT + /* Re-push the display due to previous pcie loss. */ + set_console(vt_move_to_console(MAX_NR_CONSOLES - 1, 1)); +#endif + + dev_info(priv->dev, "Loongson-2K BMC recovered done.\n"); +} + +static irqreturn_t ls2k_bmc_interrupt(int irq, void *arg) +{ + struct ls2k_bmc_pdata *priv =3D arg; + + if (system_state !=3D SYSTEM_RUNNING) + return IRQ_HANDLED; + + /* Skip interrupt in LS2K_BMC_RESET_DELAY */ + if (time_after(jiffies, priv->reset_time + LS2K_BMC_RESET_DELAY)) + schedule_work(&priv->reset_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 ls2k_bmc_gpio_reset_handler(struct ls2k_bmc_pdata *priv) +{ + int gsi =3D 16 + (BMC_RESET_GPIO & 7); + void __iomem *gpio_base; + int irq, ret =3D 0; + + /* 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) { + ret =3D PTR_ERR(gpio_base); + goto acpi_failed; + } + + 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 devm_request_irq(priv->dev, irq, ls2k_bmc_interrupt, + IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc gpio", priv); + if (ret) + dev_err(priv->dev, "ls2kbmc gpio request_irq(%d) failed\n", irq); + + iounmap(gpio_base); + +acpi_failed: + acpi_unregister_gsi(gsi); + + return ret; +} + +static void ls2k_bmc_save_pci_data(struct pci_dev *pdev, struct ls2k_bmc_p= data *priv) +{ + 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)) | BIT(17); + + pci_read_config_dword(parent, 0x71c, &priv->pci_data.d71c); + priv->pci_data.d71c |=3D BIT(26); +} + +static int ls2k_bmc_pdata_initial(struct pci_dev *pdev, struct ls2k_bmc_pd= ata *priv) +{ + int ret; + + ls2k_bmc_save_pci_data(pdev, priv); + + INIT_WORK(&priv->reset_work, ls2k_bmc_events_fn); + + ret =3D devm_request_irq(&pdev->dev, pdev->irq, ls2k_bmc_interrupt, + IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc pcie", priv); + if (ret) { + dev_err(priv->dev, "ls2kbmc pcie request_irq(%d) failed\n", pdev->irq); + return ret; + } + + return ls2k_bmc_gpio_reset_handler(priv); +} + /* * Currently the Loongson-2K0500 BMC hardware does not have an i2c interfa= ce to * adapt to the resolution. @@ -101,12 +330,25 @@ static int ls2k_bmc_probe(struct pci_dev *dev, const = struct pci_device_id *id) { int ret =3D 0; resource_size_t base; + struct ls2k_bmc_pdata *priv; struct simplefb_platform_data pd; =20 ret =3D pci_enable_device(dev); if (ret) return ret; =20 + priv =3D devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (IS_ERR(priv)) { + ret =3D -ENOMEM; + goto disable_pci; + } + + priv->dev =3D &dev->dev; + + ret =3D ls2k_bmc_pdata_initial(dev, priv); + if (ret) + goto disable_pci; + ret =3D ls2k_bmc_get_video_mode(dev, &pd); if (ret) goto disable_pci; --=20 2.47.1