Skip to content

Commit 4058255

Browse files
AaronDotMingcongBai
authored andcommitted
FROMLIST: ipmi: Add Loongson-2K BMC support
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, fifo flag is used to ensure data consistency. The single KCS message block is as follows: +-------------------------------------------------------------------------+ |FIFO flags| KCS register data | CMD data | KCS version | WR REQ | WR ACK | +-------------------------------------------------------------------------+ Co-developed-by: Chong Qiao <[email protected]> Signed-off-by: Chong Qiao <[email protected]> Reviewed-by: Huacai Chen <[email protected]> Acked-by: Corey Minyard <[email protected]> Signed-off-by: Binbin Zhou <[email protected]> Link: https://lore.kernel.org/loongarch/[email protected]/ [Mingcong Bai: Resolved minor merge conflicts in drivers/mfd/Kconfig, the original patch was rebased on post-6.17 for-mfd-next, see https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git/log/?h=for-mfd-next.] Signed-off-by: Mingcong Bai <[email protected]>
1 parent 331d2b8 commit 4058255

File tree

6 files changed

+209
-0
lines changed

6 files changed

+209
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14427,6 +14427,7 @@ LOONGSON-2K Board Management Controller (BMC) DRIVER
1442714427
M: Binbin Zhou <[email protected]>
1442814428
M: Chong Qiao <[email protected]>
1442914429
S: Maintained
14430+
F: drivers/char/ipmi/ipmi_si_ls2k.c
1443014431
F: drivers/mfd/ls2k-bmc-core.c
1443114432

1443214433
LOONGSON EDAC DRIVER

drivers/char/ipmi/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ config IPMI_IPMB
8484
bus, and it also supports direct messaging on the bus using
8585
IPMB direct messages. This module requires I2C support.
8686

87+
config IPMI_LS2K
88+
bool 'Loongson-2K IPMI interface'
89+
depends on LOONGARCH
90+
select MFD_LS2K_BMC_CORE
91+
help
92+
Provides a driver for Loongson-2K IPMI interfaces.
93+
8794
config IPMI_POWERNV
8895
depends on PPC_POWERNV
8996
tristate 'POWERNV (OPAL firmware) IPMI interface'

drivers/char/ipmi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o \
88
ipmi_si_mem_io.o
99
ipmi_si-$(CONFIG_HAS_IOPORT) += ipmi_si_port_io.o
1010
ipmi_si-$(CONFIG_PCI) += ipmi_si_pci.o
11+
ipmi_si-$(CONFIG_IPMI_LS2K) += ipmi_si_ls2k.o
1112
ipmi_si-$(CONFIG_PARISC) += ipmi_si_parisc.o
1213

1314
obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o

drivers/char/ipmi/ipmi_si.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ void ipmi_si_pci_shutdown(void);
101101
static inline void ipmi_si_pci_init(void) { }
102102
static inline void ipmi_si_pci_shutdown(void) { }
103103
#endif
104+
#ifdef CONFIG_IPMI_LS2K
105+
void ipmi_si_ls2k_init(void);
106+
void ipmi_si_ls2k_shutdown(void);
107+
#else
108+
static inline void ipmi_si_ls2k_init(void) { }
109+
static inline void ipmi_si_ls2k_shutdown(void) { }
110+
#endif
104111
#ifdef CONFIG_PARISC
105112
void ipmi_si_parisc_init(void);
106113
void ipmi_si_parisc_shutdown(void);

drivers/char/ipmi/ipmi_si_intf.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,6 +2120,8 @@ static int __init init_ipmi_si(void)
21202120

21212121
ipmi_si_pci_init();
21222122

2123+
ipmi_si_ls2k_init();
2124+
21232125
ipmi_si_parisc_init();
21242126

21252127
mutex_lock(&smi_infos_lock);
@@ -2331,6 +2333,8 @@ static void cleanup_ipmi_si(void)
23312333

23322334
ipmi_si_pci_shutdown();
23332335

2336+
ipmi_si_ls2k_shutdown();
2337+
23342338
ipmi_si_parisc_shutdown();
23352339

