File Index
Files and directories
Sign in
FILE

ipmi_si_ls2k.c

File preview with syntax highlighting.

linux-6.19.14/drivers/char/ipmi/ipmi_si_ls2k.c C
  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 <qiaochong@loongson.cn>
  9 *	Binbin Zhou <zhoubinbin@loongson.cn>
 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
 42static bool ls2k_registered;
 43
 44static 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
 59static 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
 82static 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
105static 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
126static void ls2k_mem_cleanup(struct si_sm_io *io)
127{
128	if (io->addr)
129		iounmap(io->addr);
130}
131
132static 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
149static 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
166static void ipmi_ls2k_remove(struct platform_device *pdev)
167{
168	ipmi_si_remove_by_dev(&pdev->dev);
169}
170
171struct 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
179void ipmi_si_ls2k_init(void)
180{
181	platform_driver_register(&ipmi_ls2k_platform_driver);
182	ls2k_registered = true;
183}
184
185void ipmi_si_ls2k_shutdown(void)
186{
187	if (ls2k_registered)
188		platform_driver_unregister(&ipmi_ls2k_platform_driver);
189}
~/ Read-only file preview.