mfd: ls2kbmc: Introduce Loongson-2K BMC core driver

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-3 CPU. It supports multiple sub-devices like DRM and IPMI.

Co-developed-by: Chong Qiao <qiaochong@loongson.cn>
Signed-off-by: Chong Qiao <qiaochong@loongson.cn>
Reviewed-by: Huacai Chen <chenhuacai@loongson.cn>
Acked-by: Corey Minyard <corey@minyard.net>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
Link: https://lore.kernel.org/r/0dc1fd53020ce2562453961ffed2cd9eb8f359e1.1756987761.git.zhoubinbin@loongson.cn
Signed-off-by: Lee Jones <lee@kernel.org>
This commit is contained in:
Binbin Zhou 2025-09-04 20:35:05 +08:00 committed by Lee Jones
parent 719d02a25a
commit 0d64f6d1ff
4 changed files with 210 additions and 0 deletions

View file

@ -14417,6 +14417,12 @@ S: Maintained
F: Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml
F: drivers/thermal/loongson2_thermal.c
LOONGSON-2K Board Management Controller (BMC) DRIVER
M: Binbin Zhou <zhoubinbin@loongson.cn>
M: Chong Qiao <qiaochong@loongson.cn>
S: Maintained
F: drivers/mfd/ls2k-bmc-core.c
LOONGSON EDAC DRIVER
M: Zhao Qunqin <zhaoqunqin@loongson.cn>
L: linux-edac@vger.kernel.org

View file

@ -2480,6 +2480,19 @@ config MFD_LOONGSON_SE
commands to the engine and must first send them to the controller,
which will forward them to the corresponding engine.
config MFD_LS2K_BMC_CORE
bool "Loongson-2K Board Management Controller Support"
depends on PCI && ACPI_GENERIC_GSI
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 display and IPMI. This driver provides common
support for accessing the devices.
The display is enabled by default in the driver, while the IPMI interface
is enabled independently through the IPMI_LS2K option in the IPMI section.
config MFD_QNAP_MCU
tristate "QNAP microcontroller unit core driver"
depends on SERIAL_DEV_BUS

View file

@ -290,6 +290,8 @@ obj-$(CONFIG_MFD_INTEL_M10_BMC_CORE) += intel-m10-bmc-core.o
obj-$(CONFIG_MFD_INTEL_M10_BMC_SPI) += intel-m10-bmc-spi.o
obj-$(CONFIG_MFD_INTEL_M10_BMC_PMCI) += intel-m10-bmc-pmci.o
obj-$(CONFIG_MFD_LS2K_BMC_CORE) += ls2k-bmc-core.o
obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o
obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o

189
drivers/mfd/ls2k-bmc-core.c Normal file
View file