23362340
ipmi_si_platform_shutdown();

drivers/char/ipmi/ipmi_si_ls2k.c

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* Driver for Loongson-2K BMC IPMI interface
4+
*
5+
* Copyright (C) 2024-2025 Loongson Technology Corporation Limited.
6+
*
7+
* Authors:
8+
* Chong Qiao <[email protected]>
9+
* Binbin Zhou <[email protected]>
10+
*/
11+
12+
#include <linux/bitfield.h>
13+
#include <linux/ioport.h>
14+
#include <linux/module.h>
15+
#include <linux/types.h>
16+
17+
#include "ipmi_si.h"
18+
19+
#define LS2K_KCS_FIFO_IBFH 0x0
20+
#define LS2K_KCS_FIFO_IBFT 0x1
21+
#define LS2K_KCS_FIFO_OBFH 0x2
22+
#define LS2K_KCS_FIFO_OBFT 0x3
23+
24+
/* KCS registers */
25+
#define LS2K_KCS_REG_STS 0x4
26+
#define LS2K_KCS_REG_DATA_OUT 0x5
27+
#define LS2K_KCS_REG_DATA_IN 0x6
28+
#define LS2K_KCS_REG_CMD 0x8
29+
30+
#define LS2K_KCS_CMD_DATA 0xa
31+
#define LS2K_KCS_VERSION 0xb
32+
#define LS2K_KCS_WR_REQ 0xc
33+
#define LS2K_KCS_WR_ACK 0x10
34+
35+
#define LS2K_KCS_STS_OBF BIT(0)
36+
#define LS2K_KCS_STS_IBF BIT(1)
37+
#define LS2K_KCS_STS_SMS_ATN BIT(2)
38+
#define LS2K_KCS_STS_CMD BIT(3)
39+
40+
#define LS2K_KCS_DATA_MASK (LS2K_KCS_STS_OBF | LS2K_KCS_STS_IBF | LS2K_KCS_STS_CMD)
41+
42+
static bool ls2k_registered;
43+
44+
static unsigned char ls2k_mem_inb_v0(const struct si_sm_io *io, unsigned int offset)
45+
{
46+
void __iomem *addr = io->addr;
47+
int reg_offset;
48+
49+
if (offset & BIT(0)) {
50+
reg_offset = LS2K_KCS_REG_STS;
51+
} else {
52+
writeb(readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_STS_OBF, addr + LS2K_KCS_REG_STS);
53+
reg_offset = LS2K_KCS_REG_DATA_OUT;
54+
}
55+
56+
return readb(addr + reg_offset);
57+
}
58+
59+
static unsigned char ls2k_mem_inb_v1(const struct si_sm_io *io, unsigned int offset)
60+
{
61+
void __iomem *addr = io->addr;
62+
unsigned char inb = 0, cmd;
63+
bool obf, ibf;
64+
65+
obf = readb(addr + LS2K_KCS_FIFO_OBFH) ^ readb(addr + LS2K_KCS_FIFO_OBFT);
66+
ibf = readb(addr + LS2K_KCS_FIFO_IBFH) ^ readb(addr + LS2K_KCS_FIFO_IBFT);
67+
cmd = readb(addr + LS2K_KCS_CMD_DATA);
68+
69+
if (offset & BIT(0)) {
70+
inb = readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_DATA_MASK;
71+
inb |= FIELD_PREP(LS2K_KCS_STS_OBF, obf)
72+
| FIELD_PREP(LS2K_KCS_STS_IBF, ibf)
73+
| FIELD_PREP(LS2K_KCS_STS_CMD, cmd);
74+
} else {
75+
inb = readb(addr + LS2K_KCS_REG_DATA_OUT);
76+
writeb(readb(addr + LS2K_KCS_FIFO_OBFH), addr + LS2K_KCS_FIFO_OBFT);
77+
}
78+
79+
return inb;
80+
}
81+
82+
static void ls2k_mem_outb_v0(const struct si_sm_io *io, unsigned int offset,
83+
unsigned char val)
84+
{
85+
void __iomem *addr = io->addr;
86+
unsigned char sts = readb(addr + LS2K_KCS_REG_STS);
87+
int reg_offset;
88+
89+
if (sts & LS2K_KCS_STS_IBF)
90+
return;
91+
92+
if (offset & BIT(0)) {
93+
reg_offset = LS2K_KCS_REG_CMD;
94+
sts |= LS2K_KCS_STS_CMD;
95+
} else {
96+
reg_offset = LS2K_KCS_REG_DATA_IN;
97+
sts &= ~LS2K_KCS_STS_CMD;
98+
}
99+
100+
writew(val, addr + reg_offset);
101+
writeb(sts | LS2K_KCS_STS_IBF, addr + LS2K_KCS_REG_STS);
102+
writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ);
103+
}
104+
105+
static void ls2k_mem_outb_v1(const struct si_sm_io *io, unsigned int offset,
106+
unsigned char val)
107+
{
108+
void __iomem *addr = io->addr;
109+
unsigned char ibfh, ibft;
110+
int reg_offset;
111+
112+
ibfh = readb(addr + LS2K_KCS_FIFO_IBFH);
113+
ibft = readb(addr + LS2K_KCS_FIFO_IBFT);
114+
115+
if (ibfh ^ ibft)
116+
return;
117+
118+
reg_offset = (offset & BIT(0)) ? LS2K_KCS_REG_CMD : LS2K_KCS_REG_DATA_IN;
119+
writew(val, addr + reg_offset);
120+
121+
writeb(offset & BIT(0), addr + LS2K_KCS_CMD_DATA);
122+
writeb(!ibft, addr + LS2K_KCS_FIFO_IBFH);
123+
writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ);
124+
}
125+
126+
static void ls2k_mem_cleanup(struct si_sm_io *io)
127+
{
128+
if (io->addr)
129+
iounmap(io->addr);
130+
}
131+
132+
static int ipmi_ls2k_mem_setup(struct si_sm_io *io)
133+
{
134+
unsigned char version;
135+
136+
io->addr = ioremap(io->addr_data, io->regspacing);
137+
if (!io->addr)
138+
return -EIO;
139+
140+
version = readb(io->addr + LS2K_KCS_VERSION);
141+
142+
io->inputb = version ? ls2k_mem_inb_v1 : ls2k_mem_inb_v0;
143+
io->outputb = version ? ls2k_mem_outb_v1 : ls2k_mem_outb_v0;
144+
io->io_cleanup = ls2k_mem_cleanup;
145+
146+
return 0;
147+
}
148+
149+
static int ipmi_ls2k_probe(struct platform_device *pdev)
150+
{
151+
struct si_sm_io io;
152+
153+
memset(&io, 0, sizeof(io));
154+
155+
io.si_info = &ipmi_kcs_si_info;
156+
io.io_setup = ipmi_ls2k_mem_setup;
157+
io.addr_data = pdev->resource[0].start;
158+
io.regspacing = resource_size(&pdev->resource[0]);
159+
io.dev = &pdev->dev;
160+
161+
dev_dbg(&pdev->dev, "addr 0x%lx, spacing %d.\n", io.addr_data, io.regspacing);
162+
163+
return ipmi_si_add_smi(&io);
164+
}
165+
166+
static void ipmi_ls2k_remove(struct platform_device *pdev)
167+
{
168+
ipmi_si_remove_by_dev(&pdev->dev);
169+
}
170+
171+
struct platform_driver ipmi_ls2k_platform_driver = {
172+
.driver = {
173+
.name = "ls2k-ipmi-si",
174+
},
175+
.probe = ipmi_ls2k_probe,
176+
.remove = ipmi_ls2k_remove,
177+
};
178+
179+
void ipmi_si_ls2k_init(void)
180+
{
181+
platform_driver_register(&ipmi_ls2k_platform_driver);
182+
ls2k_registered = true;
183+
}
184+
185+
void ipmi_si_ls2k_shutdown(void)
186+
{
187+
if (ls2k_registered)
188+
platform_driver_unregister(&ipmi_ls2k_platform_driver);
189+
}

0 commit comments

Comments
 (0)