From nobody Mon Jun 8 04:11:45 2026 Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9648B1B6D1A for ; Tue, 2 Jun 2026 14:48:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780411710; cv=none; b=OHLx0NN3yqRSL2LvbeHJjddD5z7GBtipalkb46ynhKL/fpfh2UH8GjDvtBgPIWROLjwOeOFMmDa5elAAoqr8JD9TLx2IMWb+Shc2RWGRcGcF9DhfGP1dwD/PaHl+l1gcbeci026Q7mBSQYMnXIYrOFJkiChqrP7P9TM/2z+5FEU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780411710; c=relaxed/simple; bh=sTqhCrXa/GCHTYK/FKIzABEzyLHDT+MTrf0kvwJ0nFY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=XBIX7dkjyXRNHjVLtYTKByxH+h81rJhwgUEiqdEhTMwb49btev7meJFJO1lSlAstYOfHLVkFGjmtpFo1eiBMPmlDjbnjG1W/dE9tjifPsRPT9OR9G1zjMLZjB57XzJmCxRdCWDYfNp/sPvc7bk0W1MZTM21Fg2BkGIprDs0VPpk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=9elements.com; spf=pass smtp.mailfrom=9elements.com; dkim=pass (2048-bit key) header.d=9elements.com header.i=@9elements.com header.b=Biaqm7Y9; arc=none smtp.client-ip=209.85.128.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=9elements.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=9elements.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=9elements.com header.i=@9elements.com header.b="Biaqm7Y9" Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-4906238c62eso94419285e9.3 for ; Tue, 02 Jun 2026 07:48:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=9elements.com; s=google; t=1780411706; x=1781016506; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=850xZSGatl2MZGDDCuD84AxCq40+igB4pW8/OpsSSW8=; b=Biaqm7Y9fWap4WiQZ10q/oQDLbZBXKcP0DKozVzj7PDlvLw7JcwYChx52xeVi0GrZb sdF8l6duQHCLpjSBd/doZmypnBedUNAnpXv7nL0gGYOOtaQqL7Bi+Px+91b217pCtFnD B1HY5RYFSo0Ugb3Z+rhq8Q/Zf2OsCNMA+qlGkK41gqNkjiaVN2mn8kEsJ0jFGzcCA17n pa7cNqq7S+2lS157Cp9DFFk/lxq3nscuvOvM+J6+YBDvSPD/2Wb7WKQVNmAheMDfDdHx YFtlvNzlbZElej3H8eqtPj0HMZt2qoTRcI3sN2rx86P6yp62+T/rhsZprIMOwA2gom4w CbVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780411706; x=1781016506; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=850xZSGatl2MZGDDCuD84AxCq40+igB4pW8/OpsSSW8=; b=ApL1z8jO4qoTFNR8c0tyAe7r0ZOYivvJTZSdTPvNQ8POPFqCF655MYeL2xEy8irsyA Ed1193Zl6M6zTp0KNX5CR3LH7/0OoAXziSIwz2N4XS2oqtsl+EsuW8xjC0skfNDbibwN EB7Hl4rz6rfv5sYuhJ9y7IXn4tW7wxj75isuO9MKNuJzkI+f817SJOeSyas1hkPMd44k 56HnHfoDmdqM+3aOJmxP34CxCcDXPK9WTNoKbMJzaqoOVN69HtEXOaBgPX9uy3t4RNck S1SUbI5DGkZghZYPDQQxy45jt2o96jvrqFTP8JQgMN1RNnMtGu3fo537Wjst61J/ruJX DZFg== X-Forwarded-Encrypted: i=1; AFNElJ+My34yselK8TGS5eX33uY0oqka7msJnADJcUr4+9vFioAZXkxy6BXDE+Pi6uR2Lf+kvGE8udxJ45uAGP8=@vger.kernel.org X-Gm-Message-State: AOJu0YxNUM8U0NOPACsozyjJjUG/as0mNfJxO0Fr65OCvdialJk/pLQz Qqo13plVRGxGi/iKythm/VPBuKsiChoXTLXU+LT0wfCgnE9bRhATMk7lUvKExqU/R38= X-Gm-Gg: Acq92OF5CkG1ykVWOuNArW/QtW3Onss38qD/8OxAlHQP5gpBKKOrL50VDflLSLLywWj uLWCgldjaDF/kYyu2xFOQm0Vy8EJEIClEsnDfK9fIm5RgckkFDvpbgzvUPlMNqaME1Z7t65ItQb dAtQJvXPl4Rmr1Ix4xazFuOMVqenyXmYVJ5xp5IrTfCvjjHR/07TaPQSgttjTL6ssJBJjA8IC/A RLDtA+yrx7D9YkPBjl5z8liKX10WItlJ62C+8/DdY3cn4BxUSXMGBXD129bOej7ttgBIL/Kgv9M PlVbM25/L6G5pKtUwh8uBzECStvSXFCFwoKAGie+p0IcTmppuOY4NkgGizDpVp3XEtfjKAy6HrY 9FqgY0QgqbwgcY8S8cSZcVhAD02w1Y2ap3ZZGf2uAgLkZn3VPatIz63msytih7kqgH0W2cSspRI 7WI26qWshVTQ75J2CTJTqMLUpVyp7/6D6fTNhJ3K0T85tFFTSAhKoX0lHit5oq X-Received: by 2002:a05:600c:3495:b0:48a:7676:30bc with SMTP id 5b1f17b1804b1-490b5081cc2mr968605e9.14.1780411705853; Tue, 02 Jun 2026 07:48:25 -0700 (PDT) Received: from gregwork.sec.9e.network ([188.111.3.154]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-490b0e88fdesm74881395e9.14.2026.06.02.07.48.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Jun 2026 07:48:25 -0700 (PDT) From: =?UTF-8?q?Gr=C3=A9goire=20Layet?= To: joel@jms.id.au, andrew@codeconstruct.com.au Cc: jacky_chou@aspeedtech.com, yh_chung@aspeedtech.com, ninad@linux.ibm.com, linux-aspeed@lists.ozlabs.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, =?UTF-8?q?Gr=C3=A9goire=20Layet?= Subject: [PATCH v1 1/2] soc: aspeed: add BMC-side PCIe BMC device driver Date: Tue, 2 Jun 2026 16:42:43 +0200 Message-ID: <4011e55ed57bdb87d04a49a26dfdc43af4799bac.1780409151.git.gregoire.layet@9elements.com> X-Mailer: git-send-email 2.51.2 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-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Taken from ASPEED 6.18 Kernel SDK Add support for PCIe communication BMC<->host. This add BMC side driver. Signed-off-by: Jacky Chou Signed-off-by: aspeedyh Signed-off-by: Gr=C3=A9goire Layet Tested-by: Gr=C3=A9goire Layet --- drivers/soc/aspeed/Kconfig | 9 + drivers/soc/aspeed/Makefile | 1 + drivers/soc/aspeed/aspeed-bmc-dev.c | 701 ++++++++++++++++++++++++++++ 3 files changed, 711 insertions(+) create mode 100644 drivers/soc/aspeed/aspeed-bmc-dev.c diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index f579ee0b5afa..341728df07b3 100644 --- a/drivers/soc/aspeed/Kconfig +++ b/drivers/soc/aspeed/Kconfig @@ -4,6 +4,15 @@ if ARCH_ASPEED || COMPILE_TEST =20 menu "ASPEED SoC drivers" =20 +config ASPEED_BMC_DEV + tristate "ASPEED BMC Device" + default n + help + Enable support for the ASPEED AST2600/AST2700 BMC Device. + This exposes the BMC to the host over PCIe, + providing a shared-memory BAR, host-to-BMC and BMC-to-host + message queues with doorbell interrupts and PCIe-to-LPC bridge. + config ASPEED_LPC_CTRL tristate "ASPEED LPC firmware cycle control" select REGMAP diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index b35d74592964..fab0d247df66 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_ASPEED_BMC_DEV) +=3D aspeed-bmc-dev.o obj-$(CONFIG_ASPEED_LPC_CTRL) +=3D aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_SNOOP) +=3D aspeed-lpc-snoop.o obj-$(CONFIG_ASPEED_UART_ROUTING) +=3D aspeed-uart-routing.o diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspee= d-bmc-dev.c new file mode 100644 index 000000000000..06005fa41d2a --- /dev/null +++ b/drivers/soc/aspeed/aspeed-bmc-dev.c @@ -0,0 +1,701 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) ASPEED Technology Inc. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#define SCU_TRIGGER_MSI + +/* AST2600 SCU */ +#define ASPEED_SCU04 0x04 +#define AST2600A3_SCU04 0x05030303 +#define ASPEED_SCUC20 0xC20 +#define ASPEED_SCUC24 0xC24 +#define MSI_ROUTING_MASK GENMASK(11, 10) +#define PCIDEV1_INTX_MSI_HOST2BMC_EN BIT(18) +#define MSI_ROUTING_PCIe2LPC_PCIDEV0 (0x1 << 10) +#define MSI_ROUTING_PCIe2LPC_PCIDEV1 (0x2 << 10) +/* AST2700 SCU */ +#define SCU0_REVISION_ID 0x0 +#define REVISION_ID GENMASK(23, 16) +#define SCU0_PCIE_CONF_CTRL 0x970 +/* Host2BMC */ +#define ASPEED_BMC_MEM_BAR 0xF10 +#define PCIE2PCI_MEM_BAR_ENABLE BIT(1) +#define HOST2BMC_MEM_BAR_ENABLE BIT(0) +#define ASPEED_BMC_MEM_BAR_REMAP 0xF18 + +#define ASPEED_BMC_SHADOW_CTRL 0xF50 +#define READ_ONLY_MASK BIT(31) +#define MASK_BAR1 BIT(2) +#define MASK_BAR0 BIT(1) +#define SHADOW_CFG BIT(0) + +#define ASPEED_BMC_HOST2BMC_Q1 0xA000 +#define ASPEED_BMC_HOST2BMC_Q2 0xA010 +#define ASPEED_BMC_BMC2HOST_Q1 0xA020 +#define ASPEED_BMC_BMC2HOST_Q2 0xA030 +#define ASPEED_BMC_BMC2HOST_STS 0xA040 +#define BMC2HOST_INT_STS_DOORBELL BIT(31) +#define BMC2HOST_ENABLE_INTB BIT(30) +#define BMC2HOST_Q1_FULL BIT(27) +#define BMC2HOST_Q1_EMPTY BIT(26) +#define BMC2HOST_Q2_FULL BIT(25) +#define BMC2HOST_Q2_EMPTY BIT(24) +#define BMC2HOST_Q1_FULL_UNMASK BIT(23) +#define BMC2HOST_Q1_EMPTY_UNMASK BIT(22) +#define BMC2HOST_Q2_FULL_UNMASK BIT(21) +#define BMC2HOST_Q2_EMPTY_UNMASK BIT(20) + +#define ASPEED_BMC_HOST2BMC_STS 0xA044 +#define HOST2BMC_INT_STS_DOORBELL BIT(31) +#define HOST2BMC_ENABLE_INTB BIT(30) +#define HOST2BMC_Q1_FULL BIT(27) +#define HOST2BMC_Q1_EMPTY BIT(26) +#define HOST2BMC_Q2_FULL BIT(25) +#define HOST2BMC_Q2_EMPTY BIT(24) +#define HOST2BMC_Q1_FULL_UNMASK BIT(23) +#define HOST2BMC_Q1_EMPTY_UNMASK BIT(22) +#define HOST2BMC_Q2_FULL_UNMASK BIT(21) +#define HOST2BMC_Q2_EMPTY_UNMASK BIT(20) + +#define ASPEED_SCU_PCIE_CONF_CTRL 0xC20 +#define SCU_PCIE_CONF_BMC_DEV_EN BIT(8) +#define SCU_PCIE_CONF_BMC_DEV_EN_MMIO BIT(9) +#define SCU_PCIE_CONF_BMC_DEV_EN_MSI BIT(11) +#define SCU_PCIE_CONF_BMC_DEV_EN_IRQ BIT(13) +#define SCU_PCIE_CONF_BMC_DEV_EN_DMA BIT(14) +#define SCU_PCIE_CONF_BMC_DEV_EN_E2L BIT(15) +#define SCU_PCIE_CONF_BMC_DEV_EN_LPC_DECODE BIT(21) + +#define ASPEED_SCU_BMC_DEV_CLASS 0xC68 + +#define ASPEED_QUEUE_NUM 2 +enum queue_index { + QUEUE1 =3D 0, + QUEUE2, +}; + +struct aspeed_platform { + int (*init)(struct platform_device *pdev); + ssize_t (*queue_rx)(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, char *buf, loff_t off, size_t cou= nt); + ssize_t (*queue_tx)(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, char *buf, loff_t off, size_t cou= nt); +}; + +struct aspeed_queue_message { + /* Queue waiters for idle engine */ + wait_queue_head_t tx_wait; + wait_queue_head_t rx_wait; + struct kernfs_node *kn; + struct bin_attribute bin; + int index; + struct aspeed_bmc_device *bmc_device; +}; + +struct aspeed_bmc_device { + unsigned char *host2bmc_base_virt; + struct device *dev; + struct miscdevice miscdev; + int id; + void __iomem *reg_base; + dma_addr_t bmc_mem_phy; + phys_addr_t bmc_mem_size; + void *bmc_mem_cpu; + + int pcie2lpc; + int irq; + + struct aspeed_queue_message queue[ASPEED_QUEUE_NUM]; + + const struct aspeed_platform *platform; + + /* AST2700 */ + struct regmap *device; + struct regmap *e2m; + + struct regmap *scu; + int pcie_irq; +}; + +static struct aspeed_bmc_device *file_aspeed_bmc_device(struct file *file) +{ + return container_of(file->private_data, struct aspeed_bmc_device, + miscdev); +} + +static int aspeed_bmc_device_mmap(struct file *file, struct vm_area_struct= *vma) +{ + struct aspeed_bmc_device *bmc_device =3D file_aspeed_bmc_device(file); + unsigned long vsize =3D vma->vm_end - vma->vm_start; + + if (vsize > bmc_device->bmc_mem_size) + return -EINVAL; + + return dma_mmap_coherent(bmc_device->dev, vma, + bmc_device->bmc_mem_cpu, + bmc_device->bmc_mem_phy, + bmc_device->bmc_mem_size); + +} + +static const struct file_operations aspeed_bmc_device_fops =3D { + .owner =3D THIS_MODULE, + .mmap =3D aspeed_bmc_device_mmap, +}; + +static ssize_t aspeed_ast2600_queue_rx(struct file *filp, struct kobject *= kobj, + const struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct aspeed_queue_message *queue =3D attr->private; + struct aspeed_bmc_device *bmc_device =3D queue->bmc_device; + int index =3D queue->index; + u32 *data =3D (u32 *)buf; + u32 scu_id; + int ret; + + ret =3D wait_event_interruptible(queue->rx_wait, + !(readl(bmc_device->reg_base + ASPEED_BMC_HOST2BMC_STS) & + ((index =3D=3D QUEUE1) ? HOST2BMC_Q1_EMPTY : HOST2BMC_Q2_EMPTY)= )); + if (ret) + return -EINTR; + + data[0] =3D readl(bmc_device->reg_base + + ((index =3D=3D QUEUE1) ? ASPEED_BMC_HOST2BMC_Q1 : ASPEED_BMC_HOST2BMC_Q= 2)); + + regmap_read(bmc_device->scu, ASPEED_SCU04, &scu_id); + if (scu_id =3D=3D AST2600A3_SCU04) { + writel(BMC2HOST_INT_STS_DOORBELL | BMC2HOST_ENABLE_INTB, + bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS); + } else { + //A0 : BIT(12) A1 : BIT(15) + regmap_update_bits(bmc_device->scu, 0x560, BIT(15), BIT(15)); + regmap_update_bits(bmc_device->scu, 0x560, BIT(15), 0); + } + + return sizeof(u32); +} + +static ssize_t aspeed_ast2600_queue_tx(struct file *filp, struct kobject *= kobj, + const struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct aspeed_queue_message *queue =3D attr->private; + struct aspeed_bmc_device *bmc_device =3D queue->bmc_device; + int index =3D queue->index; + u32 tx_buff; + u32 scu_id; + int ret; + + if (count !=3D sizeof(u32)) + return -EINVAL; + + ret =3D wait_event_interruptible(queue->tx_wait, + !(readl(bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS) & + ((index =3D=3D QUEUE1) ? BMC2HOST_Q1_FULL : BMC2HOST_Q2_FULL))); + if (ret) + return -EINTR; + + memcpy(&tx_buff, buf, 4); + writel(tx_buff, bmc_device->reg_base + ((index =3D=3D QUEUE1) ? ASPEED_BM= C_BMC2HOST_Q1 : + ASPEED_BMC_BMC2HOST_Q2)); + + /* trigger to host + * Only After AST2600A3 support DoorBell MSI + */ + regmap_read(bmc_device->scu, ASPEED_SCU04, &scu_id); + if (scu_id =3D=3D AST2600A3_SCU04) { + writel(BMC2HOST_INT_STS_DOORBELL | BMC2HOST_ENABLE_INTB, + bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS); + } else { + //A0 : BIT(12) A1 : BIT(15) + regmap_update_bits(bmc_device->scu, 0x560, BIT(15), BIT(15)); + regmap_update_bits(bmc_device->scu, 0x560, BIT(15), 0); + } + + return sizeof(u32); +} + +static ssize_t aspeed_ast2700_queue_rx(struct file *filp, struct kobject *= kobj, + const struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct aspeed_queue_message *queue =3D attr->private; + struct aspeed_bmc_device *bmc_device =3D queue->bmc_device; + int index =3D queue->index; + u32 *data =3D (u32 *)buf; + int ret; + + ret =3D wait_event_interruptible(queue->rx_wait, + !(readl(bmc_device->reg_base + ASPEED_BMC_HOST2BMC_STS) & + ((index =3D=3D QUEUE1) ? HOST2BMC_Q1_EMPTY : HOST2BMC_Q2_EMPTY)= )); + if (ret) + return -EINTR; + + data[0] =3D readl(bmc_device->reg_base + + ((index =3D=3D QUEUE1) ? ASPEED_BMC_HOST2BMC_Q1 : ASPEED_BMC_HOST2BMC_Q= 2)); + + writel(BMC2HOST_INT_STS_DOORBELL | BMC2HOST_ENABLE_INTB, + bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS); + + return sizeof(u32); +} + +static ssize_t aspeed_ast2700_queue_tx(struct file *filp, struct kobject *= kobj, + const struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct aspeed_queue_message *queue =3D attr->private; + struct aspeed_bmc_device *bmc_device =3D queue->bmc_device; + int index =3D queue->index; + u32 tx_buff; + int ret; + + if (count !=3D sizeof(u32)) + return -EINVAL; + + ret =3D wait_event_interruptible(queue->tx_wait, + !(readl(bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS) & + ((index =3D=3D QUEUE1) ? BMC2HOST_Q1_FULL : BMC2HOST_Q2_FULL))); + if (ret) + return -EINTR; + + memcpy(&tx_buff, buf, 4); + writel(tx_buff, bmc_device->reg_base + ((index =3D=3D QUEUE1) ? ASPEED_BM= C_BMC2HOST_Q1 : + ASPEED_BMC_BMC2HOST_Q2)); + + writel(BMC2HOST_INT_STS_DOORBELL | BMC2HOST_ENABLE_INTB, + bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS); + + return sizeof(u32); +} + +/* AST2600 */ +static irqreturn_t aspeed_bmc_dev_pcie_isr(int irq, void *dev_id) +{ + struct aspeed_bmc_device *bmc_device =3D dev_id; + + while (!(readl(bmc_device->reg_base + ASPEED_BMC_HOST2BMC_STS) & HOST2BMC= _Q1_EMPTY)) + readl(bmc_device->reg_base + ASPEED_BMC_HOST2BMC_Q1); + + while (!(readl(bmc_device->reg_base + ASPEED_BMC_HOST2BMC_STS) & HOST2BMC= _Q2_EMPTY)) + readl(bmc_device->reg_base + ASPEED_BMC_HOST2BMC_Q2); + + return IRQ_HANDLED; +} + +static irqreturn_t aspeed_bmc_dev_isr(int irq, void *dev_id) +{ + struct aspeed_bmc_device *bmc_device =3D dev_id; + u32 host2bmc_q_sts =3D readl(bmc_device->reg_base + ASPEED_BMC_HOST2BMC_S= TS); + + if (host2bmc_q_sts & HOST2BMC_INT_STS_DOORBELL) + writel(HOST2BMC_INT_STS_DOORBELL, bmc_device->reg_base + ASPEED_BMC_HOST= 2BMC_STS); + + if (host2bmc_q_sts & HOST2BMC_ENABLE_INTB) + writel(HOST2BMC_ENABLE_INTB, bmc_device->reg_base + ASPEED_BMC_HOST2BMC_= STS); + + if (host2bmc_q_sts & HOST2BMC_Q1_FULL) + dev_info(bmc_device->dev, "Q1 Full\n"); + + if (host2bmc_q_sts & HOST2BMC_Q2_FULL) + dev_info(bmc_device->dev, "Q2 Full\n"); + + if (!(readl(bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS) & BMC2HOST_Q1= _FULL)) + wake_up_interruptible(&bmc_device->queue[QUEUE1].tx_wait); + + if (!(readl(bmc_device->reg_base + ASPEED_BMC_HOST2BMC_STS) & HOST2BMC_Q1= _EMPTY)) + wake_up_interruptible(&bmc_device->queue[QUEUE1].rx_wait); + + if (!(readl(bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS) & BMC2HOST_Q2= _FULL)) + wake_up_interruptible(&bmc_device->queue[QUEUE2].tx_wait); + + if (!(readl(bmc_device->reg_base + ASPEED_BMC_HOST2BMC_STS) & HOST2BMC_Q2= _EMPTY)) + wake_up_interruptible(&bmc_device->queue[QUEUE2].rx_wait); + + return IRQ_HANDLED; +} + +static int aspeed_ast2600_init(struct platform_device *pdev) +{ + struct aspeed_bmc_device *bmc_device =3D platform_get_drvdata(pdev); + struct device *dev =3D &pdev->dev; + u32 pcie_config_ctl =3D SCU_PCIE_CONF_BMC_DEV_EN_IRQ | + SCU_PCIE_CONF_BMC_DEV_EN_MMIO | SCU_PCIE_CONF_BMC_DEV_EN; + u32 scu_id; + + bmc_device->scu =3D syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed= ,scu"); + if (IS_ERR(bmc_device->scu)) { + dev_err(&pdev->dev, "failed to find SCU regmap\n"); + return PTR_ERR(bmc_device->scu); + } + + if (bmc_device->pcie2lpc) + pcie_config_ctl |=3D SCU_PCIE_CONF_BMC_DEV_EN_E2L | + SCU_PCIE_CONF_BMC_DEV_EN_LPC_DECODE; + + regmap_update_bits(bmc_device->scu, ASPEED_SCU_PCIE_CONF_CTRL, + pcie_config_ctl, pcie_config_ctl); + + /* update class code to others as it is a MFD device */ + regmap_write(bmc_device->scu, ASPEED_SCU_BMC_DEV_CLASS, 0xff000000); + +#ifdef SCU_TRIGGER_MSI + //SCUC24[17]: Enable PCI device 1 INTx/MSI from SCU560[15]. Will be added= in next version + regmap_update_bits(bmc_device->scu, ASPEED_SCUC20, BIT(11) | BIT(14), BIT= (11) | BIT(14)); + + regmap_read(bmc_device->scu, ASPEED_SCU04, &scu_id); + if (scu_id =3D=3D AST2600A3_SCU04) + regmap_update_bits(bmc_device->scu, ASPEED_SCUC24, + PCIDEV1_INTX_MSI_HOST2BMC_EN | MSI_ROUTING_MASK, + PCIDEV1_INTX_MSI_HOST2BMC_EN | MSI_ROUTING_PCIe2LPC_PCIDEV1); + else + regmap_update_bits(bmc_device->scu, ASPEED_SCUC24, + BIT(17) | BIT(14) | BIT(11), BIT(17) | BIT(14) | BIT(11)); +#else + //SCUC24[18]: Enable PCI device 1 INTx/MSI from Host-to-BMC controller. + regmap_update_bits(bmc_device->scu, 0xc24, BIT(18) | BIT(14), BIT(18) | B= IT(14)); +#endif + + writel((~(bmc_device->bmc_mem_size - 1) & 0xFFFFFFFF) | HOST2BMC_MEM_BAR_= ENABLE, + bmc_device->reg_base + ASPEED_BMC_MEM_BAR); + writel(bmc_device->bmc_mem_phy, bmc_device->reg_base + ASPEED_BMC_MEM_BAR= _REMAP); + + //Setting BMC to Host Q register + writel(BMC2HOST_Q2_FULL_UNMASK | BMC2HOST_Q1_FULL_UNMASK | BMC2HOST_ENABL= E_INTB, + bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS); + writel(HOST2BMC_Q2_FULL_UNMASK | HOST2BMC_Q1_FULL_UNMASK | HOST2BMC_ENAB= LE_INTB, + bmc_device->reg_base + ASPEED_BMC_HOST2BMC_STS); + + return 0; +} + +static int aspeed_ast2700_init(struct platform_device *pdev) +{ + struct aspeed_bmc_device *bmc_device =3D platform_get_drvdata(pdev); + struct device *dev =3D &pdev->dev; + u32 pcie_config_ctl; + u32 scu_id; + int i; + + bmc_device->device =3D syscon_regmap_lookup_by_phandle(dev->of_node, "asp= eed,device"); + if (IS_ERR(bmc_device->device)) { + dev_err(&pdev->dev, "failed to find device regmap\n"); + return PTR_ERR(bmc_device->device); + } + + bmc_device->e2m =3D syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed= ,e2m"); + if (IS_ERR(bmc_device->e2m)) { + dev_err(&pdev->dev, "failed to find e2m regmap\n"); + return PTR_ERR(bmc_device->e2m); + } + + bmc_device->scu =3D syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed= ,scu"); + if (IS_ERR(bmc_device->scu)) { + dev_err(&pdev->dev, "failed to find SCU regmap\n"); + return PTR_ERR(bmc_device->scu); + } + + if (bmc_device->pcie2lpc) { + pcie_config_ctl =3D SCU_PCIE_CONF_BMC_DEV_EN_E2L | + SCU_PCIE_CONF_BMC_DEV_EN_LPC_DECODE; + regmap_update_bits(bmc_device->scu, SCU0_PCIE_CONF_CTRL, + pcie_config_ctl, pcie_config_ctl); + } + + /* update class code to others as it is a MFD device */ + regmap_write(bmc_device->device, 0x18, 0xff000027); + + /* MSI */ + regmap_update_bits(bmc_device->device, 0x74, GENMASK(7, 4), BIT(7) | (5 <= < 4)); + /* EnPCIaMSI:BIT(25), EnPCIaIntA:BIT(17), EnPCIaMst:BIT(9), EnPCIaDev:BIT= (1) */ + regmap_read(bmc_device->scu, SCU0_REVISION_ID, &scu_id); + if (scu_id & REVISION_ID) + regmap_update_bits(bmc_device->device, 0x70, + BIT(25) | BIT(17) | BIT(9) | BIT(1), + BIT(25) | BIT(17) | BIT(9) | BIT(1)); + else + /* Disable MSI[bit25] in ast2700A0 int only */ + regmap_update_bits(bmc_device->device, 0x70, + BIT(17) | BIT(9) | BIT(1), + BIT(25) | BIT(17) | BIT(9) | BIT(1)); + + /* bar size check for 4k align */ + for (i =3D 1; i < 16; i++) { + if ((bmc_device->bmc_mem_size / 4096) =3D=3D (1 << (i - 1))) + break; + } + if (i =3D=3D 16) { + i =3D 0; + dev_warn(bmc_device->dev, + "Bar size not align for 4K : %dK\n", (u32)bmc_device->bmc_mem_size / 1= 024); + } + + /* + * BAR assign in scu + * ((bar_mem / 4k) << 8) | per_size + */ + regmap_write(bmc_device->device, 0x1c, ((bmc_device->bmc_mem_phy) >> 4) |= i); + + if (bmc_device->id =3D=3D 0) + /* Node 0 Bar 0 */ + regmap_write(bmc_device->e2m, 0x108, ((bmc_device->bmc_mem_phy) >> 4) | = i); + else + /* Node 1 Bar 0 */ + regmap_write(bmc_device->e2m, 0x128, ((bmc_device->bmc_mem_phy) >> 4) | = i); + + /* Setting BMC to Host Q register */ + writel(BMC2HOST_Q2_FULL_UNMASK | BMC2HOST_Q1_FULL_UNMASK | BMC2HOST_ENABL= E_INTB, + bmc_device->reg_base + ASPEED_BMC_BMC2HOST_STS); + writel(HOST2BMC_Q2_FULL_UNMASK | HOST2BMC_Q1_FULL_UNMASK | HOST2BMC_ENABL= E_INTB, + bmc_device->reg_base + ASPEED_BMC_HOST2BMC_STS); + + return 0; +} + +static int aspeed_bmc_device_setup_queue(struct platform_device *pdev) +{ + struct aspeed_bmc_device *bmc_device =3D platform_get_drvdata(pdev); + struct device *dev =3D &pdev->dev; + int ret, i; + + for (i =3D 0; i < ASPEED_QUEUE_NUM; i++) { + struct aspeed_queue_message *queue =3D &bmc_device->queue[i]; + + init_waitqueue_head(&queue->tx_wait); + init_waitqueue_head(&queue->rx_wait); + + sysfs_bin_attr_init(&queue->bin); + + /* Queue name index starts from 1 */ + queue->bin.attr.name =3D + devm_kasprintf(dev, GFP_KERNEL, "bmc-dev-queue%d", (i + 1)); + queue->bin.attr.mode =3D 0600; + queue->bin.read =3D bmc_device->platform->queue_rx; + queue->bin.write =3D bmc_device->platform->queue_tx; + queue->bin.size =3D 4; + queue->bin.private =3D queue; + + ret =3D sysfs_create_bin_file(&pdev->dev.kobj, &queue->bin); + if (ret) { + dev_err(dev, "error for bin%d file\n", i); + return ret; + } + + queue->kn =3D kernfs_find_and_get(dev->kobj.sd, queue->bin.attr.name); + if (!queue->kn) { + sysfs_remove_bin_file(&dev->kobj, &queue->bin); + return ret; + } + + queue->index =3D i; + queue->bmc_device =3D bmc_device; + } + + return 0; +} + +static int aspeed_bmc_device_setup_memory_mapping(struct platform_device *= pdev) +{ + struct aspeed_bmc_device *bmc_device =3D platform_get_drvdata(pdev); + struct device *dev =3D &pdev->dev; + int ret; + + bmc_device->miscdev.minor =3D MISC_DYNAMIC_MINOR; + bmc_device->miscdev.name =3D devm_kasprintf(dev, GFP_KERNEL, "bmc-device%= d", bmc_device->id); + bmc_device->miscdev.fops =3D &aspeed_bmc_device_fops; + bmc_device->miscdev.parent =3D dev; + ret =3D misc_register(&bmc_device->miscdev); + if (ret) { + dev_err(dev, "Unable to register device\n"); + return ret; + } + + return 0; +} + +static struct aspeed_platform ast2600_plaform =3D { + .init =3D aspeed_ast2600_init, + .queue_rx =3D aspeed_ast2600_queue_rx, + .queue_tx =3D aspeed_ast2600_queue_tx +}; + +static struct aspeed_platform ast2700_plaform =3D { + .init =3D aspeed_ast2700_init, + .queue_rx =3D aspeed_ast2700_queue_rx, + .queue_tx =3D aspeed_ast2700_queue_tx +}; + +static const struct of_device_id aspeed_bmc_device_of_matches[] =3D { + { .compatible =3D "aspeed,ast2600-bmc-device", .data =3D &ast2600_plaform= }, + { .compatible =3D "aspeed,ast2700-bmc-device", .data =3D &ast2700_plaform= }, + {}, +}; +MODULE_DEVICE_TABLE(of, aspeed_bmc_device_of_matches); + +static int aspeed_bmc_device_probe(struct platform_device *pdev) +{ + struct aspeed_bmc_device *bmc_device; + struct device *dev =3D &pdev->dev; + struct resource res; + const void *md =3D of_device_get_match_data(dev); + struct device_node *np; + int ret =3D 0, i; + + if (!md) + return -ENODEV; + + bmc_device =3D devm_kzalloc(&pdev->dev, sizeof(struct aspeed_bmc_device),= GFP_KERNEL); + if (!bmc_device) + return -ENOMEM; + dev_set_drvdata(dev, bmc_device); + + bmc_device->platform =3D md; + + bmc_device->id =3D of_alias_get_id(dev->of_node, "bmcdev"); + if (bmc_device->id < 0) + bmc_device->id =3D 0; + + bmc_device->dev =3D dev; + bmc_device->reg_base =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(bmc_device->reg_base)) + return PTR_ERR(bmc_device->reg_base); + + ret =3D of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "of_reserved_mem_device_init failed: %d\n", ret); + return ret; + } + + ret =3D dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err(dev, "cannot set 64-bits DMA mask\n"); + return ret; + } + + np =3D of_parse_phandle(dev->of_node, "memory-region", 0); + if (!np || of_address_to_resource(np, 0, &res)) { + dev_err(dev, "Failed to find memory-region.\n"); + return -ENOMEM; + } + + of_node_put(np); + + bmc_device->bmc_mem_size =3D resource_size(&res); + bmc_device->bmc_mem_cpu =3D dmam_alloc_coherent(dev, bmc_device->bmc_mem_= size, + &bmc_device->bmc_mem_phy, GFP_KERNEL); + if (!bmc_device->bmc_mem_cpu) { + dev_err(dev, "Failed to allocate BMC memory.\n"); + return -ENOMEM; + } + + bmc_device->irq =3D platform_get_irq(pdev, 0); + if (bmc_device->irq < 0) { + dev_err(&pdev->dev, "platform get of irq[=3D%d] failed!\n", bmc_device->= irq); + return bmc_device->irq; + } + + ret =3D devm_request_irq(&pdev->dev, bmc_device->irq, aspeed_bmc_dev_isr,= 0, + dev_name(&pdev->dev), bmc_device); + if (ret) { + dev_err(dev, "aspeed bmc device Unable to get IRQ"); + return ret; + } + + ret =3D aspeed_bmc_device_setup_queue(pdev); + if (ret) { + dev_err(dev, "Cannot setup queue message"); + goto out; + } + + ret =3D aspeed_bmc_device_setup_memory_mapping(pdev); + if (ret) { + dev_err(dev, "Cannot setup memory mapping misc"); + goto out_free_queue; + } + + if (of_property_read_bool(dev->of_node, "pcie2lpc")) + bmc_device->pcie2lpc =3D 1; + + ret =3D bmc_device->platform->init(pdev); + if (ret) { + dev_err(dev, "Initialize bmc device failed\n"); + goto out_free_misc; + } + + bmc_device->pcie_irq =3D platform_get_irq(pdev, 1); + if (bmc_device->pcie_irq < 0) { + dev_warn(&pdev->dev, + "platform get of pcie irq[=3D%d] failed!\n", bmc_device->pcie_irq); + } else { + ret =3D devm_request_irq(&pdev->dev, bmc_device->pcie_irq, + aspeed_bmc_dev_pcie_isr, IRQF_SHARED, + dev_name(&pdev->dev), bmc_device); + if (ret < 0) { + dev_warn(dev, "Failed to request PCI-E IRQ %d.\n", ret); + bmc_device->pcie_irq =3D -1; + } + } + + dev_info(dev, "aspeed bmc device: driver successfully loaded.\n"); + + return 0; + +out_free_misc: + misc_deregister(&bmc_device->miscdev); +out_free_queue: + for (i =3D 0; i < ASPEED_QUEUE_NUM; i++) + sysfs_remove_bin_file(&pdev->dev.kobj, &bmc_device->queue[i].bin); +out: + dev_warn(dev, "aspeed bmc device: driver init failed (ret=3D%d)!\n", ret); + return ret; +} + +static void aspeed_bmc_device_remove(struct platform_device *pdev) +{ + struct aspeed_bmc_device *bmc_device =3D platform_get_drvdata(pdev); + int i; + + for (i =3D 0; i < ASPEED_QUEUE_NUM; i++) + sysfs_remove_bin_file(&pdev->dev.kobj, &bmc_device->queue[i].bin); + misc_deregister(&bmc_device->miscdev); + devm_free_irq(&pdev->dev, bmc_device->irq, bmc_device); + devm_free_irq(&pdev->dev, bmc_device->pcie_irq, bmc_device); + + devm_kfree(&pdev->dev, bmc_device); +} + +static struct platform_driver aspeed_bmc_device_driver =3D { + .probe =3D aspeed_bmc_device_probe, + .remove =3D aspeed_bmc_device_remove, + .driver =3D { + .name =3D KBUILD_MODNAME, + .of_match_table =3D aspeed_bmc_device_of_matches, + }, +}; + +module_platform_driver(aspeed_bmc_device_driver); + +MODULE_AUTHOR("Ryan Chen "); +MODULE_DESCRIPTION("ASPEED BMC DEVICE Driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file --=20 2.51.2 From nobody Mon Jun 8 04:11:45 2026 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AEE3A3E2ABF for ; Tue, 2 Jun 2026 14:48:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780411716; cv=none; b=p5nI/LNZfPxNXIe32WLrbrcNfe8uY9E9Qi1fjB+I50LWcUhfTuodj7wxtipr2F+Hoaecr3JcFuxEq0gurY6wqz6n96Qlp1/eWguSpNrB2BqrZaitfo6ToU38iPIDZJGWDt6h/Tm5xQ74V1etqhi+bLfy0EFYUIqkoHKA5v9/xlA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780411716; c=relaxed/simple; bh=t85kD50Lr93VRMgrldVpsP4aR2NvHbNHBw59SD5z6SE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lMiTlJ2d7melFS7rz4JvRwSdeyfEuLcOItAjRQMeWaZGSD8QhwW2b3eq3/p/wCvvVYbJdcE5qhmR7lLlAFyFQQ0643W91E9NicUsWdoTEQ2JeArWrMkMukH0BJB7Cc38Y+QUzFUKMEfISbeOHeFK3/K30nTYW7aeO5O82OL+YjU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=9elements.com; spf=pass smtp.mailfrom=9elements.com; dkim=pass (2048-bit key) header.d=9elements.com header.i=@9elements.com header.b=WXjoeAkr; arc=none smtp.client-ip=209.85.128.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=9elements.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=9elements.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=9elements.com header.i=@9elements.com header.b="WXjoeAkr" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-49041e84237so94127035e9.1 for ; Tue, 02 Jun 2026 07:48:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=9elements.com; s=google; t=1780411713; x=1781016513; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7x249jX8ld0QHuh7j+Gf35qQREt3Vaq29sGjtrOr/jo=; b=WXjoeAkrApNIUdNsBFIs4qmgUA7Rr+x5wkDQWYF7rDR3osuKMhbFBurQ6YVUl21tPn HiAlS1HkjCNi08MTzWg/c5NWtKNUQalavYNHvLKK8rLL/Gz14km2agBvHzdFxPfdxQQm QGOG9yRkCzSfj53U1AHlsr6V8DeObkSPug3fZvc+ha4rfZKgxQq9x7hwOBLgbQqdCdDR 93FPxZkOZ/spfhG6SY3jwiBwoVUTNYU6L4F5Mxx+QhZtyr2QF5QOQSNtH7qcFmsBoCI/ lVM5jvv7vLwS6u+R1tq2hoZaeQDo1s7UFJ93+A5xqHpQmSKYnm3pKHT0onoUTn/KtRwR +iCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780411713; x=1781016513; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=7x249jX8ld0QHuh7j+Gf35qQREt3Vaq29sGjtrOr/jo=; b=Fq+92prifrs1GA/neq6fdaDRLEd/rUiSk4zMIp6MZfo4VwKRrlVDwSCAbNj1gocRP5 jbTVKQrl7abmjaGgTZifQGa7Bp90pBH3lOuECPQImnKDxooMwNetLRGSUR+y9wOaUd0p 1OJJdAkkAek0CpuMQNTRw8wgWPpcw6bb/d1pcC0kRluFKSbHk+5q/YNlJ0Ib+cNZkNAw VEK9K+AjKuG5J270X7kU1Y/jNvQmB5yiTZePoME8CE53MfRcnv70oTzu/1CdfP5SoSjG 5asqIQN+wQ9/lQEiQal8rQ6htSI69I6fO3IFnCnE6GuV+Oq96fABlk/nG8kxS9zv04rd Li8w== X-Forwarded-Encrypted: i=1; AFNElJ//aCysqSeeFnYm8MyJYXkPx7hQMK0fy1lVO9fV5RqAlCJNlY8TXzbY/d42YzEgr7tcaMxo5MYjIJ8UhUg=@vger.kernel.org X-Gm-Message-State: AOJu0YxFStmKBOZd5MJc9x9PJK71IomV22Aj8qm3vXPpkr1ZB2tpyk8s gXaQ0cvaAl3b0XjSXaaQ7DC+9Vdb/eL5deEd8vVtFVvN2G0o/GoDyucEJhFibZOi+hwOc/FMfAv YyWWqvBo= X-Gm-Gg: Acq92OFGnPlcLhvDiwcG/FMq5r641cZHPvD3p6bhaN54YS5tLfcZFZUrJ2R27n7zkbE A/aEdsJXHZQABgcLS67jkVHAFqHJ3lzOwDcUBdMftWiy/3eznumSyF9ttSfgi5zaKjmkZX4SNc2 SB95tLg0k5uJXHuJjESi5Z5t9lu6caAMhR/6RKED6zqcCJOe3gpDSAsngfLEz/wkF+MQG5ERPAt HoTQwWqVYdmML5XVHqB7Xx8g94Rmu1K0dHxfNkK9hvqMggGeQtoxhwN361uHQs/3mu0V9iqXZTM l4NNo6kMSZWv8UqSmbUQHdm3zK5ZzCnf//+xxlC7hWpk1F+InubaImLYu+b47IYZ+0ve3s7XXqv aEH6I8WzXojqKsKNWMSfOmWehsJKn/qUPTlbTlFdGhtwSFFSyedp9C0w8pbE6fZENNaREavazQr N40wg+kOYXdkG2Ncp76cTjQxSt754JMdsiuUKKty5BlNnszvflyJwLd9YIpMWqBJ+CR5EsuZQ= X-Received: by 2002:a05:600c:c84:b0:48a:53cb:8604 with SMTP id 5b1f17b1804b1-490b0e9f45cmr63144845e9.14.1780411712899; Tue, 02 Jun 2026 07:48:32 -0700 (PDT) Received: from gregwork.sec.9e.network ([188.111.3.154]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-490b0e88fdesm74881395e9.14.2026.06.02.07.48.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Jun 2026 07:48:32 -0700 (PDT) From: =?UTF-8?q?Gr=C3=A9goire=20Layet?= To: joel@jms.id.au, andrew@codeconstruct.com.au Cc: jacky_chou@aspeedtech.com, yh_chung@aspeedtech.com, ninad@linux.ibm.com, linux-aspeed@lists.ozlabs.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, =?UTF-8?q?Gr=C3=A9goire=20Layet?= Subject: [PATCH v1 2/2] soc: aspeed: add host-side PCIe BMC device driver Date: Tue, 2 Jun 2026 16:42:44 +0200 Message-ID: X-Mailer: git-send-email 2.51.2 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-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Taken from ASPEED 6.18 Kernel SDK Add support for PCIe communication BMC<->host. This add Host side driver. Signed-off-by: Jacky Chou Signed-off-by: aspeedyh Signed-off-by: Gr=C3=A9goire Layet Tested-by: Gr=C3=A9goire Layet --- drivers/soc/aspeed/Kconfig | 13 + drivers/soc/aspeed/Makefile | 1 + drivers/soc/aspeed/aspeed-host-bmc-dev.c | 664 +++++++++++++++++++++++ 3 files changed, 678 insertions(+) create mode 100644 drivers/soc/aspeed/aspeed-host-bmc-dev.c diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index 341728df07b3..e8670dfef1e6 100644 --- a/drivers/soc/aspeed/Kconfig +++ b/drivers/soc/aspeed/Kconfig @@ -13,6 +13,19 @@ config ASPEED_BMC_DEV providing a shared-memory BAR, host-to-BMC and BMC-to-host message queues with doorbell interrupts and PCIe-to-LPC bridge. =20 +config ASPEED_HOST_BMC_DEV + tristate "ASPEED Host BMC Device" + depends on PCI + depends on SERIAL_8250 + help + Enable support for the ASPEED AST2600/AST2700 BMC Device on the Host. + This configure the PCIe and setup: + - Two 8250 compatible VUART ports. + - A character device exposing the BMC's shared memory + region for host<->BMC data exchange. + - A mailbox interrupt path and BMC message queue handler for + doorbell-style host<->BMC signaling. + config ASPEED_LPC_CTRL tristate "ASPEED LPC firmware cycle control" select REGMAP diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index fab0d247df66..3fd3f6d8d36e 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_ASPEED_BMC_DEV) +=3D aspeed-bmc-dev.o +obj-$(CONFIG_ASPEED_HOST_BMC_DEV) +=3D aspeed-host-bmc-dev.o obj-$(CONFIG_ASPEED_LPC_CTRL) +=3D aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_SNOOP) +=3D aspeed-lpc-snoop.o obj-$(CONFIG_ASPEED_UART_ROUTING) +=3D aspeed-uart-routing.o diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/= aspeed-host-bmc-dev.c new file mode 100644 index 000000000000..9e6f1d39f18a --- /dev/null +++ b/drivers/soc/aspeed/aspeed-host-bmc-dev.c @@ -0,0 +1,664 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) ASPEED Technology Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define PCI_BMC_HOST2BMC_Q1 0x30000 +#define PCI_BMC_HOST2BMC_Q2 0x30010 +#define PCI_BMC_BMC2HOST_Q1 0x30020 +#define PCI_BMC_BMC2HOST_Q2 0x30030 +#define PCI_BMC_BMC2HOST_STS 0x30040 +#define BMC2HOST_INT_STS_DOORBELL BIT(31) +#define BMC2HOST_ENABLE_INTB BIT(30) + +#define BMC2HOST_Q1_FULL BIT(27) +#define BMC2HOST_Q1_EMPTY BIT(26) +#define BMC2HOST_Q2_FULL BIT(25) +#define BMC2HOST_Q2_EMPTY BIT(24) +#define BMC2HOST_Q1_FULL_UNMASK BIT(23) +#define BMC2HOST_Q1_EMPTY_UNMASK BIT(22) +#define BMC2HOST_Q2_FULL_UNMASK BIT(21) +#define BMC2HOST_Q2_EMPTY_UNMASK BIT(20) + +#define PCI_BMC_HOST2BMC_STS 0x30044 +#define HOST2BMC_INT_STS_DOORBELL BIT(31) +#define HOST2BMC_ENABLE_INTB BIT(30) + +#define HOST2BMC_Q1_FULL BIT(27) +#define HOST2BMC_Q1_EMPTY BIT(26) +#define HOST2BMC_Q2_FULL BIT(25) +#define HOST2BMC_Q2_EMPTY BIT(24) +#define HOST2BMC_Q1_FULL_UNMASK BIT(23) +#define HOST2BMC_Q1_EMPTY_UNMASK BIT(22) +#define HOST2BMC_Q2_FULL_UNMASK BIT(21) +#define HOST2BMC_Q2_EMPTY_UNMASK BIT(20) + +static DEFINE_IDA(bmc_device_ida); + +#define VUART_MAX_PARMS 2 +#define ASPEED_QUEUE_NUM 2 +#define MAX_MSI_NUM 8 + +enum aspeed_platform_id { + ASPEED, +}; + +enum queue_index { + QUEUE1 =3D 0, + QUEUE2, +}; + +enum msi_index { + BMC_MSI, + MBX_MSI, + VUART0_MSI, + VUART1_MSI, +}; + +/* Match msi_index */ +static int ast2600_msi_idx_table[MAX_MSI_NUM] =3D { 4, 21, 16, 15 }; +static int ast2700_soc0_msi_idx_table[MAX_MSI_NUM] =3D { 0, 11, 6, 5 }; + +struct aspeed_platform { + int (*setup)(struct pci_dev *pdev); +}; + +struct aspeed_queue_message { + /* Queue waiters for idle engine */ + wait_queue_head_t tx_wait; + wait_queue_head_t rx_wait; + struct kernfs_node *kn; + struct bin_attribute bin; + int index; + struct aspeed_pci_bmc_dev *pci_bmc_device; +}; + +struct aspeed_pci_bmc_dev { + struct device *dev; + struct miscdevice miscdev; + struct aspeed_platform *platform; + kernel_ulong_t driver_data; + int id; + + unsigned long mem_bar_base; + unsigned long mem_bar_size; + void __iomem *mem_bar_reg; + + unsigned long message_bar_base; + unsigned long message_bar_size; + void __iomem *msg_bar_reg; + + void __iomem *pcie_sio_decode_addr; + + struct aspeed_queue_message queue[ASPEED_QUEUE_NUM]; + + void __iomem *sio_mbox_reg; + struct uart_8250_port uart[VUART_MAX_PARMS]; + int uart_line[VUART_MAX_PARMS]; + + /* Interrupt + * The index of array is using to enum msi_index + */ + int *msi_idx_table; +}; + +#define PCIE_DEVICE_SIO_ADDR (0x2E * 4) +#define BMC_MULTI_MSI 32 + +#define DRIVER_NAME "aspeed-host-bmc-dev" + +static struct aspeed_pci_bmc_dev *file_aspeed_bmc_device(struct file *file) +{ + return container_of(file->private_data, struct aspeed_pci_bmc_dev, + miscdev); +} + +static int aspeed_pci_bmc_dev_mmap(struct file *file, + struct vm_area_struct *vma) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D file_aspeed_bmc_device(file); + unsigned long vsize =3D vma->vm_end - vma->vm_start; + pgprot_t prot =3D vma->vm_page_prot; + + if (vma->vm_pgoff + vsize > pci_bmc_dev->mem_bar_base + 0x100000) + return -EINVAL; + + prot =3D pgprot_noncached(prot); + + if (remap_pfn_range(vma, vma->vm_start, + (pci_bmc_dev->mem_bar_base >> PAGE_SHIFT) + + vma->vm_pgoff, + vsize, prot)) + return -EAGAIN; + + return 0; +} + +static const struct file_operations aspeed_pci_bmc_dev_fops =3D { + .owner =3D THIS_MODULE, + .mmap =3D aspeed_pci_bmc_dev_mmap, +}; + +static ssize_t aspeed_queue_rx(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct aspeed_queue_message *queue =3D attr->private; + struct aspeed_pci_bmc_dev *pci_bmc_device =3D queue->pci_bmc_device; + int index =3D queue->index; + u32 *data =3D (u32 *)buf; + int ret; + + ret =3D wait_event_interruptible( + queue->rx_wait, + !(readl(pci_bmc_device->msg_bar_reg + PCI_BMC_BMC2HOST_STS) & + ((index =3D=3D QUEUE1) ? BMC2HOST_Q1_EMPTY : BMC2HOST_Q2_EMPTY))); + if (ret) + return -EINTR; + + data[0] =3D readl(pci_bmc_device->msg_bar_reg + + ((index =3D=3D QUEUE1) ? PCI_BMC_BMC2HOST_Q1 : + PCI_BMC_BMC2HOST_Q2)); + + writel(HOST2BMC_INT_STS_DOORBELL | HOST2BMC_ENABLE_INTB, + pci_bmc_device->msg_bar_reg + PCI_BMC_HOST2BMC_STS); + + return sizeof(u32); +} + +static ssize_t aspeed_queue_tx(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct aspeed_queue_message *queue =3D attr->private; + struct aspeed_pci_bmc_dev *pci_bmc_device =3D queue->pci_bmc_device; + int index =3D queue->index; + u32 tx_buff; + int ret; + + if (count !=3D sizeof(u32)) + return -EINVAL; + + ret =3D wait_event_interruptible( + queue->tx_wait, + !(readl(pci_bmc_device->msg_bar_reg + PCI_BMC_HOST2BMC_STS) & + ((index =3D=3D QUEUE1) ? HOST2BMC_Q1_FULL : HOST2BMC_Q2_FULL))); + if (ret) + return -EINTR; + + memcpy(&tx_buff, buf, 4); + writel(tx_buff, pci_bmc_device->msg_bar_reg + + ((index =3D=3D QUEUE1) ? PCI_BMC_HOST2BMC_Q1 : + PCI_BMC_HOST2BMC_Q2)); + //trigger to host + writel(HOST2BMC_INT_STS_DOORBELL | HOST2BMC_ENABLE_INTB, + pci_bmc_device->msg_bar_reg + PCI_BMC_HOST2BMC_STS); + + return sizeof(u32); +} + +static irqreturn_t aspeed_pci_host_bmc_device_interrupt(int irq, void *dev= _id) +{ + struct aspeed_pci_bmc_dev *pci_bmc_device =3D dev_id; + u32 bmc2host_q_sts =3D + readl(pci_bmc_device->msg_bar_reg + PCI_BMC_BMC2HOST_STS); + + if (bmc2host_q_sts & BMC2HOST_INT_STS_DOORBELL) + writel(BMC2HOST_INT_STS_DOORBELL, + pci_bmc_device->msg_bar_reg + PCI_BMC_BMC2HOST_STS); + + if (bmc2host_q_sts & BMC2HOST_ENABLE_INTB) + writel(BMC2HOST_ENABLE_INTB, + pci_bmc_device->msg_bar_reg + PCI_BMC_BMC2HOST_STS); + + if (bmc2host_q_sts & BMC2HOST_Q1_FULL) + dev_info(pci_bmc_device->dev, "Q1 Full\n"); + + if (bmc2host_q_sts & BMC2HOST_Q2_FULL) + dev_info(pci_bmc_device->dev, "Q2 Full\n"); + + //check q1 + if (!(readl(pci_bmc_device->msg_bar_reg + PCI_BMC_HOST2BMC_STS) & + HOST2BMC_Q1_FULL)) + wake_up_interruptible(&pci_bmc_device->queue[QUEUE1].tx_wait); + + if (!(readl(pci_bmc_device->msg_bar_reg + PCI_BMC_BMC2HOST_STS) & + BMC2HOST_Q1_EMPTY)) + wake_up_interruptible(&pci_bmc_device->queue[QUEUE1].rx_wait); + //chech q2 + if (!(readl(pci_bmc_device->msg_bar_reg + PCI_BMC_HOST2BMC_STS) & + HOST2BMC_Q2_FULL)) + wake_up_interruptible(&pci_bmc_device->queue[QUEUE2].tx_wait); + + if (!(readl(pci_bmc_device->msg_bar_reg + PCI_BMC_BMC2HOST_STS) & + BMC2HOST_Q2_EMPTY)) + wake_up_interruptible(&pci_bmc_device->queue[QUEUE2].rx_wait); + + return IRQ_HANDLED; +} + +static irqreturn_t aspeed_pci_host_mbox_interrupt(int irq, void *dev_id) +{ + struct aspeed_pci_bmc_dev *pci_bmc_device =3D dev_id; + u32 isr =3D readl(pci_bmc_device->sio_mbox_reg + 0x94); + + if (isr & BIT(7)) + writel(BIT(7), pci_bmc_device->sio_mbox_reg + 0x94); + + return IRQ_HANDLED; +} + +static void aspeed_pci_setup_irq_resource(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D pci_get_drvdata(pdev); + + /* Assign static msi index table by platform */ + if (pdev->revision =3D=3D 0x27) { + pci_bmc_dev->msi_idx_table =3D ast2700_soc0_msi_idx_table; + } else { + pci_bmc_dev->msi_idx_table =3D ast2600_msi_idx_table; + } + + if (pci_alloc_irq_vectors(pdev, 1, BMC_MULTI_MSI, + PCI_IRQ_INTX | PCI_IRQ_MSI) <=3D 1) + /* Set all msi index to the first vector */ + memset(pci_bmc_dev->msi_idx_table, 0, + sizeof(int) * MAX_MSI_NUM); +} + +static int aspeed_pci_bmc_device_setup_queue(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_device =3D pci_get_drvdata(pdev); + struct device *dev =3D &pdev->dev; + int ret, i; + + for (i =3D 0; i < ASPEED_QUEUE_NUM; i++) { + struct aspeed_queue_message *queue =3D &pci_bmc_device->queue[i]; + + init_waitqueue_head(&queue->tx_wait); + init_waitqueue_head(&queue->rx_wait); + + sysfs_bin_attr_init(&queue->bin); + + /* Queue name index starts from 1 */ + queue->bin.attr.name =3D devm_kasprintf( + dev, GFP_KERNEL, "pci-bmc-dev-queue%d", (i + 1)); + queue->bin.attr.mode =3D 0600; + queue->bin.read =3D aspeed_queue_rx; + queue->bin.write =3D aspeed_queue_tx; + queue->bin.size =3D 4; + queue->bin.private =3D queue; + + ret =3D sysfs_create_bin_file(&pdev->dev.kobj, &queue->bin); + if (ret) { + dev_err(dev, "error for bin%d file\n", i); + return ret; + } + + queue->kn =3D + kernfs_find_and_get(dev->kobj.sd, queue->bin.attr.name); + if (!queue->kn) { + sysfs_remove_bin_file(&dev->kobj, &queue->bin); + return ret; + } + + queue->index =3D i; + queue->pci_bmc_device =3D pci_bmc_device; + } + + return 0; +} + +static int aspeed_pci_bmc_device_setup_vuart(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D pci_get_drvdata(pdev); + struct device *dev =3D &pdev->dev; + u16 vuart_ioport; + int ret, i; + + for (i =3D 0; i < VUART_MAX_PARMS; i++) { + /* Assign the line to non-exist device */ + pci_bmc_dev->uart_line[i] =3D -ENOENT; + vuart_ioport =3D 0x3F8 - (i * 0x100); + pci_bmc_dev->uart[i].port.flags =3D + UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + pci_bmc_dev->uart[i].port.uartclk =3D 115200 * 16; + pci_bmc_dev->uart[i].port.irq =3D pci_irq_vector( + pdev, pci_bmc_dev->msi_idx_table[VUART0_MSI + i]); + pci_bmc_dev->uart[i].port.dev =3D dev; + pci_bmc_dev->uart[i].port.iotype =3D UPIO_MEM32; + pci_bmc_dev->uart[i].port.iobase =3D 0; + pci_bmc_dev->uart[i].port.mapbase =3D + pci_bmc_dev->message_bar_base + (vuart_ioport << 2); + pci_bmc_dev->uart[i].port.membase =3D 0; + pci_bmc_dev->uart[i].port.type =3D PORT_16550A; + pci_bmc_dev->uart[i].port.flags |=3D + (UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE); + pci_bmc_dev->uart[i].port.regshift =3D 2; + ret =3D serial8250_register_8250_port(&pci_bmc_dev->uart[i]); + if (ret < 0) { + dev_err_probe(dev, ret, "Can't setup PCIe VUART\n"); + return ret; + } + pci_bmc_dev->uart_line[i] =3D ret; + } + return 0; +} + +static int aspeed_pci_bmc_device_setup_memory_mapping(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D pci_get_drvdata(pdev); + struct device *dev =3D &pdev->dev; + int ret; + + pci_bmc_dev->miscdev.minor =3D MISC_DYNAMIC_MINOR; + pci_bmc_dev->miscdev.name =3D devm_kasprintf( + dev, GFP_KERNEL, "%s%d", DRIVER_NAME, pci_bmc_dev->id); + pci_bmc_dev->miscdev.fops =3D &aspeed_pci_bmc_dev_fops; + pci_bmc_dev->miscdev.parent =3D dev; + + ret =3D misc_register(&pci_bmc_dev->miscdev); + if (ret) { + pr_err("host bmc register fail %d\n", ret); + return ret; + } + + return 0; +} + +static int aspeed_pci_bmc_device_setup_mbox(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D pci_get_drvdata(pdev); + struct device *dev =3D &pdev->dev; + int ret; + + /* setup mbox */ + pci_bmc_dev->pcie_sio_decode_addr =3D + pci_bmc_dev->msg_bar_reg + PCIE_DEVICE_SIO_ADDR; + writel(0xaa, pci_bmc_dev->pcie_sio_decode_addr); + writel(0xa5, pci_bmc_dev->pcie_sio_decode_addr); + writel(0xa5, pci_bmc_dev->pcie_sio_decode_addr); + writel(0x07, pci_bmc_dev->pcie_sio_decode_addr); + writel(0x0e, pci_bmc_dev->pcie_sio_decode_addr + 0x04); + /* disable */ + writel(0x30, pci_bmc_dev->pcie_sio_decode_addr); + writel(0x00, pci_bmc_dev->pcie_sio_decode_addr + 0x04); + /* set decode address 0x100 */ + writel(0x60, pci_bmc_dev->pcie_sio_decode_addr); + writel(0x01, pci_bmc_dev->pcie_sio_decode_addr + 0x04); + writel(0x61, pci_bmc_dev->pcie_sio_decode_addr); + writel(0x00, pci_bmc_dev->pcie_sio_decode_addr + 0x04); + /* enable */ + writel(0x30, pci_bmc_dev->pcie_sio_decode_addr); + writel(0x01, pci_bmc_dev->pcie_sio_decode_addr + 0x04); + pci_bmc_dev->sio_mbox_reg =3D pci_bmc_dev->msg_bar_reg + 0x400; + + ret =3D devm_request_irq( + dev, pci_irq_vector(pdev, pci_bmc_dev->msi_idx_table[MBX_MSI]), + aspeed_pci_host_mbox_interrupt, IRQF_SHARED, + devm_kasprintf(dev, GFP_KERNEL, "aspeed-sio-mbox%d", + pci_bmc_dev->id), + pci_bmc_dev); + if (ret) { + pr_err("host bmc device Unable to get IRQ %d\n", ret); + return ret; + } + + return 0; +} + +static void aspeed_pci_host_bmc_device_release_queue(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D pci_get_drvdata(pdev); + int i; + + for (i =3D 0; i < ASPEED_QUEUE_NUM; i++) + sysfs_remove_bin_file(&pdev->dev.kobj, + &pci_bmc_dev->queue[i].bin); +} + +static void aspeed_pci_host_bmc_device_release_vuart(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D pci_get_drvdata(pdev); + int i; + + for (i =3D 0; i < VUART_MAX_PARMS; i++) { + if (pci_bmc_dev->uart_line[i] >=3D 0) + serial8250_unregister_port(pci_bmc_dev->uart_line[i]); + } +} + +static void +aspeed_pci_host_bmc_device_release_memory_mapping(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D pci_get_drvdata(pdev); + + if (!list_empty(&pci_bmc_dev->miscdev.list)) + misc_deregister(&pci_bmc_dev->miscdev); +} + +static int aspeed_pci_host_setup(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D pci_get_drvdata(pdev); + int rc =3D 0; + + /* Get share memory BAR */ + pci_bmc_dev->mem_bar_base =3D pci_resource_start(pdev, 0); + pci_bmc_dev->mem_bar_size =3D pci_resource_len(pdev, 0); + pci_bmc_dev->mem_bar_reg =3D pci_ioremap_bar(pdev, 0); + if (!pci_bmc_dev->mem_bar_reg) + return -ENOMEM; + + /* Get Message BAR */ + pci_bmc_dev->message_bar_base =3D pci_resource_start(pdev, 1); + pci_bmc_dev->message_bar_size =3D pci_resource_len(pdev, 1); + pci_bmc_dev->msg_bar_reg =3D pci_ioremap_bar(pdev, 1); + if (!pci_bmc_dev->msg_bar_reg) { + rc =3D -ENOMEM; + goto out_free0; + } + + /* AST2600 ERRTA40: dummy read */ + if (pdev->revision < 0x27) + (void)__raw_readl((void __iomem *)pci_bmc_dev->msg_bar_reg); + + rc =3D aspeed_pci_bmc_device_setup_queue(pdev); + if (rc) { + pr_err("Cannot setup Queue Message"); + goto out_free1; + } + + rc =3D aspeed_pci_bmc_device_setup_memory_mapping(pdev); + if (rc) { + pr_err("Cannot setup Memory Mapping"); + goto out_free_queue; + } + + rc =3D aspeed_pci_bmc_device_setup_mbox(pdev); + if (rc) { + pr_err("Cannot setup Mailnbox"); + goto out_free_mmapping; + } + + rc =3D aspeed_pci_bmc_device_setup_vuart(pdev); + if (rc) { + pr_err("Cannot setup Virtual UART"); + goto out_free_mbox; + } + + rc =3D devm_request_irq( + &pdev->dev, + pci_irq_vector(pdev, pci_bmc_dev->msi_idx_table[BMC_MSI]), + aspeed_pci_host_bmc_device_interrupt, IRQF_SHARED, + pci_bmc_dev->miscdev.name, pci_bmc_dev); + if (rc) { + pr_err("Get BMC DEVICE IRQ failed. (err=3D%d)\n", rc); + goto out_free_uart; + } + + return 0; + +out_free_uart: + aspeed_pci_host_bmc_device_release_vuart(pdev); +out_free_mbox: + devm_free_irq(&pdev->dev, + pci_irq_vector(pdev, pci_bmc_dev->msi_idx_table[MBX_MSI]), + pci_bmc_dev); +out_free_mmapping: + aspeed_pci_host_bmc_device_release_memory_mapping(pdev); +out_free_queue: + aspeed_pci_host_bmc_device_release_queue(pdev); +out_free1: + pci_iounmap(pdev, pci_bmc_dev->msg_bar_reg); +out_free0: + pci_iounmap(pdev, pci_bmc_dev->mem_bar_reg); + + pci_release_regions(pdev); + return rc; +} + +static struct aspeed_platform aspeed_pcie_host[] =3D { + { .setup =3D aspeed_pci_host_setup }, + { 0 } +}; + +static int aspeed_pci_host_bmc_device_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev; + int rc =3D 0; + + pr_info("ASPEED BMC PCI ID %04x:%04x, IRQ=3D%u\n", pdev->vendor, + pdev->device, pdev->irq); + + pci_bmc_dev =3D + devm_kzalloc(&pdev->dev, sizeof(*pci_bmc_dev), GFP_KERNEL); + if (!pci_bmc_dev) + return -ENOMEM; + + /* Get platform id */ + pci_bmc_dev->driver_data =3D ent->driver_data; + pci_bmc_dev->platform =3D &aspeed_pcie_host[ent->driver_data]; + + pci_bmc_dev->id =3D ida_alloc(&bmc_device_ida, GFP_KERNEL); + if (pci_bmc_dev->id < 0) + return pci_bmc_dev->id; + + rc =3D pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "pci_enable_device() returned error %d\n", + rc); + return rc; + } + + pci_set_master(pdev); + pci_set_drvdata(pdev, pci_bmc_dev); + + /* Prepare IRQ resource */ + aspeed_pci_setup_irq_resource(pdev); + + /* Setup BMC PCI device */ + rc =3D pci_bmc_dev->platform->setup(pdev); + if (rc) { + dev_err(&pdev->dev, + "ASPEED PCIe Host device returned error %d\n", rc); + pci_free_irq_vectors(pdev); + pci_disable_device(pdev); + return rc; + } + + return 0; +} + +static void aspeed_pci_host_bmc_device_remove(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev =3D pci_get_drvdata(pdev); + + if (pci_bmc_dev->driver_data =3D=3D ASPEED) { + aspeed_pci_host_bmc_device_release_queue(pdev); + aspeed_pci_host_bmc_device_release_memory_mapping(pdev); + aspeed_pci_host_bmc_device_release_vuart(pdev); + + devm_free_irq( + &pdev->dev, + pci_irq_vector(pdev, + pci_bmc_dev->msi_idx_table[BMC_MSI]), + pci_bmc_dev); + devm_free_irq( + &pdev->dev, + pci_irq_vector(pdev, + pci_bmc_dev->msi_idx_table[MBX_MSI]), + pci_bmc_dev); + } + + ida_free(&bmc_device_ida, pci_bmc_dev->id); + + pci_iounmap(pdev, pci_bmc_dev->msg_bar_reg); + pci_iounmap(pdev, pci_bmc_dev->mem_bar_reg); + + pci_free_irq_vectors(pdev); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +/** + * This table holds the list of (VendorID,DeviceID) supported by this driv= er + * + */ +static struct pci_device_id aspeed_host_bmc_dev_pci_ids[] =3D { + /* ASPEED BMC Device */ + { PCI_DEVICE(0x1A03, 0x2402), .class =3D 0xFF0000, .class_mask =3D 0xFFFF= 00, + .driver_data =3D ASPEED }, + { + 0, + } +}; + +MODULE_DEVICE_TABLE(pci, aspeed_host_bmc_dev_pci_ids); + +static struct pci_driver aspeed_host_bmc_dev_driver =3D { + .name =3D DRIVER_NAME, + .id_table =3D aspeed_host_bmc_dev_pci_ids, + .probe =3D aspeed_pci_host_bmc_device_probe, + .remove =3D aspeed_pci_host_bmc_device_remove, +}; + +static int __init aspeed_host_bmc_device_init(void) +{ + return pci_register_driver(&aspeed_host_bmc_dev_driver); +} + +static void aspeed_host_bmc_device_exit(void) +{ + /* unregister pci driver */ + pci_unregister_driver(&aspeed_host_bmc_dev_driver); +} + +late_initcall(aspeed_host_bmc_device_init); +module_exit(aspeed_host_bmc_device_exit); + +MODULE_AUTHOR("Ryan Chen "); +MODULE_DESCRIPTION("ASPEED Host BMC DEVICE Driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file --=20 2.51.2