* [PATCH v4 2/8] drivers/soc: Add Aspeed XDMA Engine Driver
From: Eddie James @ 2019-07-01 19:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562010839-1113-1-git-send-email-eajames@linux.ibm.com>
The XDMA engine embedded in the AST2500 SOC performs PCI DMA operations
between the SOC (acting as a BMC) and a host processor in a server.
This commit adds a driver to control the XDMA engine and adds functions
to initialize the hardware and memory and start DMA operations.
Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
MAINTAINERS | 9 +
drivers/soc/aspeed/Kconfig | 8 +
drivers/soc/aspeed/Makefile | 1 +
drivers/soc/aspeed/aspeed-xdma.c | 528 +++++++++++++++++++++++++++++++++++++++
include/uapi/linux/aspeed-xdma.h | 26 ++
5 files changed, 572 insertions(+)
create mode 100644 drivers/soc/aspeed/aspeed-xdma.c
create mode 100644 include/uapi/linux/aspeed-xdma.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 0685c8a..b1a4b50 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2612,6 +2612,15 @@ S: Maintained
F: drivers/media/platform/aspeed-video.c
F: Documentation/devicetree/bindings/media/aspeed-video.txt
+ASPEED XDMA ENGINE DRIVER
+M: Eddie James <eajames@linux.ibm.com>
+L: linux-aspeed at lists.ozlabs.org (moderated for non-subscribers)
+L: linux-kernel at vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/misc/aspeed,xdma.txt
+F: drivers/soc/aspeed/aspeed-xdma.c
+F: include/uapi/linux/aspeed-xdma.h
+
ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
M: Corentin Chary <corentin.chary@gmail.com>
L: acpi4asus-user at lists.sourceforge.net
diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
index 323e177..522b079 100644
--- a/drivers/soc/aspeed/Kconfig
+++ b/drivers/soc/aspeed/Kconfig
@@ -29,4 +29,12 @@ config ASPEED_P2A_CTRL
ioctl()s, the driver also provides an interface for userspace mappings to
a pre-defined region.
+config ASPEED_XDMA
+ tristate "Aspeed XDMA Engine Driver"
+ depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON && HAS_DMA
+ help
+ Enable support for the Aspeed XDMA Engine found on the Aspeed AST2500
+ SOC. The XDMA engine can perform automatic PCI DMA operations between
+ the AST2500 (acting as a BMC) and a host processor.
+
endmenu
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index b64be47..977b046 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
+obj-$(CONFIG_ASPEED_XDMA) += aspeed-xdma.o
diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c
new file mode 100644
index 0000000..a8dabcb
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-xdma.c
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
+
+#include <linux/aspeed-xdma.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#define DEVICE_NAME "aspeed-xdma"
+
+#define SCU_STRAP 0x070
+#define SCU_STRAP_VGA_MEM GENMASK(3, 2)
+
+#define SCU_PCIE_CONF 0x180
+#define SCU_PCIE_CONF_VGA_EN BIT(0)
+#define SCU_PCIE_CONF_VGA_EN_MMIO BIT(1)
+#define SCU_PCIE_CONF_VGA_EN_LPC BIT(2)
+#define SCU_PCIE_CONF_VGA_EN_MSI BIT(3)
+#define SCU_PCIE_CONF_VGA_EN_MCTP BIT(4)
+#define SCU_PCIE_CONF_VGA_EN_IRQ BIT(5)
+#define SCU_PCIE_CONF_VGA_EN_DMA BIT(6)
+#define SCU_PCIE_CONF_BMC_EN BIT(8)
+#define SCU_PCIE_CONF_BMC_EN_MMIO BIT(9)
+#define SCU_PCIE_CONF_BMC_EN_MSI BIT(11)
+#define SCU_PCIE_CONF_BMC_EN_MCTP BIT(12)
+#define SCU_PCIE_CONF_BMC_EN_IRQ BIT(13)
+#define SCU_PCIE_CONF_BMC_EN_DMA BIT(14)
+#define SCU_PCIE_CONF_RSVD GENMASK(19, 18)
+
+#define SCU_BMC_CLASS_REV 0x19c
+#define SCU_BMC_CLASS_REV_XDMA 0xff000001
+
+#define SDMC_CONF 0x004
+#define SDMC_CONF_MEM GENMASK(1, 0)
+#define SDMC_REMAP 0x008
+#define SDMC_REMAP_MAGIC GENMASK(17, 16)
+
+#define XDMA_CMD_SIZE 4
+#define XDMA_CMDQ_SIZE PAGE_SIZE
+#define XDMA_BYTE_ALIGN 16
+#define XDMA_MAX_LINE_SIZE BIT(10)
+#define XDMA_NUM_CMDS \
+ (XDMA_CMDQ_SIZE / sizeof(struct aspeed_xdma_cmd))
+#define XDMA_NUM_DEBUGFS_REGS 28
+
+/* Aspeed specification requires 10ms after switching the reset line */
+#define XDMA_RESET_TIME_MS 10
+
+#define XDMA_CMD_BMC_CHECK BIT(0)
+#define XDMA_CMD_BMC_ADDR GENMASK(29, 4)
+#define XDMA_CMD_BMC_DIR_US BIT(31)
+
+#define XDMA_CMD_COMM1_HI_HOST_PITCH GENMASK(14, 3)
+#define XDMA_CMD_COMM1_HI_BMC_PITCH GENMASK(30, 19)
+
+#define XDMA_CMD_CONF_CHECK BIT(1)
+#define XDMA_CMD_CONF_LINE_SIZE GENMASK(14, 4)
+#define XDMA_CMD_CONF_IRQ_BMC BIT(15)
+#define XDMA_CMD_CONF_NUM_LINES GENMASK(27, 16)
+#define XDMA_CMD_CONF_IRQ BIT(31)
+
+#define XDMA_CMD_ID_UPDIR GENMASK(17, 16)
+#define XDMA_CMD_ID_UPDIR_BMC 0
+#define XDMA_CMD_ID_UPDIR_HOST 1
+#define XDMA_CMD_ID_UPDIR_VGA 2
+
+#define XDMA_DS_PCIE_REQ_SIZE_128 0
+#define XDMA_DS_PCIE_REQ_SIZE_256 1
+#define XDMA_DS_PCIE_REQ_SIZE_512 2
+#define XDMA_DS_PCIE_REQ_SIZE_1K 3
+#define XDMA_DS_PCIE_REQ_SIZE_2K 4
+#define XDMA_DS_PCIE_REQ_SIZE_4K 5
+
+#define XDMA_HOST_CMD_QUEUE_ADDR0 0x00
+#define XDMA_HOST_CMD_QUEUE_ENDP 0x04
+#define XDMA_HOST_CMD_QUEUE_WRITEP 0x08
+#define XDMA_HOST_CMD_QUEUE_READP 0x0c
+#define XDMA_BMC_CMD_QUEUE_ADDR 0x10
+#define XDMA_BMC_CMD_QUEUE_ENDP 0x14
+#define XDMA_BMC_CMD_QUEUE_WRITEP 0x18
+#define XDMA_BMC_CMD_QUEUE_READP 0x1c
+#define XDMA_BMC_CMD_QUEUE_READP_MAGIC 0xee882266
+#define XDMA_CTRL 0x20
+#define XDMA_CTRL_US_COMP BIT(4)
+#define XDMA_CTRL_DS_COMP BIT(5)
+#define XDMA_CTRL_DS_DIRTY BIT(6)
+#define XDMA_CTRL_DS_PCIE_REQ_SIZE GENMASK(19, 17)
+#define XDMA_CTRL_DS_DATA_TIMEOUT BIT(28)
+#define XDMA_CTRL_DS_CHECK_ID BIT(29)
+#define XDMA_STATUS 0x24
+#define XDMA_STATUS_US_COMP BIT(4)
+#define XDMA_STATUS_DS_COMP BIT(5)
+#define XDMA_DS_FRAME_SIZE 0x28
+#define XDMA_PROBE_DS_PCIE 0x30
+#define XDMA_PROBE_US_PCIE 0x34
+#define XDMA_INPRG_DS_CMD1 0x38
+#define XDMA_INPRG_DS_CMD2 0x3c
+#define XDMA_INPRG_US_CMD00 0x40
+#define XDMA_INPRG_US_CMD01 0x44
+#define XDMA_INPRG_US_CMD10 0x48
+#define XDMA_INPRG_US_CMD11 0x4c
+#define XDMA_INPRG_US_CMD20 0x50
+#define XDMA_INPRG_US_CMD21 0x54
+#define XDMA_HOST_CMD_QUEUE_ADDR1 0x60
+#define XDMA_VGA_CMD_QUEUE_ADDR0 0x64
+#define XDMA_VGA_CMD_QUEUE_ENDP 0x68
+#define XDMA_VGA_CMD_QUEUE_WRITEP 0x6c
+#define XDMA_VGA_CMD_QUEUE_READP 0x70
+#define XDMA_VGA_CMD_STATUS 0x74
+#define XDMA_VGA_CMD_QUEUE_ADDR1 0x78
+
+enum {
+ XDMA_IN_PRG,
+ XDMA_UPSTREAM,
+};
+
+struct aspeed_xdma_cmd {
+ u32 host_addr_lo;
+ u32 host_addr_hi;
+ u32 bmc_addr;
+ u32 comm1_hi;
+ u32 conf;
+ u32 id;
+ u32 resv0;
+ u32 resv1;
+};
+
+struct aspeed_xdma_client;
+
+struct aspeed_xdma {
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *scu;
+ struct reset_control *reset;
+
+ unsigned long flags;
+ unsigned int cmd_idx;
+ wait_queue_head_t wait;
+ struct aspeed_xdma_client *current_client;
+
+ u32 vga_phys;
+ u32 vga_size;
+ void *cmdq;
+ void __iomem *vga_virt;
+ dma_addr_t cmdq_vga_phys;
+ void *cmdq_vga_virt;
+ struct gen_pool *vga_pool;
+};
+
+struct aspeed_xdma_client {
+ struct aspeed_xdma *ctx;
+
+ unsigned long flags;
+ void *virt;
+ dma_addr_t phys;
+ u32 size;
+};
+
+static const u32 aspeed_xdma_bmc_pcie_conf = SCU_PCIE_CONF_BMC_EN |
+ SCU_PCIE_CONF_BMC_EN_MSI | SCU_PCIE_CONF_BMC_EN_MCTP |
+ SCU_PCIE_CONF_BMC_EN_IRQ | SCU_PCIE_CONF_BMC_EN_DMA |
+ SCU_PCIE_CONF_RSVD;
+
+static const u32 aspeed_xdma_vga_pcie_conf = SCU_PCIE_CONF_VGA_EN |
+ SCU_PCIE_CONF_VGA_EN_MSI | SCU_PCIE_CONF_VGA_EN_MCTP |
+ SCU_PCIE_CONF_VGA_EN_IRQ | SCU_PCIE_CONF_VGA_EN_DMA |
+ SCU_PCIE_CONF_RSVD;
+
+static void aspeed_scu_pcie_write(struct aspeed_xdma *ctx, u32 conf)
+{
+ u32 v = 0;
+
+ regmap_write(ctx->scu, SCU_PCIE_CONF, conf);
+ regmap_read(ctx->scu, SCU_PCIE_CONF, &v);
+
+ dev_dbg(ctx->dev, "write scu pcie_conf[%08x]\n", v);
+}
+
+static u32 aspeed_xdma_reg_read(struct aspeed_xdma *ctx, u32 reg)
+{
+ u32 v = readl(ctx->base + reg);
+
+ dev_dbg(ctx->dev, "read %02x[%08x]\n", reg, v);
+ return v;
+}
+
+static void aspeed_xdma_reg_write(struct aspeed_xdma *ctx, u32 reg, u32 val)
+{
+ writel(val, ctx->base + reg);
+ dev_dbg(ctx->dev, "write %02x[%08x]\n", reg, readl(ctx->base + reg));
+}
+
+static void aspeed_xdma_init_eng(struct aspeed_xdma *ctx)
+{
+ const u32 ctrl = XDMA_CTRL_US_COMP | XDMA_CTRL_DS_COMP |
+ XDMA_CTRL_DS_DIRTY | FIELD_PREP(XDMA_CTRL_DS_PCIE_REQ_SIZE,
+ XDMA_DS_PCIE_REQ_SIZE_256) |
+ XDMA_CTRL_DS_DATA_TIMEOUT | XDMA_CTRL_DS_CHECK_ID;
+
+ aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_ENDP,
+ XDMA_CMD_SIZE * XDMA_NUM_CMDS);
+ aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_READP,
+ XDMA_BMC_CMD_QUEUE_READP_MAGIC);
+ aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_WRITEP, 0);
+ aspeed_xdma_reg_write(ctx, XDMA_CTRL, ctrl);
+
+ aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_ADDR,
+ ctx->cmdq_vga_phys);
+
+ ctx->cmd_idx = 0;
+ ctx->flags = 0;
+}
+
+static void aspeed_xdma_reset(struct aspeed_xdma *ctx)
+{
+ reset_control_assert(ctx->reset);
+
+ msleep(XDMA_RESET_TIME_MS);
+
+ reset_control_deassert(ctx->reset);
+
+ msleep(XDMA_RESET_TIME_MS);
+
+ aspeed_xdma_init_eng(ctx);
+}
+
+static void aspeed_xdma_start(struct aspeed_xdma *ctx,
+ struct aspeed_xdma_op *op, u32 bmc_addr)
+{
+ u32 conf = XDMA_CMD_CONF_CHECK | XDMA_CMD_CONF_IRQ_BMC |
+ XDMA_CMD_CONF_IRQ;
+ unsigned int line_size = op->len / XDMA_BYTE_ALIGN;
+ unsigned int num_lines = 1;
+ unsigned int nidx = (ctx->cmd_idx + 1) % XDMA_NUM_CMDS;
+ unsigned int pitch = 1;
+ struct aspeed_xdma_cmd *cmd =
+ &(((struct aspeed_xdma_cmd *)ctx->cmdq)[ctx->cmd_idx]);
+
+ if (line_size > XDMA_MAX_LINE_SIZE) {
+ unsigned int rem;
+ unsigned int total;
+
+ num_lines = line_size / XDMA_MAX_LINE_SIZE;
+ total = XDMA_MAX_LINE_SIZE * num_lines;
+ rem = line_size - total;
+ line_size = XDMA_MAX_LINE_SIZE;
+ pitch = line_size;
+
+ if (rem) {
+ unsigned int offs = total * XDMA_BYTE_ALIGN;
+ u32 r_bmc_addr = bmc_addr + offs;
+ u64 r_host_addr = op->host_addr + (u64)offs;
+ struct aspeed_xdma_cmd *r_cmd =
+ &(((struct aspeed_xdma_cmd *)ctx->cmdq)[nidx]);
+
+ r_cmd->host_addr_lo =
+ (u32)(r_host_addr & 0xFFFFFFFFULL);
+ r_cmd->host_addr_hi = (u32)(r_host_addr >> 32ULL);
+ r_cmd->bmc_addr = (r_bmc_addr & XDMA_CMD_BMC_ADDR) |
+ XDMA_CMD_BMC_CHECK |
+ (op->upstream ? XDMA_CMD_BMC_DIR_US : 0);
+ r_cmd->conf = conf |
+ FIELD_PREP(XDMA_CMD_CONF_LINE_SIZE, rem) |
+ FIELD_PREP(XDMA_CMD_CONF_NUM_LINES, 1);
+ r_cmd->comm1_hi =
+ FIELD_PREP(XDMA_CMD_COMM1_HI_HOST_PITCH, 1) |
+ FIELD_PREP(XDMA_CMD_COMM1_HI_BMC_PITCH, 1);
+
+ /* do not trigger IRQ for first command */
+ conf = XDMA_CMD_CONF_CHECK;
+
+ nidx = (nidx + 1) % XDMA_NUM_CMDS;
+ }
+
+ /* undocumented formula to get required number of lines */
+ num_lines = (num_lines * 2) - 1;
+ }
+
+ /* ctrl == 0 indicates engine hasn't started properly; restart it */
+ if (!aspeed_xdma_reg_read(ctx, XDMA_CTRL))
+ aspeed_xdma_reset(ctx);
+
+ cmd->host_addr_lo = (u32)(op->host_addr & 0xFFFFFFFFULL);
+ cmd->host_addr_hi = (u32)(op->host_addr >> 32ULL);
+ cmd->bmc_addr = (bmc_addr & XDMA_CMD_BMC_ADDR) | XDMA_CMD_BMC_CHECK |
+ (op->upstream ? XDMA_CMD_BMC_DIR_US : 0);
+ cmd->conf = conf |
+ FIELD_PREP(XDMA_CMD_CONF_LINE_SIZE, line_size) |
+ FIELD_PREP(XDMA_CMD_CONF_NUM_LINES, num_lines);
+ cmd->comm1_hi = FIELD_PREP(XDMA_CMD_COMM1_HI_HOST_PITCH, pitch) |
+ FIELD_PREP(XDMA_CMD_COMM1_HI_BMC_PITCH, pitch);
+
+ memcpy(ctx->cmdq_vga_virt, ctx->cmdq, XDMA_CMDQ_SIZE);
+
+ if (op->upstream)
+ set_bit(XDMA_UPSTREAM, &ctx->flags);
+ else
+ clear_bit(XDMA_UPSTREAM, &ctx->flags);
+
+ set_bit(XDMA_IN_PRG, &ctx->flags);
+
+ aspeed_xdma_reg_write(ctx, XDMA_BMC_CMD_QUEUE_WRITEP,
+ nidx * XDMA_CMD_SIZE);
+ ctx->cmd_idx = nidx;
+}
+
+static void aspeed_xdma_done(struct aspeed_xdma *ctx)
+{
+ if (ctx->current_client) {
+ clear_bit(XDMA_IN_PRG, &ctx->current_client->flags);
+
+ ctx->current_client = NULL;
+ }
+
+ clear_bit(XDMA_IN_PRG, &ctx->flags);
+ wake_up_interruptible_all(&ctx->wait);
+}
+
+static irqreturn_t aspeed_xdma_irq(int irq, void *arg)
+{
+ struct aspeed_xdma *ctx = arg;
+ u32 status = aspeed_xdma_reg_read(ctx, XDMA_STATUS);
+
+ if (status & XDMA_STATUS_US_COMP) {
+ if (test_bit(XDMA_UPSTREAM, &ctx->flags))
+ aspeed_xdma_done(ctx);
+ }
+
+ if (status & XDMA_STATUS_DS_COMP) {
+ if (!test_bit(XDMA_UPSTREAM, &ctx->flags))
+ aspeed_xdma_done(ctx);
+ }
+
+ aspeed_xdma_reg_write(ctx, XDMA_STATUS, status);
+
+ return IRQ_HANDLED;
+}
+
+static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx)
+{
+ int rc;
+ u32 scu_conf = 0;
+ u32 mem_size = 0x20000000;
+ const u32 mem_sizes[4] = { 0x8000000, 0x10000000, 0x20000000,
+ 0x40000000 };
+ const u32 vga_sizes[4] = { 0x800000, 0x1000000, 0x2000000, 0x4000000 };
+ void __iomem *sdmc_base = ioremap(0x1e6e0000, 0x100);
+
+ aspeed_scu_pcie_write(ctx, aspeed_xdma_bmc_pcie_conf);
+
+ regmap_write(ctx->scu, SCU_BMC_CLASS_REV, SCU_BMC_CLASS_REV_XDMA);
+
+ /*
+ * Calculate the VGA memory size and physical address from the SCU and
+ * memory controller registers.
+ */
+ regmap_read(ctx->scu, SCU_STRAP, &scu_conf);
+ ctx->vga_size = vga_sizes[FIELD_GET(SCU_STRAP_VGA_MEM, scu_conf)];
+
+ if (sdmc_base) {
+ u32 sdmc = readl(sdmc_base + SDMC_CONF);
+ u32 remap = readl(sdmc_base + SDMC_REMAP);
+
+ remap |= SDMC_REMAP_MAGIC;
+ writel(remap, sdmc_base + SDMC_REMAP);
+ remap = readl(sdmc_base + SDMC_REMAP);
+
+ mem_size = mem_sizes[sdmc & SDMC_CONF_MEM];
+ iounmap(sdmc_base);
+ }
+
+ ctx->vga_phys = (mem_size - ctx->vga_size) + 0x80000000;
+
+ ctx->cmdq = devm_kzalloc(ctx->dev, XDMA_CMDQ_SIZE, GFP_KERNEL);
+ if (!ctx->cmdq) {
+ dev_err(ctx->dev, "Failed to allocate command queue.\n");
+ return -ENOMEM;
+ }
+
+ ctx->vga_virt = ioremap(ctx->vga_phys, ctx->vga_size);
+ if (!ctx->vga_virt) {
+ dev_err(ctx->dev, "Failed to ioremap VGA memory.\n");
+ return -ENOMEM;
+ }
+
+ rc = gen_pool_add_virt(ctx->vga_pool, (unsigned long)ctx->vga_virt,
+ ctx->vga_phys, ctx->vga_size, -1);
+ if (rc) {
+ dev_err(ctx->dev, "Failed to add memory to genalloc pool.\n");
+ iounmap(ctx->vga_virt);
+ return rc;
+ }
+
+ ctx->cmdq_vga_virt = gen_pool_dma_alloc(ctx->vga_pool, XDMA_CMDQ_SIZE,
+ &ctx->cmdq_vga_phys);
+ if (!ctx->cmdq_vga_virt) {
+ dev_err(ctx->dev, "Failed to genalloc cmdq.\n");
+ iounmap(ctx->vga_virt);
+ return -ENOMEM;
+ }
+
+ dev_dbg(ctx->dev, "VGA mapped at phys[%08x], size[%08x].\n",
+ ctx->vga_phys, ctx->vga_size);
+
+ return 0;
+}
+
+static int aspeed_xdma_probe(struct platform_device *pdev)
+{
+ int irq;
+ int rc;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct aspeed_xdma *ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = dev;
+ platform_set_drvdata(pdev, ctx);
+ init_waitqueue_head(&ctx->wait);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctx->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->base)) {
+ dev_err(dev, "Unable to ioremap registers.\n");
+ return PTR_ERR(ctx->base);
+ }
+
+ irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (!irq) {
+ dev_err(dev, "Unable to find IRQ.\n");
+ return -ENODEV;
+ }
+
+ rc = devm_request_irq(dev, irq, aspeed_xdma_irq, IRQF_SHARED,
+ DEVICE_NAME, ctx);
+ if (rc < 0) {
+ dev_err(dev, "Unable to request IRQ %d.\n", irq);
+ return rc;
+ }
+
+ ctx->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu");
+ if (IS_ERR(ctx->scu)) {
+ dev_err(ctx->dev, "Unable to grab SCU regs.\n");
+ return PTR_ERR(ctx->scu);
+ }
+
+ ctx->reset = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(ctx->reset)) {
+ dev_err(dev, "Unable to request reset control.\n");
+ return PTR_ERR(ctx->reset);
+ }
+
+ ctx->vga_pool = devm_gen_pool_create(dev, ilog2(PAGE_SIZE), -1, NULL);
+ if (!ctx->vga_pool) {
+ dev_err(dev, "Unable to setup genalloc pool.\n");
+ return -ENOMEM;
+ }
+
+ reset_control_deassert(ctx->reset);
+
+ msleep(XDMA_RESET_TIME_MS);
+
+ rc = aspeed_xdma_init_mem(ctx);
+ if (rc) {
+ reset_control_assert(ctx->reset);
+ return rc;
+ }
+
+ aspeed_xdma_init_eng(ctx);
+
+ return 0;
+}
+
+static int aspeed_xdma_remove(struct platform_device *pdev)
+{
+ struct aspeed_xdma *ctx = platform_get_drvdata(pdev);
+
+ gen_pool_free(ctx->vga_pool, (unsigned long)ctx->cmdq_vga_virt,
+ XDMA_CMDQ_SIZE);
+ iounmap(ctx->vga_virt);
+ reset_control_assert(ctx->reset);
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_xdma_match[] = {
+ { .compatible = "aspeed,ast2500-xdma" },
+ { },
+};
+
+static struct platform_driver aspeed_xdma_driver = {
+ .probe = aspeed_xdma_probe,
+ .remove = aspeed_xdma_remove,
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = aspeed_xdma_match,
+ },
+};
+
+module_platform_driver(aspeed_xdma_driver);
+
+MODULE_AUTHOR("Eddie James");
+MODULE_DESCRIPTION("Aspeed XDMA Engine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/aspeed-xdma.h b/include/uapi/linux/aspeed-xdma.h
new file mode 100644
index 0000000..998459e
--- /dev/null
+++ b/include/uapi/linux/aspeed-xdma.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright IBM Corp 2019 */
+
+#ifndef _UAPI_LINUX_ASPEED_XDMA_H_
+#define _UAPI_LINUX_ASPEED_XDMA_H_
+
+#include <linux/types.h>
+
+/*
+ * aspeed_xdma_op
+ *
+ * host_addr: the DMA address on the host side, typically configured by PCI
+ * subsystem
+ *
+ * len: the size of the transfer in bytes; it should be a multiple of 16 bytes
+ *
+ * upstream: boolean indicating the direction of the DMA operation; upstream
+ * means a transfer from the BMC to the host
+ */
+struct aspeed_xdma_op {
+ __u64 host_addr;
+ __u32 len;
+ __u32 upstream;
+};
+
+#endif /* _UAPI_LINUX_ASPEED_XDMA_H_ */
--
1.8.3.1
^ permalink raw reply related
* [PATCH v4 3/8] drivers/soc: xdma: Add user interface
From: Eddie James @ 2019-07-01 19:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562010839-1113-1-git-send-email-eajames@linux.ibm.com>
This commits adds a miscdevice to provide a user interface to the XDMA
engine. The interface provides the write operation to start DMA
operations. The DMA parameters are passed as the data to the write call.
The actual data to transfer is NOT passed through write. Note that both
directions of DMA operation are accomplished through the write command;
BMC to host and host to BMC.
The XDMA engine is restricted to only accessing the reserved memory
space on the AST2500, typically used by the VGA. For this reason, the
VGA memory space is pooled and allocated with genalloc. Users calling
mmap allocate pages from this pool for their usage. The space allocated
by a client will be the space used in the DMA operation. For an
"upstream" (BMC to host) operation, the data in the client's area will
be transferred to the host. For a "downstream" (host to BMC) operation,
the host data will be placed in the client's memory area.
Poll is also provided in order to determine when the DMA operation is
complete for non-blocking IO.
Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
drivers/soc/aspeed/aspeed-xdma.c | 200 +++++++++++++++++++++++++++++++++++++++
1 file changed, 200 insertions(+)
diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c
index a8dabcb..622e3d9 100644
--- a/drivers/soc/aspeed/aspeed-xdma.c
+++ b/drivers/soc/aspeed/aspeed-xdma.c
@@ -157,6 +157,8 @@ struct aspeed_xdma {
unsigned long flags;
unsigned int cmd_idx;
+ struct mutex list_lock;
+ struct mutex start_lock;
wait_queue_head_t wait;
struct aspeed_xdma_client *current_client;
@@ -167,6 +169,8 @@ struct aspeed_xdma {
dma_addr_t cmdq_vga_phys;
void *cmdq_vga_virt;
struct gen_pool *vga_pool;
+
+ struct miscdevice misc;
};
struct aspeed_xdma_client {
@@ -358,6 +362,184 @@ static irqreturn_t aspeed_xdma_irq(int irq, void *arg)
return IRQ_HANDLED;
}
+static ssize_t aspeed_xdma_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *offset)
+{
+ int rc;
+ struct aspeed_xdma_op op;
+ struct aspeed_xdma_client *client = file->private_data;
+ struct aspeed_xdma *ctx = client->ctx;
+ u32 offs = client->phys ? (client->phys - ctx->vga_phys) :
+ XDMA_CMDQ_SIZE;
+
+ if (len != sizeof(struct aspeed_xdma_op))
+ return -EINVAL;
+
+ rc = copy_from_user(&op, buf, len);
+ if (rc)
+ return rc;
+
+ if (op.len > (ctx->vga_size - offs) || op.len < XDMA_BYTE_ALIGN)
+ return -EINVAL;
+
+ if (file->f_flags & O_NONBLOCK) {
+ if (!mutex_trylock(&ctx->start_lock))
+ return -EAGAIN;
+
+ if (test_bit(XDMA_IN_PRG, &ctx->flags)) {
+ mutex_unlock(&ctx->start_lock);
+ return -EAGAIN;
+ }
+ } else {
+ mutex_lock(&ctx->start_lock);
+
+ rc = wait_event_interruptible(ctx->wait,
+ !test_bit(XDMA_IN_PRG,
+ &ctx->flags));
+ if (rc) {
+ mutex_unlock(&ctx->start_lock);
+ return -EINTR;
+ }
+ }
+
+ ctx->current_client = client;
+ set_bit(XDMA_IN_PRG, &client->flags);
+
+ aspeed_xdma_start(ctx, &op, ctx->vga_phys + offs);
+
+ mutex_unlock(&ctx->start_lock);
+
+ if (!(file->f_flags & O_NONBLOCK)) {
+ rc = wait_event_interruptible(ctx->wait,
+ !test_bit(XDMA_IN_PRG,
+ &ctx->flags));
+ if (rc)
+ return -EINTR;
+ }
+
+ return len;
+}
+
+static __poll_t aspeed_xdma_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ __poll_t mask = 0;
+ __poll_t req = poll_requested_events(wait);
+ struct aspeed_xdma_client *client = file->private_data;
+ struct aspeed_xdma *ctx = client->ctx;
+
+ if (req & (EPOLLIN | EPOLLRDNORM)) {
+ if (test_bit(XDMA_IN_PRG, &client->flags))
+ poll_wait(file, &ctx->wait, wait);
+
+ if (!test_bit(XDMA_IN_PRG, &client->flags))
+ mask |= EPOLLIN | EPOLLRDNORM;
+ }
+
+ if (req & (EPOLLOUT | EPOLLWRNORM)) {
+ if (test_bit(XDMA_IN_PRG, &ctx->flags))
+ poll_wait(file, &ctx->wait, wait);
+
+ if (!test_bit(XDMA_IN_PRG, &ctx->flags))
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ }
+
+ return mask;
+}
+
+static void aspeed_xdma_vma_close(struct vm_area_struct *vma)
+{
+ struct aspeed_xdma_client *client = vma->vm_private_data;
+
+ gen_pool_free(client->ctx->vga_pool, (unsigned long)client->virt,
+ client->size);
+
+ client->virt = NULL;
+ client->phys = 0;
+ client->size = 0;
+}
+
+static const struct vm_operations_struct aspeed_xdma_vm_ops = {
+ .close = aspeed_xdma_vma_close,
+};
+
+static int aspeed_xdma_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int rc;
+ struct aspeed_xdma_client *client = file->private_data;
+ struct aspeed_xdma *ctx = client->ctx;
+
+ /* restrict file to one mapping */
+ if (client->size)
+ return -ENOMEM;
+
+ client->size = vma->vm_end - vma->vm_start;
+ client->virt = gen_pool_dma_alloc(ctx->vga_pool, client->size,
+ &client->phys);
+ if (!client->virt) {
+ client->phys = 0;
+ client->size = 0;
+ return -ENOMEM;
+ }
+
+ vma->vm_pgoff = (client->phys - ctx->vga_phys) >> PAGE_SHIFT;
+ vma->vm_ops = &aspeed_xdma_vm_ops;
+ vma->vm_private_data = client;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ rc = io_remap_pfn_range(vma, vma->vm_start, client->phys >> PAGE_SHIFT,
+ client->size, vma->vm_page_prot);
+ if (rc) {
+ gen_pool_free(ctx->vga_pool, (unsigned long)client->virt,
+ client->size);
+
+ client->virt = NULL;
+ client->phys = 0;
+ client->size = 0;
+ return rc;
+ }
+
+ dev_dbg(ctx->dev, "mmap: v[%08lx] to p[%08x], s[%08x]\n",
+ vma->vm_start, (u32)client->phys, client->size);
+
+ return 0;
+}
+
+static int aspeed_xdma_open(struct inode *inode, struct file *file)
+{
+ struct miscdevice *misc = file->private_data;
+ struct aspeed_xdma *ctx = container_of(misc, struct aspeed_xdma, misc);
+ struct aspeed_xdma_client *client = kzalloc(sizeof(*client),
+ GFP_KERNEL);
+
+ if (!client)
+ return -ENOMEM;
+
+ client->ctx = ctx;
+ file->private_data = client;
+ return 0;
+}
+
+static int aspeed_xdma_release(struct inode *inode, struct file *file)
+{
+ struct aspeed_xdma_client *client = file->private_data;
+
+ if (client->ctx->current_client == client)
+ client->ctx->current_client = NULL;
+
+ kfree(client);
+ return 0;
+}
+
+static const struct file_operations aspeed_xdma_fops = {
+ .owner = THIS_MODULE,
+ .write = aspeed_xdma_write,
+ .poll = aspeed_xdma_poll,
+ .mmap = aspeed_xdma_mmap,
+ .open = aspeed_xdma_open,
+ .release = aspeed_xdma_release,
+};
+
static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx)
{
int rc;
@@ -441,6 +623,8 @@ static int aspeed_xdma_probe(struct platform_device *pdev)
ctx->dev = dev;
platform_set_drvdata(pdev, ctx);
init_waitqueue_head(&ctx->wait);
+ mutex_init(&ctx->list_lock);
+ mutex_init(&ctx->start_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctx->base = devm_ioremap_resource(dev, res);
@@ -492,6 +676,21 @@ static int aspeed_xdma_probe(struct platform_device *pdev)
aspeed_xdma_init_eng(ctx);
+ ctx->misc.minor = MISC_DYNAMIC_MINOR;
+ ctx->misc.fops = &aspeed_xdma_fops;
+ ctx->misc.name = "aspeed-xdma";
+ ctx->misc.parent = dev;
+ rc = misc_register(&ctx->misc);
+ if (rc) {
+ dev_err(dev, "Unable to register xdma miscdevice.\n");
+
+ gen_pool_free(ctx->vga_pool, (unsigned long)ctx->cmdq_vga_virt,
+ XDMA_CMDQ_SIZE);
+ iounmap(ctx->vga_virt);
+ reset_control_assert(ctx->reset);
+ return rc;
+ }
+
return 0;
}
@@ -499,6 +698,7 @@ static int aspeed_xdma_remove(struct platform_device *pdev)
{
struct aspeed_xdma *ctx = platform_get_drvdata(pdev);
+ misc_deregister(&ctx->misc);
gen_pool_free(ctx->vga_pool, (unsigned long)ctx->cmdq_vga_virt,
XDMA_CMDQ_SIZE);
iounmap(ctx->vga_virt);
--
1.8.3.1
^ permalink raw reply related
* [PATCH v4 4/8] Documentation: ABI: Add aspeed-xdma sysfs documentation
From: Eddie James @ 2019-07-01 19:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562010839-1113-1-git-send-email-eajames@linux.ibm.com>
Document the pcidev sysfs attribute used by the aspeed-xdma driver.
Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
Documentation/ABI/testing/sysfs-devices-platform-aspeed-xdma | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-aspeed-xdma
diff --git a/Documentation/ABI/testing/sysfs-devices-platform-aspeed-xdma b/Documentation/ABI/testing/sysfs-devices-platform-aspeed-xdma
new file mode 100644
index 0000000..6e22eeb
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-aspeed-xdma
@@ -0,0 +1,11 @@
+What: /sys/bus/platform/devices/1e6e7000.xdma/pcidev
+Date: May 2019
+KernelVersion: 5.2
+Contact: Eddie James <eajames@linux.ibm.com>
+Description: When written, this file sets the PCI device that will be used
+ for DMA operations by the XDMA engine. When read, it provides
+ the current PCI device being used by the XDMA engine.
+ Valid values: vga or bmc.
+ Default bmc, can be set by aspeed-xdma.pcidev= module
+ parameter.
+Users: aspeed-xdma driver
--
1.8.3.1
^ permalink raw reply related
* [PATCH v4 5/8] drivers/soc: xdma: Add PCI device configuration sysfs
From: Eddie James @ 2019-07-01 19:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562010839-1113-1-git-send-email-eajames@linux.ibm.com>
The AST2500 has two PCI devices embedded. The XDMA engine can use either
device to perform DMA transfers. Users need the capability to choose
which device to use. This commit therefore adds a sysfs file that can
toggle the AST2500 and XDMA engine between the two PCI devices.
Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
drivers/soc/aspeed/aspeed-xdma.c | 103 +++++++++++++++++++++++++++++++++++++--
1 file changed, 100 insertions(+), 3 deletions(-)
diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c
index 622e3d9..72b4c4b 100644
--- a/drivers/soc/aspeed/aspeed-xdma.c
+++ b/drivers/soc/aspeed/aspeed-xdma.c
@@ -170,6 +170,7 @@ struct aspeed_xdma {
void *cmdq_vga_virt;
struct gen_pool *vga_pool;
+ char pcidev[4];
struct miscdevice misc;
};
@@ -192,6 +193,10 @@ struct aspeed_xdma_client {
SCU_PCIE_CONF_VGA_EN_IRQ | SCU_PCIE_CONF_VGA_EN_DMA |
SCU_PCIE_CONF_RSVD;
+static char *_pcidev = "bmc";
+module_param_named(pcidev, _pcidev, charp, 0600);
+MODULE_PARM_DESC(pcidev, "Default PCI device used by XDMA engine for DMA ops");
+
static void aspeed_scu_pcie_write(struct aspeed_xdma *ctx, u32 conf)
{
u32 v = 0;
@@ -540,7 +545,7 @@ static int aspeed_xdma_release(struct inode *inode, struct file *file)
.release = aspeed_xdma_release,
};
-static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx)
+static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx, u32 conf)
{
int rc;
u32 scu_conf = 0;
@@ -550,7 +555,7 @@ static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx)
const u32 vga_sizes[4] = { 0x800000, 0x1000000, 0x2000000, 0x4000000 };
void __iomem *sdmc_base = ioremap(0x1e6e0000, 0x100);
- aspeed_scu_pcie_write(ctx, aspeed_xdma_bmc_pcie_conf);
+ aspeed_scu_pcie_write(ctx, conf);
regmap_write(ctx->scu, SCU_BMC_CLASS_REV, SCU_BMC_CLASS_REV_XDMA);
@@ -609,10 +614,91 @@ static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx)
return 0;
}
+static int aspeed_xdma_change_pcie_conf(struct aspeed_xdma *ctx, u32 conf)
+{
+ int rc;
+
+ mutex_lock(&ctx->start_lock);
+ rc = wait_event_interruptible_timeout(ctx->wait,
+ !test_bit(XDMA_IN_PRG,
+ &ctx->flags),
+ msecs_to_jiffies(1000));
+ if (rc < 0) {
+ mutex_unlock(&ctx->start_lock);
+ return -EINTR;
+ }
+
+ /* Previous operation didn't complete; wake up waiters anyway. */
+ if (!rc)
+ wake_up_interruptible_all(&ctx->wait);
+
+ reset_control_assert(ctx->reset);
+ msleep(XDMA_RESET_TIME_MS);
+
+ aspeed_scu_pcie_write(ctx, conf);
+ msleep(XDMA_RESET_TIME_MS);
+
+ reset_control_deassert(ctx->reset);
+ msleep(XDMA_RESET_TIME_MS);
+
+ aspeed_xdma_init_eng(ctx);
+
+ mutex_unlock(&ctx->start_lock);
+
+ return 0;
+}
+
+static int aspeed_xdma_pcidev_to_conf(struct aspeed_xdma *ctx,
+ const char *pcidev, u32 *conf)
+{
+ if (!strncasecmp(pcidev, "vga", 3)) {
+ *conf = aspeed_xdma_vga_pcie_conf;
+ return 0;
+ }
+
+ if (!strncasecmp(pcidev, "bmc", 3)) {
+ *conf = aspeed_xdma_bmc_pcie_conf;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t aspeed_xdma_show_pcidev(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct aspeed_xdma *ctx = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%s\n", ctx->pcidev);
+}
+
+static ssize_t aspeed_xdma_store_pcidev(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 conf;
+ struct aspeed_xdma *ctx = dev_get_drvdata(dev);
+ int rc = aspeed_xdma_pcidev_to_conf(ctx, buf, &conf);
+
+ if (rc)
+ return rc;
+
+ rc = aspeed_xdma_change_pcie_conf(ctx, conf);
+ if (rc)
+ return rc;
+
+ strncpy(ctx->pcidev, buf, 3);
+ return count;
+}
+static DEVICE_ATTR(pcidev, 0644, aspeed_xdma_show_pcidev,
+ aspeed_xdma_store_pcidev);
+
static int aspeed_xdma_probe(struct platform_device *pdev)
{
int irq;
int rc;
+ u32 conf;
struct resource *res;
struct device *dev = &pdev->dev;
struct aspeed_xdma *ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
@@ -668,7 +754,14 @@ static int aspeed_xdma_probe(struct platform_device *pdev)
msleep(XDMA_RESET_TIME_MS);
- rc = aspeed_xdma_init_mem(ctx);
+ if (aspeed_xdma_pcidev_to_conf(ctx, _pcidev, &conf)) {
+ conf = aspeed_xdma_bmc_pcie_conf;
+ strncpy(ctx->pcidev, "bmc", 3);
+ } else {
+ strncpy(ctx->pcidev, _pcidev, 3);
+ }
+
+ rc = aspeed_xdma_init_mem(ctx, conf);
if (rc) {
reset_control_assert(ctx->reset);
return rc;
@@ -691,6 +784,8 @@ static int aspeed_xdma_probe(struct platform_device *pdev)
return rc;
}
+ device_create_file(dev, &dev_attr_pcidev);
+
return 0;
}
@@ -698,6 +793,8 @@ static int aspeed_xdma_remove(struct platform_device *pdev)
{
struct aspeed_xdma *ctx = platform_get_drvdata(pdev);
+ device_remove_file(ctx->dev, &dev_attr_pcidev);
+
misc_deregister(&ctx->misc);
gen_pool_free(ctx->vga_pool, (unsigned long)ctx->cmdq_vga_virt,
XDMA_CMDQ_SIZE);
--
1.8.3.1
^ permalink raw reply related
* [PATCH v4 6/8] drivers/soc: xdma: Add debugfs entries
From: Eddie James @ 2019-07-01 19:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562010839-1113-1-git-send-email-eajames@linux.ibm.com>
Add debugfs entries for the relevant XDMA engine registers and for
dumping the AST2500 reserved memory space.
Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
drivers/soc/aspeed/aspeed-xdma.c | 128 +++++++++++++++++++++++++++++++++++++++
1 file changed, 128 insertions(+)
diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c
index 72b4c4b..08128ce 100644
--- a/drivers/soc/aspeed/aspeed-xdma.c
+++ b/drivers/soc/aspeed/aspeed-xdma.c
@@ -172,6 +172,12 @@ struct aspeed_xdma {
char pcidev[4];
struct miscdevice misc;
+ struct dentry *debugfs_dir;
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+ struct debugfs_regset32 regset;
+ struct debugfs_reg32 regs[XDMA_NUM_DEBUGFS_REGS];
+#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
};
struct aspeed_xdma_client {
@@ -614,6 +620,126 @@ static int aspeed_xdma_init_mem(struct aspeed_xdma *ctx, u32 conf)
return 0;
}
+static ssize_t aspeed_xdma_debugfs_vga_read(struct file *file,
+ char __user *buf, size_t len,
+ loff_t *offset)
+{
+ int rc = -ENOMEM;
+ struct inode *inode = file_inode(file);
+ struct aspeed_xdma *ctx = inode->i_private;
+ loff_t offs = *offset;
+ void *tmp;
+
+ if (len + offs > ctx->vga_size) {
+ if (offs < ctx->vga_size)
+ len = ctx->vga_size - offs;
+ else
+ return 0;
+ }
+
+ tmp = kzalloc(len, GFP_KERNEL);
+ if (!tmp)
+ return rc;
+
+ memcpy_fromio(tmp, ctx->vga_virt + offs, len);
+
+ rc = copy_to_user(buf, tmp, len);
+ if (rc)
+ goto free;
+
+ *offset = offs + len;
+ rc = len;
+
+free:
+ kfree(tmp);
+
+ return rc;
+}
+
+static const struct file_operations aspeed_xdma_debugfs_vga_fops = {
+ .owner = THIS_MODULE,
+ .llseek = generic_file_llseek,
+ .read = aspeed_xdma_debugfs_vga_read,
+};
+
+static void aspeed_xdma_init_debugfs(struct aspeed_xdma *ctx)
+{
+ if (!IS_ENABLED(CONFIG_DEBUG_FS))
+ return;
+
+ ctx->debugfs_dir = debugfs_create_dir(DEVICE_NAME, NULL);
+ if (IS_ERR(ctx->debugfs_dir)) {
+ dev_warn(ctx->dev, "Failed to create debugfs directory.\n");
+ return;
+ }
+
+ debugfs_create_file("vga", 0444, ctx->debugfs_dir, ctx,
+ &aspeed_xdma_debugfs_vga_fops);
+
+ ctx->regs[0].name = "host_q_addr31";
+ ctx->regs[0].offset = XDMA_HOST_CMD_QUEUE_ADDR0;
+ ctx->regs[1].name = "host_q_addr63";
+ ctx->regs[1].offset = XDMA_HOST_CMD_QUEUE_ADDR1;
+ ctx->regs[2].name = "host_q_endp";
+ ctx->regs[2].offset = XDMA_HOST_CMD_QUEUE_ENDP;
+ ctx->regs[3].name = "host_q_writep";
+ ctx->regs[3].offset = XDMA_HOST_CMD_QUEUE_WRITEP;
+ ctx->regs[4].name = "host_q_readp";
+ ctx->regs[4].offset = XDMA_HOST_CMD_QUEUE_READP;
+ ctx->regs[5].name = "bmc_q_addr";
+ ctx->regs[5].offset = XDMA_BMC_CMD_QUEUE_ADDR;
+ ctx->regs[6].name = "bmc_q_endp";
+ ctx->regs[6].offset = XDMA_BMC_CMD_QUEUE_ENDP;
+ ctx->regs[7].name = "bmc_q_writep";
+ ctx->regs[7].offset = XDMA_BMC_CMD_QUEUE_WRITEP;
+ ctx->regs[8].name = "bmc_q_readp";
+ ctx->regs[8].offset = XDMA_BMC_CMD_QUEUE_READP;
+ ctx->regs[9].name = "control";
+ ctx->regs[9].offset = XDMA_CTRL;
+ ctx->regs[10].name = "status";
+ ctx->regs[10].offset = XDMA_STATUS;
+ ctx->regs[11].name = "ds_frame_size";
+ ctx->regs[11].offset = XDMA_DS_FRAME_SIZE;
+ ctx->regs[12].name = "probe_ds_pcie";
+ ctx->regs[12].offset = XDMA_PROBE_DS_PCIE;
+ ctx->regs[13].name = "probe_us_pcie";
+ ctx->regs[13].offset = XDMA_PROBE_US_PCIE;
+ ctx->regs[14].name = "inprg_ds1";
+ ctx->regs[14].offset = XDMA_INPRG_DS_CMD1;
+ ctx->regs[15].name = "inprg_ds2";
+ ctx->regs[15].offset = XDMA_INPRG_DS_CMD2;
+ ctx->regs[16].name = "inprg_us031";
+ ctx->regs[16].offset = XDMA_INPRG_US_CMD00;
+ ctx->regs[17].name = "inprg_us063";
+ ctx->regs[17].offset = XDMA_INPRG_US_CMD01;
+ ctx->regs[18].name = "inprg_us131";
+ ctx->regs[18].offset = XDMA_INPRG_US_CMD10;
+ ctx->regs[19].name = "inprg_us163";
+ ctx->regs[19].offset = XDMA_INPRG_US_CMD11;
+ ctx->regs[20].name = "inprg_us231";
+ ctx->regs[20].offset = XDMA_INPRG_US_CMD20;
+ ctx->regs[21].name = "inprg_us263";
+ ctx->regs[21].offset = XDMA_INPRG_US_CMD21;
+ ctx->regs[22].name = "vga_q_addr31";
+ ctx->regs[22].offset = XDMA_VGA_CMD_QUEUE_ADDR0;
+ ctx->regs[23].name = "vga_q_addr63";
+ ctx->regs[23].offset = XDMA_VGA_CMD_QUEUE_ADDR1;
+ ctx->regs[24].name = "vga_q_endp";
+ ctx->regs[24].offset = XDMA_VGA_CMD_QUEUE_ENDP;
+ ctx->regs[25].name = "vga_q_writep";
+ ctx->regs[25].offset = XDMA_VGA_CMD_QUEUE_WRITEP;
+ ctx->regs[26].name = "vga_q_readp";
+ ctx->regs[26].offset = XDMA_VGA_CMD_QUEUE_READP;
+ ctx->regs[27].name = "vga_cmd_status";
+ ctx->regs[27].offset = XDMA_VGA_CMD_STATUS;
+
+ ctx->regset.regs = ctx->regs;
+ ctx->regset.nregs = XDMA_NUM_DEBUGFS_REGS;
+ ctx->regset.base = ctx->base;
+
+ debugfs_create_regset32("regs", 0444, ctx->debugfs_dir, &ctx->regset);
+}
+
static int aspeed_xdma_change_pcie_conf(struct aspeed_xdma *ctx, u32 conf)
{
int rc;
@@ -785,6 +911,7 @@ static int aspeed_xdma_probe(struct platform_device *pdev)
}
device_create_file(dev, &dev_attr_pcidev);
+ aspeed_xdma_init_debugfs(ctx);
return 0;
}
@@ -793,6 +920,7 @@ static int aspeed_xdma_remove(struct platform_device *pdev)
{
struct aspeed_xdma *ctx = platform_get_drvdata(pdev);
+ debugfs_remove_recursive(ctx->debugfs_dir);
device_remove_file(ctx->dev, &dev_attr_pcidev);
misc_deregister(&ctx->misc);
--
1.8.3.1
^ permalink raw reply related
* [PATCH v4 7/8] ARM: dts: aspeed: Add XDMA Engine
From: Eddie James @ 2019-07-01 19:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562010839-1113-1-git-send-email-eajames@linux.ibm.com>
Add a node for the XDMA engine with all the necessary information.
Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
arch/arm/boot/dts/aspeed-g5.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 5b1ca26..bfc8328 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -243,6 +243,14 @@
interrupts = <0x19>;
};
+ xdma: xdma at 1e6e7000 {
+ compatible = "aspeed,ast2500-xdma";
+ reg = <0x1e6e7000 0x100>;
+ resets = <&syscon ASPEED_RESET_XDMA>;
+ interrupts = <6>;
+ status = "disabled";
+ };
+
adc: adc at 1e6e9000 {
compatible = "aspeed,ast2500-adc";
reg = <0x1e6e9000 0xb0>;
--
1.8.3.1
^ permalink raw reply related
* [PATCH v4 8/8] ARM: dts: aspeed: witherspoon: Enable XDMA Engine
From: Eddie James @ 2019-07-01 19:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562010839-1113-1-git-send-email-eajames@linux.ibm.com>
Enable the XDMA engine node.
Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
index 31ea34e..1d247cd 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
@@ -653,4 +653,8 @@
memory-region = <&video_engine_memory>;
};
+&xdma {
+ status = "okay";
+};
+
#include "ibm-power9-dual.dtsi"
--
1.8.3.1
^ permalink raw reply related
* [PATCH v2 0/8] pinctrl: aspeed: Preparation for AST2600
From: Linus Walleij @ 2019-07-03 8:40 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190628023838.15426-1-andrew@aj.id.au>
Hi Andrew,
On Fri, Jun 28, 2019 at 4:39 AM Andrew Jeffery <andrew@aj.id.au> wrote:
>
> Hello!
>
> The ASPEED AST2600 is in the pipeline, and we have enough information to start
> preparing to upstream support for it. This series lays some ground work;
> splitting the bindings and dicing the implementation up a little further to
> facilitate differences between the 2600 and previous SoC generations.
>
> v2 addresses Rob's comments on the bindings conversion patches. v1 can be found
> here:
I have applied this series, I had to strip some changes of the header
because it was based on some SPDX cleanups upstream but no
big deal I think. Check the result please.
Yours,
Linus Walleij
^ permalink raw reply
* [patch v3 1/5] AST2500 DMA UART driver
From: Greg KH @ 2019-07-03 17:49 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1561459476-14268-2-git-send-email-open.sudheer@gmail.com>
On Tue, Jun 25, 2019 at 04:14:32PM +0530, sudheer.v wrote:
> From: sudheer veliseti <sudheer.open@gmail.com>
>
> UART driver for Aspeed's bmc chip AST2500
>
> Design approch:
> AST2500 has dedicated Uart DMA controller which has 12 sets of Tx and RX channels
> connected to UART controller directly.
> Since the DMA controller have dedicated buffers and registers,
> there would be little benifit in adding DMA framework overhead.
> So the software for DMA controller is included within the UART driver itself.
>
> implementation details:
> 'struct uart_8250_port' serial port is populated and registered with 8250_core.
> Rx and Tx dma channels are requested from DMA controller software Layer, which
> is part of uart driver itself.
> Interrupt service routine for DMA controller is the crucial one for Handling all
> the tx and rx data. ISRs installed for individual uarts are just dummy,and are helpful
> only to report any spurious interrupts in hardware.
>
>
> Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
> ---
>
> Changes in v3:
> -custom debug replaced by in kerenl dynamic debug: pr_debug
> -change-logs added
>
> .../tty/serial/8250/8250_ast2500_uart_dma.c | 1879 +++++++++++++++++
> 1 file changed, 1879 insertions(+)
> create mode 100644 drivers/tty/serial/8250/8250_ast2500_uart_dma.c
>
> diff --git a/drivers/tty/serial/8250/8250_ast2500_uart_dma.c b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
> new file mode 100644
> index 000000000000..13911a0a745a
> --- /dev/null
> +++ b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
> @@ -0,0 +1,1879 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * DMA UART Driver for ASPEED BMC chip: AST2500
> + *
> + * Copyright (C) 2019 sudheer Kumar veliseti, Aspeed technology Inc.
> + * <open.sudheer@gmail.com>
> + *
> + */
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/clk.h>
> +#include <linux/console.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/init.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/mutex.h>
> +#include <linux/nmi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/serial.h>
> +#include <linux/serial_8250.h>
> +#include <linux/serial_core.h>
> +#include <linux/serial_reg.h>
> +#include <linux/slab.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
Do you really need all of these include files? Seems like a lot...
> +
> +#include "8250.h"
> +
> +#define DMA_BUFF_SIZE 0x1000 // 4096
> +#define SDMA_RX_BUFF_SIZE 0x10000 // 65536
We know what 0x1000 and 0x10000 is :)
Also, try to line up your defines.
> +
> +#define SDDMA_RX_FIX 1
> +/* enum ast_uart_chan_op
> + * operation codes passed to the DMA code by the user, and also used
> + * to inform the current channel owner of any changes to the system state
> + */
> +
> +enum ast_uart_chan_op {
Very odd spacing, add one above the comment and remove the one below.
> + AST_UART_DMAOP_TRIGGER,
> + AST_UART_DMAOP_STOP,
> + AST_UART_DMAOP_PAUSE,
> +};
> +
> +/* ast_uart_dma_cbfn_t * * buffer callback routine type */
Do not use _t if possible. ANd odd placement of "* *"
> +typedef void (*ast_uart_dma_cbfn_t)(void *dev_id, u16 len);
> +
> +struct ast_sdma_info {
> + u8 ch_no;
> + u8 direction;
> + u8 enable;
> + void *priv;
> + char *sdma_virt_addr;
> + dma_addr_t dma_phy_addr;
> + /* cdriver callbacks */
> + ast_uart_dma_cbfn_t callback_fn; /* buffer done callback */
> +};
> +
> +#define AST_UART_SDMA_CH 12
> +
> +struct ast_sdma_ch {
> + struct ast_sdma_info tx_dma_info[AST_UART_SDMA_CH];
> + struct ast_sdma_info rx_dma_info[AST_UART_SDMA_CH];
> +};
> +
> +struct ast_sdma {
> + void __iomem *reg_base;
> + int dma_irq;
> + struct ast_sdma_ch *dma_ch;
> + struct regmap *map;
> +};
> +
> +
> +
> +#define UART_TX_SDMA_EN 0x00
> +#define UART_RX_SDMA_EN 0x04
> +#define UART_SDMA_CONF 0x08
> +#define UART_SDMA_TIMER 0x0C
> +#define UART_TX_SDMA_REST 0x20
> +#define UART_RX_SDMA_REST 0x24
> +#define UART_TX_SDMA_IER 0x30
> +#define UART_TX_SDMA_ISR 0x34
> +#define UART_RX_SDMA_IER 0x38
> +#define UART_RX_SDMA_ISR 0x3C
> +#define UART_TX_R_POINT(x) (0x40 + (x * 0x20))
> +#define UART_TX_W_POINT(x) (0x44 + (x * 0x20))
> +#define UART_TX_SDMA_ADDR(x) (0x48 + (x * 0x20))
> +#define UART_RX_R_POINT(x) (0x50 + (x * 0x20))
> +#define UART_RX_W_POINT(x) (0x54 + (x * 0x20))
> +#define UART_RX_SDMA_ADDR(x) (0x58 + (x * 0x20))
Please use a tab to line these up.
> +/* UART_TX_SDMA_EN-0x00 : UART TX DMA Enable */
> +/* UART_RX_SDMA_EN-0x04 : UART RX DMA Enable */
What are these for?
> +#define SDMA_CH_EN(x) (0x1 << (x))
BIT()?
> +
> +/* UART_SDMA_CONF - 0x08 : Misc, Buffer size */
> +#define SDMA_TX_BUFF_SIZE_MASK (0x3)
> +#define SDMA_SET_TX_BUFF_SIZE(x) (x)
> +#define SDMA_BUFF_SIZE_1KB (0x0)
> +#define SDMA_BUFF_SIZE_4KB (0x1)
> +#define SDMA_BUFF_SIZE_16KB (0x2)
> +#define SDMA_BUFF_SIZE_64KB (0x3)
> +#define SDMA_RX_BUFF_SIZE_MASK (0x3 << 2)
> +#define SDMA_SET_RX_BUFF_SIZE(x) (x << 2)
> +#define SDMA_TIMEOUT_DIS (0x1 << 4)
> +
> +/* UART_SDMA_TIMER-0x0C : UART DMA time out timer */
> +
> +/* UART_TX_SDMA_IER 0x30 */
> +/* UART_TX_SDMA_ISR 0x34 */
What is this?
> +
> +#define UART_SDMA11_INT (1 << 11)
> +#define UART_SDMA10_INT (1 << 10)
> +#define UART_SDMA9_INT (1 << 9)
> +#define UART_SDMA8_INT (1 << 8)
> +#define UART_SDMA7_INT (1 << 7)
> +#define UART_SDMA6_INT (1 << 6)
> +#define UART_SDMA5_INT (1 << 5)
> +#define UART_SDMA4_INT (1 << 4)
> +#define UART_SDMA3_INT (1 << 3)
> +#define UART_SDMA2_INT (1 << 2)
> +#define UART_SDMA1_INT (1 << 1)
> +#define UART_SDMA0_INT (1 << 0)
Please use BIT()
> +
> +
> +/*
> + * Configuration:
> + * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option
> + * is unsafe when used on edge-triggered interrupts.
> + */
> +static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
> +
> +static unsigned int nr_uarts = CONFIG_AST_RUNTIME_DMA_UARTS;
> +
> +
> +#define PASS_LIMIT 256
> +
> +#include <asm/serial.h>
Why way down here?
> +
> +#define UART_DMA_NR CONFIG_AST_NR_DMA_UARTS
> +
> +
> +struct ast_uart_priv_data {
> +
> + unsigned short line; //index of uart port
No need for a blank line.
> + struct uart_8250_port *up;
> + u8 dma_ch; // dma channel number
> + struct circ_buf rx_dma_buf;
> + struct circ_buf tx_dma_buf;
> + dma_addr_t dma_rx_addr; /* Mapped ADMA descr. table */
> + dma_addr_t dma_tx_addr; /* Mapped ADMA descr. table */
> +#ifdef SDDMA_RX_FIX
> + struct tasklet_struct rx_tasklet;
> +#else
> + struct timer_list rx_timer;
> +#endif
> + struct tasklet_struct tx_tasklet;
> + spinlock_t lock;
> + int tx_done;
> + int tx_count;
> +};
> +
> +
> +static inline struct uart_8250_port *
> +to_uart_8250_port(struct uart_port *uart) {
> + return container_of(uart, struct uart_8250_port, port);
> +}
Use a #define for a container_of() macro please.
> +
> +struct irq_info {
> + spinlock_t lock;
> + struct uart_8250_port *up;
> +};
> +
> +static struct irq_info ast_uart_irq[1];
> +static DEFINE_MUTEX(ast_uart_mutex);
> +
> +/*
> + * Here we define the default xmit fifo size used for each type of UART.
> + */
> +static const struct serial8250_config uart_config[] = {
> + [PORT_UNKNOWN] = {
> + .name = "unknown",
> + .fifo_size = 1,
> + .tx_loadsz = 1,
> + },
> + [PORT_8250] = {
> + .name = "8250",
> + .fifo_size = 1,
> + .tx_loadsz = 1,
> + },
> + [PORT_16450] = {
> + .name = "16450",
> + .fifo_size = 1,
> + .tx_loadsz = 1,
> + },
> + [PORT_16550] = {
> + .name = "16550",
> + .fifo_size = 1,
> + .tx_loadsz = 1,
> + },
> + [PORT_16550A] = {
> + .name = "16550A",
> + .fifo_size = 16,
> + .tx_loadsz = 16,
> + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10
> + | UART_FCR_DMA_SELECT,
> + .flags = UART_CAP_FIFO,
> + },
> +};
> +
> +/* sane hardware needs no mapping */
> +#define map_8250_in_reg(up, offset) (offset)
> +#define map_8250_out_reg(up, offset) (offset)
Why is this even needed?
> +
> +// SDMA - software Layer : (previously was in ast-uart-sdma.c)
"previously"?
> +static inline void ast_uart_sdma_write(struct ast_sdma *sdma,
> + u32 val, u32 reg)
> +{
> + pr_debug("uart dma write:val:%x,reg:%x\n", val, reg);
No need for debugging, use ftrace.
> + writel(val, sdma->reg_base + reg);
> +}
> +
> +static inline u32 ast_uart_sdma_read(struct ast_sdma *sdma, u32 reg)
> +{
> + return readl(sdma->reg_base + reg);
> +}
> +
> +struct ast_sdma ast_uart_sdma;
static?
> +
> +int ast_uart_rx_sdma_enqueue(u8 ch, dma_addr_t rx_buff)
> +{
> + unsigned long flags;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> +
> + pr_debug("ch = %d, rx buff = %x\n", ch, rx_buff);
Remove debugging please, use ftrace.
> +
> + local_irq_save(flags);
> + ast_uart_sdma_write(sdma, rx_buff, UART_RX_SDMA_ADDR(ch));
> + local_irq_restore(flags);
> +
> + return 0;
> +}
If these functions are not used, why are they here?
> +
> +int ast_uart_tx_sdma_enqueue(u8 ch, dma_addr_t tx_buff)
> +{
> + unsigned long flags;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> +
> + pr_debug("ch = %d, tx buff = %x\n", ch, tx_buff);
> +
> + local_irq_save(flags);
> + ast_uart_sdma_write(sdma, tx_buff, UART_TX_SDMA_ADDR(ch));
> + local_irq_restore(flags);
> +
> + return 0;
> +}
> +
> +int ast_uart_rx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
> +{
> + unsigned long flags;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> + struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
> +
> + pr_debug("RX DMA CTRL [ch %d]\n", ch);
Again, please remove. Same thing everywhere in these patches.
> +
> + local_irq_save(flags);
> +
> + switch (op) {
> + case AST_UART_DMAOP_TRIGGER:
> + pr_debug("Trigger\n");
> + dma_ch->enable = 1;
> +#ifdef SDDMA_RX_FIX
If you can not define this as a build option, then just remove it from
here.
> +#else
> + ast_uart_set_sdma_time_out(0xffff);
> +#endif
> + // set enable
> + ast_uart_sdma_write(sdma,
> + ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) | (0x1 << ch),
> + UART_RX_SDMA_EN);
> + break;
Did you run this patch through checkpatch? Please properly indent the
case statement blocks.
> + case AST_UART_DMAOP_STOP:
> + // disable engine
> + pr_debug("STOP\n");
> + dma_ch->enable = 0;
> + ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
> + ~(0x1 << ch),
> + UART_RX_SDMA_EN);
> + // set reset
> + ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) |
> + (0x1 << ch),
> + UART_RX_SDMA_REST);
> + ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) &
> + ~(0x1 << ch),
> + UART_RX_SDMA_REST);
> +
> + ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(ch));
> + ast_uart_sdma_write(sdma, dma_ch->dma_phy_addr, UART_RX_SDMA_ADDR(ch));
> + break;
> + case AST_UART_DMAOP_PAUSE:
> + // disable engine
> + dma_ch->enable = 0;
> + ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
> + ~(0x1 << ch),
> + UART_RX_SDMA_EN);
> + break;
> + }
> +
> + local_irq_restore(flags);
> + return 0;
> +}
> +
> +int ast_uart_tx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
> +{
> + unsigned long flags;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> + struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
> +
> + pr_debug("TX DMA CTRL [ch %d]\n", ch);
> +
> + local_irq_save(flags);
> +
> + switch (op) {
> + case AST_UART_DMAOP_TRIGGER:
> + pr_debug("TRIGGER : Enable\n");
> + dma_ch->enable = 1;
> + // set enable
> + ast_uart_sdma_write(sdma,
> + ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) | (0x1 << ch),
> + UART_TX_SDMA_EN);
> + break;
> + case AST_UART_DMAOP_STOP:
> + pr_debug("STOP : DISABLE & RESET\n");
> + dma_ch->enable = 0;
> + // disable engine
> + ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) &
> + ~(0x1 << ch),
> + UART_TX_SDMA_EN);
> + // set reset
> + ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_REST) |
> + (0x1 << ch),
> + UART_TX_SDMA_REST);
> + ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_REST) &
> + ~(0x1 << ch),
> + UART_TX_SDMA_REST);
> +
> + ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(ch));
> + break;
> + case AST_UART_DMAOP_PAUSE:
> + pr_debug("PAUSE : DISABLE\n");
> + dma_ch->enable = 0;
> + // disable engine
> + ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) &
> + ~(0x1 << ch),
> + UART_TX_SDMA_EN);
> + }
> +
> + local_irq_restore(flags);
> + return 0;
> +}
> +
> +u32 ast_uart_get_tx_sdma_pt(u8 ch)
> +{
> + struct ast_sdma *sdma = &ast_uart_sdma;
> +
> + return ast_uart_sdma_read(sdma, UART_TX_R_POINT(ch));
> +}
> +
> +int ast_uart_tx_sdma_update(u8 ch, u16 point)
> +{
> + unsigned long flags;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> +
> + pr_debug("TX DMA CTRL [ch %d] point %d\n", ch, point);
> + local_irq_save(flags);
> + ast_uart_sdma_write(sdma, point, UART_TX_W_POINT(ch));
> + local_irq_restore(flags);
> + return 0;
> +}
> +
> +int ast_uart_tx_sdma_request(u8 ch, ast_uart_dma_cbfn_t rtn, void *id)
> +{
> + unsigned long flags;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> + struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
> +
> + pr_debug("TX DMA REQUEST ch = %d\n", ch);
> +
> + local_irq_save(flags);
> +
> + if (dma_ch->enable) {
> + local_irq_restore(flags);
> + return -EBUSY;
> + }
> + dma_ch->priv = id;
> + dma_ch->callback_fn = rtn;
> +
> + // DMA IRQ En
> + ast_uart_sdma_write(sdma,
> + ast_uart_sdma_read(sdma, UART_TX_SDMA_IER) | (1 << ch),
> + UART_TX_SDMA_IER);
> +
> + local_irq_restore(flags);
> +
> + return 0;
> +}
> +
> +int ast_uart_rx_sdma_update(u8 ch, u16 point)
> +{
> + unsigned long flags;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> +
> + pr_debug("RX DMA CTRL [ch %d] point %x\n", ch, point);
> +
> + local_irq_save(flags);
> + ast_uart_sdma_write(sdma, point, UART_RX_R_POINT(ch));
> + local_irq_restore(flags);
> + return 0;
> +}
> +
> +#ifdef SDDMA_RX_FIX
> +char *ast_uart_rx_sdma_request(u8 ch, ast_uart_dma_cbfn_t rtn, void *id)
> +{
> + unsigned long flags;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> + struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
> +
> + pr_debug("RX DMA REQUEST ch = %d\n", ch);
> +
> + local_irq_save(flags);
> +
> + if (dma_ch->enable) {
> + local_irq_restore(flags);
> + return 0;
> + }
> + dma_ch->priv = id;
> +
> + dma_ch->callback_fn = rtn;
> +
> + // DMA IRQ En
> + ast_uart_sdma_write(sdma,
> + ast_uart_sdma_read(sdma, UART_RX_SDMA_IER) | (1 << ch),
> + UART_RX_SDMA_IER);
> +
> + local_irq_restore(flags);
> +
> + return dma_ch->sdma_virt_addr;
> +}
> +
> +#else
> +char *ast_uart_rx_sdma_request(u8 ch, void *id)
> +{
> + unsigned long flags;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> + struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
> +
> + pr_debug("RX DMA REQUEST ch = %d\n", ch);
> +
> + local_irq_save(flags);
> +
> + if (dma_ch->enable) {
> + local_irq_restore(flags);
> + return -EBUSY;
> + }
> + dma_ch->priv = id;
> +
> + local_irq_restore(flags);
> + return dma_ch->sdma_virt_addr;
> +}
> +#endif
> +
> +u16 ast_uart_get_rx_sdma_pt(u8 ch)
> +{
> + struct ast_sdma *sdma = &ast_uart_sdma;
> +
> + return ast_uart_sdma_read(sdma, UART_RX_W_POINT(ch));
> +}
> +
> +void ast_uart_set_sdma_time_out(u16 val)
> +{
> + struct ast_sdma *sdma = &ast_uart_sdma;
> +
> + ast_uart_sdma_write(sdma, val, UART_SDMA_TIMER);
> +}
> +
> +static inline void ast_sdma_bufffdone(struct ast_sdma_info *sdma_ch)
> +{
> + u32 len;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> +
> + if (sdma_ch->enable == 0) {
> + pr_debug("sdma Please check ch_no %x %s!!!!!\n",
> + sdma_ch->ch_no, sdma_ch->direction ? "TX" : "RX");
> + if (sdma_ch->direction) {
> + ast_uart_sdma_write(sdma,
> + ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
> + & ~(0x1 << sdma_ch->ch_no), UART_TX_SDMA_EN);
> + } else {
> + ast_uart_sdma_write(sdma,
> + ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
> + ~(0x1 << sdma_ch->ch_no), UART_RX_SDMA_EN);
> + ast_uart_rx_sdma_update(sdma_ch->ch_no,
> + ast_uart_get_rx_sdma_pt(sdma_ch->ch_no));
> + pr_debug("OFFSET : UART_RX_SDMA_EN = %x\n ",
> + ast_uart_sdma_read(sdma, UART_RX_SDMA_EN));
> + }
> + return;
> + }
> +
> + if (sdma_ch->direction) {
> + len = ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no));
> + pr_debug("tx rp %x , wp %x\n",
> + ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no)),
> + ast_uart_sdma_read(sdma, UART_TX_W_POINT(sdma_ch->ch_no))
> + );
> + } else {
> + pr_debug("rx rp %x , wp %x\n",
> + ast_uart_sdma_read(sdma, UART_RX_R_POINT(sdma_ch->ch_no)),
> + ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no))
> + );
> + len = ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no));
> + }
> +
> + pr_debug("<dma dwn>: ch[%d] : %s ,len : %d\n", sdma_ch->ch_no,
> + sdma_ch->direction ? "tx" : "rx", len);
> +
> + if (sdma_ch->callback_fn != NULL)
> + (sdma_ch->callback_fn)(sdma_ch->priv, len);
> +}
> +
> +static irqreturn_t ast_uart_sdma_isr(int irq, void *dev_id)
> +{
> + struct ast_sdma *sdma = (struct ast_sdma *)dev_id;
> +
> + u32 tx_sts = ast_uart_sdma_read(sdma, UART_TX_SDMA_ISR);
> + u32 rx_sts = ast_uart_sdma_read(sdma, UART_RX_SDMA_ISR);
> +
> + pr_debug("tx sts : %x, rx sts : %x\n", tx_sts, rx_sts);
> +
> + if ((tx_sts == 0) && (rx_sts == 0)) {
> + pr_debug("SDMA IRQ ERROR !!!\n");
> + return IRQ_HANDLED;
> + }
> +
> + if (rx_sts & UART_SDMA0_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA0_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[0]));
> + } else if (rx_sts & UART_SDMA1_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA1_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[1]));
> + } else if (rx_sts & UART_SDMA2_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA2_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[2]));
> + } else if (rx_sts & UART_SDMA3_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA3_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[3]));
> + } else if (rx_sts & UART_SDMA4_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA4_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[4]));
> + } else if (rx_sts & UART_SDMA5_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA5_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[5]));
> + } else if (rx_sts & UART_SDMA6_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA6_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[6]));
> + } else if (rx_sts & UART_SDMA7_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA7_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[7]));
> + } else if (rx_sts & UART_SDMA8_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA8_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[8]));
> + } else if (rx_sts & UART_SDMA9_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA9_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[9]));
> + } else if (rx_sts & UART_SDMA10_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA10_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[10]));
> + } else if (rx_sts & UART_SDMA11_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA11_INT, UART_RX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[11]));
> + } else {
> +
> + }
Why a blank else {} ?
> +
> + if (tx_sts & UART_SDMA0_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA0_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[0]));
> + } else if (tx_sts & UART_SDMA1_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA1_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[1]));
> + } else if (tx_sts & UART_SDMA2_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA2_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[2]));
> + } else if (tx_sts & UART_SDMA3_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA3_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[3]));
> + } else if (tx_sts & UART_SDMA4_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA4_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[4]));
> + } else if (tx_sts & UART_SDMA5_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA5_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[5]));
> + } else if (tx_sts & UART_SDMA6_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA6_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[6]));
> + } else if (tx_sts & UART_SDMA7_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA7_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[7]));
> + } else if (tx_sts & UART_SDMA8_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA8_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[8]));
> + } else if (tx_sts & UART_SDMA9_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA9_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[9]));
> + } else if (tx_sts & UART_SDMA10_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA10_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[10]));
> + } else if (tx_sts & UART_SDMA11_INT) {
> + ast_uart_sdma_write(sdma, UART_SDMA11_INT, UART_TX_SDMA_ISR);
> + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[11]));
> + } else {
> + }
Why a blank else {} ?
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int ast_uart_sdma_probe(void)
> +{
> + int i;
> + struct device_node *node;
> + int ret;
> + struct ast_sdma *sdma = &ast_uart_sdma;
> + char *rx_dma_virt_addr;
> + dma_addr_t rx_dma_phy_addr;
> +
> + sdma->dma_ch = kzalloc(sizeof(struct ast_sdma_ch), GFP_KERNEL);
> + if (!sdma->dma_ch)
> + return -ENOMEM;
> +
> + // sdma memory mapping
> + node = of_find_compatible_node(NULL, NULL, "aspeed,ast-uart-sdma");
> + if (!node)
> + return -ENODEV;
> +
> + sdma->reg_base = of_iomap(node, 0);
> + if (IS_ERR(sdma->reg_base))
> + return PTR_ERR(sdma->map);
> + rx_dma_virt_addr = dma_alloc_coherent(NULL,
> + SDMA_RX_BUFF_SIZE * AST_UART_SDMA_CH, &rx_dma_phy_addr, GFP_KERNEL);
Properly indent things, this is impossible to read.
> +
> + if (!rx_dma_virt_addr) {
> + pr_debug("rx_dma_virt_addr Err:dma alloc Failed\n");
> + return -ENOMEM;
> + }
> + for (i = 0; i < AST_UART_SDMA_CH; i++) {
> + // TX ------------------------
> + sdma->dma_ch->tx_dma_info[i].enable = 0;
> + sdma->dma_ch->tx_dma_info[i].ch_no = i;
> + sdma->dma_ch->tx_dma_info[i].direction = 1;
> + ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(i));
> + // RX ------------------------
> + sdma->dma_ch->rx_dma_info[i].enable = 0;
> + sdma->dma_ch->rx_dma_info[i].ch_no = i;
> + sdma->dma_ch->rx_dma_info[i].direction = 0;
> + sdma->dma_ch->rx_dma_info[i].sdma_virt_addr =
> + rx_dma_virt_addr + (SDMA_RX_BUFF_SIZE * i);
> + sdma->dma_ch->rx_dma_info[i].dma_phy_addr =
> + rx_dma_phy_addr + (SDMA_RX_BUFF_SIZE * i);
> + ast_uart_sdma_write(sdma,
> + sdma->dma_ch->rx_dma_info[i].dma_phy_addr,
> + UART_RX_SDMA_ADDR(i));
> + ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(i));
> + }
> +
> + ast_uart_sdma_write(sdma, 0xffffffff, UART_TX_SDMA_REST);
> + ast_uart_sdma_write(sdma, 0x0, UART_TX_SDMA_REST);
> +
> + ast_uart_sdma_write(sdma, 0xffffffff, UART_RX_SDMA_REST);
> + ast_uart_sdma_write(sdma, 0x0, UART_RX_SDMA_REST);
> +
> + ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_EN);
> + ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_EN);
> +
> +#ifdef SDDMA_RX_FIX
> + ast_uart_sdma_write(sdma, 0x200, UART_SDMA_TIMER);
> +#else
> + ast_uart_sdma_write(sdma, 0xffff, UART_SDMA_TIMER);
> +#endif
> +
> + // TX
> + ast_uart_sdma_write(sdma, 0xfff, UART_TX_SDMA_ISR);
> + ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_IER);
> +
> + // RX
> + ast_uart_sdma_write(sdma, 0xfff, UART_RX_SDMA_ISR);
> + ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_IER);
> +
> + sdma->dma_irq = of_irq_get(node, 0);
> + ret = request_irq(sdma->dma_irq, ast_uart_sdma_isr, 0,
> + "sdma-intr", sdma);
> + if (ret) {
> + pr_debug("Unable to get UART SDMA IRQ %x\n", ret);
> + return -ENODEV;
> + }
> +
> + ast_uart_sdma_write(sdma, SDMA_SET_TX_BUFF_SIZE(SDMA_BUFF_SIZE_4KB) |
> + SDMA_SET_RX_BUFF_SIZE(SDMA_BUFF_SIZE_64KB),
> + UART_SDMA_CONF);
> + return 0;
> +}
> +
> +// END of SDMA Layer
> +
> +// UART Driver Layer
> +
> +static unsigned int ast_serial_in(struct uart_8250_port *up, int offset)
> +{
> + offset = map_8250_in_reg(up, offset) << up->port.regshift;
> + return readb(up->port.membase + offset);
> +}
> +
> +static void ast_serial_out(struct uart_8250_port *up, int offset, int value)
> +{
> + /* Save the offset before it's remapped */
> + offset = map_8250_out_reg(up, offset) << up->port.regshift;
> + writeb(value, up->port.membase + offset);
> +}
> +
> +/*
> + * We used to support using pause I/O for certain machines. We
> + * haven't supported this for a while, but just in case it's badly
> + * needed for certain old 386 machines, I've left these #define's
> + * in....
It looks like you copied this whole thing from somewhere else, please
make it your own and do not leave things in for no good reason.
> + */
> +#define serial_inp(up, offset) ast_serial_in(up, offset)
> +#define serial_outp(up, offset, value) ast_serial_out(up, offset, value)
> +
> +/* Uart divisor latch read */
> +static inline int _serial_dl_read(struct uart_8250_port *up)
> +{
> + return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8;
> +}
> +
> +/* Uart divisor latch write */
> +static inline void _serial_dl_write(struct uart_8250_port *up, int value)
> +{
> + serial_outp(up, UART_DLL, value & 0xff);
> + serial_outp(up, UART_DLM, value >> 8 & 0xff);
> +}
> +
> +#define serial_dl_read(up) _serial_dl_read(up)
> +#define serial_dl_write(up, value) _serial_dl_write(up, value)
> +
> +static void ast_uart_tx_sdma_tasklet_func(unsigned long data)
> +{
> +
> + struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
> + struct uart_8250_port *up = priv->up;
> + struct circ_buf *xmit = NULL;
> + u32 tx_pt;
> +
> +
> + if (!up)
> + return;
> + xmit = &up->port.state->xmit;
> + spin_lock(&up->port.lock);
> + priv->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + dma_sync_single_for_device(up->port.dev, priv->dma_tx_addr,
> + UART_XMIT_SIZE, DMA_TO_DEVICE);
> + tx_pt = ast_uart_get_tx_sdma_pt(priv->dma_ch);
> +
> + if (tx_pt > xmit->head) {
> + if ((tx_pt & 0xfffc) == 0)
> + ast_uart_tx_sdma_update(priv->dma_ch, 0xffff);
> + else
> + ast_uart_tx_sdma_update(priv->dma_ch, 0);
> + } else {
> + ast_uart_tx_sdma_update(priv->dma_ch, xmit->head);
> + }
> + ast_uart_tx_sdma_update(priv->dma_ch, xmit->head);
> + spin_unlock(&up->port.lock);
> +}
> +
> +static void ast_uart_tx_buffdone(void *dev_id, u16 len)
> +{
> +
> + struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)dev_id;
> + struct uart_8250_port *up = priv->up;
> + struct circ_buf *xmit;
> +
> + if (!up)
> + return;
> + xmit = &(up->port.state->xmit);
> +
> + pr_debug("line[%d] : tx len = % d\n", priv->line, len);
> + spin_lock(&up->port.lock);
> + xmit->tail = len;
> + pr_debug(" line[%d], xmit->head = %d, xmit->tail = % d\n",
> + priv->line, xmit->head, xmit->tail);
> +
> + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + uart_write_wakeup(&up->port);
> +
> + if (xmit->head != xmit->tail)
> + tasklet_schedule(&priv->tx_tasklet);
> +
> + spin_unlock(&up->port.lock);
> +}
> +
> +#ifdef SDDMA_RX_FIX
> +static void ast_uart_rx_sdma_tasklet_func(unsigned long data)
> +{
> + struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
> + struct circ_buf *rx_ring = &priv->rx_dma_buf;
> + struct tty_port *ttyport;
> + int count;
> + int copy = 0;
> + struct uart_8250_port *up = priv->up;
> +
> + if (!up)
> + return;
> +
> + ttyport = &up->port.state->port;
> +
> + pr_debug("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
> + up->port.line, rx_ring->head, rx_ring->tail);
> + spin_lock(&up->port.lock);
> + if (rx_ring->head > rx_ring->tail) {
> + count = rx_ring->head - rx_ring->tail;
> + copy = tty_insert_flip_string(ttyport,
> + rx_ring->buf + rx_ring->tail, count);
> + } else if (rx_ring->head < rx_ring->tail) {
> + count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
> + copy = tty_insert_flip_string(ttyport,
> + rx_ring->buf + rx_ring->tail, count);
> + } else {
> + count = 0;
> + }
> +
> + if (copy != count)
> + pr_debug(" !!!!!!!!ERROR 111\n");
That's useless. Make it a real error with dev_err() please.
> + if (count) {
> + rx_ring->tail += count;
> + rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
> + up->port.icount.rx += count;
> + tty_flip_buffer_push(ttyport);
> + ast_uart_rx_sdma_update(priv->dma_ch, rx_ring->tail);
> + }
> + spin_unlock(&up->port.lock);
> +}
> +
> +static void ast_uart_rx_buffdone(void *dev_id, u16 len)
> +{
> + struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)dev_id;
> + struct circ_buf *rx_ring = &priv->rx_dma_buf;
> + struct uart_8250_port *up = priv->up;
> +
> + if (!up)
> + return;
> + pr_debug("line[%d], head = %d,len:%d\n",
> + priv->line, priv->rx_dma_buf.head, len);
> + spin_lock(&up->port.lock);
> + rx_ring->head = len;
> + spin_unlock(&up->port.lock);
> + tasklet_schedule(&priv->rx_tasklet);
> +}
> +
> +#else
> +static void ast_uart_rx_timer_func(unsigned long data)
> +{
> + struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
> + struct uart_8250_port *up = priv->up;
> + struct tty_port *ttyport;
> + struct circ_buf *rx_ring;
> + struct tty_struct *tty;
> + char flag;
> + int count;
> + int copy;
> +
> +
> + if (!up)
> + return;
> + ttyport = &up->port.state->port;
> + rx_ring = &up->rx_dma_buf;
> + tty = up->port.state->port.tty;
> +
> + pr_debug("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
> + up->port.line, rx_ring->head, rx_ring->tail);
> + rx_ring->head = ast_uart_get_rx_sdma_pt(priv->dma_ch);
> + del_timer(&up->rx_timer);
> +
> + if (rx_ring->head > rx_ring->tail) {
> + ast_uart_set_sdma_time_out(0xffff);
> + count = rx_ring->head - rx_ring->tail;
> + copy = tty_insert_flip_string(ttyport,
> + rx_ring->buf + rx_ring->tail, count);
> + } else if (rx_ring->head < rx_ring->tail) {
> + ast_uart_set_sdma_time_out(0xffff);
> + count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
> + copy = tty_insert_flip_string(ttyport,
> + rx_ring->buf + rx_ring->tail, count);
> + } else {
> + count = 0;
> + // pr_debug("@@--%s-- ch = 0x%x\n", __func__, ch);
> + }
> +
> + if (copy != count)
> + pr_debug(" !!!!!!!!ERROR 111\n");
> + rx_ring->tail += count;
> + rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
> +
> + if (count) {
> + //pr_debug("\n count = % d\n", count);
> + up->port.icount.rx += count;
> + spin_lock(&up->port.lock);
> + tty_flip_buffer_push(ttyport);
> + spin_unlock(&up->port.lock);
> + //pr_debug("update rx_ring->tail % x\n", rx_ring->tail);
> + ast_uart_rx_sdma_update(priv->dma_ch, rx_ring->tail);
> + priv->workaround = 1;
> + } else {
> + if (priv->workaround) {
> + priv->workaround++;
> + if (priv->workaround > 1)
> + ast_uart_set_sdma_time_out(0);
> + else
> + ast_uart_set_sdma_time_out(0xffff);
> + }
> + }
> + add_timer(&up->rx_timer);
> +}
> +#endif
> +
> +/*
> + * FIFO support.
> + */
> +static inline void ast25xx_uart_clear_fifos(struct uart_8250_port *p)
> +{
> + serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
> + serial_outp(p, UART_FCR,
> + UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
> + serial_outp(p, UART_FCR, 0);
> +}
> +
> +/*
> + * This routine is called by rs_init() to initialize a specific serial
> + * port.
> + */
> +static void autoconfig(struct uart_8250_port *up)
> +{
> + unsigned long flags;
> +
> + pr_debug("line[%d]\n", up->port.line);
> + if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
> + return;
> +
> + pr_debug("ttyDMA%d : autoconf (0x%04lx, 0x%p) : ", up->port.line,
> + up->port.iobase, up->port.membase);
> +
> + spin_lock_irqsave(&up->port.lock, flags);
> +
> + up->capabilities = 0;
> + up->bugs = 0;
> +
> + up->port.type = PORT_16550A;
> + up->capabilities |= UART_CAP_FIFO;
> +
> + up->port.fifosize = uart_config[up->port.type].fifo_size;
> + up->capabilities = uart_config[up->port.type].flags;
> + up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
> +
> + if (up->port.type == PORT_UNKNOWN)
> + goto out;
> +
> + /*
> + * Reset the UART.
> + */
> + ast25xx_uart_clear_fifos(up);
> + ast_serial_in(up, UART_RX);
> + serial_outp(up, UART_IER, 0);
> +
> +out:
> + spin_unlock_irqrestore(&up->port.lock, flags);
> + pr_debug("type=%s\n", uart_config[up->port.type].name);
> +}
> +
> +static inline void __stop_tx(struct uart_8250_port *p)
> +{
> + if (p->ier & UART_IER_THRI) {
> + p->ier &= ~UART_IER_THRI;
> + ast_serial_out(p, UART_IER, p->ier);
> + }
> +}
> +
> +static void ast25xx_uart_stop_tx(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> +
> + pr_debug("line[%d]\n", up->port.line);
> + __stop_tx(up);
> +}
> +
> +static void transmit_chars(struct uart_8250_port *up);
> +
> +static void ast25xx_uart_start_tx(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + struct ast_uart_priv_data *priv = up->port.private_data;
> +
> + pr_debug("line[%d]\n", port->line);
> + tasklet_schedule(&priv->tx_tasklet);
> +}
> +
> +static void ast25xx_uart_stop_rx(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> +
> + pr_debug("line[%d]\n", port->line);
> + up->ier &= ~UART_IER_RLSI;
> + up->port.read_status_mask &= ~UART_LSR_DR;
> + ast_serial_out(up, UART_IER, up->ier);
> +}
> +
> +static void ast25xx_uart_enable_ms(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> +
> + pr_debug("line[%d]\n", port->line);
> + up->ier |= UART_IER_MSI;
> + ast_serial_out(up, UART_IER, up->ier);
> +}
> +
> +static void transmit_chars(struct uart_8250_port *up)
> +{
> + struct circ_buf *xmit = &up->port.state->xmit;
> + int count;
> +
> + if (up->port.x_char) {
> + serial_outp(up, UART_TX, up->port.x_char);
> + up->port.icount.tx++;
> + up->port.x_char = 0;
> + return;
> + }
> + if (uart_tx_stopped(&up->port)) {
> + ast25xx_uart_stop_tx(&up->port);
> + return;
> + }
> + if (uart_circ_empty(xmit)) {
> + __stop_tx(up);
> + return;
> + }
> +
> + count = up->tx_loadsz;
> + do {
> + ast_serial_out(up, UART_TX, xmit->buf[xmit->tail]);
> + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> + up->port.icount.tx++;
> + if (uart_circ_empty(xmit))
> + break;
> + } while (--count > 0);
> +
> + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + uart_write_wakeup(&up->port);
> +
> + if (uart_circ_empty(xmit))
> + __stop_tx(up);
> +}
> +
> +static unsigned int check_modem_status(struct uart_8250_port *up)
> +{
> + unsigned int status = ast_serial_in(up, UART_MSR);
> +
> + pr_debug("line[%d]\n", up->port.line);
> + status |= up->msr_saved_flags;
> + up->msr_saved_flags = 0;
> + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI
> + && up->port.state != NULL) {
> + if (status & UART_MSR_TERI)
> + up->port.icount.rng++;
> + if (status & UART_MSR_DDSR)
> + up->port.icount.dsr++;
> + if (status & UART_MSR_DDCD)
> + uart_handle_dcd_change(&up->port,
> + status & UART_MSR_DCD);
> + if (status & UART_MSR_DCTS)
> + uart_handle_cts_change(&up->port,
> + status & UART_MSR_CTS);
> +
> + wake_up_interruptible(&up->port.state->port.delta_msr_wait);
> + }
> +
> + return status;
> +}
> +
> +/*
> + * This handles the interrupt from one port.
> + */
> +static inline void ast25xx_uart_handle_port(struct uart_8250_port *up)
> +{
> + unsigned int status;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&up->port.lock, flags);
> +
> + status = serial_inp(up, UART_LSR);
> +
> + pr_debug("status = %x\n", status);
> +
> + check_modem_status(up);
> + if (status & UART_LSR_THRE)
> + transmit_chars(up);
> +
> + spin_unlock_irqrestore(&up->port.lock, flags);
> +}
> +
> +/*
> + * This is the serial driver's interrupt routine.
> + */
> +static irqreturn_t ast_uart_interrupt(int irq, void *dev_id)
> +{
> + struct irq_info *i = dev_id;
> + int pass_counter = 0, handled = 0, end = 0;
> +
> + pr_debug("(%d)-", irq);
> + spin_lock(&i->lock);
> +
> + do {
> + struct uart_8250_port *up;
> + unsigned int iir;
> +
> + up = (struct uart_8250_port *)(i->up);
> +
> + iir = ast_serial_in(up, UART_IIR);
> + if (!(iir & UART_IIR_NO_INT)) {
> + ast25xx_uart_handle_port(up);
> + handled = 1;
> +
> + } else
> + end = 1;
> +
> + if (pass_counter++ > PASS_LIMIT) {
> + /* If we hit this, we're dead. */
> + pr_err("ast-uart-dma:too much work for irq%d\n", irq);
> + break;
> + }
> + } while (end);
> +
> + spin_unlock(&i->lock);
> +
> + pr_debug("-(%d)\n", irq);
> +
> + return IRQ_RETVAL(handled);
> +}
> +
> +static unsigned int ast25xx_uart_tx_empty(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + unsigned long flags;
> + unsigned int lsr;
> +
> + pr_debug("line[%d]\n", up->port.line);
> +
> + spin_lock_irqsave(&up->port.lock, flags);
> + lsr = ast_serial_in(up, UART_LSR);
> + up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
> + spin_unlock_irqrestore(&up->port.lock, flags);
> +
> + return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
> +}
> +
> +static unsigned int ast25xx_uart_get_mctrl(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + unsigned int status;
> + unsigned int ret;
> +
> + status = check_modem_status(up);
> +
> + ret = 0;
> + if (status & UART_MSR_DCD)
> + ret |= TIOCM_CAR;
> + if (status & UART_MSR_RI)
> + ret |= TIOCM_RNG;
> + if (status & UART_MSR_DSR)
> + ret |= TIOCM_DSR;
> + if (status & UART_MSR_CTS)
> + ret |= TIOCM_CTS;
> + return ret;
> +}
> +
> +static void ast25xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + unsigned char mcr = 0;
> +
> + mctrl = 0;
> +
> + if (mctrl & TIOCM_RTS)
> + mcr |= UART_MCR_RTS;
> + if (mctrl & TIOCM_DTR)
> + mcr |= UART_MCR_DTR;
> + if (mctrl & TIOCM_OUT1)
> + mcr |= UART_MCR_OUT1;
> + if (mctrl & TIOCM_OUT2)
> + mcr |= UART_MCR_OUT2;
> + if (mctrl & TIOCM_LOOP)
> + mcr |= UART_MCR_LOOP;
> +
> + mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
> +
> + ast_serial_out(up, UART_MCR, mcr);
> +
> +}
> +
> +static void ast25xx_uart_break_ctl(struct uart_port *port, int break_state)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&up->port.lock, flags);
> + if (break_state == -1)
> + up->lcr |= UART_LCR_SBC;
> + else
> + up->lcr &= ~UART_LCR_SBC;
> + ast_serial_out(up, UART_LCR, up->lcr);
> + spin_unlock_irqrestore(&up->port.lock, flags);
> +}
> +
> +static int ast25xx_uart_startup(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + // TX DMA
> + struct circ_buf *xmit = &up->port.state->xmit;
> + struct ast_uart_priv_data *priv = up->port.private_data;
> + unsigned long flags;
> + unsigned char lsr, iir;
> + int retval;
> + int irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
> +
> + priv->up = up;
> + pr_debug("line[%d]\n", port->line);
> + up->capabilities = uart_config[up->port.type].flags;
> + up->mcr = 0;
> + /*
> + * Clear the FIFO buffers and disable them.
> + * (they will be reenabled in set_termios())
> + */
> + ast25xx_uart_clear_fifos(up);
> + /*
> + * Clear the interrupt registers.
> + */
> + (void)serial_inp(up, UART_LSR);
> + (void)serial_inp(up, UART_RX);
> + (void)serial_inp(up, UART_IIR);
> + (void)serial_inp(up, UART_MSR);
> +
> + ast_uart_irq[0].up = up;
> + retval = request_irq(up->port.irq, ast_uart_interrupt, irq_flags,
> + "ast-uart-dma", ast_uart_irq);
> + if (retval)
> + return retval;
> +
> + /*
> + * Now, initialize the UART
> + */
> + serial_outp(up, UART_LCR, UART_LCR_WLEN8);
> +
> + spin_lock_irqsave(&up->port.lock, flags);
> + up->port.mctrl |= TIOCM_OUT2;
> +
> + ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
> +
> + /*
> + * Do a quick test to see if we receive an
> + * interrupt when we enable the TX irq.
> + */
> + serial_outp(up, UART_IER, UART_IER_THRI);
> + lsr = ast_serial_in(up, UART_LSR);
> + iir = ast_serial_in(up, UART_IIR);
> + serial_outp(up, UART_IER, 0);
> +
> + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
> + if (!(up->bugs & UART_BUG_TXEN)) {
> + up->bugs |= UART_BUG_TXEN;
> + pr_debug("ttyDMA%d-enabling bad tx status\n",
> + port->line);
> + }
> + } else {
> + up->bugs &= ~UART_BUG_TXEN;
> + }
> +
> + spin_unlock_irqrestore(&up->port.lock, flags);
> +
> + /*
> + * Clear the interrupt registers again for luck, and clear the
> + * saved flags to avoid getting false values from polling
> + * routines or the previous session.
> + */
> + serial_inp(up, UART_LSR);
> + serial_inp(up, UART_RX);
> + serial_inp(up, UART_IIR);
> + serial_inp(up, UART_MSR);
> + up->lsr_saved_flags = 0;
> + up->msr_saved_flags = 0;
> +
> + // RX DMA
> + priv->rx_dma_buf.head = 0;
> + priv->rx_dma_buf.tail = 0;
> + up->port.icount.rx = 0;
> +
> + priv->tx_done = 1;
> + priv->tx_count = 0;
> +
> + priv->rx_dma_buf.head = 0;
> + priv->rx_dma_buf.tail = 0;
> +#ifdef SDDMA_RX_FIX
> +#else
> + priv->workaround = 0;
> +#endif
> + // pr_debug("Sending trigger for % x\n", priv->dma_ch);
> + ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_STOP);
> + ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_TRIGGER);
> +#ifdef SDDMA_RX_FIX
> +#else
> + add_timer(&priv->rx_timer);
> +#endif
> + priv->tx_dma_buf.head = 0;
> + priv->tx_dma_buf.tail = 0;
> + priv->tx_dma_buf.buf = xmit->buf;
> +
> + pr_debug("head:0x%x tail:0x%x\n", xmit->head, xmit->tail);
> + xmit->head = 0;
> + xmit->tail = 0;
> +
> + priv->dma_tx_addr = dma_map_single(port->dev, priv->tx_dma_buf.buf,
> + UART_XMIT_SIZE, DMA_TO_DEVICE);
> +
> + ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_STOP);
> + ast_uart_tx_sdma_enqueue(priv->dma_ch, priv->dma_tx_addr);
> + ast_uart_tx_sdma_update(priv->dma_ch, 0);
> + ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_TRIGGER);
> + return 0;
> +}
> +
> +static void ast25xx_uart_shutdown(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + struct ast_uart_priv_data *priv = up->port.private_data;
> + unsigned long flags;
> +
> + pr_debug("line[%d]\n", port->line);
> + priv->up = NULL;
> +
> + up->ier = 0;
> + serial_outp(up, UART_IER, 0);
> +
> + spin_lock_irqsave(&up->port.lock, flags);
> + up->port.mctrl &= ~TIOCM_OUT2;
> +
> + ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
> + spin_unlock_irqrestore(&up->port.lock, flags);
> +
> + /*
> + * Disable break condition and FIFOs
> + */
> + ast_serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
> + ast25xx_uart_clear_fifos(up);
> +
> + (void)ast_serial_in(up, UART_RX);
> +
> + ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_PAUSE);
> + ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_PAUSE);
> +#ifdef SDDMA_RX_FIX
> +#else
> + del_timer_sync(&up->rx_timer);
> +#endif
> +
> + // Tx buffer will free by serial_core.c
> + free_irq(up->port.irq, ast_uart_irq);
> +}
> +
> +static unsigned int ast25xx_uart_get_divisor(struct uart_port *port,
> + unsigned int baud)
> +{
> + unsigned int quot;
> +
> + quot = uart_get_divisor(port, baud);
> +
> + return quot;
> +}
> +
> +static void ast25xx_uart_set_termios(struct uart_port *port,
> + struct ktermios *termios,
> + struct ktermios *old)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + unsigned char cval, fcr = 0;
> + unsigned long flags;
> + unsigned int baud, quot;
> +
> + switch (termios->c_cflag & CSIZE) {
> + case CS5:
> + cval = UART_LCR_WLEN5;
> + break;
> + case CS6:
> + cval = UART_LCR_WLEN6;
> + break;
> + case CS7:
> + cval = UART_LCR_WLEN7;
> + break;
> + default:
> + case CS8:
> + cval = UART_LCR_WLEN8;
> + break;
> + }
> +
> + if (termios->c_cflag & CSTOPB)
> + cval |= UART_LCR_STOP;
> + if (termios->c_cflag & PARENB)
> + cval |= UART_LCR_PARITY;
> + if (!(termios->c_cflag & PARODD))
> + cval |= UART_LCR_EPAR;
> +#ifdef CMSPAR
> + if (termios->c_cflag & CMSPAR)
> + cval |= UART_LCR_SPAR;
> +#endif
> +
> + /*
> + * Ask the core to calculate the divisor for us.
> + */
> + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
> + quot = ast25xx_uart_get_divisor(port, baud);
> +
> + if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
> + if (baud < 2400)
> + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
> + else
> + fcr = uart_config[up->port.type].fcr;
> + }
> +
> + /*
> + * Ok, we're now changing the port state. Do it with
> + * interrupts disabled.
> + */
> + spin_lock_irqsave(&up->port.lock, flags);
> +
> + /*
> + * Update the per-port timeout.
> + */
> + uart_update_timeout(port, termios->c_cflag, baud);
> +
> + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
> + if (termios->c_iflag & INPCK)
> + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
> + if (termios->c_iflag & (BRKINT | PARMRK))
> + up->port.read_status_mask |= UART_LSR_BI;
> +
> + /*
> + * Characteres to ignore
> + */
> + up->port.ignore_status_mask = 0;
> + if (termios->c_iflag & IGNPAR)
> + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
> + if (termios->c_iflag & IGNBRK) {
> + up->port.ignore_status_mask |= UART_LSR_BI;
> + /*
> + * If we're ignoring parity and break indicators,
> + * ignore overruns too (for real raw support).
> + */
> + if (termios->c_iflag & IGNPAR)
> + up->port.ignore_status_mask |= UART_LSR_OE;
> + }
> +
> + /*
> + * ignore all characters if CREAD is not set
> + */
> + if ((termios->c_cflag & CREAD) == 0)
> + up->port.ignore_status_mask |= UART_LSR_DR;
> +
> + /*
> + * CTS flow control flag and modem status interrupts
> + */
> + up->ier &= ~UART_IER_MSI;
> + if (UART_ENABLE_MS(&up->port, termios->c_cflag))
> + up->ier |= UART_IER_MSI;
> +
> + ast_serial_out(up, UART_IER, up->ier);
> +
> + serial_outp(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
> +
> + serial_dl_write(up, quot);
> +
> + /*
> + * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
> + * is written without DLAB set, this mode will be disabled.
> + */
> +
> + serial_outp(up, UART_LCR, cval); /* reset DLAB */
> + up->lcr = cval; /* Save LCR */
> + if (fcr & UART_FCR_ENABLE_FIFO) {
> + /* emulated UARTs (Lucent Venus 167x) need two steps */
> + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
> + }
> + serial_outp(up, UART_FCR, fcr); /* set fcr */
> + ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
> + spin_unlock_irqrestore(&up->port.lock, flags);
> + /* Don't rewrite B0 */
> + if (tty_termios_baud_rate(termios))
> + tty_termios_encode_baud_rate(termios, baud, baud);
> +}
> +
> +/*
> + * Resource handling.
> + */
> +static int ast25xx_uart_request_std_resource(struct uart_8250_port *up)
> +{
> + unsigned int size = 8 << up->port.regshift;
> + int ret = 0;
> +
> + if (!up->port.mapbase)
> + return ret;
> +
> + if (!request_mem_region(up->port.mapbase, size, "ast-uart-dma")) {
> + ret = -EBUSY;
> + return ret;
> + }
> +
> + if (up->port.flags & UPF_IOREMAP) {
> + up->port.membase = ioremap_nocache(up->port.mapbase, size);
> + if (!up->port.membase) {
> + release_mem_region(up->port.mapbase, size);
> + ret = -ENOMEM;
> + return ret;
> + }
> + }
> + return ret;
> +}
> +
> +static void ast25xx_uart_release_std_resource(struct uart_8250_port *up)
> +{
> + unsigned int size = 8 << up->port.regshift;
> +
> + if (!up->port.mapbase)
> + return;
> +
> + if (up->port.flags & UPF_IOREMAP) {
> + iounmap(up->port.membase);
> + up->port.membase = NULL;
> + }
> +
> + release_mem_region(up->port.mapbase, size);
> +}
> +
> +static void ast25xx_uart_release_port(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> +
> + ast25xx_uart_release_std_resource(up);
> +}
> +
> +static int ast25xx_uart_request_port(struct uart_port *port)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + int ret;
> +
> + ret = ast25xx_uart_request_std_resource(up);
> + if (ret == 0)
> + ast25xx_uart_release_std_resource(up);
> +
> + return ret;
> +}
> +
> +static void ast25xx_uart_config_port(struct uart_port *port, int flags)
> +{
> + struct uart_8250_port *up = to_uart_8250_port(port);
> + int ret;
> +
> + /*
> + * Find the region that we can probe for. This in turn
> + * tells us whether we can probe for the type of port.
> + */
> + ret = ast25xx_uart_request_std_resource(up);
> + if (ret < 0)
> + return;
> +
> + if (flags & UART_CONFIG_TYPE)
> + autoconfig(up);
> +
> + if (up->port.type == PORT_UNKNOWN)
> + ast25xx_uart_release_std_resource(up);
> +}
> +
> +static int ast25xx_uart_verify_port(struct uart_port *port,
> + struct serial_struct *ser)
> +{
> + return 0;
> +}
> +
> +static const char *ast25xx_uart_type(struct uart_port *port)
> +{
> + int type = port->type;
> +
> + if (type >= ARRAY_SIZE(uart_config))
> + type = 0;
> + return uart_config[type].name;
> +}
> +
> +
> +
> +static unsigned int ast25xx_uart_serial_in(struct uart_port *port, int offset)
> +{
> + offset = offset << port->regshift;
> + return readb(port->membase + offset);
> +
> +}
> +
> +
> +static void ast25xx_uart_serial_out(struct uart_port *port,
> + int offset, int value)
> +{
> + offset = offset << port->regshift;
> + writeb(value, port->membase + offset);
> +}
> +
> +static const struct uart_ops ast25xx_uart_pops = {
> + .tx_empty = ast25xx_uart_tx_empty,
> + .set_mctrl = ast25xx_uart_set_mctrl,
> + .get_mctrl = ast25xx_uart_get_mctrl,
> + .stop_tx = ast25xx_uart_stop_tx,
> + .start_tx = ast25xx_uart_start_tx,
> + .stop_rx = ast25xx_uart_stop_rx,
> + .enable_ms = ast25xx_uart_enable_ms,
> + .break_ctl = ast25xx_uart_break_ctl,
> + .startup = ast25xx_uart_startup,
> + .shutdown = ast25xx_uart_shutdown,
> + .set_termios = ast25xx_uart_set_termios,
> + .type = ast25xx_uart_type,
> + .release_port = ast25xx_uart_release_port,
> + .request_port = ast25xx_uart_request_port,
> + .config_port = ast25xx_uart_config_port,
> + .verify_port = ast25xx_uart_verify_port,
> +};
> +
> +
> +
> +/*
> + * Register a set of serial devices attached to a platform device. The
> + * list is terminated with a zero flags entry, which means we expect
> + * all entries to have at least UPF_BOOT_AUTOCONF set.
> + */
> +struct clk *clk;
static?
> +
> +static int ast25xx_uart_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct ast_uart_priv_data *priv;
> + struct uart_8250_port port_8250;
> + struct uart_8250_port *up;
> + int ret;
> + u32 read, dma_channel = 0;
> + struct resource *res;
> +
> + if (UART_XMIT_SIZE > DMA_BUFF_SIZE)
> + pr_debug("UART_XMIT_SIZE > DMA_BUFF_SIZE : Please Check\n");
> +
> + priv = (struct ast_uart_priv_data *)devm_kzalloc(&pdev->dev,
> + sizeof(struct ast_uart_priv_data), GFP_KERNEL);
> + if (priv == NULL)
> + return -ENOMEM;
> +
> + up = &port_8250;
> + memset(up, 0, sizeof(struct uart_8250_port));
> + up->port.flags = UPF_IOREMAP;
> +
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (res == NULL) {
> + dev_err(&pdev->dev, "IRQ resource not found");
> + return -ENODEV;
> + }
> + up->port.irq = res->start;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "Register base not found");
> + return -ENODEV;
> + }
> + up->port.mapbase = res->start;
> +
> + ret = ast25xx_uart_request_std_resource(up);
> + if (ret) {
> + dev_err(&pdev->dev, "ioremap_nocache Failed");
> + return ret;
> + }
> +
> + clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(clk))
> + dev_err(&pdev->dev, "missing controller clock");
> +
> + ret = clk_prepare_enable(clk);
> + if (ret)
> + dev_err(&pdev->dev, "failed to enable DMA UART Clk");
> +
> + up->port.uartclk = clk_get_rate(clk);
> +
> + if (of_property_read_u32(np, "reg-shift", &read) == 0)
> + up->port.regshift = read;
> + if (of_property_read_u32(np, "dma-channel", &read) == 0) {
> + dma_channel = read;
> + priv->dma_ch = dma_channel;
> + }
> + up->port.iotype = UPIO_MEM;
> + up->port.flags |= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
> + up->port.dev = &pdev->dev;
> + if (share_irqs)
> + up->port.flags |= UPF_SHARE_IRQ;
> + up->port.fifosize = uart_config[up->port.type].fifo_size;
> + up->port.type = PORT_16550;
> + up->port.iotype = UPIO_MEM;
> + up->port.flags = UPF_FIXED_TYPE;
> + up->port.startup = ast25xx_uart_startup;
> + up->port.shutdown = ast25xx_uart_shutdown;
> + up->port.set_termios = ast25xx_uart_set_termios;
> + up->port.set_mctrl = ast25xx_uart_set_mctrl;
> + up->port.serial_in = ast25xx_uart_serial_in;
> + up->port.serial_out = ast25xx_uart_serial_out;
> + up->capabilities = uart_config[up->port.type].flags;
> + up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
> + up->capabilities |= UART_CAP_FIFO;
> +
> + up->port.private_data = priv;
> +
> + ret = serial8250_register_8250_port(up);
> + if (ret < 0) {
> + dev_err(&pdev->dev,
> + "unable to registr port (IO%lx MEM%llx IRQ%d):%d\n",
> + up->port.iobase, (unsigned long long)up->port.mapbase,
> + up->port.irq, ret);
> + return ret;
> + }
> + priv->line = ret;
> +
> + tasklet_init(&priv->tx_tasklet, ast_uart_tx_sdma_tasklet_func,
> + (unsigned long)priv);
> +#ifdef SDDMA_RX_FIX
> + tasklet_init(&priv->rx_tasklet, ast_uart_rx_sdma_tasklet_func,
> + (unsigned long)priv);
> +#else
> + uart->rx_timer.data = (unsigned long)port;
> + uart->rx_timer.expires = jiffies + (HZ);
> + uart->rx_timer.function = ast_uart_rx_timer_func;
> + init_timer(&priv->rx_timer);
> +#endif
> +
> +//DMA request
> +#ifdef SDDMA_RX_FIX
> + priv->rx_dma_buf.buf =
> + ast_uart_rx_sdma_request(priv->dma_ch, ast_uart_rx_buffdone,
> + priv);
> + if (priv->rx_dma_buf.buf < 0) {
> + pr_debug("Error : failed to get rx dma channel[%d]\n",
> + priv->dma_ch);
> + return -EBUSY;
> +}
> +#else
> + priv->rx_dma_buf.buf = ast_uart_rx_sdma_request(
> + priv->dma_ch, priv);
> + if (priv->rx_dma_buf.buf < 0) {
> + pr_debug("Error : failed to get rx dma channel[%d]\n",
> + priv->dma_ch);
> + return -EBUSY;
> + }
> +#endif
> + if (ast_uart_tx_sdma_request(
> + priv->dma_ch, ast_uart_tx_buffdone, priv) < 0) {
> + pr_debug("Error : failed to get tx dma channel[%d]\n",
> + priv->dma_ch);
> + return -EBUSY;
> + }
> +
> + platform_set_drvdata(pdev, priv);
> + return 0;
> +}
> +
> +/*
> + * Remove serial ports registered against a platform device.
> + */
> +static int ast25xx_uart_remove(struct platform_device *pdev)
> +{
> + struct ast_uart_priv_data *priv;
> +
> + priv = platform_get_drvdata(pdev);
> + serial8250_unregister_port(priv->line);
> + return 0;
> +}
> +
> +static int ast25xx_uart_suspend(struct platform_device *pdev,
> + pm_message_t state)
> +{
> + struct ast_uart_priv_data *priv;
> +
> + priv = platform_get_drvdata(pdev);
> + serial8250_suspend_port(priv->line);
> + return 0;
> +}
> +
> +static int ast25xx_uart_resume(struct platform_device *pdev)
> +{
> + struct ast_uart_priv_data *priv;
> +
> + priv = platform_get_drvdata(pdev);
> + serial8250_resume_port(priv->line);
> + return 0;
> +}
> +
> +static const struct of_device_id ast_serial_dt_ids[] = {
> + { .compatible = "aspeed,ast-sdma-uart", },
> + { /* sentinel */ }
> +};
> +
> +static struct platform_driver ast25xx_uart_driver = {
> + .probe = ast25xx_uart_probe,
> + .remove = ast25xx_uart_remove,
> + .suspend = ast25xx_uart_suspend,
> + .resume = ast25xx_uart_resume,
> + .driver = {
> + .name = "ast-uart-dma",
> + .of_match_table = of_match_ptr(ast_serial_dt_ids),
Indent properly.
This whole file looks like it can be made smaller, please remove the
unneeded and unused code for your next submission.
thanks,
greg k-h
^ permalink raw reply
* [patch v3 2/5] build configuration for AST2500 DMA UART driver
From: Greg KH @ 2019-07-03 17:50 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1561459476-14268-3-git-send-email-open.sudheer@gmail.com>
On Tue, Jun 25, 2019 at 04:14:33PM +0530, sudheer.v wrote:
> From: sudheer veliseti <sudheer.open@gmail.com>
>
> build config for DMA based UART driver in AST2500.
> Total Available UARTs in AST2500 are 4
>
> Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
> ---
>
> Changes in v3:
> - change logs added
>
> drivers/tty/serial/8250/Kconfig | 35 +++++++++++++++++++++++++++++++-
> drivers/tty/serial/8250/Makefile | 1 +
> 2 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
> index 15c2c5463835..c793466a1c47 100644
> --- a/drivers/tty/serial/8250/Kconfig
> +++ b/drivers/tty/serial/8250/Kconfig
> @@ -45,7 +45,7 @@ config SERIAL_8250_DEPRECATED_OPTIONS
> keep the 8250_core.* options around until they revert the changes
> they already did.
>
> - If 8250 is built as a module, this adds 8250_core alias instead.
> + If 8250 is built as a module, this adds 8250_core alias instead.
>
> If you did not notice yet and/or you have userspace from pre-3.7, it
> is safe (and recommended) to say N here.
Why did you change this line?
> @@ -189,6 +189,39 @@ config SERIAL_8250_RUNTIME_UARTS
> with the module parameter "nr_uarts", or boot-time parameter
> 8250.nr_uarts
>
> +config AST_SERIAL_DMA_UART
> + tristate "AST UART driver with DMA"
> + select SERIAL_CORE
> + help
> + UART driver with DMA support for Aspeed BMC AST25XX.
> + this driver supports UARTs in AST2500,AST2600. It uses
> + DMA channel of DMA engines present in these chips.
> + since this dma engine is used only by UARTs it is not
> + added as a separate DMA driver instead added as a layer
> + within UART driver.
> +
> +
> +config AST_NR_DMA_UARTS
> + int "Maximum number of uart dma serial ports"
> + depends on AST_SERIAL_DMA_UART
> + default "4"
> + help
> + Set this to the number of serial ports you want the driver
> + to support. This includes any ports discovered via ACPI or
> + PCI enumeration and any ports that may be added at run-time
> + via hot-plug, or any ISA multi-port serial cards.
> +
> +config AST_RUNTIME_DMA_UARTS
> + int "Number of uart dma serial ports to register at runtime"
> + depends on AST_SERIAL_DMA_UART
> + range 0 AST_NR_DMA_UARTS
> + default "4"
> + help
> + Set this to the maximum number of serial ports you want
> + the kernel to register at boot time. This can be overridden
> + with the module parameter "nr_uarts", or boot-time parameter
> + 8250.nr_uarts
That boot paramter is not correct, right?
Are you sure these all work like you think they work?
thanks,
greg k-h
^ permalink raw reply
* [patch v3 3/5] DT nodes for AST2500 DMA UART driver
From: Greg KH @ 2019-07-03 17:50 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1561459476-14268-4-git-send-email-open.sudheer@gmail.com>
On Tue, Jun 25, 2019 at 04:14:34PM +0530, sudheer.v wrote:
> From: sudheer veliseti <sudheer.open@gmail.com>
>
> DT node for DMA controller(ast_uart_sdma) doesn't bind to any DMA controller driver.
> This is because Software for DMA controller is not based on DMA framework,but is dedicated
> and serves only UARTs in AST2500. ast_uart_sdma node is searched by compatible string in the
> driver software.basic use of this node is to provide register base address of DMA controller and DMA irq number(<50>).
> IRQ of DMA controller is of crucial importance, which does RX and TX of UART data.
Properly line-wrap your changelog.
thanks,
greg k-h
^ permalink raw reply
* [patch v3 4/5] defconfig and MAINTAINERS updated for AST2500 DMA UART driver
From: Greg KH @ 2019-07-03 17:51 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1561459476-14268-5-git-send-email-open.sudheer@gmail.com>
On Tue, Jun 25, 2019 at 04:14:35PM +0530, sudheer.v wrote:
> From: sudheer veliseti <sudheer.open@gmail.com>
>
> defconfig changes to add DMA based UART in AST2500
> Maintainers File updated.
> Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
Blank line needed before signed-off-by.
> ---
>
> Changes in v3:
> - Added changes logs
>
> MAINTAINERS | 13 +++++++++++++
> arch/arm/configs/aspeed_g5_defconfig | 1 +
> 2 files changed, 14 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 997e27ab492f..c9a9790b97f6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1347,6 +1347,19 @@ F: drivers/crypto/axis
> F: drivers/pinctrl/pinctrl-artpec*
> F: Documentation/devicetree/bindings/pinctrl/axis,artpec6-pinctrl.txt
>
> +ARM/ASPEED DMA UART DRIVER
> +M: sudheer v <sudheer.open@gmail.com>
> +M: ShivahShankar <shivahshankar.shankarnarayanrao@aspeedtech.com>
> +R: Joel Stanley <joel@jms.id.au>
> +R: Andrew Jeffery <andrew@aj.id.au>
> +R: Vinod Koul <vkoul@kernel.org>
> +L: dmaengine at vger.kernel.org
> +L: openbmc at lists.ozlabs.org
> +L: linux-aspeed at lists.ozlabs.org
> +S: Maintained
> +F: drivers/tty/serial/8250/8250_aspeed_uart_dma.c
> +F: Documentation/devicetree/bindings/serial/ast-sdma-uart.txt
> +
> ARM/ASPEED I2C DRIVER
> M: Brendan Higgins <brendanhiggins@google.com>
> R: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig
> index 1849cbc161b4..25bf26630939 100644
> --- a/arch/arm/configs/aspeed_g5_defconfig
> +++ b/arch/arm/configs/aspeed_g5_defconfig
> @@ -144,6 +144,7 @@ CONFIG_SERIAL_8250=y
> CONFIG_SERIAL_8250_CONSOLE=y
> CONFIG_SERIAL_8250_NR_UARTS=6
> CONFIG_SERIAL_8250_RUNTIME_UARTS=6
> +CONFIG_AST_SERIAL_DMA_UART=y
This shows that the config option should be:
CONFIG_SERIAL_AST_DMA_UART
right?
thanks,
greg k-h
^ permalink raw reply
* [patch v3 1/5] AST2500 DMA UART driver
From: Joe Perches @ 2019-07-03 19:16 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190703174926.GA12813@kroah.com>
On Wed, 2019-07-03 at 19:49 +0200, Greg KH wrote:
> On Tue, Jun 25, 2019 at 04:14:32PM +0530, sudheer.v wrote:
> > +#define UART_TX_R_POINT(x) (0x40 + (x * 0x20))
> > +#define UART_TX_W_POINT(x) (0x44 + (x * 0x20))
> > +#define UART_TX_SDMA_ADDR(x) (0x48 + (x * 0x20))
> > +#define UART_RX_R_POINT(x) (0x50 + (x * 0x20))
> > +#define UART_RX_W_POINT(x) (0x54 + (x * 0x20))
> > +#define UART_RX_SDMA_ADDR(x) (0x58 + (x * 0x20))
>
> Please use a tab to line these up.
Also x should be surrounded by parentheses
#define UART_TX_R_POINT(x) (0x40 + ((x) * 0x20))
etc...
^ permalink raw reply
* [linux,dev-5.1 v1] dt-bindings: gpio: aspeed: Add SGPIO support
From: Hongwei Zhang @ 2019-07-03 20:01 UTC (permalink / raw)
To: linux-aspeed
Add bindings to support SGPIO on AST2400 or AST2500.
Signed-off-by: Hongwei Zhang <hongweiz@ami.com>
---
.../devicetree/bindings/gpio/sgpio-aspeed.txt | 36 ++++++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
diff --git a/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt b/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
new file mode 100644
index 0000000..f5fc6ef
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
@@ -0,0 +1,36 @@
+Aspeed SGPIO controller Device Tree Bindings
+-------------------------------------------
+
+Required properties:
+- compatible : Either "aspeed,ast2400-sgpio" or "aspeed,ast2500-sgpio"
+
+- #gpio-cells : Should be two
+ - First cell is the GPIO line number
+ - Second cell is used to specify optional
+ parameters (unused)
+
+- reg : Address and length of the register set for the device
+- gpio-controller : Marks the device node as a GPIO controller.
+- interrupts : Interrupt specifier (see interrupt bindings for
+ details)
+- interrupt-controller : Mark the GPIO controller as an interrupt-controller
+
+Optional properties:
+
+- clocks : A phandle to the clock to use for debounce timings
+
+The sgpio and interrupt properties are further described in their respective
+bindings documentation:
+
+- Documentation/devicetree/bindings/sgpio/gpio.txt
+- Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+
+ Example:
+ sgpio at 1e780200 {
+ #gpio-cells = <2>;
+ compatible = "aspeed,ast2500-sgpio";
+ gpio-controller;
+ interrupts = <40>;
+ reg = <0x1e780200 0x0100>;
+ interrupt-controller;
+ };
--
2.7.4
^ permalink raw reply related
* [PATCH 2/3 linux,dev-5.1 v1] ARM: dts: aspeed: Add SGPIO driver
From: Hongwei Zhang @ 2019-07-03 21:09 UTC (permalink / raw)
To: linux-aspeed
Add SGPIO driver support for Aspeed AST2500 SoC.
Signed-off-by: Hongwei Zhang <hongweiz@ami.com>
---
drivers/gpio/sgpio-aspeed.c | 470 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 470 insertions(+)
create mode 100644 drivers/gpio/sgpio-aspeed.c
diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c
new file mode 100644
index 0000000..108ed13
--- /dev/null
+++ b/drivers/gpio/sgpio-aspeed.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2019 American Megatrends International LLC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/gpio/aspeed.h>
+#include <linux/hashtable.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#define NR_SGPIO 80
+
+struct aspeed_sgpio {
+ struct gpio_chip chip;
+ spinlock_t lock;
+ void __iomem *base;
+ int irq;
+};
+
+struct aspeed_sgpio_bank {
+ uint16_t val_regs;
+ uint16_t rdata_reg;
+ uint16_t irq_regs;
+ const char names[4][3];
+};
+
+/*
+ * Note: The "value" register returns the input value sampled on the
+ * line even when the GPIO is configured as an output. Since
+ * that input goes through synchronizers, writing, then reading
+ * back may not return the written value right away.
+ *
+ * The "rdata" register returns the content of the write latch
+ * and thus can be used to read back what was last written
+ * reliably.
+ */
+
+static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = {
+ {
+ .val_regs = 0x0000,
+ .rdata_reg = 0x0070,
+ .irq_regs = 0x0004,
+ .names = { "A", "B", "C", "D" },
+ },
+ {
+ .val_regs = 0x001C,
+ .rdata_reg = 0x0074,
+ .irq_regs = 0x0020,
+ .names = { "E", "F", "G", "H" },
+ },
+ {
+ .val_regs = 0x0038,
+ .rdata_reg = 0x0078,
+ .irq_regs = 0x003C,
+ .names = { "I", "J" },
+ },
+};
+
+enum aspeed_sgpio_reg {
+ reg_val,
+ reg_rdata,
+ reg_irq_enable,
+ reg_irq_type0,
+ reg_irq_type1,
+ reg_irq_type2,
+ reg_irq_status,
+};
+
+#define GPIO_VAL_VALUE 0x00
+#define GPIO_VAL_DIR 0x04
+#define GPIO_IRQ_ENABLE 0x00
+#define GPIO_IRQ_TYPE0 0x04
+#define GPIO_IRQ_TYPE1 0x08
+#define GPIO_IRQ_TYPE2 0x0C
+#define GPIO_IRQ_STATUS 0x10
+
+/* This will be resolved at compile time */
+static inline void __iomem *bank_reg(struct aspeed_sgpio *gpio,
+ const struct aspeed_sgpio_bank *bank,
+ const enum aspeed_sgpio_reg reg)
+{
+ switch (reg) {
+ case reg_val:
+ return gpio->base + bank->val_regs + GPIO_VAL_VALUE;
+ case reg_rdata:
+ return gpio->base + bank->rdata_reg;
+ case reg_irq_enable:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE;
+ case reg_irq_type0:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0;
+ case reg_irq_type1:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1;
+ case reg_irq_type2:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2;
+ case reg_irq_status:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS;
+ }
+ BUG_ON(1);
+}
+
+#define GPIO_BANK(x) ((x) >> 5)
+#define GPIO_OFFSET(x) ((x) & 0x1f)
+#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
+
+static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
+{
+ unsigned int bank = GPIO_BANK(offset);
+
+ WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks));
+ return &aspeed_sgpio_banks[bank];
+}
+
+static inline bool have_gpio(struct aspeed_sgpio *gpio, unsigned int offset)
+{
+ const struct aspeed_sgpio_bank *bank = to_bank(offset);
+ unsigned int group = GPIO_OFFSET(offset) / 8;
+
+ return bank->names[group][0] != '\0';
+}
+
+static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+ const struct aspeed_sgpio_bank *bank = to_bank(offset);
+
+ return !!(ioread32(bank_reg(gpio, bank, reg_val)) & GPIO_BIT(offset));
+}
+
+static void __aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset,
+ int val)
+{
+ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+ const struct aspeed_sgpio_bank *bank = to_bank(offset);
+ void __iomem *addr;
+ u32 reg;
+
+ addr = bank_reg(gpio, bank, reg_val);
+
+ if (val)
+ reg |= GPIO_BIT(offset);
+ else
+ reg &= ~GPIO_BIT(offset);
+
+ iowrite32(reg, addr);
+}
+
+static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset,
+ int val)
+{
+ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio->lock, flags);
+
+ __aspeed_sgpio_set(gc, offset, val);
+
+ spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
+static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset)
+{
+ /* By default all SGPIO Pins are input */
+ return 0;
+}
+
+static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ /* By default all SGPIO Pins are input */
+ return 1;
+
+}
+
+static inline int irqd_to_aspeed_sgpio_data(struct irq_data *d,
+ struct aspeed_sgpio **gpio,
+ const struct aspeed_sgpio_bank **bank,
+ u32 *bit, int *offset)
+{
+ struct aspeed_sgpio *internal;
+
+ *offset = irqd_to_hwirq(d);
+
+ internal = irq_data_get_irq_chip_data(d);
+
+ *gpio = internal;
+ *bank = to_bank(*offset);
+ *bit = GPIO_BIT(*offset);
+
+ return 0;
+}
+
+static void aspeed_sgpio_irq_ack(struct irq_data *d)
+{
+ const struct aspeed_sgpio_bank *bank;
+ struct aspeed_sgpio *gpio;
+ unsigned long flags;
+ void __iomem *status_addr;
+ int rc, offset;
+ u32 bit;
+
+ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
+ if (rc)
+ return;
+
+ status_addr = bank_reg(gpio, bank, reg_irq_status);
+
+ spin_lock_irqsave(&gpio->lock, flags);
+
+ iowrite32(bit, status_addr);
+
+ spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
+static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set)
+{
+ const struct aspeed_sgpio_bank *bank;
+ struct aspeed_sgpio *gpio;
+ unsigned long flags;
+ u32 reg, bit;
+ void __iomem *addr;
+ int rc, offset;
+
+ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
+ if (rc)
+ return;
+
+ addr = bank_reg(gpio, bank, reg_irq_enable);
+
+ spin_lock_irqsave(&gpio->lock, flags);
+
+ reg = ioread32(addr);
+ if (set)
+ reg |= bit;
+ else
+ reg &= ~bit;
+
+ iowrite32(reg, addr);
+
+ spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
+static void aspeed_sgpio_irq_mask(struct irq_data *d)
+{
+ aspeed_sgpio_irq_set_mask(d, false);
+}
+
+static void aspeed_sgpio_irq_unmask(struct irq_data *d)
+{
+ aspeed_sgpio_irq_set_mask(d, true);
+}
+
+static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type)
+{
+ u32 type0 = 0;
+ u32 type1 = 0;
+ u32 type2 = 0;
+ u32 bit, reg;
+ const struct aspeed_sgpio_bank *bank;
+ irq_flow_handler_t handler;
+ struct aspeed_sgpio *gpio;
+ unsigned long flags;
+ void __iomem *addr;
+ int rc, offset;
+
+ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
+ if (rc)
+ return -EINVAL;
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_BOTH:
+ type2 |= bit;
+ /* fall through */
+ case IRQ_TYPE_EDGE_RISING:
+ type0 |= bit;
+ /* fall through */
+ case IRQ_TYPE_EDGE_FALLING:
+ handler = handle_edge_irq;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ type0 |= bit;
+ /* fall through */
+ case IRQ_TYPE_LEVEL_LOW:
+ type1 |= bit;
+ handler = handle_level_irq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&gpio->lock, flags);
+
+ addr = bank_reg(gpio, bank, reg_irq_type0);
+ reg = ioread32(addr);
+ reg = (reg & ~bit) | type0;
+ iowrite32(reg, addr);
+
+ addr = bank_reg(gpio, bank, reg_irq_type1);
+ reg = ioread32(addr);
+ reg = (reg & ~bit) | type1;
+ iowrite32(reg, addr);
+
+ addr = bank_reg(gpio, bank, reg_irq_type2);
+ reg = ioread32(addr);
+ reg = (reg & ~bit) | type2;
+ iowrite32(reg, addr);
+
+ spin_unlock_irqrestore(&gpio->lock, flags);
+
+ irq_set_handler_locked(d, handler);
+
+ return 0;
+}
+
+static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct irq_chip *ic = irq_desc_get_chip(desc);
+ struct aspeed_sgpio *data = gpiochip_get_data(gc);
+ unsigned int i, p, girq;
+ unsigned long reg;
+
+ chained_irq_enter(ic, desc);
+
+ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
+ const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i];
+
+ reg = ioread32(bank_reg(data, bank, reg_irq_status));
+
+ for_each_set_bit(p, ®, 32) {
+ girq = irq_find_mapping(gc->irq.domain, i * 32 + p);
+ generic_handle_irq(girq);
+ }
+
+ }
+
+ chained_irq_exit(ic, desc);
+}
+
+static struct irq_chip aspeed_sgpio_irqchip = {
+ .name = "aspeed-sgpio",
+ .irq_ack = aspeed_sgpio_irq_ack,
+ .irq_mask = aspeed_sgpio_irq_mask,
+ .irq_unmask = aspeed_sgpio_irq_unmask,
+ .irq_set_type = aspeed_sgpio_set_type,
+};
+
+static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
+ struct platform_device *pdev)
+{
+ int rc, i;
+ const struct aspeed_sgpio_bank *bank;
+
+ rc = platform_get_irq(pdev, 0);
+ if (rc < 0)
+ return rc;
+
+ gpio->irq = rc;
+
+ /* Disable IRQ and clear Interrupt status registers for all SPGIO Pins. */
+ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
+ bank = &aspeed_sgpio_banks[i];
+ /* disable irq enable bits */
+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable));
+ /* clear status bits */
+ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status));
+ }
+
+ rc = gpiochip_irqchip_add(&gpio->chip, &aspeed_sgpio_irqchip,
+ 0, handle_bad_irq, IRQ_TYPE_NONE);
+ if (rc) {
+ dev_info(&pdev->dev, "Could not add irqchip\n");
+ return rc;
+ }
+
+ gpiochip_set_chained_irqchip(&gpio->chip, &aspeed_sgpio_irqchip,
+ gpio->irq, aspeed_sgpio_irq_handler);
+
+ /* set IRQ settings and Enable Interrupt */
+ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
+ bank = &aspeed_sgpio_banks[i];
+ /* set falling or level-low irq */
+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0));
+ /* trigger type is edge */
+ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1));
+ /* dual edge trigger mode. */
+ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type2));
+ /* enable irq */
+ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_enable));
+ }
+
+ return 0;
+}
+
+static int aspeed_sgpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ if (!have_gpio(gpiochip_get_data(chip), offset))
+ return -ENODEV;
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_sgpio_of_table[] = {
+ { .compatible = "aspeed,ast2400-sgpio", .data = NULL, },
+ { .compatible = "aspeed,ast2500-sgpio", .data = NULL,},
+ {}
+};
+MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table);
+
+static int __init aspeed_sgpio_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *gpio_id;
+ struct aspeed_sgpio *gpio;
+ struct resource *res;
+ int rc, i;
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gpio->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpio->base))
+ return PTR_ERR(gpio->base);
+
+ spin_lock_init(&gpio->lock);
+
+ gpio_id = of_match_node(aspeed_sgpio_of_table, pdev->dev.of_node);
+ if (!gpio_id)
+ return -EINVAL;
+
+ gpio->chip.parent = &pdev->dev;
+ gpio->chip.ngpio = NR_SGPIO;
+ gpio->chip.direction_input = aspeed_sgpio_dir_in;
+ gpio->chip.direction_output = NULL;
+ gpio->chip.get_direction = aspeed_sgpio_get_direction;
+ gpio->chip.request = aspeed_sgpio_request;
+ gpio->chip.free = NULL;
+ gpio->chip.get = aspeed_sgpio_get;
+ gpio->chip.set = aspeed_sgpio_set;
+ gpio->chip.set_config = NULL;
+ gpio->chip.label = dev_name(&pdev->dev);
+ gpio->chip.base = -1;
+
+ rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
+ if (rc < 0)
+ return rc;
+
+ return aspeed_sgpio_setup_irqs(gpio, pdev);
+}
+
+static struct platform_driver aspeed_sgpio_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = aspeed_sgpio_of_table,
+ },
+};
+
+module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe);
+MODULE_DESCRIPTION("Aspeed Serial GPIO Driver");
+MODULE_LICENSE("GPL");
--
2.7.4
^ permalink raw reply related
* [patch v3 1/5] AST2500 DMA UART driver
From: Benjamin Herrenschmidt @ 2019-07-03 23:04 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190703174926.GA12813@kroah.com>
On Wed, 2019-07-03 at 19:49 +0200, Greg KH wrote:
> > +
> > + if (tx_sts & UART_SDMA0_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA0_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[0]));
> > + } else if (tx_sts & UART_SDMA1_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA1_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[1]));
> > + } else if (tx_sts & UART_SDMA2_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA2_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[2]));
> > + } else if (tx_sts & UART_SDMA3_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA3_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[3]));
> > + } else if (tx_sts & UART_SDMA4_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA4_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[4]));
> > + } else if (tx_sts & UART_SDMA5_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA5_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[5]));
> > + } else if (tx_sts & UART_SDMA6_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA6_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[6]));
> > + } else if (tx_sts & UART_SDMA7_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA7_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[7]));
> > + } else if (tx_sts & UART_SDMA8_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA8_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[8]));
> > + } else if (tx_sts & UART_SDMA9_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA9_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[9]));
> > + } else if (tx_sts & UART_SDMA10_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA10_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[10]));
> > + } else if (tx_sts & UART_SDMA11_INT) {
> > + ast_uart_sdma_write(sdma, UART_SDMA11_INT, UART_TX_SDMA_ISR);
> > + ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[11]));
> > + } else {
> > + }
Also this should be a for () loop...
Cheers,
Ben.
^ permalink raw reply
* [PATCH 2/3 linux,dev-5.1 v1] ARM: dts: aspeed: Add SGPIO driver
From: Andrew Jeffery @ 2019-07-04 0:06 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562188172-23178-1-git-send-email-hongweiz@ami.com>
Hello Hongwei,
As this is patch is sent to the upstream lists (linux-gpio@ etc) please
drop the OpenBMC-specific "linux,dev-5.1" from the subject.
Also, it looks like you may have manually added the series revision (v1).
For the record you can make `git format-patch` do this for you with the
`-v`option (e.g. if you really want it here, `-v 1`).
On Thu, 4 Jul 2019, at 07:09, Hongwei Zhang wrote:
> Add SGPIO driver support for Aspeed AST2500 SoC.
>
> Signed-off-by: Hongwei Zhang <hongweiz@ami.com>
> ---
> drivers/gpio/sgpio-aspeed.c | 470 ++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 470 insertions(+)
> create mode 100644 drivers/gpio/sgpio-aspeed.c
>
> diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c
> new file mode 100644
> index 0000000..108ed13
> --- /dev/null
> +++ b/drivers/gpio/sgpio-aspeed.c
> @@ -0,0 +1,470 @@
> +/*
> + * Copyright 2019 American Megatrends International LLC.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
You should use the SPDX license identifier here rather than the GPL
blurb, and it should be the first line of the file. Keep your copyright
line in place though:
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright 2019 American Megatrends International LLC.
> + */
> +
> +#include <linux/gpio/driver.h>
> +#include <linux/gpio/aspeed.h>
> +#include <linux/hashtable.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +
> +#define NR_SGPIO 80
> +
> +struct aspeed_sgpio {
> + struct gpio_chip chip;
> + spinlock_t lock;
> + void __iomem *base;
> + int irq;
> +};
> +
> +struct aspeed_sgpio_bank {
> + uint16_t val_regs;
> + uint16_t rdata_reg;
> + uint16_t irq_regs;
> + const char names[4][3];
> +};
> +
> +/*
> + * Note: The "value" register returns the input value sampled on the
> + * line even when the GPIO is configured as an output. Since
> + * that input goes through synchronizers, writing, then reading
> + * back may not return the written value right away.
> + *
> + * The "rdata" register returns the content of the write latch
> + * and thus can be used to read back what was last written
> + * reliably.
> + */
> +
> +static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = {
> + {
> + .val_regs = 0x0000,
> + .rdata_reg = 0x0070,
> + .irq_regs = 0x0004,
> + .names = { "A", "B", "C", "D" },
> + },
> + {
> + .val_regs = 0x001C,
> + .rdata_reg = 0x0074,
> + .irq_regs = 0x0020,
> + .names = { "E", "F", "G", "H" },
> + },
> + {
> + .val_regs = 0x0038,
> + .rdata_reg = 0x0078,
> + .irq_regs = 0x003C,
> + .names = { "I", "J" },
> + },
> +};
> +
> +enum aspeed_sgpio_reg {
> + reg_val,
> + reg_rdata,
> + reg_irq_enable,
> + reg_irq_type0,
> + reg_irq_type1,
> + reg_irq_type2,
> + reg_irq_status,
> +};
> +
> +#define GPIO_VAL_VALUE 0x00
> +#define GPIO_VAL_DIR 0x04
> +#define GPIO_IRQ_ENABLE 0x00
> +#define GPIO_IRQ_TYPE0 0x04
> +#define GPIO_IRQ_TYPE1 0x08
> +#define GPIO_IRQ_TYPE2 0x0C
> +#define GPIO_IRQ_STATUS 0x10
> +
> +/* This will be resolved at compile time */
> +static inline void __iomem *bank_reg(struct aspeed_sgpio *gpio,
> + const struct aspeed_sgpio_bank *bank,
> + const enum aspeed_sgpio_reg reg)
> +{
> + switch (reg) {
> + case reg_val:
> + return gpio->base + bank->val_regs + GPIO_VAL_VALUE;
> + case reg_rdata:
> + return gpio->base + bank->rdata_reg;
> + case reg_irq_enable:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE;
> + case reg_irq_type0:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0;
> + case reg_irq_type1:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1;
> + case reg_irq_type2:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2;
> + case reg_irq_status:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS;
> + }
> + BUG_ON(1);
This isn't appropriate - we shouldn't take down the kernel on a
faulty peripheral access. Please change this to WARN().
> +}
> +
> +#define GPIO_BANK(x) ((x) >> 5)
> +#define GPIO_OFFSET(x) ((x) & 0x1f)
> +#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
> +
> +static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
> +{
> + unsigned int bank = GPIO_BANK(offset);
> +
> + WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks));
> + return &aspeed_sgpio_banks[bank];
> +}
> +
> +static inline bool have_gpio(struct aspeed_sgpio *gpio, unsigned int
> offset)
> +{
> + const struct aspeed_sgpio_bank *bank = to_bank(offset);
> + unsigned int group = GPIO_OFFSET(offset) / 8;
> +
> + return bank->names[group][0] != '\0';
Lets just drop have_gpio() altogether, it's a contiguous set of 80 GPIOs.
At best this should just be:
static inline bool have_gpio(struct aspeed_sgpio *gpio, unsigned int offset)
{
return offset < NR_SGPIO;
}
But lets just assume that we've properly configured the gpio subsystem
for the controller and remove it completely.
> +}
> +
> +static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
> +{
> + struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
> + const struct aspeed_sgpio_bank *bank = to_bank(offset);
> +
> + return !!(ioread32(bank_reg(gpio, bank, reg_val)) & GPIO_BIT(offset));
> +}
> +
> +static void __aspeed_sgpio_set(struct gpio_chip *gc, unsigned int
> offset,
> + int val)
No need to split this out from aspeed_sgpio_set() below. Separating
the implementation was necessary in the parallel GPIO driver for reasons
that aren't relevant here.
> +{
> + struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
> + const struct aspeed_sgpio_bank *bank = to_bank(offset);
> + void __iomem *addr;
> + u32 reg;
> +
> + addr = bank_reg(gpio, bank, reg_val);
> +
> + if (val)
> + reg |= GPIO_BIT(offset);
> + else
> + reg &= ~GPIO_BIT(offset);
> +
> + iowrite32(reg, addr);
> +}
> +
> +static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset,
> + int val)
> +{
> + struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&gpio->lock, flags);
> +
> + __aspeed_sgpio_set(gc, offset, val);
> +
> + spin_unlock_irqrestore(&gpio->lock, flags);
> +}
> +
> +static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int
> offset)
> +{
> + /* By default all SGPIO Pins are input */
Right, but with your implementation below you can never mark them as
output.
> + return 0;
> +}
> +
> +static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned
> int offset)
> +{
> + /* By default all SGPIO Pins are input */
> + return 1;
As above. Given my understanding of SGPIO, I think you should be
implementing both dir_in() and dir_out(), and capturing which state
userspace "wants" the GPIO to be in, and directing reads/writes to the
DATA_READ/DATA_VALUE registers as appropriate. There's no state
we need to modify in the hardware, but that doesn't mean we shouldn't
capture the intent of userspace at all.
> +
> +}
> +
> +static inline int irqd_to_aspeed_sgpio_data(struct irq_data *d,
> + struct aspeed_sgpio **gpio,
> + const struct aspeed_sgpio_bank **bank,
> + u32 *bit, int *offset)
> +{
> + struct aspeed_sgpio *internal;
> +
> + *offset = irqd_to_hwirq(d);
> +
> + internal = irq_data_get_irq_chip_data(d);
> +
> + *gpio = internal;
> + *bank = to_bank(*offset);
> + *bit = GPIO_BIT(*offset);
> +
> + return 0;
> +}
> +
> +static void aspeed_sgpio_irq_ack(struct irq_data *d)
> +{
> + const struct aspeed_sgpio_bank *bank;
> + struct aspeed_sgpio *gpio;
> + unsigned long flags;
> + void __iomem *status_addr;
> + int rc, offset;
> + u32 bit;
> +
> + rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
> + if (rc)
> + return;
> +
> + status_addr = bank_reg(gpio, bank, reg_irq_status);
> +
> + spin_lock_irqsave(&gpio->lock, flags);
> +
> + iowrite32(bit, status_addr);
> +
> + spin_unlock_irqrestore(&gpio->lock, flags);
> +}
> +
> +static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set)
> +{
> + const struct aspeed_sgpio_bank *bank;
> + struct aspeed_sgpio *gpio;
> + unsigned long flags;
> + u32 reg, bit;
> + void __iomem *addr;
> + int rc, offset;
> +
> + rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
> + if (rc)
> + return;
> +
> + addr = bank_reg(gpio, bank, reg_irq_enable);
> +
> + spin_lock_irqsave(&gpio->lock, flags);
> +
> + reg = ioread32(addr);
> + if (set)
> + reg |= bit;
> + else
> + reg &= ~bit;
> +
> + iowrite32(reg, addr);
> +
> + spin_unlock_irqrestore(&gpio->lock, flags);
> +}
> +
> +static void aspeed_sgpio_irq_mask(struct irq_data *d)
> +{
> + aspeed_sgpio_irq_set_mask(d, false);
> +}
> +
> +static void aspeed_sgpio_irq_unmask(struct irq_data *d)
> +{
> + aspeed_sgpio_irq_set_mask(d, true);
> +}
> +
> +static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type)
> +{
> + u32 type0 = 0;
> + u32 type1 = 0;
> + u32 type2 = 0;
> + u32 bit, reg;
> + const struct aspeed_sgpio_bank *bank;
> + irq_flow_handler_t handler;
> + struct aspeed_sgpio *gpio;
> + unsigned long flags;
> + void __iomem *addr;
> + int rc, offset;
> +
> + rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
> + if (rc)
> + return -EINVAL;
> +
> + switch (type & IRQ_TYPE_SENSE_MASK) {
> + case IRQ_TYPE_EDGE_BOTH:
> + type2 |= bit;
> + /* fall through */
> + case IRQ_TYPE_EDGE_RISING:
> + type0 |= bit;
> + /* fall through */
> + case IRQ_TYPE_EDGE_FALLING:
> + handler = handle_edge_irq;
> + break;
> + case IRQ_TYPE_LEVEL_HIGH:
> + type0 |= bit;
> + /* fall through */
> + case IRQ_TYPE_LEVEL_LOW:
> + type1 |= bit;
> + handler = handle_level_irq;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + spin_lock_irqsave(&gpio->lock, flags);
> +
> + addr = bank_reg(gpio, bank, reg_irq_type0);
> + reg = ioread32(addr);
> + reg = (reg & ~bit) | type0;
> + iowrite32(reg, addr);
> +
> + addr = bank_reg(gpio, bank, reg_irq_type1);
> + reg = ioread32(addr);
> + reg = (reg & ~bit) | type1;
> + iowrite32(reg, addr);
> +
> + addr = bank_reg(gpio, bank, reg_irq_type2);
> + reg = ioread32(addr);
> + reg = (reg & ~bit) | type2;
> + iowrite32(reg, addr);
> +
> + spin_unlock_irqrestore(&gpio->lock, flags);
> +
> + irq_set_handler_locked(d, handler);
> +
> + return 0;
> +}
> +
> +static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
> +{
> + struct gpio_chip *gc = irq_desc_get_handler_data(desc);
> + struct irq_chip *ic = irq_desc_get_chip(desc);
> + struct aspeed_sgpio *data = gpiochip_get_data(gc);
> + unsigned int i, p, girq;
> + unsigned long reg;
> +
> + chained_irq_enter(ic, desc);
> +
> + for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
> + const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i];
> +
> + reg = ioread32(bank_reg(data, bank, reg_irq_status));
> +
> + for_each_set_bit(p, ®, 32) {
> + girq = irq_find_mapping(gc->irq.domain, i * 32 + p);
> + generic_handle_irq(girq);
> + }
> +
> + }
> +
> + chained_irq_exit(ic, desc);
> +}
> +
> +static struct irq_chip aspeed_sgpio_irqchip = {
> + .name = "aspeed-sgpio",
> + .irq_ack = aspeed_sgpio_irq_ack,
> + .irq_mask = aspeed_sgpio_irq_mask,
> + .irq_unmask = aspeed_sgpio_irq_unmask,
> + .irq_set_type = aspeed_sgpio_set_type,
> +};
> +
> +static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
> + struct platform_device *pdev)
> +{
> + int rc, i;
> + const struct aspeed_sgpio_bank *bank;
> +
> + rc = platform_get_irq(pdev, 0);
> + if (rc < 0)
> + return rc;
> +
> + gpio->irq = rc;
> +
> + /* Disable IRQ and clear Interrupt status registers for all SPGIO
> Pins. */
> + for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
> + bank = &aspeed_sgpio_banks[i];
> + /* disable irq enable bits */
> + iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable));
> + /* clear status bits */
> + iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status));
> + }
> +
> + rc = gpiochip_irqchip_add(&gpio->chip, &aspeed_sgpio_irqchip,
> + 0, handle_bad_irq, IRQ_TYPE_NONE);
> + if (rc) {
> + dev_info(&pdev->dev, "Could not add irqchip\n");
> + return rc;
> + }
> +
> + gpiochip_set_chained_irqchip(&gpio->chip, &aspeed_sgpio_irqchip,
> + gpio->irq, aspeed_sgpio_irq_handler);
> +
> + /* set IRQ settings and Enable Interrupt */
> + for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
> + bank = &aspeed_sgpio_banks[i];
> + /* set falling or level-low irq */
> + iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0));
> + /* trigger type is edge */
> + iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1));
> + /* dual edge trigger mode. */
> + iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type2));
> + /* enable irq */
> + iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_enable));
> + }
> +
> + return 0;
> +}
> +
> +static int aspeed_sgpio_request(struct gpio_chip *chip, unsigned int
> offset)
> +{
> + if (!have_gpio(gpiochip_get_data(chip), offset))
> + return -ENODEV;
> +
> + return 0;
> +}
> +
> +static const struct of_device_id aspeed_sgpio_of_table[] = {
> + { .compatible = "aspeed,ast2400-sgpio", .data = NULL, },
> + { .compatible = "aspeed,ast2500-sgpio", .data = NULL,},
You can drop the assignment to data.
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table);
> +
> +static int __init aspeed_sgpio_probe(struct platform_device *pdev)
> +{
> + const struct of_device_id *gpio_id;
> + struct aspeed_sgpio *gpio;
> + struct resource *res;
> + int rc, i;
> +
> + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
> + if (!gpio)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + gpio->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(gpio->base))
> + return PTR_ERR(gpio->base);
> +
> + spin_lock_init(&gpio->lock);
> +
> + gpio_id = of_match_node(aspeed_sgpio_of_table, pdev->dev.of_node);
> + if (!gpio_id)
> + return -EINVAL;
gpio_id isn't used, so you can drop the of_match_node() above.
> +
> + gpio->chip.parent = &pdev->dev;
> + gpio->chip.ngpio = NR_SGPIO;
> + gpio->chip.direction_input = aspeed_sgpio_dir_in;
> + gpio->chip.direction_output = NULL;
We can do outputs too - we shouldn't be omitting the direction_output
callback, see the discussion above about dir_in()/dir_out()/get_direction()
Andrew
> + gpio->chip.get_direction = aspeed_sgpio_get_direction;
> + gpio->chip.request = aspeed_sgpio_request;
> + gpio->chip.free = NULL;
> + gpio->chip.get = aspeed_sgpio_get;
> + gpio->chip.set = aspeed_sgpio_set;
> + gpio->chip.set_config = NULL;
> + gpio->chip.label = dev_name(&pdev->dev);
> + gpio->chip.base = -1;
> +
> + rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
> + if (rc < 0)
> + return rc;
> +
> + return aspeed_sgpio_setup_irqs(gpio, pdev);
> +}
> +
> +static struct platform_driver aspeed_sgpio_driver = {
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = aspeed_sgpio_of_table,
> + },
> +};
> +
> +module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe);
> +MODULE_DESCRIPTION("Aspeed Serial GPIO Driver");
> +MODULE_LICENSE("GPL");
> --
> 2.7.4
>
>
^ permalink raw reply
* [PATCH v2 0/8] pinctrl: aspeed: Preparation for AST2600
From: Andrew Jeffery @ 2019-07-04 0:28 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <CACRpkdaxiFR3ezt4FzhRxpqc4DYYjsbBeysPUaaQH+_QgYjudw@mail.gmail.com>
On Wed, 3 Jul 2019, at 18:40, Linus Walleij wrote:
> Hi Andrew,
>
> On Fri, Jun 28, 2019 at 4:39 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> >
> > Hello!
> >
> > The ASPEED AST2600 is in the pipeline, and we have enough information to start
> > preparing to upstream support for it. This series lays some ground work;
> > splitting the bindings and dicing the implementation up a little further to
> > facilitate differences between the 2600 and previous SoC generations.
> >
> > v2 addresses Rob's comments on the bindings conversion patches. v1 can be found
> > here:
>
> I have applied this series, I had to strip some changes of the header
> because it was based on some SPDX cleanups upstream but no
> big deal I think. Check the result please.
Thanks. Have you pushed the branch yet? I just fetched your pinctrl tree
and can't see the patches.
Andrew
^ permalink raw reply
* [linux,dev-5.1 v1] dt-bindings: gpio: aspeed: Add SGPIO support
From: Rob Herring @ 2019-07-09 18:53 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562184069-22332-1-git-send-email-hongweiz@ami.com>
On Wed, Jul 3, 2019 at 2:01 PM Hongwei Zhang <hongweiz@ami.com> wrote:
>
> Add bindings to support SGPIO on AST2400 or AST2500.
>
> Signed-off-by: Hongwei Zhang <hongweiz@ami.com>
> ---
> .../devicetree/bindings/gpio/sgpio-aspeed.txt | 36 ++++++++++++++++++++++
Is this SGPIO as in the blinky lights for HDDs in servers? If so, that
has nothing to do with Linux GPIO subsystem.
BTW, You might want to look at Calxeda highbank SATA driver. It has a
bit-banged SGPIO interface using GPIO lines in it.
Rob
^ permalink raw reply
* [linux,dev-5.1 v1] dt-bindings: gpio: aspeed: Add SGPIO support
From: Andrew Jeffery @ 2019-07-10 2:00 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <CAL_Jsq+0z4OZ7qbaiUva1v5xCKXpsaPBZ9tj_M4HbEihsU_MfA@mail.gmail.com>
On Wed, 10 Jul 2019, at 04:23, Rob Herring wrote:
> On Wed, Jul 3, 2019 at 2:01 PM Hongwei Zhang <hongweiz@ami.com> wrote:
> >
> > Add bindings to support SGPIO on AST2400 or AST2500.
> >
> > Signed-off-by: Hongwei Zhang <hongweiz@ami.com>
> > ---
> > .../devicetree/bindings/gpio/sgpio-aspeed.txt | 36 ++++++++++++++++++++++
>
> Is this SGPIO as in the blinky lights for HDDs in servers? If so, that
> has nothing to do with Linux GPIO subsystem.
>
No, this is just literal serialised GPIO, which can be used with e.g. nexperia
74LV595 / 74LV165 parts.
There is a separate chunk of IP in the SoC that acts as an SFF-8485 (blinky
lights) slave monitor.
Andrew
^ permalink raw reply
* [linux,dev-5.1 v1] dt-bindings: gpio: aspeed: Add SGPIO support
From: Andrew Jeffery @ 2019-07-10 2:16 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1562184069-22332-1-git-send-email-hongweiz@ami.com>
On Thu, 4 Jul 2019, at 05:31, Hongwei Zhang wrote:
> Add bindings to support SGPIO on AST2400 or AST2500.
>
> Signed-off-by: Hongwei Zhang <hongweiz@ami.com>
> ---
> .../devicetree/bindings/gpio/sgpio-aspeed.txt | 36 ++++++++++++++++++++++
> 1 file changed, 36 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
>
> diff --git a/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
> b/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
> new file mode 100644
> index 0000000..f5fc6ef
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
> @@ -0,0 +1,36 @@
> +Aspeed SGPIO controller Device Tree Bindings
> +-------------------------------------------
> +
> +Required properties:
> +- compatible : Either "aspeed,ast2400-sgpio" or "aspeed,ast2500-sgpio"
> +
> +- #gpio-cells : Should be two
> + - First cell is the GPIO line number
> + - Second cell is used to specify optional
> + parameters (unused)
> +
> +- reg : Address and length of the register set for the device
> +- gpio-controller : Marks the device node as a GPIO controller.
> +- interrupts : Interrupt specifier (see interrupt bindings for
> + details)
> +- interrupt-controller : Mark the GPIO controller as an
> interrupt-controller
As this is a serial GPIO controller, a critical piece of configuration
information is how many GPIOs we wish to serialise. This is done
in multiples of 8, up to 80 pins.
The bindings need to describe the "ngpios" property from the
generic GPIO bindings and how this affects the behaviour of
the controller.
We also need to add the "bus-frequency" property here to control
the rate of SGPMCK.
> +
> +Optional properties:
> +
> +- clocks : A phandle to the clock to use for debounce
> timings
We need this, but not for the reason specified, and it should be a
required property. We need PCLK (the APB clock) to derive the SGPIO
bus frequency. Despite what the datasheet blurb says, there's no
debounce control for the SGPIO master (this is a copy/paste mistake
from the description of the parallel GPIO master).
> +
> +The sgpio and interrupt properties are further described in their
> respective
> +bindings documentation:
> +
> +- Documentation/devicetree/bindings/sgpio/gpio.txt
> +- Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> +
> + Example:
> + sgpio at 1e780200 {
> + #gpio-cells = <2>;
> + compatible = "aspeed,ast2500-sgpio";
> + gpio-controller;
> + interrupts = <40>;
> + reg = <0x1e780200 0x0100>;
> + interrupt-controller;
> + };
You'll need to fix up the example after making the changes mentioned
above.
Andrew
> --
> 2.7.4
>
>
^ permalink raw reply
* [PATCH 00/12] treewide: Fix GENMASK misuses
From: Joe Perches @ 2019-07-10 5:04 UTC (permalink / raw)
To: linux-aspeed
These GENMASK uses are inverted argument order and the
actual masks produced are incorrect. Fix them.
Add checkpatch tests to help avoid more misuses too.
Joe Perches (12):
checkpatch: Add GENMASK tests
clocksource/drivers/npcm: Fix misuse of GENMASK macro
drm: aspeed_gfx: Fix misuse of GENMASK macro
iio: adc: max9611: Fix misuse of GENMASK macro
irqchip/gic-v3-its: Fix misuse of GENMASK macro
mmc: meson-mx-sdio: Fix misuse of GENMASK macro
net: ethernet: mediatek: Fix misuses of GENMASK macro
net: stmmac: Fix misuses of GENMASK macro
rtw88: Fix misuse of GENMASK macro
phy: amlogic: G12A: Fix misuse of GENMASK macro
staging: media: cedrus: Fix misuse of GENMASK macro
ASoC: wcd9335: Fix misuse of GENMASK macro
drivers/clocksource/timer-npcm7xx.c | 2 +-
drivers/gpu/drm/aspeed/aspeed_gfx.h | 2 +-
drivers/iio/adc/max9611.c | 2 +-
drivers/irqchip/irq-gic-v3-its.c | 2 +-
drivers/mmc/host/meson-mx-sdio.c | 2 +-
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +-
drivers/net/ethernet/mediatek/mtk_sgmii.c | 2 +-
drivers/net/ethernet/stmicro/stmmac/descs.h | 2 +-
drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 4 ++--
drivers/net/wireless/realtek/rtw88/rtw8822b.c | 2 +-
drivers/phy/amlogic/phy-meson-g12a-usb2.c | 2 +-
drivers/staging/media/sunxi/cedrus/cedrus_regs.h | 2 +-
scripts/checkpatch.pl | 15 +++++++++++++++
sound/soc/codecs/wcd-clsh-v2.c | 2 +-
14 files changed, 29 insertions(+), 14 deletions(-)
--
2.15.0
^ permalink raw reply
* [PATCH 03/12] drm: aspeed_gfx: Fix misuse of GENMASK macro
From: Joe Perches @ 2019-07-10 5:04 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <cover.1562734889.git.joe@perches.com>
Arguments are supposed to be ordered high then low.
Signed-off-by: Joe Perches <joe@perches.com>
---
drivers/gpu/drm/aspeed/aspeed_gfx.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h
index a10358bb61ec..095ea03e5833 100644
--- a/drivers/gpu/drm/aspeed/aspeed_gfx.h
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h
@@ -74,7 +74,7 @@ int aspeed_gfx_create_output(struct drm_device *drm);
/* CTRL2 */
#define CRT_CTRL_DAC_EN BIT(0)
#define CRT_CTRL_VBLANK_LINE(x) (((x) << 20) & CRT_CTRL_VBLANK_LINE_MASK)
-#define CRT_CTRL_VBLANK_LINE_MASK GENMASK(20, 31)
+#define CRT_CTRL_VBLANK_LINE_MASK GENMASK(31, 20)
/* CRT_HORIZ0 */
#define CRT_H_TOTAL(x) (x)
--
2.15.0
^ permalink raw reply related
* [PATCH 00/12] treewide: Fix GENMASK misuses
From: Johannes Berg @ 2019-07-10 9:17 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <cover.1562734889.git.joe@perches.com>
On Tue, 2019-07-09 at 22:04 -0700, Joe Perches wrote:
> These GENMASK uses are inverted argument order and the
> actual masks produced are incorrect. Fix them.
>
> Add checkpatch tests to help avoid more misuses too.
>
> Joe Perches (12):
> checkpatch: Add GENMASK tests
IMHO this doesn't make a lot of sense as a checkpatch test - just throw
in a BUILD_BUG_ON()?
johannes
^ permalink raw reply
* [PATCH 00/12] treewide: Fix GENMASK misuses
From: Russell King - ARM Linux admin @ 2019-07-10 9:43 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <5fa1fa6998332642c49e2d5209193ffe2713f333.camel@sipsolutions.net>
On Wed, Jul 10, 2019 at 11:17:31AM +0200, Johannes Berg wrote:
> On Tue, 2019-07-09 at 22:04 -0700, Joe Perches wrote:
> > These GENMASK uses are inverted argument order and the
> > actual masks produced are incorrect. Fix them.
> >
> > Add checkpatch tests to help avoid more misuses too.
> >
> > Joe Perches (12):
> > checkpatch: Add GENMASK tests
>
> IMHO this doesn't make a lot of sense as a checkpatch test - just throw
> in a BUILD_BUG_ON()?
My personal take on this is that GENMASK() is really not useful, it's
just pure obfuscation and leads to exactly these kinds of mistakes.
Yes, I fully understand the argument that you can just specify the
start and end bits, and it _in theory_ makes the code more readable.
However, the problem is when writing code. GENMASK(a, b). Is a the
starting bit or ending bit? Is b the number of bits? It's confusing
and causes mistakes resulting in incorrect code. A BUILD_BUG_ON()
can catch some of the cases, but not all of them.
For example:
GENMASK(6, 2)
would satisify the requirement that a > b, so a BUILD_BUG_ON() will
not trigger, but was the author meaning 0x3c or 0xc0?
Personally, I've decided I am _not_ going to use GENMASK() in my code
because I struggle to get the macro arguments correct - I'm _much_
happier, and it is way more reliable for me to write the mask in hex
notation.
I think this is where use of a ternary operator would come in use. The
normal way of writing a number of bits tends to be "a:b", so if GENMASK
took something like GENMASK(6:2), then I'd have less issue with it,
because it's argument is then in a familiar notation.
Yes, I'm sure that someone will point out that the GENMASK arguments
are just in the same order, but that doesn't prevent _me_ frequently
getting it wrong - and that's the point. The macro seems to me to
cause more problems than it solves.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox