From nobody Fri Dec 19 19:19:24 2025 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by smtp.subspace.kernel.org (Postfix) with ESMTP id CA2941A256B for ; Mon, 30 Dec 2024 09:31:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=114.242.206.163 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735551099; cv=none; b=Ue7a+gYyp9zbe4J5o8VWBcERUCaJlnKONJJJofFeGcqKVgCfFfitKp+9qRvi5nMO4CNN//WMWhrIRbuh9rM2dakiNSpxBd9WaTMd+raWdf/6gMDp0SU1pBCmLQZLTTKjz2bZuNNCwtoisYHFmkpU1B7UHi59clYFW1Lio/iW0kk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735551099; c=relaxed/simple; bh=Msv0l4BrNFwtmUlpgJuSYwD3cDeYqqYSA5dJVfi/jiw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kwDl/lFIGk0OcOVbGhRy0gfYdiY6OeAg+ApSm93Lf84PucX0Vpn5+gLWJYAfqvUBo7FLzErnTqVP9fMmjESYqHAb0jWiW74vbi9WGgZBMoUQ5bBXQV2Y9vu+sHpl7HbX7vXxCWZVa0Uf6yyLZsn6a4TvJEJ3j2rE5XuNxzjsLw0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=loongson.cn; spf=pass smtp.mailfrom=loongson.cn; arc=none smtp.client-ip=114.242.206.163 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=loongson.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=loongson.cn Received: from loongson.cn (unknown [223.64.68.63]) by gateway (Coremail) with SMTP id _____8CxyuF0aHJnQs9bAA--.52326S3; Mon, 30 Dec 2024 17:31:32 +0800 (CST) Received: from localhost.localdomain (unknown [223.64.68.63]) by front1 (Coremail) with SMTP id qMiowMCxncVkaHJnp74NAA--.4295S4; Mon, 30 Dec 2024 17:31:19 +0800 (CST) From: Binbin Zhou To: Binbin Zhou , Huacai Chen , Lee Jones , Corey Minyard , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter Cc: Huacai Chen , linux-kernel@vger.kernel.org, openipmi-developer@lists.sourceforge.net, dri-devel@lists.freedesktop.org, Xuerui Wang , loongarch@lists.linux.dev, Binbin Zhou , Chong Qiao Subject: [PATCH v1 2/4] ipmi: Add Loongson-2K BMC support Date: Mon, 30 Dec 2024 17:31:09 +0800 Message-ID: X-Mailer: git-send-email 2.43.5 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: qMiowMCxncVkaHJnp74NAA--.4295S4 X-CM-SenderInfo: p2kr3uplqex0o6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj93XoW3XFyUJF17Ar45KFW7Ary8CrX_yoW3tw4Upa 15K34kCw48tF47K3srJrykZFWrJr93Wa45tFW7X3yfurW3try0grn2yFy3ZF9rKFyDKF13 tFZ8Ar43WFW7A3gCm3ZEXasCq-sJn29KB7ZKAUJUUUUD529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUBab4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r126r13M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Ar0_tr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Cr0_Gr1UM28EF7xvwVC2z280aVAFwI0_Gr1j6F4UJwA2z4x0Y4vEx4A2jsIEc7CjxVAF wI0_Gr1j6F4UJwAaw2AFwI0_JF0_Jw1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2 xF0cIa020Ex4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_ Wrv_ZF1lYx0Ex4A2jsIE14v26r4j6F4UMcvjeVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x 0EwIxGrwCY1x0262kKe7AKxVWUtVW8ZwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkE bVWUJVW8JwCFI7km07C267AKxVWUAVWUtwC20s026c02F40E14v26r1j6r18MI8I3I0E74 80Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_GFv_WrylIxkGc2Ij64vIr41lIxAIcVC0 I7IYx2IY67AKxVW5JVW7JwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAIcVCF04 k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r4j6F4UMIIF0xvEx4A2jsIEc7Cj xVAFwI0_Gr0_Gr1UYxBIdaVFxhVjvjDU0xZFpf9x07j-b1bUUUUU= Content-Type: text/plain; charset="utf-8" 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, I use fifo pointer to ensure data consistency. Therefore I made the whole IPMI driver independent. Co-developed-by: Chong Qiao Signed-off-by: Chong Qiao Signed-off-by: Binbin Zhou --- drivers/char/ipmi/Makefile | 1 + drivers/char/ipmi/ipmi_si.h | 8 + drivers/char/ipmi/ipmi_si_intf.c | 3 + drivers/char/ipmi/ipmi_si_ls2k.c | 250 +++++++++++++++++++++++++++++++ 4 files changed, 262 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..614be45863b4 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -7,6 +7,7 @@ ipmi_si-y :=3D ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o = ipmi_bt_sm.o \ ipmi_si_hotmod.o ipmi_si_hardcode.o ipmi_si_platform.o \ ipmi_si_mem_io.o ipmi_si-$(CONFIG_HAS_IOPORT) +=3D ipmi_si_port_io.o +ipmi_si-$(CONFIG_LOONGARCH) +=3D ipmi_si_ls2k.o ipmi_si-$(CONFIG_PCI) +=3D ipmi_si_pci.o ipmi_si-$(CONFIG_PARISC) +=3D ipmi_si_parisc.o =20 diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h index a7ead2a4c753..0a4af352a42c 100644 --- a/drivers/char/ipmi/ipmi_si.h +++ b/drivers/char/ipmi/ipmi_si.h @@ -101,6 +101,14 @@ static inline void ipmi_si_parisc_init(void) { } static inline void ipmi_si_parisc_shutdown(void) { } #endif =20 +#ifdef CONFIG_LOONGARCH +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 + int ipmi_si_port_setup(struct si_sm_io *io); int ipmi_si_mem_setup(struct si_sm_io *io); =20 diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_i= ntf.c index eea23a3b966e..7227bc61be79 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2108,6 +2108,7 @@ static int __init init_ipmi_si(void) ipmi_si_pci_init(); =20 ipmi_si_parisc_init(); + ipmi_si_ls2k_init(); =20 /* We prefer devices with interrupts, but in the case of a machine with multiple BMCs we assume that there will be several instances @@ -2292,6 +2293,8 @@ static void cleanup_ipmi_si(void) =20 ipmi_si_platform_shutdown(); =20 + ipmi_si_ls2k_shutdown(); + mutex_lock(&smi_infos_lock); list_for_each_entry_safe(e, tmp_e, &smi_infos, link) cleanup_one_si(e); diff --git a/drivers/char/ipmi/ipmi_si_ls2k.c b/drivers/char/ipmi/ipmi_si_l= s2k.c new file mode 100644 index 000000000000..cb31bb989fca --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_ls2k.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Loongson-2K BMC IPMI + * + * Copyright (C) 2024 Loongson Technology Corporation Limited. + * + * Originally written by Chong Qiao + * Rewritten for mainline by Binbin Zhou + */ + +#include +#include +#include + +#include "ipmi_si.h" + +#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) + +/* Read and write fifo pointers for data consistency. */ +struct ls2k_fifo_flag { + u8 ibfh; + u8 ibft; + u8 obfh; + u8 obft; +}; + +struct ls2k_kcs_reg { + u8 status; + u8 data_out; + s16 data_in; + s16 cmd; +}; + +struct ls2k_kcs_data { + struct ls2k_fifo_flag fifo; + struct ls2k_kcs_reg reg; + u8 cmd_data; + u8 version; + u32 write_req; + u32 write_ack; + u32 reserved[2]; +}; + +static void ls2k_set_obf(struct ls2k_kcs_data *ik, u8 sts) +{ + ik->reg.status =3D (ik->reg.status & ~LS2K_KCS_STS_OBF) | (sts & BIT(0)); +} + +static void ls2k_set_ibf(struct ls2k_kcs_data *ik, u8 sts) +{ + ik->reg.status =3D (ik->reg.status & ~LS2K_KCS_STS_IBF) | ((sts & BIT(0))= << 1); +} + +static u8 ls2k_get_ibf(struct ls2k_kcs_data *ik) +{ + return (ik->reg.status >> 1) & BIT(0); +} + +static unsigned char intf_sim_inb_v0(struct ls2k_kcs_data *ik, + unsigned int offset) +{ + u32 inb =3D 0; + + switch (offset & BIT(0)) { + case 0: + inb =3D ik->reg.data_out; + ls2k_set_obf(ik, 0); + break; + case 1: + inb =3D ik->reg.status; + break; + } + + return inb; +} + +static unsigned char intf_sim_inb_v1(struct ls2k_kcs_data *ik, + unsigned int offset) +{ + u32 inb =3D 0; + int cmd; + bool obf, ibf; + + obf =3D ik->fifo.obfh !=3D ik->fifo.obft; + ibf =3D ik->fifo.ibfh !=3D ik->fifo.ibft; + cmd =3D ik->cmd_data; + + switch (offset & BIT(0)) { + case 0: + inb =3D ik->reg.data_out; + ik->fifo.obft =3D ik->fifo.obfh; + break; + case 1: + inb =3D ik->reg.status & ~LS2K_KCS_DATA_MASK; + inb |=3D obf | (ibf << 1) | (cmd << 3); + break; + } + + return inb; +} + +static unsigned char ls2k_mem_inb(const struct si_sm_io *io, + unsigned int offset) +{ + struct ls2k_kcs_data *ik =3D io->addr; + int inb =3D 0; + + if (ik->version =3D=3D 0) + inb =3D intf_sim_inb_v0(ik, offset); + else if (ik->version =3D=3D 1) + inb =3D intf_sim_inb_v1(ik, offset); + + return inb; +} + +static void intf_sim_outb_v0(struct ls2k_kcs_data *ik, unsigned int offset, + unsigned char val) +{ + if (ls2k_get_ibf(ik)) + return; + + switch (offset & BIT(0)) { + case 0: + ik->reg.data_in =3D val; + ik->reg.status &=3D ~LS2K_KCS_STS_CMD; + break; + + case 1: + ik->reg.cmd =3D val; + ik->reg.status |=3D LS2K_KCS_STS_CMD; + break; + } + + ls2k_set_ibf(ik, 1); + ik->write_req++; +} + +static void intf_sim_outb_v1(struct ls2k_kcs_data *ik, unsigned int offset, + unsigned char val) +{ + if (ik->fifo.ibfh !=3D ik->fifo.ibft) + return; + + switch (offset & BIT(0)) { + case 0: + ik->reg.data_in =3D val; + ik->cmd_data =3D 0; + break; + + case 1: + ik->reg.cmd =3D val; + ik->cmd_data =3D 1; + break; + } + + ik->fifo.ibfh =3D !ik->fifo.ibft; + ik->write_req++; +} + +static void ls2k_mem_outb(const struct si_sm_io *io, unsigned int offset, + unsigned char val) +{ + struct ls2k_kcs_data *ik =3D io->addr; + + if (ik->version =3D=3D 0) + intf_sim_outb_v0(ik, offset, val); + else if (ik->version =3D=3D 1) + intf_sim_outb_v1(ik, offset, val); +} + +static void ls2k_mem_cleanup(struct si_sm_io *io) +{ + if (io->addr) + iounmap(io->addr); +} + +static int ipmi_ls2k_sim_setup(struct si_sm_io *io) +{ + io->addr =3D ioremap(io->addr_data, io->regspacing); + if (!io->addr) + return -EIO; + + io->inputb =3D ls2k_mem_inb; + io->outputb =3D ls2k_mem_outb; + io->io_cleanup =3D ls2k_mem_cleanup; + + return 0; +} + +static int ipmi_ls2k_probe(struct platform_device *pdev) +{ + struct si_sm_io io; + + dev_info(&pdev->dev, "probing via ls2k platform"); + memset(&io, 0, sizeof(io)); + + io.addr_source =3D SI_PLATFORM; + io.si_type =3D SI_KCS; + io.addr_space =3D IPMI_MEM_ADDR_SPACE; + io.io_setup =3D ipmi_ls2k_sim_setup; + io.addr_data =3D pdev->resource[0].start; + io.regspacing =3D pdev->resource[0].end - pdev->resource[0].start + 1; + io.regsize =3D DEFAULT_REGSIZE; + io.regshift =3D 0; + io.dev =3D &pdev->dev; + io.irq =3D 0; + if (io.irq) + io.irq_setup =3D ipmi_std_irq_setup; + + dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n", + &pdev->resource[0], io.regsize, io.regspacing, io.irq); + + 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 =3D { + .driver =3D { + .name =3D "ls2k-ipmi-si", + }, + .probe =3D ipmi_ls2k_probe, + .remove =3D ipmi_ls2k_remove, +}; + +static bool platform_registered; +void ipmi_si_ls2k_init(void) +{ + int rv; + + rv =3D platform_driver_register(&ipmi_ls2k_platform_driver); + if (rv) + pr_err("Unable to register driver: %d\n", rv); + else + platform_registered =3D true; +} + +void ipmi_si_ls2k_shutdown(void) +{ + if (platform_registered) + platform_driver_unregister(&ipmi_ls2k_platform_driver); +} --=20 2.43.5