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