Hi, Binbin, On Thu, May 29, 2025 at 8:23 PM Binbin Zhou <zhoubin...@loongson.cn> wrote: > > 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 <qiaoch...@loongson.cn> > Signed-off-by: Chong Qiao <qiaoch...@loongson.cn> > Signed-off-by: Binbin Zhou <zhoubin...@loongson.cn> > --- > drivers/char/ipmi/Makefile | 1 + > drivers/char/ipmi/ipmi_si.h | 7 ++ > drivers/char/ipmi/ipmi_si_intf.c | 3 + > drivers/char/ipmi/ipmi_si_ls2k.c | 190 +++++++++++++++++++++++++++++++ > 4 files changed, 201 insertions(+) > create mode 100644 drivers/char/ipmi/ipmi_si_ls2k.c > > diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile > index e0944547c9d0..17308dd6be20 100644 > --- a/drivers/char/ipmi/Makefile > +++ b/drivers/char/ipmi/Makefile > @@ -8,6 +8,7 @@ ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o > ipmi_bt_sm.o \ > ipmi_si_mem_io.o > ipmi_si-$(CONFIG_HAS_IOPORT) += ipmi_si_port_io.o > ipmi_si-$(CONFIG_PCI) += ipmi_si_pci.o > +ipmi_si-$(CONFIG_MFD_LS2K_BMC) += ipmi_si_ls2k.o > ipmi_si-$(CONFIG_PARISC) += ipmi_si_parisc.o > > obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o > diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h > index 508c3fd45877..f38ea4f4c891 100644 > --- a/drivers/char/ipmi/ipmi_si.h > +++ b/drivers/char/ipmi/ipmi_si.h > @@ -101,6 +101,13 @@ void ipmi_si_pci_shutdown(void); > static inline void ipmi_si_pci_init(void) { } > static inline void ipmi_si_pci_shutdown(void) { } > #endif > +#ifdef CONFIG_MFD_LS2K_BMC > +void ipmi_si_ls2k_init(void); > +void ipmi_si_ls2k_shutdown(void); > +#else > +static inline void ipmi_si_ls2k_init(void) { } > +static inline void ipmi_si_ls2k_shutdown(void) { } > +#endif > #ifdef CONFIG_PARISC > void ipmi_si_parisc_init(void); > void ipmi_si_parisc_shutdown(void); > diff --git a/drivers/char/ipmi/ipmi_si_intf.c > b/drivers/char/ipmi/ipmi_si_intf.c > index 7fe891783a37..c13d5132fffc 100644 > --- a/drivers/char/ipmi/ipmi_si_intf.c > +++ b/drivers/char/ipmi/ipmi_si_intf.c > @@ -2120,6 +2120,7 @@ static int __init init_ipmi_si(void) > > ipmi_si_pci_init(); > > + ipmi_si_ls2k_init(); > ipmi_si_parisc_init(); > > mutex_lock(&smi_infos_lock); > @@ -2334,6 +2335,8 @@ static void cleanup_ipmi_si(void) > > ipmi_si_pci_shutdown(); > > + ipmi_si_ls2k_shutdown(); > + > ipmi_si_parisc_shutdown(); > > ipmi_si_platform_shutdown(); > diff --git a/drivers/char/ipmi/ipmi_si_ls2k.c > b/drivers/char/ipmi/ipmi_si_ls2k.c > new file mode 100644 > index 000000000000..71159e9882b7 > --- /dev/null > +++ b/drivers/char/ipmi/ipmi_si_ls2k.c > @@ -0,0 +1,190 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Driver for Loongson-2K BMC IPMI > + * > + * Copyright (C) 2024-2025 Loongson Technology Corporation Limited. > + * > + * Authors: > + * Chong Qiao <qiaoch...@loongson.cn> > + * Binbin Zhou <zhoubin...@loongson.cn> > + */ > + > +#include <linux/bitfield.h> > +#include <linux/ioport.h> > +#include <linux/module.h> > +#include <linux/types.h> > + > +#include "ipmi_si.h" > + > +#define LS2K_KCS_FIFO_IBFH 0x0 > +#define LS2K_KCS_FIFO_IBFT 0x1 > +#define LS2K_KCS_FIFO_OBFH 0x2 > +#define LS2K_KCS_FIFO_OBFT 0x3 > + > +/* KCS registers */ > +#define LS2K_KCS_REG_STS 0x4 > +#define LS2K_KCS_REG_DATA_OUT 0x5 > +#define LS2K_KCS_REG_DATA_IN 0x6 > +#define LS2K_KCS_REG_CMD 0x8 > + > +#define LS2K_KCS_CMD_DATA 0xa > +#define LS2K_KCS_VERSION 0xb > +#define LS2K_KCS_WR_REQ 0xc > +#define LS2K_KCS_WR_ACK 0x10 > + > +#define LS2K_KCS_STS_OBF BIT(0) > +#define LS2K_KCS_STS_IBF BIT(1) > +#define LS2K_KCS_STS_SMS_ATN BIT(2) > +#define LS2K_KCS_STS_CMD BIT(3) > + > +#define LS2K_KCS_DATA_MASK (LS2K_KCS_STS_OBF | LS2K_KCS_STS_IBF | > LS2K_KCS_STS_CMD) > + > +static bool ls2k_registered; > + > +static unsigned char ls2k_mem_inb_v0(const struct si_sm_io *io, unsigned int > offset) > +{ > + void __iomem *addr = io->addr; > + int reg_offset; > + > + if (offset & BIT(0)) { > + reg_offset = LS2K_KCS_REG_STS; > + } else { > + writeb(readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_STS_OBF, > + addr + LS2K_KCS_REG_STS); Newline is unnecessary here.
Huacai > + reg_offset = LS2K_KCS_REG_DATA_OUT; > + } > + > + return readb(addr + reg_offset); > +} > + > +static unsigned char ls2k_mem_inb_v1(const struct si_sm_io *io, unsigned int > offset) > +{ > + void __iomem *addr = io->addr; > + unsigned char inb = 0, cmd; > + bool obf, ibf; > + > + obf = readb(addr + LS2K_KCS_FIFO_OBFH) ^ readb(addr + > LS2K_KCS_FIFO_OBFT); > + ibf = readb(addr + LS2K_KCS_FIFO_IBFH) ^ readb(addr + > LS2K_KCS_FIFO_IBFT); > + cmd = readb(addr + LS2K_KCS_CMD_DATA); > + > + if (offset & BIT(0)) { > + inb = readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_DATA_MASK; > + inb |= FIELD_PREP(LS2K_KCS_STS_OBF, obf) > + | FIELD_PREP(LS2K_KCS_STS_IBF, ibf) > + | FIELD_PREP(LS2K_KCS_STS_CMD, cmd); > + } else { > + inb = readb(addr + LS2K_KCS_REG_DATA_OUT); > + writeb(readb(addr + LS2K_KCS_FIFO_OBFH), addr + > LS2K_KCS_FIFO_OBFT); > + } > + > + return inb; > +} > + > +static void ls2k_mem_outb_v0(const struct si_sm_io *io, unsigned int offset, > + unsigned char val) > +{ > + void __iomem *addr = io->addr; > + unsigned char sts = readb(addr + LS2K_KCS_REG_STS); > + int reg_offset; > + > + if (sts & LS2K_KCS_STS_IBF) > + return; > + > + if (offset & BIT(0)) { > + reg_offset = LS2K_KCS_REG_CMD; > + sts |= LS2K_KCS_STS_CMD; > + } else { > + reg_offset = LS2K_KCS_REG_DATA_IN; > + sts &= ~LS2K_KCS_STS_CMD; > + } > + > + writew(val, addr + reg_offset); > + writeb(sts | LS2K_KCS_STS_IBF, addr + LS2K_KCS_REG_STS); > + writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ); > +} > + > +static void ls2k_mem_outb_v1(const struct si_sm_io *io, unsigned int offset, > + unsigned char val) > +{ > + void __iomem *addr = io->addr; > + unsigned char ibfh, ibft; > + int reg_offset; > + > + ibfh = readb(addr + LS2K_KCS_FIFO_IBFH); > + ibft = readb(addr + LS2K_KCS_FIFO_IBFT); > + > + if (ibfh ^ ibft) > + return; > + > + reg_offset = (offset & BIT(0)) ? LS2K_KCS_REG_CMD : > LS2K_KCS_REG_DATA_IN; > + writew(val, addr + reg_offset); > + > + writeb(offset & BIT(0), addr + LS2K_KCS_CMD_DATA); > + writeb(!ibft, addr + LS2K_KCS_FIFO_IBFH); > + writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ); > +} > + > +static void ls2k_mem_cleanup(struct si_sm_io *io) > +{ > + if (io->addr) > + iounmap(io->addr); > +} > + > +static int ipmi_ls2k_mem_setup(struct si_sm_io *io) > +{ > + unsigned char version; > + > + io->addr = ioremap(io->addr_data, io->regspacing); > + if (!io->addr) > + return -EIO; > + > + version = readb(io->addr + LS2K_KCS_VERSION); > + > + io->inputb = version ? ls2k_mem_inb_v1 : ls2k_mem_inb_v0; > + io->outputb = version ? ls2k_mem_outb_v1 : ls2k_mem_outb_v0; > + io->io_cleanup = ls2k_mem_cleanup; > + > + return 0; > +} > + > +static int ipmi_ls2k_probe(struct platform_device *pdev) > +{ > + struct si_sm_io io; > + > + memset(&io, 0, sizeof(io)); > + > + io.si_info = &ipmi_kcs_si_info; > + io.io_setup = ipmi_ls2k_mem_setup; > + io.addr_data = pdev->resource[0].start; > + io.regspacing = resource_size(&pdev->resource[0]); > + io.dev = &pdev->dev; > + > + dev_dbg(&pdev->dev, "addr 0x%lx, spacing %d.\n", io.addr_data, > io.regspacing); > + > + return ipmi_si_add_smi(&io); > +} > + > +static void ipmi_ls2k_remove(struct platform_device *pdev) > +{ > + ipmi_si_remove_by_dev(&pdev->dev); > +} > + > +struct platform_driver ipmi_ls2k_platform_driver = { > + .driver = { > + .name = "ls2k-ipmi-si", > + }, > + .probe = ipmi_ls2k_probe, > + .remove = ipmi_ls2k_remove, > +}; > + > +void ipmi_si_ls2k_init(void) > +{ > + platform_driver_register(&ipmi_ls2k_platform_driver); > + ls2k_registered = true; > +} > + > +void ipmi_si_ls2k_shutdown(void) > +{ > + if (ls2k_registered) > + platform_driver_unregister(&ipmi_ls2k_platform_driver); > +} > -- > 2.47.1 > > _______________________________________________ Openipmi-developer mailing list Openipmi-developer@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openipmi-developer