File Index
Files and directories
Sign in
FILE

ls2k-bmc-core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mfd/ls2k-bmc-core.c C
  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/bitfield.h>
 14#include <linux/delay.h>
 15#include <linux/errno.h>
 16#include <linux/init.h>
 17#include <linux/iopoll.h>
 18#include <linux/kbd_kern.h>
 19#include <linux/kernel.h>
 20#include <linux/mfd/core.h>
 21#include <linux/module.h>
 22#include <linux/pci.h>
 23#include <linux/pci_ids.h>
 24#include <linux/platform_data/simplefb.h>
 25#include <linux/platform_device.h>
 26#include <linux/stop_machine.h>
 27#include <linux/vt_kern.h>
 28
 29/* LS2K BMC resources */
 30#define LS2K_DISPLAY_RES_START		(SZ_16M + SZ_2M)
 31#define LS2K_IPMI_RES_SIZE		0x1C
 32#define LS2K_IPMI0_RES_START		(SZ_16M + 0xF00000)
 33#define LS2K_IPMI1_RES_START		(LS2K_IPMI0_RES_START + LS2K_IPMI_RES_SIZE)
 34#define LS2K_IPMI2_RES_START		(LS2K_IPMI1_RES_START + LS2K_IPMI_RES_SIZE)
 35#define LS2K_IPMI3_RES_START		(LS2K_IPMI2_RES_START + LS2K_IPMI_RES_SIZE)
 36#define LS2K_IPMI4_RES_START		(LS2K_IPMI3_RES_START + LS2K_IPMI_RES_SIZE)
 37
 38#define LS7A_PCI_CFG_SIZE		0x100
 39
 40/* LS7A bridge registers */
 41#define LS7A_PCIE_PORT_CTL0		0x0
 42#define LS7A_PCIE_PORT_STS1		0xC
 43#define LS7A_GEN2_CTL			0x80C
 44#define LS7A_SYMBOL_TIMER		0x71C
 45
 46/* Bits of LS7A_PCIE_PORT_CTL0 */
 47#define LS2K_BMC_PCIE_LTSSM_ENABLE	BIT(3)
 48
 49/* Bits of LS7A_PCIE_PORT_STS1 */
 50#define LS2K_BMC_PCIE_LTSSM_STS		GENMASK(5, 0)
 51#define LS2K_BMC_PCIE_CONNECTED		0x11
 52
 53#define LS2K_BMC_PCIE_DELAY_US		1000
 54#define LS2K_BMC_PCIE_TIMEOUT_US	1000000
 55
 56/* Bits of LS7A_GEN2_CTL */
 57#define LS7A_GEN2_SPEED_CHANG		BIT(17)
 58#define LS7A_CONF_PHY_TX		BIT(18)
 59
 60/* Bits of LS7A_SYMBOL_TIMER */
 61#define LS7A_MASK_LEN_MATCH		BIT(26)
 62
 63/* Interval between interruptions */
 64#define LS2K_BMC_INT_INTERVAL		(60 * HZ)
 65
 66/* Maximum time to wait for U-Boot and DDR to be ready with ms. */
 67#define LS2K_BMC_RESET_WAIT_TIME	10000
 68
 69/* It's an experience value */
 70#define LS7A_BAR0_CHECK_MAX_TIMES	2000
 71
 72#define PCI_REG_STRIDE			0x4
 73
 74#define LS2K_BMC_RESET_GPIO		14
 75#define LOONGSON_GPIO_REG_BASE		0x1FE00500
 76#define LOONGSON_GPIO_REG_SIZE		0x18
 77#define LOONGSON_GPIO_OEN		0x0
 78#define LOONGSON_GPIO_FUNC		0x4
 79#define LOONGSON_GPIO_INTPOL		0x10
 80#define LOONGSON_GPIO_INTEN		0x14
 81
 82#define LOONGSON_IO_INT_BASE		16
 83#define LS2K_BMC_RESET_GPIO_INT_VEC	(LS2K_BMC_RESET_GPIO % 8)
 84#define LS2K_BMC_RESET_GPIO_GSI		(LOONGSON_IO_INT_BASE + LS2K_BMC_RESET_GPIO_INT_VEC)
 85
 86enum {
 87	LS2K_BMC_DISPLAY,
 88	LS2K_BMC_IPMI0,
 89	LS2K_BMC_IPMI1,
 90	LS2K_BMC_IPMI2,
 91	LS2K_BMC_IPMI3,
 92	LS2K_BMC_IPMI4,
 93};
 94
 95static struct resource ls2k_display_resources[] = {
 96	DEFINE_RES_MEM_NAMED(LS2K_DISPLAY_RES_START, SZ_4M, "simpledrm-res"),
 97};
 98
 99static struct resource ls2k_ipmi0_resources[] = {
100	DEFINE_RES_MEM_NAMED(LS2K_IPMI0_RES_START, LS2K_IPMI_RES_SIZE, "ipmi0-res"),
101};
102
103static struct resource ls2k_ipmi1_resources[] = {
104	DEFINE_RES_MEM_NAMED(LS2K_IPMI1_RES_START, LS2K_IPMI_RES_SIZE, "ipmi1-res"),
105};
106
107static struct resource ls2k_ipmi2_resources[] = {
108	DEFINE_RES_MEM_NAMED(LS2K_IPMI2_RES_START, LS2K_IPMI_RES_SIZE, "ipmi2-res"),
109};
110
111static struct resource ls2k_ipmi3_resources[] = {
112	DEFINE_RES_MEM_NAMED(LS2K_IPMI3_RES_START, LS2K_IPMI_RES_SIZE, "ipmi3-res"),
113};
114
115static struct resource ls2k_ipmi4_resources[] = {
116	DEFINE_RES_MEM_NAMED(LS2K_IPMI4_RES_START, LS2K_IPMI_RES_SIZE, "ipmi4-res"),
117};
118
119static struct mfd_cell ls2k_bmc_cells[] = {
120	[LS2K_BMC_DISPLAY] = {
121		.name = "simple-framebuffer",
122		.num_resources = ARRAY_SIZE(ls2k_display_resources),
123		.resources = ls2k_display_resources
124	},
125	[LS2K_BMC_IPMI0] = {
126		.name = "ls2k-ipmi-si",
127		.num_resources = ARRAY_SIZE(ls2k_ipmi0_resources),
128		.resources = ls2k_ipmi0_resources
129	},
130	[LS2K_BMC_IPMI1] = {
131		.name = "ls2k-ipmi-si",
132		.num_resources = ARRAY_SIZE(ls2k_ipmi1_resources),
133		.resources = ls2k_ipmi1_resources
134	},
135	[LS2K_BMC_IPMI2] = {
136		.name = "ls2k-ipmi-si",
137		.num_resources = ARRAY_SIZE(ls2k_ipmi2_resources),
138		.resources = ls2k_ipmi2_resources
139	},
140	[LS2K_BMC_IPMI3] = {
141		.name = "ls2k-ipmi-si",
142		.num_resources = ARRAY_SIZE(ls2k_ipmi3_resources),
143		.resources = ls2k_ipmi3_resources
144	},
145	[LS2K_BMC_IPMI4] = {
146		.name = "ls2k-ipmi-si",
147		.num_resources = ARRAY_SIZE(ls2k_ipmi4_resources),
148		.resources = ls2k_ipmi4_resources
149	},
150};
151
152/* Index of the BMC PCI configuration space to be restored at BMC reset. */
153struct ls2k_bmc_pci_data {
154	u32 pci_command;
155	u32 base_address0;
156	u32 interrupt_line;
157};
158
159/* Index of the parent PCI configuration space to be restored at BMC reset. */
160struct ls2k_bmc_bridge_pci_data {
161	u32 pci_command;
162	u32 base_address[6];
163	u32 rom_addreess;
164	u32 interrupt_line;
165	u32 msi_hi;
166	u32 msi_lo;
167	u32 devctl;
168	u32 linkcap;
169	u32 linkctl_sts;
170	u32 symbol_timer;
171	u32 gen2_ctrl;
172};
173
174struct ls2k_bmc_ddata {
175	struct device *dev;
176	struct work_struct bmc_reset_work;
177	struct ls2k_bmc_pci_data bmc_pci_data;
178	struct ls2k_bmc_bridge_pci_data bridge_pci_data;
179};
180
181static bool ls2k_bmc_bar0_addr_is_set(struct pci_dev *pdev)
182{
183	u32 addr;
184
185	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &addr);
186
187	return addr & PCI_BASE_ADDRESS_MEM_MASK ? true : false;
188}
189
190static bool ls2k_bmc_pcie_is_connected(struct pci_dev *parent, struct ls2k_bmc_ddata *ddata)
191{
192	void __iomem *base;
193	int val, ret;
194
195	base = pci_iomap(parent, 0, LS7A_PCI_CFG_SIZE);
196	if (!base)
197		return false;
198
199	val = readl(base + LS7A_PCIE_PORT_CTL0);
200	writel(val | LS2K_BMC_PCIE_LTSSM_ENABLE, base + LS7A_PCIE_PORT_CTL0);
201
202	ret = readl_poll_timeout_atomic(base + LS7A_PCIE_PORT_STS1, val,
203					(val & LS2K_BMC_PCIE_LTSSM_STS) == LS2K_BMC_PCIE_CONNECTED,
204					LS2K_BMC_PCIE_DELAY_US, LS2K_BMC_PCIE_TIMEOUT_US);
205	if (ret) {
206		pci_iounmap(parent, base);
207		dev_err(ddata->dev, "PCI-E training failed status=0x%x\n", val);
208		return false;
209	}
210
211	pci_iounmap(parent, base);
212	return true;
213}
214
215static void ls2k_bmc_restore_bridge_pci_data(struct pci_dev *parent, struct ls2k_bmc_ddata *ddata)
216{
217	int base, i = 0;
218
219	pci_write_config_dword(parent, PCI_COMMAND, ddata->bridge_pci_data.pci_command);
220
221	for (base = PCI_BASE_ADDRESS_0; base <= PCI_BASE_ADDRESS_5; base += PCI_REG_STRIDE, i++)
222		pci_write_config_dword(parent, base, ddata->bridge_pci_data.base_address[i]);
223
224	pci_write_config_dword(parent, PCI_ROM_ADDRESS, ddata->bridge_pci_data.rom_addreess);
225	pci_write_config_dword(parent, PCI_INTERRUPT_LINE, ddata->bridge_pci_data.interrupt_line);
226
227	pci_write_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_LO,
228			       ddata->bridge_pci_data.msi_lo);
229	pci_write_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_HI,
230			       ddata->bridge_pci_data.msi_hi);
231	pci_write_config_dword(parent, parent->pcie_cap + PCI_EXP_DEVCTL,
232			       ddata->bridge_pci_data.devctl);
233	pci_write_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCAP,
234			       ddata->bridge_pci_data.linkcap);
235	pci_write_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCTL,
236			       ddata->bridge_pci_data.linkctl_sts);
237
238	pci_write_config_dword(parent, LS7A_GEN2_CTL, ddata->bridge_pci_data.gen2_ctrl);
239	pci_write_config_dword(parent, LS7A_SYMBOL_TIMER, ddata->bridge_pci_data.symbol_timer);
240}
241
242static int ls2k_bmc_recover_pci_data(void *data)
243{
244	struct ls2k_bmc_ddata *ddata = data;
245	struct pci_dev *pdev = to_pci_dev(ddata->dev);
246	struct pci_dev *parent = pdev->bus->self;
247	u32 i;
248
249	/*
250	 * Clear the bus, io and mem resources of the PCI-E bridge to zero, so that
251	 * the processor can not access the LS2K PCI-E port, to avoid crashing due to
252	 * the lack of return signal from accessing the LS2K PCI-E port.
253	 */
254	pci_write_config_dword(parent, PCI_BASE_ADDRESS_2, 0);
255	pci_write_config_dword(parent, PCI_BASE_ADDRESS_3, 0);
256	pci_write_config_dword(parent, PCI_BASE_ADDRESS_4, 0);
257
258	/*
259	 * When the LS2K BMC is reset, the LS7A PCI-E port is also reset, and its PCI
260	 * BAR0 register is cleared. Due to the time gap between the GPIO interrupt
261	 * generation and the LS2K BMC reset, the LS7A PCI BAR0 register is read to
262	 * determine whether the reset has begun.
263	 */
264	for (i = LS7A_BAR0_CHECK_MAX_TIMES; i > 0 ; i--) {
265		if (!ls2k_bmc_bar0_addr_is_set(parent))
266			break;
267		mdelay(1);
268	}
269
270	if (i == 0)
271		return false;
272
273	ls2k_bmc_restore_bridge_pci_data(parent, ddata);
274
275	/* Check if PCI-E is connected */
276	if (!ls2k_bmc_pcie_is_connected(parent, ddata))
277		return false;
278
279	/* Waiting for U-Boot and DDR ready */
280	mdelay(LS2K_BMC_RESET_WAIT_TIME);
281	if (!ls2k_bmc_bar0_addr_is_set(parent))
282		return false;
283
284	/* Restore LS2K BMC PCI-E config data */
285	pci_write_config_dword(pdev, PCI_COMMAND, ddata->bmc_pci_data.pci_command);
286	pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, ddata->bmc_pci_data.base_address0);
287	pci_write_config_dword(pdev, PCI_INTERRUPT_LINE, ddata->bmc_pci_data.interrupt_line);
288
289	return 0;
290}
291
292static void ls2k_bmc_events_fn(struct work_struct *work)
293{
294	struct ls2k_bmc_ddata *ddata = container_of(work, struct ls2k_bmc_ddata, bmc_reset_work);
295
296	/*
297	 * The PCI-E is lost when the BMC resets, at which point access to the PCI-E
298	 * from other CPUs is suspended to prevent a crash.
299	 */
300	stop_machine(ls2k_bmc_recover_pci_data, ddata, NULL);
301
302	if (IS_ENABLED(CONFIG_VT)) {
303		/* Re-push the display due to previous PCI-E loss. */
304		set_console(vt_move_to_console(MAX_NR_CONSOLES - 1, 1));
305	}
306}
307
308static irqreturn_t ls2k_bmc_interrupt(int irq, void *arg)
309{
310	struct ls2k_bmc_ddata *ddata = arg;
311	static unsigned long last_jiffies;
312
313	if (system_state != SYSTEM_RUNNING)
314		return IRQ_HANDLED;
315
316	/* Skip interrupt in LS2K_BMC_INT_INTERVAL */
317	if (time_after(jiffies, last_jiffies + LS2K_BMC_INT_INTERVAL)) {
318		schedule_work(&ddata->bmc_reset_work);
319		last_jiffies = jiffies;
320	}
321
322	return IRQ_HANDLED;
323}
324
325/*
326 * Saves the BMC parent device (LS7A) and its own PCI configuration space registers
327 * that need to be restored after BMC reset.
328 */
329static void ls2k_bmc_save_pci_data(struct pci_dev *pdev, struct ls2k_bmc_ddata *ddata)
330{
331	struct pci_dev *parent = pdev->bus->self;
332	int base, i = 0;
333
334	pci_read_config_dword(parent, PCI_COMMAND, &ddata->bridge_pci_data.pci_command);
335
336	for (base = PCI_BASE_ADDRESS_0; base <= PCI_BASE_ADDRESS_5; base += PCI_REG_STRIDE, i++)
337		pci_read_config_dword(parent, base, &ddata->bridge_pci_data.base_address[i]);
338
339	pci_read_config_dword(parent, PCI_ROM_ADDRESS, &ddata->bridge_pci_data.rom_addreess);
340	pci_read_config_dword(parent, PCI_INTERRUPT_LINE, &ddata->bridge_pci_data.interrupt_line);
341
342	pci_read_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_LO,
343			      &ddata->bridge_pci_data.msi_lo);
344	pci_read_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_HI,
345			      &ddata->bridge_pci_data.msi_hi);
346
347	pci_read_config_dword(parent, parent->pcie_cap + PCI_EXP_DEVCTL,
348			      &ddata->bridge_pci_data.devctl);
349	pci_read_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCAP,
350			      &ddata->bridge_pci_data.linkcap);
351	pci_read_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCTL,
352			      &ddata->bridge_pci_data.linkctl_sts);
353
354	pci_read_config_dword(parent, LS7A_GEN2_CTL, &ddata->bridge_pci_data.gen2_ctrl);
355	ddata->bridge_pci_data.gen2_ctrl |= FIELD_PREP(LS7A_GEN2_SPEED_CHANG, 0x1) |
356					    FIELD_PREP(LS7A_CONF_PHY_TX, 0x0);
357
358	pci_read_config_dword(parent, LS7A_SYMBOL_TIMER, &ddata->bridge_pci_data.symbol_timer);
359	ddata->bridge_pci_data.symbol_timer |= LS7A_MASK_LEN_MATCH;
360
361	pci_read_config_dword(pdev, PCI_COMMAND, &ddata->bmc_pci_data.pci_command);
362	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &ddata->bmc_pci_data.base_address0);
363	pci_read_config_dword(pdev, PCI_INTERRUPT_LINE, &ddata->bmc_pci_data.interrupt_line);
364}
365
366static int ls2k_bmc_init(struct ls2k_bmc_ddata *ddata)
367{
368	struct pci_dev *pdev = to_pci_dev(ddata->dev);
369	void __iomem *gpio_base;
370	int gpio_irq, ret, val;
371
372	ls2k_bmc_save_pci_data(pdev, ddata);
373
374	INIT_WORK(&ddata->bmc_reset_work, ls2k_bmc_events_fn);
375
376	ret = devm_request_irq(&pdev->dev, pdev->irq, ls2k_bmc_interrupt,
377			       IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc pcie", ddata);
378	if (ret) {
379		dev_err(ddata->dev, "Failed to request LS2KBMC PCI-E IRQ %d.\n", pdev->irq);
380		return ret;
381	}
382
383	gpio_base = ioremap(LOONGSON_GPIO_REG_BASE, LOONGSON_GPIO_REG_SIZE);
384	if (!gpio_base)
385		return -ENOMEM;
386
387	/* Disable GPIO output */
388	val = readl(gpio_base + LOONGSON_GPIO_OEN);
389	writel(val | BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_OEN);
390
391	/* Enable GPIO functionality */
392	val = readl(gpio_base + LOONGSON_GPIO_FUNC);
393	writel(val & ~BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_FUNC);
394
395	/* Set GPIO interrupts to low-level active */
396	val = readl(gpio_base + LOONGSON_GPIO_INTPOL);
397	writel(val & ~BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_INTPOL);
398
399	/* Enable GPIO interrupts */
400	val = readl(gpio_base + LOONGSON_GPIO_INTEN);
401	writel(val | BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_INTEN);
402
403	iounmap(gpio_base);
404
405	/*
406	 * Since gpio_chip->to_irq is not implemented in the Loongson-3 GPIO driver,
407	 * acpi_register_gsi() is used to obtain the GPIO IRQ. The GPIO interrupt is a
408	 * watchdog interrupt that is triggered when the BMC resets.
409	 */
410	gpio_irq = acpi_register_gsi(NULL, LS2K_BMC_RESET_GPIO_GSI, ACPI_EDGE_SENSITIVE,
411				     ACPI_ACTIVE_LOW);
412	if (gpio_irq < 0)
413		return gpio_irq;
414
415	ret = devm_request_irq(ddata->dev, gpio_irq, ls2k_bmc_interrupt,
416			       IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc gpio", ddata);
417	if (ret)
418		dev_err(ddata->dev, "Failed to request LS2KBMC GPIO IRQ %d.\n", gpio_irq);
419
420	acpi_unregister_gsi(LS2K_BMC_RESET_GPIO_GSI);
421	return ret;
422}
423
424/*
425 * Currently the Loongson-2K BMC hardware does not have an I2C interface to adapt to the
426 * resolution. We set the resolution by presetting "video=1280x1024-16@2M" to the BMC memory.
427 */
428static int ls2k_bmc_parse_mode(struct pci_dev *pdev, struct simplefb_platform_data *pd)
429{
430	char *mode;
431	int depth, ret;
432
433	/* The last 16M of PCI BAR0 is used to store the resolution string. */
434	mode = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0) + SZ_16M, SZ_16M);
435	if (!mode)
436		return -ENOMEM;
437
438	/* The resolution field starts with the flag "video=". */
439	if (!strncmp(mode, "video=", 6))
440		mode = mode + 6;
441
442	ret = kstrtoint(strsep(&mode, "x"), 10, &pd->width);
443	if (ret)
444		return ret;
445
446	ret = kstrtoint(strsep(&mode, "-"), 10, &pd->height);
447	if (ret)
448		return ret;
449
450	ret = kstrtoint(strsep(&mode, "@"), 10, &depth);
451	if (ret)
452		return ret;
453
454	pd->stride = pd->width * depth / 8;
455	pd->format = depth == 32 ? "a8r8g8b8" : "r5g6b5";
456
457	return 0;
458}
459
460static int ls2k_bmc_probe(struct pci_dev *dev, const struct pci_device_id *id)
461{
462	struct simplefb_platform_data pd;
463	struct ls2k_bmc_ddata *ddata;
464	resource_size_t base;
465	int ret;
466
467	ret = pci_enable_device(dev);
468	if (ret)
469		return ret;
470
471	ddata = devm_kzalloc(&dev->dev, sizeof(*ddata), GFP_KERNEL);
472	if (!ddata) {
473		ret = -ENOMEM;
474		goto disable_pci;
475	}
476
477	ddata->dev = &dev->dev;
478
479	ret = ls2k_bmc_init(ddata);
480	if (ret)
481		goto disable_pci;
482
483	ret = ls2k_bmc_parse_mode(dev, &pd);
484	if (ret)
485		goto disable_pci;
486
487	ls2k_bmc_cells[LS2K_BMC_DISPLAY].platform_data = &pd;
488	ls2k_bmc_cells[LS2K_BMC_DISPLAY].pdata_size = sizeof(pd);
489	base = dev->resource[0].start + LS2K_DISPLAY_RES_START;
490
491	/* Remove conflicting efifb device */
492	ret = aperture_remove_conflicting_devices(base, SZ_4M, "simple-framebuffer");
493	if (ret) {
494		dev_err(&dev->dev, "Failed to removed firmware framebuffers: %d\n", ret);
495		goto disable_pci;
496	}
497
498	ret = devm_mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
499				   ls2k_bmc_cells, ARRAY_SIZE(ls2k_bmc_cells),
500				   &dev->resource[0], 0, NULL);
501	if (ret)
502		goto disable_pci;
503
504	return 0;
505
506disable_pci:
507	pci_disable_device(dev);
508	return ret;
509}
510
511static void ls2k_bmc_remove(struct pci_dev *dev)
512{
513	pci_disable_device(dev);
514}
515
516static struct pci_device_id ls2k_bmc_devices[] = {
517	{ PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x1a05) },
518	{ }
519};
520MODULE_DEVICE_TABLE(pci, ls2k_bmc_devices);
521
522static struct pci_driver ls2k_bmc_driver = {
523	.name = "ls2k-bmc",
524	.id_table = ls2k_bmc_devices,
525	.probe = ls2k_bmc_probe,
526	.remove = ls2k_bmc_remove,
527};
528module_pci_driver(ls2k_bmc_driver);
529
530MODULE_DESCRIPTION("Loongson-2K Board Management Controller (BMC) Core driver");
531MODULE_AUTHOR("Loongson Technology Corporation Limited");
532MODULE_LICENSE("GPL");
~/ Read-only file preview.