1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Loongson-2K Board Management Controller (BMC) Core Driver. 4 * 5 * Copyright (C) 2024-2025 Loongson Technology Corporation Limited. 6 * 7 * Authors: 8 * Chong Qiao <qiaochong@loongson.cn> 9 * Binbin Zhou <zhoubinbin@loongson.cn> 10 */ 11 12 #include <linux/aperture.h> 13 #include <linux/errno.h> 14 #include <linux/init.h> 15 #include <linux/kernel.h> 16 #include <linux/mfd/core.h> 17 #include <linux/module.h> 18 #include <linux/pci.h> 19 #include <linux/pci_ids.h> 20 #include <linux/platform_data/simplefb.h> 21 #include <linux/platform_device.h> 22 23 /* LS2K BMC resources */ 24 #define LS2K_DISPLAY_RES_START (SZ_16M + SZ_2M) 25 #define LS2K_IPMI_RES_SIZE 0x1C 26 #define LS2K_IPMI0_RES_START (SZ_16M + 0xF00000) 27 #define LS2K_IPMI1_RES_START (LS2K_IPMI0_RES_START + LS2K_IPMI_RES_SIZE) 28 #define LS2K_IPMI2_RES_START (LS2K_IPMI1_RES_START + LS2K_IPMI_RES_SIZE) 29 #define LS2K_IPMI3_RES_START (LS2K_IPMI2_RES_START + LS2K_IPMI_RES_SIZE) 30 #define LS2K_IPMI4_RES_START (LS2K_IPMI3_RES_START + LS2K_IPMI_RES_SIZE) 31 32 enum { 33 LS2K_BMC_DISPLAY, 34 LS2K_BMC_IPMI0, 35 LS2K_BMC_IPMI1, 36 LS2K_BMC_IPMI2, 37 LS2K_BMC_IPMI3, 38 LS2K_BMC_IPMI4, 39 }; 40 41 static struct resource ls2k_display_resources[] = { 42 DEFINE_RES_MEM_NAMED(LS2K_DISPLAY_RES_START, SZ_4M, "simpledrm-res"), 43 }; 44 45 static struct resource ls2k_ipmi0_resources[] = { 46 DEFINE_RES_MEM_NAMED(LS2K_IPMI0_RES_START, LS2K_IPMI_RES_SIZE, "ipmi0-res"), 47 }; 48 49 static struct resource ls2k_ipmi1_resources[] = { 50 DEFINE_RES_MEM_NAMED(LS2K_IPMI1_RES_START, LS2K_IPMI_RES_SIZE, "ipmi1-res"), 51 }; 52 53 static struct resource ls2k_ipmi2_resources[] = { 54 DEFINE_RES_MEM_NAMED(LS2K_IPMI2_RES_START, LS2K_IPMI_RES_SIZE, "ipmi2-res"), 55 }; 56 57 static struct resource ls2k_ipmi3_resources[] = { 58 DEFINE_RES_MEM_NAMED(LS2K_IPMI3_RES_START, LS2K_IPMI_RES_SIZE, "ipmi3-res"), 59 }; 60 61 static struct resource ls2k_ipmi4_resources[] = { 62 DEFINE_RES_MEM_NAMED(LS2K_IPMI4_RES_START, LS2K_IPMI_RES_SIZE, "ipmi4-res"), 63 }; 64 65 static struct mfd_cell ls2k_bmc_cells[] = { 66 [LS2K_BMC_DISPLAY] = { 67 .name = "simple-framebuffer", 68 .num_resources = ARRAY_SIZE(ls2k_display_resources), 69 .resources = ls2k_display_resources 70 }, 71 [LS2K_BMC_IPMI0] = { 72 .name = "ls2k-ipmi-si", 73 .num_resources = ARRAY_SIZE(ls2k_ipmi0_resources), 74 .resources = ls2k_ipmi0_resources 75 }, 76 [LS2K_BMC_IPMI1] = { 77 .name = "ls2k-ipmi-si", 78 .num_resources = ARRAY_SIZE(ls2k_ipmi1_resources), 79 .resources = ls2k_ipmi1_resources 80 }, 81 [LS2K_BMC_IPMI2] = { 82 .name = "ls2k-ipmi-si", 83 .num_resources = ARRAY_SIZE(ls2k_ipmi2_resources), 84 .resources = ls2k_ipmi2_resources 85 }, 86 [LS2K_BMC_IPMI3] = { 87 .name = "ls2k-ipmi-si", 88 .num_resources = ARRAY_SIZE(ls2k_ipmi3_resources), 89 .resources = ls2k_ipmi3_resources 90 }, 91 [LS2K_BMC_IPMI4] = { 92 .name = "ls2k-ipmi-si", 93 .num_resources = ARRAY_SIZE(ls2k_ipmi4_resources), 94 .resources = ls2k_ipmi4_resources 95 }, 96 }; 97 98 /* 99 * Currently the Loongson-2K BMC hardware does not have an I2C interface to adapt to the 100 * resolution. We set the resolution by presetting "video=1280x1024-16@2M" to the BMC memory. 101 */ 102 static int ls2k_bmc_parse_mode(struct pci_dev *pdev, struct simplefb_platform_data *pd) 103 { 104 char *mode; 105 int depth, ret; 106 107 /* The last 16M of PCI BAR0 is used to store the resolution string. */ 108 mode = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0) + SZ_16M, SZ_16M); 109 if (!mode) 110 return -ENOMEM; 111 112 /* The resolution field starts with the flag "video=". */ 113 if (!strncmp(mode, "video=", 6)) 114 mode = mode + 6; 115 116 ret = kstrtoint(strsep(&mode, "x"), 10, &pd->width); 117 if (ret) 118 return ret; 119 120 ret = kstrtoint(strsep(&mode, "-"), 10, &pd->height); 121 if (ret) 122 return ret; 123 124 ret = kstrtoint(strsep(&mode, "@"), 10, &depth); 125 if (ret) 126 return ret; 127 128 pd->stride = pd->width * depth / 8; 129 pd->format = depth == 32 ? "a8r8g8b8" : "r5g6b5"; 130 131 return 0; 132 } 133 134 static int ls2k_bmc_probe(struct pci_dev *dev, const struct pci_device_id *id) 135 { 136 struct simplefb_platform_data pd; 137 resource_size_t base; 138 int ret; 139 140 ret = pci_enable_device(dev); 141 if (ret) 142 return ret; 143 144 ret = ls2k_bmc_parse_mode(dev, &pd); 145 if (ret) 146 goto disable_pci; 147 148 ls2k_bmc_cells[LS2K_BMC_DISPLAY].platform_data = &pd; 149 ls2k_bmc_cells[LS2K_BMC_DISPLAY].pdata_size = sizeof(pd); 150 base = dev->resource[0].start + LS2K_DISPLAY_RES_START; 151 152 /* Remove conflicting efifb device */ 153 ret = aperture_remove_conflicting_devices(base, SZ_4M, "simple-framebuffer"); 154 if (ret) { 155 dev_err(&dev->dev, "Failed to removed firmware framebuffers: %d\n", ret); 156 goto disable_pci; 157 } 158 159 return devm_mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO, 160 ls2k_bmc_cells, ARRAY_SIZE(ls2k_bmc_cells), 161 &dev->resource[0], 0, NULL); 162 163 disable_pci: 164 pci_disable_device(dev); 165 return ret; 166 } 167 168 static void ls2k_bmc_remove(struct pci_dev *dev) 169 { 170 pci_disable_device(dev); 171 } 172 173 static struct pci_device_id ls2k_bmc_devices[] = { 174 { PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x1a05) }, 175 { } 176 }; 177 MODULE_DEVICE_TABLE(pci, ls2k_bmc_devices); 178 179 static struct pci_driver ls2k_bmc_driver = { 180 .name = "ls2k-bmc", 181 .id_table = ls2k_bmc_devices, 182 .probe = ls2k_bmc_probe, 183 .remove = ls2k_bmc_remove, 184 }; 185 module_pci_driver(ls2k_bmc_driver); 186 187 MODULE_DESCRIPTION("Loongson-2K Board Management Controller (BMC) Core driver"); 188 MODULE_AUTHOR("Loongson Technology Corporation Limited"); 189 MODULE_LICENSE("GPL"); 190