diff --git a/arch/arm64/boot/dts/broadcom/rp1.dtsi b/arch/arm64/boot/dts/broadcom/rp1.dtsi index 6a29960db57665..8847c32e1019f9 100644 --- a/arch/arm64/boot/dts/broadcom/rp1.dtsi +++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi @@ -1057,6 +1057,8 @@ reg = <0xc0 0x40178000 0x0 0x20>; compatible = "raspberrypi,rp1-pio"; firmware = <&rp1_firmware>; + interrupts = , + ; dmas = <&rp1_dma RP1_DMA_PIO_CH0_TX>, <&rp1_dma RP1_DMA_PIO_CH0_RX>, <&rp1_dma RP1_DMA_PIO_CH1_TX>, <&rp1_dma RP1_DMA_PIO_CH1_RX>, <&rp1_dma RP1_DMA_PIO_CH2_TX>, <&rp1_dma RP1_DMA_PIO_CH2_RX>, diff --git a/drivers/misc/rp1-fw-pio.h b/drivers/misc/rp1-fw-pio.h index ba28cba38f84c4..0a674709de6cf4 100644 --- a/drivers/misc/rp1-fw-pio.h +++ b/drivers/misc/rp1-fw-pio.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2023 2023-2024 Raspberry Pi Ltd. + * Copyright (C) 2023-2026 Raspberry Pi Ltd. */ #ifndef __SOC_RP1_FIRMWARE_OPS_H__ @@ -49,8 +49,24 @@ enum rp1_pio_ops { PIO_SM_FIFO_STATE, // u16 sm, u8 tx -> u16 level, u8 empty, u8 full PIO_SM_DRAIN_TX, // u16 sm + PIO_SM_GET_FLAGS, // u16 sm, u8 clear, u8 rsvd, u32 flags, u32 wait -> u32 level + PIO_SM_GET_DMACTRL, // u16 sm, u16 is_tx -> u32 ctrl + + PIO_SET_IRQN_SOURCE_MASK_ENABLED, // u16 irq_index, u8 enabled, u8 rsvd, + // uint32_t source_mask + PIO_INTERRUPT_GET, // u16 pio_interrupt_num -> u8 active + PIO_INTERRUPT_CLEAR, // u16 pio_interrupt_num PIO_COUNT }; +// Don't include this here to avoid multiple definitions +// +// enum pio_sm_flags { +// PIO_SM_FLAG_TXSTALL = 0x0001, +// PIO_SM_FLAG_RXSTALL = 0x0002, +// PIO_SM_FLAG_TXOVER = 0x0004, +// PIO_SM_FLAG_RXUNDER = 0x0008, +// }; + #endif diff --git a/drivers/misc/rp1-pio.c b/drivers/misc/rp1-pio.c index e9a437bcc0b2fa..4dc67a3daed211 100644 --- a/drivers/misc/rp1-pio.c +++ b/drivers/misc/rp1-pio.c @@ -2,7 +2,7 @@ /* * PIO driver for RP1 * - * Copyright (C) 2023-2024 Raspberry Pi Ltd. + * Copyright (C) 2023-2026 Raspberry Pi Ltd. * * Parts of this driver are based on: * - vcio.c, by Noralf Trønnes @@ -20,9 +20,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -67,6 +69,9 @@ struct dma_xfer_state { struct dma_info *dma; void (*callback)(void *param); void *callback_param; + struct dma_buf_info *dbi; + void *data; + size_t data_bytes; }; struct dma_buf_info { @@ -85,6 +90,16 @@ struct dma_info { struct dma_buf_info bufs[DMA_BOUNCE_BUFFER_COUNT]; }; +struct rp1_pio_client; + +struct irq_info { + int irq; + bool enabled; + struct rp1_pio_client *owner; + pio_irq_handler_t handler; + void *context; +}; + struct rp1_pio_device { struct platform_device *pdev; struct rp1_firmware *fw; @@ -94,6 +109,8 @@ struct rp1_pio_device { struct class *dev_class; struct cdev cdev; phys_addr_t phys_addr; + uint irq_count; + struct irq_info irqs[RP1_PIO_IRQ_COUNT]; uint32_t claimed_sms; uint32_t claimed_dmas; spinlock_t lock; @@ -107,10 +124,14 @@ struct rp1_pio_device { struct rp1_pio_client { struct rp1_pio_device *pio; + spinlock_t lock; + struct completion completion; + uint32_t wake_reason; uint32_t claimed_sms; uint32_t claimed_instrs; uint32_t claimed_dmas; int error; + int irqs[RP1_PIO_IRQ_COUNT]; }; static struct rp1_pio_device *g_pio; @@ -225,7 +246,7 @@ int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) offset = rp1_pio_find_program(pio, args); mutex_unlock(&pio->instr_mutex); if (offset >= 0) - return offset; + return 1; /* Don't send the instructions, just the header */ return rp1_pio_message(pio, PIO_CAN_ADD_PROGRAM, args, @@ -487,6 +508,30 @@ int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) } EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl); +int rp1_pio_sm_get_dmactrl(struct rp1_pio_client *client, void *param) +{ + struct rp1_pio_device *pio = client->pio; + struct rp1_pio_sm_get_dmactrl_args *args = param; + int ret; + + if (pio->fw_pio_count >= PIO_SM_GET_DMACTRL) { + struct rp1_access_hw_args hwargs; + + hwargs.addr = 0xf00000cc + args->sm * 0x20 + (args->is_tx ? 0x18 : 0x1c); + hwargs.len = 0x4; + hwargs.data = &args->ctrl; + ret = rp1_pio_message_resp(pio, READ_HW, &hwargs, 8, + &args->ctrl, NULL, 4); + } else { + ret = rp1_pio_message_resp(pio, PIO_SM_GET_DMACTRL, args, sizeof(*args), + &args->ctrl, NULL, 4); + } + if (ret >= 0) + return offsetof(struct rp1_pio_sm_set_dmactrl_args, ctrl) + ret; + return ret; +} +EXPORT_SYMBOL_GPL(rp1_pio_sm_get_dmactrl); + int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param) { struct rp1_pio_sm_fifo_state_args *args = param; @@ -509,6 +554,20 @@ int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param) } EXPORT_SYMBOL_GPL(rp1_pio_sm_drain_tx); +int rp1_pio_sm_get_flags(struct rp1_pio_client *client, void *param) +{ + struct rp1_pio_sm_get_flags_args *args = param; + int ret; + + ret = rp1_pio_message_resp(client->pio, PIO_SM_GET_FLAGS, args, sizeof(*args), + &args->flags, NULL, sizeof(args->flags)); + if (ret >= 0) + return offsetof(struct rp1_pio_sm_get_flags_args, flags) + ret; + args->flags = ~0; + return ret; +} +EXPORT_SYMBOL_GPL(rp1_pio_sm_get_flags); + int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) { struct rp1_gpio_init_args *args = param; @@ -573,6 +632,238 @@ int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) } EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_drive_strength); +static void rp1_pio_irq_handler_userspace(void *context) +{ + struct irq_info *irqi = context; + struct rp1_pio_client *client = irqi->owner; + struct rp1_pio_device *pio = client->pio; + uint32_t intmask = 1 << (irqi - pio->irqs); + + spin_lock(&client->lock); + client->wake_reason |= intmask; + spin_unlock(&client->lock); + + complete(&client->completion); +} + +int rp1_pio_irq_claim(struct rp1_pio_client *client, void *param) +{ + struct rp1_pio_irq_claim_args *args = param; + struct rp1_pio_device *pio = client->pio; + struct irq_info *irqi; + int ret = -1; + int i; + + // Try to claim one of the PIO interrupts + + args->irq_index = -EBUSY; + + spin_lock(&pio->lock); + for (i = 0; i < pio->irq_count; i++) { + irqi = &pio->irqs[i]; + if (!irqi->owner) { + irqi->owner = client; + irqi->handler = rp1_pio_irq_handler_userspace; + irqi->context = irqi; + args->irq_index = i; + ret = sizeof(*args); + break; + } + } + spin_unlock(&pio->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(rp1_pio_irq_claim); + +int rp1_pio_irq_wait(struct rp1_pio_client *client, void *param) +{ + struct rp1_pio_irq_wait_args *args = param; + unsigned long timeout; + long rem; + + if (args->timeout_ms) + timeout = msecs_to_jiffies(args->timeout_ms); + else + timeout = MAX_SCHEDULE_TIMEOUT; + rem = wait_for_completion_interruptible_timeout(&client->completion, timeout); + if (rem < 0) + return (int)rem; + + spin_lock(&client->lock); + args->active_mask = rem ? client->wake_reason : 0; + client->wake_reason = 0; + spin_unlock(&client->lock); + return sizeof(*args); +} +EXPORT_SYMBOL_GPL(rp1_pio_irq_wait); + +int rp1_pio_set_irqn_source_mask_enabled(struct rp1_pio_client *client, void *param) +{ + struct rp1_pio_set_irqn_source_mask_enabled_args *args = param; + + return rp1_pio_message(client->pio, PIO_SET_IRQN_SOURCE_MASK_ENABLED, + args, sizeof(*args)); +} +EXPORT_SYMBOL_GPL(rp1_pio_set_irqn_source_mask_enabled); + +int rp1_pio_interrupt_get(struct rp1_pio_client *client, void *param) +{ + struct rp1_pio_interrupt_get_args *args = param; + int ret; + + ret = rp1_pio_message_resp(client->pio, PIO_INTERRUPT_GET, args, sizeof(*args), + &args->active, NULL, sizeof(args->active)); + if (ret >= 0) + return offsetof(struct rp1_pio_interrupt_get_args, active) + ret; + return ret; +} +EXPORT_SYMBOL_GPL(rp1_pio_interrupt_get); + +int rp1_pio_interrupt_clear(struct rp1_pio_client *client, void *param) +{ + struct rp1_pio_interrupt_clear_args *args = param; + + return rp1_pio_message(client->pio, PIO_INTERRUPT_CLEAR, args, sizeof(*args)); +} +EXPORT_SYMBOL_GPL(rp1_pio_interrupt_clear); + +int rp1_pio_irq_set_enabled(struct rp1_pio_client *client, void *param) +{ + struct rp1_pio_irq_set_enabled_args *args = param; + struct rp1_pio_device *pio = client->pio; + uint irq_index = args->irq_index; + struct irq_info *irqi; + + if (irq_index >= ARRAY_SIZE(pio->irqs)) + return -EINVAL; + if (irq_index >= pio->irq_count) + return -EINVAL; + irqi = &pio->irqs[irq_index]; + if (irqi->owner != client) + return -EBUSY; + + spin_lock(&pio->lock); + if (args->enabled && !irqi->enabled) { + enable_irq(irqi->irq); + irqi->enabled = true; + } else if (!args->enabled && irqi->enabled) { + disable_irq(irqi->irq); + irqi->enabled = false; + } + spin_unlock(&pio->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(rp1_pio_irq_set_enabled); + +int rp1_pio_irq_is_enabled(struct rp1_pio_client *client, void *param) +{ + struct rp1_pio_irq_set_enabled_args *args = param; + struct rp1_pio_device *pio = client->pio; + uint irq_index = args->irq_index; + struct irq_info *irqi; + + if (irq_index >= ARRAY_SIZE(pio->irqs)) + return -EINVAL; + if (irq_index >= pio->irq_count) + return -EINVAL; + irqi = &pio->irqs[irq_index]; + if (irqi->owner != client) + return -EBUSY; + + args->enabled = irqi->enabled; + + return 0; +} +EXPORT_SYMBOL_GPL(rp1_pio_irq_is_enabled); + +void rp1_pio_irq_add_handler(struct rp1_pio_client *client, uint num, + pio_irq_handler_t handler, void *context) +{ + struct rp1_pio_device *pio = client->pio; + struct irq_info *irqi; + + if (num >= pio->irq_count) { + dev_err(&pio->pdev->dev, "%s: bad irq index %u\n", __func__, num); + return; + } + + irqi = &pio->irqs[num]; + + spin_lock(&pio->lock); + if (irqi->owner && irqi->owner != client) { + spin_unlock(&pio->lock); + dev_err(&pio->pdev->dev, "%s: irq %u already claimed\n", __func__, num); + return; + } + + irqi->owner = client; + irqi->handler = handler; + irqi->context = context; + spin_unlock(&pio->lock); +} +EXPORT_SYMBOL_GPL(rp1_pio_irq_add_handler); + +void rp1_pio_irq_remove_handler(struct rp1_pio_client *client, + uint num, pio_irq_handler_t handler) +{ + struct rp1_pio_device *pio = client->pio; + struct irq_info *irqi; + + if (num >= pio->irq_count) + return; + + irqi = &pio->irqs[num]; + + spin_lock(&pio->lock); + if (irqi->owner == client && irqi->handler == handler) { + irqi->handler = NULL; + irqi->context = NULL; + irqi->owner = NULL; + } + spin_unlock(&pio->lock); +} +EXPORT_SYMBOL_GPL(rp1_pio_irq_remove_handler); + +pio_irq_handler_t rp1_pio_irq_get_handler(struct rp1_pio_client *client, uint num) +{ + struct rp1_pio_device *pio = client->pio; + struct irq_info *irqi; + pio_irq_handler_t handler; + + if (num >= pio->irq_count) + return false; + + irqi = &pio->irqs[num]; + + spin_lock(&pio->lock); + handler = irqi->handler; + spin_unlock(&pio->lock); + + return handler; +} +EXPORT_SYMBOL_GPL(rp1_pio_irq_get_handler); + +uint rp1_pio_irq_map(struct rp1_pio_client *client, uint irqn) +{ + int pirq; + + spin_lock(&client->lock); + pirq = client->irqs[irqn]; + if (pirq < 0) { + struct rp1_pio_irq_claim_args args = { .irq_index = -1 }; + + rp1_pio_irq_claim(client, &args); + pirq = args.irq_index; + if (pirq >= 0) + client->irqs[irqn] = pirq; + } + spin_unlock(&client->lock); + return (uint)pirq; +} +EXPORT_SYMBOL_GPL(rp1_pio_irq_map); + static void rp1_pio_sm_dma_callback(void *param) { struct dma_info *dma = param; @@ -584,6 +875,9 @@ static void rp1_pio_sm_kernel_dma_callback(void *param) { struct dma_xfer_state *dxs = param; + if (dxs->dbi) + memcpy(dxs->data, dxs->dbi->buf, dxs->data_bytes); + dxs->dma->tail_idx++; up(&dxs->dma->buf_sem); @@ -611,8 +905,8 @@ static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint s struct rp1_pio_sm_set_dmactrl_args set_dmactrl_args; struct rp1_pio_device *pio = client->pio; struct platform_device *pdev = pio->pdev; + struct dma_slave_config config = { 0 }; struct device *dev = &pdev->dev; - struct dma_slave_config config = {}; struct dma_slave_caps dma_caps; phys_addr_t fifo_addr; struct dma_info *dma = NULL; @@ -944,19 +1238,25 @@ int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, uint sm, uint dir, if (!dma_addr) { dxs = kmalloc(sizeof(*dxs), GFP_KERNEL); + if (!dxs) + return -ENOMEM; dxs->dma = dma; dxs->callback = callback; dxs->callback_param = param; + dxs->dbi = NULL; callback = rp1_pio_sm_kernel_dma_callback; param = dxs; - if (!dma->buf_count || data_bytes > dma->buf_size) + if (!dma->buf_count || data_bytes > dma->buf_size) { + kfree(dxs); return -EINVAL; + } /* Grab a dma buffer */ if (dma->head_idx - dma->tail_idx == dma->buf_count) { if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { dev_err(dev, "DMA wait timed out\n"); + kfree(dxs); return -ETIMEDOUT; } } @@ -964,8 +1264,13 @@ int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, uint sm, uint dir, dbi = &dma->bufs[dma->head_idx % dma->buf_count]; dma_addr = dbi->dma_addr; - if (dir == PIO_DIR_TO_SM) + if (dir == PIO_DIR_TO_SM) { memcpy(dbi->buf, data, data_bytes); + } else { + dxs->dbi = dbi; + dxs->data = data; + dxs->data_bytes = data_bytes; + } } sg_init_table(&sg, 1); @@ -1032,6 +1337,8 @@ struct handler_info { HANDLER(SM_SET_DMACTRL, sm_set_dmactrl), HANDLER(SM_FIFO_STATE, sm_fifo_state), HANDLER(SM_DRAIN_TX, sm_drain_tx), + HANDLER(SM_GET_FLAGS, sm_get_flags), + HANDLER(SM_GET_DMACTRL, sm_get_dmactrl), HANDLER(GPIO_INIT, gpio_init), HANDLER(GPIO_SET_FUNCTION, gpio_set_function), @@ -1044,11 +1351,20 @@ struct handler_info { HANDLER(READ_HW, read_hw), HANDLER(WRITE_HW, write_hw), + + HANDLER(IRQ_CLAIM, irq_claim), + HANDLER(IRQ_WAIT, irq_wait), + HANDLER(IRQ_SET_ENABLED, irq_set_enabled), + HANDLER(IRQ_IS_ENABLED, irq_is_enabled), + HANDLER(SET_IRQN_SOURCE_MASK_ENABLED, set_irqn_source_mask_enabled), + HANDLER(INTERRUPT_GET, interrupt_get), + HANDLER(INTERRUPT_CLEAR, interrupt_clear), }; struct rp1_pio_client *rp1_pio_open(void) { struct rp1_pio_client *client; + int i; if (!g_pio) return ERR_PTR(-EPROBE_DEFER); @@ -1059,7 +1375,12 @@ struct rp1_pio_client *rp1_pio_open(void) if (!client) return ERR_PTR(-ENOMEM); + for (i = 0; i < RP1_PIO_IRQ_COUNT; i++) + client->irqs[i] = -1; + client->pio = g_pio; + spin_lock_init(&client->lock); + init_completion(&client->completion); return client; } @@ -1068,18 +1389,39 @@ EXPORT_SYMBOL_GPL(rp1_pio_open); void rp1_pio_close(struct rp1_pio_client *client) { struct rp1_pio_device *pio = client->pio; - uint claimed_dmas = client->claimed_dmas; + uint32_t claimed; + struct irq_info *irqi; int i; + /* Wake any waiter */ + spin_lock(&client->lock); + client->wake_reason = 0; + spin_unlock(&client->lock); + complete(&client->completion); + /* Free any allocated resources */ - for (i = 0; claimed_dmas; i++) { + for (i = 0; i < pio->irq_count; i++) { + irqi = &pio->irqs[i]; + if (irqi->owner == client) { + if (irqi->enabled) + irq_set_enabled(client, i, false); + + irqi->owner = NULL; + irqi->handler = NULL; + irqi->context = NULL; + } + } + + claimed = client->claimed_dmas; + + for (i = 0; claimed; i++) { uint mask = (1 << i); - if (claimed_dmas & mask) { + if (claimed & mask) { struct dma_info *dma = &pio->dma_configs[i >> 1][i & 1]; - claimed_dmas &= ~mask; + claimed &= ~mask; rp1_pio_sm_dma_free(&pio->pdev->dev, dma); } } @@ -1271,6 +1613,30 @@ static long rp1_pio_compat_ioctl(struct file *filp, unsigned int ioctl_num, #define rp1_pio_compat_ioctl NULL #endif +static irqreturn_t rp1_pio_irq_handler(int irq, void *dev_id) +{ + struct rp1_pio_device *pio = dev_id; + struct irq_info *irqi = NULL; + int i; + + for (i = 0; i < pio->irq_count; i++) { + if (irq == pio->irqs[i].irq) { + irqi = &pio->irqs[i]; + break; + } + } + + if (!irqi || !irqi->handler) { + pr_err("disabling unwanted interrupt\n"); + disable_irq(irq); + return IRQ_NONE; + } + + irqi->handler(irqi->context); + + return IRQ_HANDLED; +} + const struct file_operations rp1_pio_fops = { .owner = THIS_MODULE, .open = rp1_pio_file_open, @@ -1290,6 +1656,7 @@ static int rp1_pio_probe(struct platform_device *pdev) struct device *cdev; char dev_name[16]; void *p; + int irq; int ret; int i; @@ -1301,7 +1668,7 @@ static int rp1_pio_probe(struct platform_device *pdev) return -EINVAL; } - pdev->id = of_alias_get_id(pdev->dev.of_node, "pio"); + pdev->id = of_alias_get_id(dev->of_node, "pio"); if (pdev->id < 0) return dev_err_probe(dev, pdev->id, "alias is missing\n"); @@ -1317,7 +1684,7 @@ static int rp1_pio_probe(struct platform_device *pdev) if (ret < 0) goto out_err; - pio = devm_kzalloc(&pdev->dev, sizeof(*pio), GFP_KERNEL); + pio = devm_kzalloc(dev, sizeof(*pio), GFP_KERNEL); if (!pio) { ret = -ENOMEM; goto out_err; @@ -1337,6 +1704,25 @@ static int rp1_pio_probe(struct platform_device *pdev) goto out_err; } + if (pio->fw_pio_count > PIO_INTERRUPT_CLEAR) { + for (i = 0; i < ARRAY_SIZE(pio->irqs); i++) { + irq = of_irq_get(dev->of_node, i); + if (irq < 0) + break; + ret = devm_request_irq(dev, irq, rp1_pio_irq_handler, + 0, pdev->name, pio); + if (ret < 0) { + dev_err(dev, "failed to request an irq (rc=%d)\n", ret); + goto out_err; + } + + disable_irq(irq); + pio->irqs[i].irq = irq; + } + + pio->irq_count = i; + } + pio->phys_addr = ioresource->start; ret = alloc_chrdev_region(&pio->dev_num, 0, 1, DRIVER_NAME); @@ -1369,7 +1755,8 @@ static int rp1_pio_probe(struct platform_device *pdev) g_pio = pio; - dev_info(dev, "Created instance as %s\n", dev_name); + dev_info(dev, "Created instance as %s (op count %d, %d interrupts)\n", + dev_name, pio->fw_pio_count, pio->irq_count); return 0; out_cdev_del: @@ -1413,7 +1800,7 @@ static struct platform_driver rp1_pio_driver = { .of_match_table = of_match_ptr(rp1_pio_ids), }, .probe = rp1_pio_probe, - .remove = rp1_pio_remove, + .remove = rp1_pio_remove, .shutdown = rp1_pio_remove, }; diff --git a/include/linux/pio_rp1.h b/include/linux/pio_rp1.h index 68083b83a20132..825c15eedaf8b5 100644 --- a/include/linux/pio_rp1.h +++ b/include/linux/pio_rp1.h @@ -46,6 +46,7 @@ #define GPIOS_MASK ((1 << RP1_PIO_GPIO_COUNT) - 1) #define PICO_NO_HARDWARE 0 +#define PICO_PIO_VERSION 0 #define pio0 pio_open_helper(0) @@ -102,6 +103,18 @@ #define PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS 0x0000001f #define PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB 0 +#define irq_add_shared_handler(num, handler, ...) \ + irq_add_handler(pio, num, handler, NULL) + +#define irq_has_shared_handler(num, handler, ...) \ + (irq_get_handler(pio, num, handler, NULL) != NULL) + +#define irq_set_exclusive_handler(num, handler) \ + irq_add_handler(pio, num, handler, NULL) + +#define irq_get_exclusive_handler(num) \ + ((void *)irq_get_handler(pio, num)) + enum pio_fifo_join { PIO_FIFO_JOIN_NONE = 0, PIO_FIFO_JOIN_TX = 1, @@ -172,14 +185,43 @@ enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_12MA = 3 }; +enum pio_sm_flags { + PIO_SM_FLAG_TXSTALL = 0x0001, + PIO_SM_FLAG_RXSTALL = 0x0002, + PIO_SM_FLAG_TXOVER = 0x0004, + PIO_SM_FLAG_RXUNDER = 0x0008, +}; + + +enum pio_interrupt_source { + PIS_SM0_RX_FIFO_NOT_EMPTY, + PIS_SM1_RX_FIFO_NOT_EMPTY, + PIS_SM2_RX_FIFO_NOT_EMPTY, + PIS_SM3_RX_FIFO_NOT_EMPTY, + PIS_SM0_TX_FIFO_NOT_FULL, + PIS_SM1_TX_FIFO_NOT_FULL, + PIS_SM2_TX_FIFO_NOT_FULL, + PIS_SM3_TX_FIFO_NOT_FULL, + PIS_INTERRUPT0, + PIS_INTERRUPT1, + PIS_INTERRUPT2, + PIS_INTERRUPT3, + + PIS_MAX +}; + struct fp24_8 { uint32_t val; }; typedef rp1_pio_sm_config pio_sm_config; +typedef void (*pio_irq_handler_t)(void *context); + typedef struct rp1_pio_client *PIO; +static struct rp1_pio_client *g_client; + int rp1_pio_init(void); PIO rp1_pio_open(void); void rp1_pio_close(struct rp1_pio_client *client); @@ -213,8 +255,10 @@ int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param); int rp1_pio_sm_put(struct rp1_pio_client *client, void *param); int rp1_pio_sm_get(struct rp1_pio_client *client, void *param); int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param); +int rp1_pio_sm_get_dmactrl(struct rp1_pio_client *client, void *param); int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param); int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param); +int rp1_pio_sm_get_flags(struct rp1_pio_client *client, void *param); int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param); int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param); int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param); @@ -224,6 +268,22 @@ int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param); int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param); int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param); +int rp1_pio_irq_claim(struct rp1_pio_client *client, void *param); +int rp1_pio_irq_wait(struct rp1_pio_client *client, void *param); +int rp1_pio_set_irqn_source_mask_enabled(struct rp1_pio_client *client, void *param); +int rp1_pio_interrupt_get(struct rp1_pio_client *client, void *param); +int rp1_pio_interrupt_clear(struct rp1_pio_client *client, void *param); +int rp1_pio_irq_set_enabled(struct rp1_pio_client *client, void *param); +int rp1_pio_irq_is_enabled(struct rp1_pio_client *client, void *param); + +void rp1_pio_irq_add_handler(struct rp1_pio_client *client, uint num, + pio_irq_handler_t handler, void *context); +void rp1_pio_irq_remove_handler(struct rp1_pio_client *client, + uint num, pio_irq_handler_t handler); +pio_irq_handler_t rp1_pio_irq_get_handler(struct rp1_pio_client *client, uint num); +uint rp1_pio_irq_map(struct rp1_pio_client *client, uint irqn); + + static inline int pio_init(void) { return rp1_pio_init(); @@ -231,11 +291,14 @@ static inline int pio_init(void) static inline struct rp1_pio_client *pio_open(void) { - return rp1_pio_open(); + g_client = rp1_pio_open(); + return g_client; } static inline void pio_close(struct rp1_pio_client *client) { + if (g_client == client) + g_client = NULL; rp1_pio_close(client); } @@ -300,7 +363,7 @@ static inline bool pio_can_add_program_at_offset(struct rp1_pio_client *client, args.num_instrs = program->length; memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); - return !rp1_pio_can_add_program(client, &args); + return rp1_pio_can_add_program(client, &args); } static inline uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) @@ -517,7 +580,7 @@ static inline int pio_sm_set_pindirs_with_mask(struct rp1_pio_client *client, ui if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || (pin_dirs & GPIOS_MASK) != pin_dirs || - (pin_mask & pin_mask) != pin_mask)) + (pin_mask & GPIOS_MASK) != pin_mask)) return -EINVAL; return rp1_pio_sm_set_pindirs(client, &args); } @@ -611,6 +674,15 @@ static inline int pio_sm_set_dmactrl(struct rp1_pio_client *client, uint sm, boo return rp1_pio_sm_set_dmactrl(client, &args); }; +static inline uint32_t pio_sm_get_dmactrl(struct rp1_pio_client *client, uint sm, bool is_tx) +{ + struct rp1_pio_sm_get_dmactrl_args args = { .sm = sm, .is_tx = is_tx }; + + if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) + rp1_pio_sm_get_dmactrl(client, &args); + return args.ctrl; +}; + static inline int pio_sm_drain_tx_fifo(struct rp1_pio_client *client, uint sm) { struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; @@ -620,6 +692,17 @@ static inline int pio_sm_drain_tx_fifo(struct rp1_pio_client *client, uint sm) return rp1_pio_sm_drain_tx(client, &args); }; +static inline uint32_t pio_sm_get_flags(struct rp1_pio_client *client, uint sm, uint32_t flags, + bool clear, uint32_t timeout) +{ + struct rp1_pio_sm_get_flags_args args = { .sm = sm, .flags = flags, .clear = clear, + .timeout = timeout }; + + if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) + rp1_pio_sm_get_flags(client, &args); + return args.flags; +}; + static inline int pio_sm_put(struct rp1_pio_client *client, uint sm, uint32_t data) { struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = false, .data = data }; @@ -1017,4 +1100,119 @@ static inline int pio_gpio_disable_pulls(struct rp1_pio_client *client, uint gpi return pio_gpio_set_pulls(client, gpio, false, false); } +static inline int pio_get_irq_num(struct rp1_pio_client *client, uint irqn) +{ + return rp1_pio_irq_map(client, irqn); +} + +static inline void pio_set_irqn_source_mask_enabled(struct rp1_pio_client *client, uint irq_index, + uint32_t source_mask, bool enabled) +{ + struct rp1_pio_set_irqn_source_mask_enabled_args args = { + .irq_index = rp1_pio_irq_map(client, irq_index), + .source_mask = source_mask, .enabled = enabled, + }; + + rp1_pio_set_irqn_source_mask_enabled(client, &args); +} + +static inline void pio_set_irq0_source_enabled(struct rp1_pio_client *client, + enum pio_interrupt_source source, + bool enabled) +{ + pio_set_irqn_source_mask_enabled(client, 0, 1 << source, enabled); +} + +static inline void pio_set_irq1_source_enabled(struct rp1_pio_client *client, + enum pio_interrupt_source source, + bool enabled) +{ + pio_set_irqn_source_mask_enabled(client, 1, 1 << source, enabled); +} + +static inline void pio_set_irq0_source_mask_enabled(struct rp1_pio_client *client, + uint32_t source_mask, + bool enabled) +{ + pio_set_irqn_source_mask_enabled(client, 0, source_mask, enabled); +} + +static inline void pio_set_irq1_source_mask_enabled(struct rp1_pio_client *client, + uint32_t source_mask, + bool enabled) +{ + pio_set_irqn_source_mask_enabled(client, 1, source_mask, enabled); +} + +static inline void pio_set_irqn_source_enabled(struct rp1_pio_client *client, uint irq_index, + enum pio_interrupt_source source, bool enabled) +{ + pio_set_irqn_source_mask_enabled(client, irq_index, 1 << source, enabled); +} + +static inline uint pio_interrupt_rel(uint sm, uint pio_interrupt_num) +{ + return (pio_interrupt_num & 0x1c) | ((pio_interrupt_num + sm) & 0x3); +} + +static inline void irq_add_handler(struct rp1_pio_client *client, uint num, + pio_irq_handler_t handler, + void *context) +{ + return rp1_pio_irq_add_handler(client, num, handler, context); +} + +static inline pio_irq_handler_t irq_get_handler(struct rp1_pio_client *client, uint num) +{ + return rp1_pio_irq_get_handler(client, num); +} + +static inline bool irq_has_handler(struct rp1_pio_client *client, uint num) +{ + return rp1_pio_irq_get_handler(client, num) != NULL; +} + +static inline void irq_set_enabled(struct rp1_pio_client *client, uint num, bool enabled) +{ + struct rp1_pio_irq_set_enabled_args args = { .irq_index = num, .enabled = enabled }; + + if (bad_params_if(client, num >= RP1_PIO_IRQ_COUNT)) + return; + + rp1_pio_irq_set_enabled(client, &args); +}; + +static inline bool irq_is_enabled(struct rp1_pio_client *client, uint num) +{ + struct rp1_pio_irq_set_enabled_args args = { .irq_index = num }; + + if (bad_params_if(client, num >= RP1_PIO_IRQ_COUNT)) + return false; + + (void)rp1_pio_irq_is_enabled(client, &args); + + return !!args.enabled; +} + +static inline bool pio_interrupt_get(struct rp1_pio_client *client, uint pio_interrupt_num) +{ + struct rp1_pio_interrupt_get_args args = { .pio_interrupt_num = pio_interrupt_num }; + + if (bad_params_if(client, pio_interrupt_num >= PIS_MAX)) + return false; + + (void)rp1_pio_interrupt_get(client, &args); + + return !!args.active; +} + +static inline void pio_interrupt_clear(struct rp1_pio_client *client, uint pio_interrupt_num) +{ + struct rp1_pio_interrupt_get_args args = { .pio_interrupt_num = pio_interrupt_num }; + + if (bad_params_if(client, pio_interrupt_num >= PIS_MAX)) + return; + (void)rp1_pio_interrupt_clear(client, &args); +} + #endif diff --git a/include/uapi/misc/rp1_pio_if.h b/include/uapi/misc/rp1_pio_if.h index a9a54d3322ec64..8827b60eeb34a3 100644 --- a/include/uapi/misc/rp1_pio_if.h +++ b/include/uapi/misc/rp1_pio_if.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 + WITH Linux-syscall-note */ /* - * Copyright (c) 2023-24 Raspberry Pi Ltd. + * Copyright (c) 2023-26 Raspberry Pi Ltd. * All rights reserved. */ #ifndef _PIO_RP1_IF_H @@ -8,16 +8,17 @@ #include -#define RP1_PIO_INSTRUCTION_COUNT 32 -#define RP1_PIO_SM_COUNT 4 -#define RP1_PIO_GPIO_COUNT 28 -#define RP1_GPIO_FUNC_PIO 7 +#define RP1_PIO_INSTRUCTION_COUNT 32 +#define RP1_PIO_SM_COUNT 4 +#define RP1_PIO_GPIO_COUNT 28 +#define RP1_GPIO_FUNC_PIO 7 +#define RP1_PIO_IRQ_COUNT 2 -#define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0)) +#define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0)) -#define RP1_PIO_DIR_TO_SM 0 -#define RP1_PIO_DIR_FROM_SM 1 -#define RP1_PIO_DIR_COUNT 2 +#define RP1_PIO_DIR_TO_SM 0 +#define RP1_PIO_DIR_FROM_SM 1 +#define RP1_PIO_DIR_COUNT 2 typedef struct { uint32_t clkdiv; @@ -124,6 +125,13 @@ struct rp1_pio_sm_set_dmactrl_args { uint32_t ctrl; }; +struct rp1_pio_sm_get_dmactrl_args { + uint16_t sm; + uint8_t is_tx; + uint8_t rsvd; + uint32_t ctrl; /* OUT */ +}; + struct rp1_pio_sm_fifo_state_args { uint16_t sm; uint8_t tx; @@ -133,6 +141,14 @@ struct rp1_pio_sm_fifo_state_args { uint8_t full; /* OUT */ }; +struct rp1_pio_sm_get_flags_args { + uint16_t sm; + uint8_t clear; + uint8_t rsvd; + uint32_t flags; /* IN/OUT */ + uint32_t timeout; +}; + struct rp1_gpio_init_args { uint16_t gpio; }; @@ -171,6 +187,7 @@ struct rp1_pio_sm_xfer_data_args { uint16_t sm; uint16_t dir; uint16_t data_bytes; + uint16_t rsvd; void *data; }; @@ -187,6 +204,38 @@ struct rp1_access_hw_args { void *data; }; +struct rp1_pio_irq_claim_args { + int irq_index; /* OUT */ +}; + +struct rp1_pio_irq_wait_args { + uint32_t timeout_ms; + uint32_t active_mask; /* OUT */ +}; + +struct rp1_pio_irq_set_enabled_args { + uint16_t irq_index; + uint8_t enabled; + uint8_t rsvd; +}; + +struct rp1_pio_set_irqn_source_mask_enabled_args { + uint16_t irq_index; + uint8_t enabled; + uint8_t rsvd; + uint32_t source_mask; +}; + +struct rp1_pio_interrupt_get_args { + uint16_t pio_interrupt_num; + uint16_t rsvd; + uint8_t active; /* OUT */ +}; + +struct rp1_pio_interrupt_clear_args { + uint16_t pio_interrupt_num; +}; + #define PIO_IOC_MAGIC 102 #define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) @@ -220,8 +269,10 @@ struct rp1_access_hw_args { #define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) #define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) #define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) -#define PIO_IOC_SM_FIFO_STATE _IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) +#define PIO_IOC_SM_FIFO_STATE _IOWR(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) #define PIO_IOC_SM_DRAIN_TX _IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args) +#define PIO_IOC_SM_GET_FLAGS _IOWR(PIO_IOC_MAGIC, 46, struct rp1_pio_sm_get_flags_args) +#define PIO_IOC_SM_GET_DMACTRL _IOWR(PIO_IOC_MAGIC, 47, struct rp1_pio_sm_get_dmactrl_args) #define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) #define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) @@ -232,4 +283,13 @@ struct rp1_access_hw_args { #define PIO_IOC_GPIO_SET_INPUT_ENABLED _IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args) #define PIO_IOC_GPIO_SET_DRIVE_STRENGTH _IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args) +#define PIO_IOC_IRQ_CLAIM _IOWR(PIO_IOC_MAGIC, 60, struct rp1_pio_irq_claim_args) +#define PIO_IOC_IRQ_WAIT _IOWR(PIO_IOC_MAGIC, 61, struct rp1_pio_irq_wait_args) +#define PIO_IOC_IRQ_SET_ENABLED _IOW(PIO_IOC_MAGIC, 62, struct rp1_pio_irq_set_enabled_args) +#define PIO_IOC_IRQ_IS_ENABLED _IOWR(PIO_IOC_MAGIC, 63, struct rp1_pio_irq_set_enabled_args) +#define PIO_IOC_SET_IRQN_SOURCE_MASK_ENABLED \ + _IOW(PIO_IOC_MAGIC, 64, struct rp1_pio_set_irqn_source_mask_enabled_args) +#define PIO_IOC_INTERRUPT_GET _IOWR(PIO_IOC_MAGIC, 65, struct rp1_pio_interrupt_get_args) +#define PIO_IOC_INTERRUPT_CLEAR _IOW(PIO_IOC_MAGIC, 66, struct rp1_pio_interrupt_clear_args) + #endif