@ -0,0 +1,189 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Loongson-2K Board Management Controller (BMC) Core Driver.
*
* Copyright (C) 2024-2025 Loongson Technology Corporation Limited.
*
* Authors:
* Chong Qiao <qiaochong@loongson.cn>
* Binbin Zhou <zhoubinbin@loongson.cn>
*/
#include <linux/aperture.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/platform_data/simplefb.h>
#include <linux/platform_device.h>
/* LS2K BMC resources */
#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)
enum {
LS2K_BMC_DISPLAY,
LS2K_BMC_IPMI0,
LS2K_BMC_IPMI1,
LS2K_BMC_IPMI2,
LS2K_BMC_IPMI3,
LS2K_BMC_IPMI4,
};
static struct resource ls2k_display_resources[] = {
DEFINE_RES_MEM_NAMED(LS2K_DISPLAY_RES_START, SZ_4M, "simpledrm-res"),
};
static struct resource ls2k_ipmi0_resources[] = {
DEFINE_RES_MEM_NAMED(LS2K_IPMI0_RES_START, LS2K_IPMI_RES_SIZE, "ipmi0-res"),
};
static struct resource ls2k_ipmi1_resources[] = {
DEFINE_RES_MEM_NAMED(LS2K_IPMI1_RES_START, LS2K_IPMI_RES_SIZE, "ipmi1-res"),
};
static struct resource ls2k_ipmi2_resources[] = {
DEFINE_RES_MEM_NAMED(LS2K_IPMI2_RES_START, LS2K_IPMI_RES_SIZE, "ipmi2-res"),
};
static struct resource ls2k_ipmi3_resources[] = {
DEFINE_RES_MEM_NAMED(LS2K_IPMI3_RES_START, LS2K_IPMI_RES_SIZE, "ipmi3-res"),
};
static struct resource ls2k_ipmi4_resources[] = {
DEFINE_RES_MEM_NAMED(LS2K_IPMI4_RES_START, LS2K_IPMI_RES_SIZE, "ipmi4-res"),
};
static struct mfd_cell ls2k_bmc_cells[] = {
[LS2K_BMC_DISPLAY] = {
.name = "simple-framebuffer",
.num_resources = ARRAY_SIZE(ls2k_display_resources),
.resources = ls2k_display_resources
},
[LS2K_BMC_IPMI0] = {
.name = "ls2k-ipmi-si",
.num_resources = ARRAY_SIZE(ls2k_ipmi0_resources),
.resources = ls2k_ipmi0_resources
},
[LS2K_BMC_IPMI1] = {
.name = "ls2k-ipmi-si",
.num_resources = ARRAY_SIZE(ls2k_ipmi1_resources),
.resources = ls2k_ipmi1_resources
},
[LS2K_BMC_IPMI2] = {
.name = "ls2k-ipmi-si",
.num_resources = ARRAY_SIZE(ls2k_ipmi2_resources),
.resources = ls2k_ipmi2_resources
},
[LS2K_BMC_IPMI3] = {
.name = "ls2k-ipmi-si",
.num_resources = ARRAY_SIZE(ls2k_ipmi3_resources),
.resources = ls2k_ipmi3_resources
},
[LS2K_BMC_IPMI4] = {
.name = "ls2k-ipmi-si",
.num_resources = ARRAY_SIZE(ls2k_ipmi4_resources),
.resources = ls2k_ipmi4_resources
},
};
/*
* Currently the Loongson-2K BMC hardware does not have an I2C interface to adapt to the
* resolution. We set the resolution by presetting "video=1280x1024-16@2M" to the BMC memory.
*/
static int ls2k_bmc_parse_mode(struct pci_dev *pdev, struct simplefb_platform_data *pd)
{
char *mode;
int depth, ret;
/* The last 16M of PCI BAR0 is used to store the resolution string. */
mode = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0) + SZ_16M, SZ_16M);
if (!mode)
return -ENOMEM;
/* The resolution field starts with the flag "video=". */
if (!strncmp(mode, "video=", 6))
mode = mode + 6;
ret = kstrtoint(strsep(&mode, "x"), 10, &pd->width);
if (ret)
return ret;
ret = kstrtoint(strsep(&mode, "-"), 10, &pd->height);
if (ret)
return ret;
ret = kstrtoint(strsep(&mode, "@"), 10, &depth);
if (ret)
return ret;
pd->stride = pd->width * depth / 8;
pd->format = depth == 32 ? "a8r8g8b8" : "r5g6b5";
return 0;
}
static int ls2k_bmc_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct simplefb_platform_data pd;
resource_size_t base;
int ret;
ret = pci_enable_device(dev);
if (ret)
return ret;
ret = ls2k_bmc_parse_mode(dev, &pd);
if (ret)
goto disable_pci;
ls2k_bmc_cells[LS2K_BMC_DISPLAY].platform_data = &pd;
ls2k_bmc_cells[LS2K_BMC_DISPLAY].pdata_size = sizeof(pd);
base = dev->resource[0].start + LS2K_DISPLAY_RES_START;
/* Remove conflicting efifb device */
ret = aperture_remove_conflicting_devices(base, SZ_4M, "simple-framebuffer");
if (ret) {
dev_err(&dev->dev, "Failed to removed firmware framebuffers: %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[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x1a05) },
{ }
};
MODULE_DEVICE_TABLE(pci, ls2k_bmc_devices);
static struct pci_driver ls2k_bmc_driver = {
.name = "ls2k-bmc",
.id_table = ls2k_bmc_devices,
.probe = ls2k_bmc_probe,
.remove = ls2k_bmc_remove,
};
module_pci_driver(ls2k_bmc_driver);
MODULE_DESCRIPTION("Loongson-2K Board Management Controller (BMC) Core driver");
MODULE_AUTHOR("Loongson Technology Corporation Limited");
MODULE_LICENSE("GPL");