diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 0d950cd354..783215f026 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -21,6 +21,7 @@ rsource "touch/Kconfig" rsource "graphic/Kconfig" rsource "hwcrypto/Kconfig" rsource "wlan/Kconfig" +rsource "mailbox/Kconfig" rsource "phye/Kconfig" rsource "block/Kconfig" rsource "nvme/Kconfig" diff --git a/components/drivers/include/drivers/mailbox.h b/components/drivers/include/drivers/mailbox.h new file mode 100644 index 0000000000..954cd628e6 --- /dev/null +++ b/components/drivers/include/drivers/mailbox.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __MAILBOX_H__ +#define __MAILBOX_H__ + +#include +#include + +struct rt_mbox_chan; +struct rt_mbox_client; +struct rt_mbox_controller_ops; + +struct rt_mbox_controller +{ + rt_list_t list; + + struct rt_device *dev; + + const struct rt_mbox_controller_ops *ops; + + rt_size_t num_chans; + struct rt_mbox_chan *chans; +}; + +struct rt_mbox_controller_ops +{ + rt_err_t (*request)(struct rt_mbox_chan *); + void (*release)(struct rt_mbox_chan *); + rt_err_t (*send)(struct rt_mbox_chan *, const void *data); + rt_bool_t (*peek)(struct rt_mbox_chan *); + int (*ofw_parse)(struct rt_mbox_controller *, struct rt_ofw_cell_args *); +}; + +struct rt_mbox_chan +{ + struct rt_mbox_controller *ctrl; + struct rt_mbox_client *client; + + void *data; + rt_bool_t complete; + struct rt_timer timer; + struct rt_spinlock lock; + + void *priv; +}; + +struct rt_mbox_client +{ + struct rt_device *dev; + + void (*rx_callback)(struct rt_mbox_client *, void *data); + void (*tx_prepare)(struct rt_mbox_client *, const void *data); + void (*tx_done)(struct rt_mbox_client *, const void *data, rt_err_t err); +}; + +rt_err_t rt_mbox_controller_register(struct rt_mbox_controller *ctrl); +rt_err_t rt_mbox_controller_unregister(struct rt_mbox_controller *ctrl); + +rt_err_t rt_mbox_send(struct rt_mbox_chan *chan, const void *data, + rt_uint32_t timeout_ms); +void rt_mbox_send_done(struct rt_mbox_chan *chan, rt_err_t err); +rt_bool_t rt_mbox_peek(struct rt_mbox_chan *chan); +rt_err_t rt_mbox_recv(struct rt_mbox_chan *chan, void *data); + +struct rt_mbox_chan *rt_mbox_request_by_index(struct rt_mbox_client *client, int index); +struct rt_mbox_chan *rt_mbox_request_by_name(struct rt_mbox_client *client, char *name); +rt_err_t rt_mbox_release(struct rt_mbox_chan *chan); + +#endif /* __MAILBOX_H__ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index ceb02485e3..aa56b6820c 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -45,19 +45,23 @@ extern "C" { #include "drivers/core/power_domain.h" #include "drivers/platform.h" +#ifdef RT_USING_MBOX +#include "drivers/mailbox.h" +#endif /* RT_USING_MBOX */ + #ifdef RT_USING_BLK #include "drivers/blk.h" -#endif +#endif /* RT_USING_BLK */ #ifdef RT_USING_DMA #include "drivers/dma.h" -#endif +#endif /* RT_USING_DMA */ #include "drivers/iio.h" #ifdef RT_USING_NVME #include "drivers/nvme.h" -#endif +#endif /* RT_USING_NVME */ #ifdef RT_USING_OFW #include "drivers/ofw.h" @@ -69,26 +73,26 @@ extern "C" { #ifdef RT_USING_PHYE #include "drivers/phye.h" -#endif +#endif /* RT_USING_PHYE */ #ifdef RT_USING_PIC #include "drivers/pic.h" -#endif +#endif /* RT_USING_PIC */ #ifdef RT_USING_SCSI #include "drivers/scsi.h" -#endif +#endif /* RT_USING_SCSI */ #ifdef RT_MFD_SYSCON #include "drivers/syscon.h" -#endif +#endif /* RT_MFD_SYSCON */ #endif /* RT_USING_DM */ #ifdef RT_USING_RTC #include "drivers/dev_rtc.h" #ifdef RT_USING_ALARM #include "drivers/dev_alarm.h" -#endif +#endif /* RT_USING_ALARM */ #endif /* RT_USING_RTC */ #ifdef RT_USING_SPI diff --git a/components/drivers/mailbox/Kconfig b/components/drivers/mailbox/Kconfig new file mode 100644 index 0000000000..9531d3a2dd --- /dev/null +++ b/components/drivers/mailbox/Kconfig @@ -0,0 +1,14 @@ +menuconfig RT_USING_MBOX + bool "Using Hardware Mailbox device drivers" + depends on RT_USING_DM + depends on RT_USING_OFW + default n + +config RT_MBOX_PIC + bool "RT-Thread PIC Mailbox" + depends on RT_USING_MBOX + default y + +if RT_USING_MBOX + osource "$(SOC_DM_MBOX_DIR)/Kconfig" +endif diff --git a/components/drivers/mailbox/SConscript b/components/drivers/mailbox/SConscript new file mode 100755 index 0000000000..f26a44b0ce --- /dev/null +++ b/components/drivers/mailbox/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_MBOX']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['mailbox.c'] + +if GetDepend(['RT_MBOX_PIC']): + src += ['mailbox-pic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/mailbox/mailbox-pic.c b/components/drivers/mailbox/mailbox-pic.c new file mode 100644 index 0000000000..187abe0122 --- /dev/null +++ b/components/drivers/mailbox/mailbox-pic.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "mailbox.pic" +#define DBG_LVL DBG_INFO +#include + +/* + * RT-Thread PIC Mailbox device driver + * + * The mailbox device(s) may be instantiated in one of three equivalent way: + * + * Device Tree node, eg.: + * + * interrupt-controller@0 { + * interrupt-controller; + * #interrupt-cells = <1>; + * }; + * + * pic_mailbox@10000 { + * compatible = "rt-thread,pic-mailbox"; + * reg = <0x10000 0x100>; + * position = <0>; + * interrupts = <34>; + * peer-interrupts = <35>; + * uid = <0>; + * #mbox-cells = <1>; + * }; + */ + +#define MAILBOX_IMASK 0x00 +#define MAILBOX_ISTATE 0x04 +#define MAILBOX_MSG(n) (0x08 + (n) * 4) + +struct pic_mbox +{ + struct rt_mbox_controller parent; + + void *regs; + void *peer_regs; + + int position; + int chans_nr; + int irq; + int peer_hwirq; + struct rt_pic *pic; + + struct rt_spinlock lock; +}; + +#define raw_to_pic_mbox(raw) rt_container_of(raw, struct pic_mbox, parent) + +static rt_err_t pic_mbox_request(struct rt_mbox_chan *chan) +{ + int index = chan - chan->ctrl->chans; + struct pic_mbox *pic_mbox = raw_to_pic_mbox(chan->ctrl); + + HWREG32(pic_mbox->regs + MAILBOX_IMASK) &= ~RT_BIT(index); + + return RT_EOK; +} + +static void pic_mbox_release(struct rt_mbox_chan *chan) +{ + int index = chan - chan->ctrl->chans; + struct pic_mbox *pic_mbox = raw_to_pic_mbox(chan->ctrl); + + HWREG32(pic_mbox->regs + MAILBOX_IMASK) |= RT_BIT(index); +} + +static rt_err_t pic_mbox_send(struct rt_mbox_chan *chan, const void *data) +{ + rt_ubase_t level; + int index = chan - chan->ctrl->chans; + struct pic_mbox *pic_mbox = raw_to_pic_mbox(chan->ctrl); + + while (HWREG32(pic_mbox->peer_regs + MAILBOX_ISTATE) & RT_BIT(index)) + { + rt_thread_yield(); + } + + level = rt_spin_lock_irqsave(&pic_mbox->lock); + + HWREG32(pic_mbox->regs + MAILBOX_MSG(index)) = *(rt_uint32_t *)data; + HWREG32(pic_mbox->peer_regs + MAILBOX_ISTATE) |= RT_BIT(index); + rt_hw_wmb(); + + rt_pic_irq_set_state_raw(pic_mbox->pic, pic_mbox->peer_hwirq, + RT_IRQ_STATE_PENDING, RT_TRUE); + + rt_spin_unlock_irqrestore(&pic_mbox->lock, level); + + return RT_EOK; +} + +static const struct rt_mbox_controller_ops pic_mbox_ops = +{ + .request = pic_mbox_request, + .release = pic_mbox_release, + .send = pic_mbox_send, +}; + +static void pic_mbox_isr(int irqno, void *param) +{ + rt_uint32_t isr; + struct pic_mbox *pic_mbox = param; + + isr = HWREG32(pic_mbox->regs + MAILBOX_ISTATE); + + for (int idx = 0; idx < 32; ++idx) + { + rt_uint32_t msg; + + if (!(RT_BIT(idx) & isr)) + { + continue; + } + + rt_hw_rmb(); + msg = HWREG32(pic_mbox->peer_regs + MAILBOX_MSG(idx)); + + rt_mbox_recv(&pic_mbox->parent.chans[idx], &msg); + } + + HWREG32(pic_mbox->regs + MAILBOX_ISTATE) &= ~isr; +} + +static void pic_mbox_free_resource(struct pic_mbox *pic_mbox) +{ + if (pic_mbox->regs && pic_mbox->peer_regs) + { + if (pic_mbox->peer_regs > pic_mbox->regs) + { + rt_iounmap(pic_mbox->regs); + } + else + { + rt_iounmap(pic_mbox->peer_regs); + } + } + + rt_free(pic_mbox); +} + +static rt_err_t pic_mbox_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint64_t size; + rt_uint32_t value; + char dev_name[RT_NAME_MAX]; + struct rt_ofw_node *pic_np; + struct rt_device *dev = &pdev->parent; + struct pic_mbox *pic_mbox = rt_calloc(1, sizeof(*pic_mbox)); + + if (!pic_mbox) + { + return -RT_ENOMEM; + } + + if ((err = rt_dm_dev_get_address(dev, 0, RT_NULL, &size))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "position", &value))) + { + goto _fail; + } + + if (!value) + { + pic_mbox->regs = rt_dm_dev_iomap(dev, 0); + + if (!pic_mbox->regs) + { + goto _fail; + } + + pic_mbox->peer_regs = pic_mbox->regs + size / 2; + } + else + { + pic_mbox->peer_regs = rt_dm_dev_iomap(dev, 0); + + if (!pic_mbox->peer_regs) + { + goto _fail; + } + + pic_mbox->regs = pic_mbox->peer_regs + size / 2; + } + + pic_mbox->irq = rt_dm_dev_get_irq(dev, 0); + + if (pic_mbox->irq < 0) + { + err = pic_mbox->irq; + + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "peer-interrupts", &value))) + { + goto _fail; + } + pic_mbox->peer_hwirq = value; + + if ((err = rt_dm_dev_prop_read_u32(dev, "uid", &value))) + { + goto _fail; + } + + if (!(pic_np = rt_ofw_find_irq_parent(dev->ofw_node, RT_NULL))) + { + goto _fail; + } + pic_mbox->pic = rt_ofw_data(pic_np); + rt_ofw_node_put(pic_np); + + rt_spin_lock_init(&pic_mbox->lock); + + pic_mbox->parent.dev = dev; + pic_mbox->parent.num_chans = 32; + pic_mbox->parent.ops = &pic_mbox_ops; + + if ((err = rt_mbox_controller_register(&pic_mbox->parent))) + { + goto _fail; + } + + rt_snprintf(dev_name, sizeof(dev_name), "pic-mbox%d", value); + rt_hw_interrupt_install(pic_mbox->irq, pic_mbox_isr, pic_mbox, dev_name); + rt_hw_interrupt_umask(pic_mbox->irq); + + return RT_EOK; + +_fail: + pic_mbox_free_resource(pic_mbox); + + return err; +} + +static rt_err_t pic_mbox_remove(struct rt_platform_device *pdev) +{ + struct pic_mbox *pic_mbox = pdev->parent.user_data; + + rt_pic_detach_irq(pic_mbox->irq, pic_mbox); + + rt_mbox_controller_unregister(&pic_mbox->parent); + + pic_mbox_free_resource(pic_mbox); + + return RT_EOK; +} + +static const struct rt_ofw_node_id pic_mbox_ofw_ids[] = +{ + { .compatible = "rt-thread,pic-mailbox" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver pic_mbox_driver = +{ + .name = "mailbox-pic", + .ids = pic_mbox_ofw_ids, + + .probe = pic_mbox_probe, + .remove = pic_mbox_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(pic_mbox_driver); diff --git a/components/drivers/mailbox/mailbox.c b/components/drivers/mailbox/mailbox.c new file mode 100644 index 0000000000..84b9f11247 --- /dev/null +++ b/components/drivers/mailbox/mailbox.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.mailbox" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include + +static struct rt_spinlock mbox_ops_lock = {}; +static rt_list_t mbox_nodes = RT_LIST_OBJECT_INIT(mbox_nodes); + +static void mbox_chan_timeout(void *param); + +rt_err_t rt_mbox_controller_register(struct rt_mbox_controller *ctrl) +{ + int len; + struct rt_mbox_chan *chan; + char timer_name[RT_NAME_MAX]; + + if (!ctrl || !ctrl->dev || !ctrl->ops || !ctrl->num_chans) + { + return -RT_EINVAL; + } + + ctrl->chans = rt_calloc(ctrl->num_chans, sizeof(struct rt_mbox_chan)); + + if (!ctrl->chans) + { + return -RT_ENOMEM; + } + + len = rt_snprintf(timer_name, sizeof(timer_name), "%s-", + rt_dm_dev_get_name(ctrl->dev)); + + RT_ASSERT(len < sizeof(timer_name)); + + chan = &ctrl->chans[0]; + + for (int i = 0; i < ctrl->num_chans; ++i, ++chan) + { + chan->ctrl = ctrl; + rt_spin_lock_init(&chan->lock); + + rt_snprintf(&timer_name[len], sizeof(timer_name) - len, "%d", i); + rt_timer_init(&chan->timer, timer_name, mbox_chan_timeout, chan, + 0, RT_TIMER_FLAG_ONE_SHOT); + } + + rt_list_init(&ctrl->list); + rt_dm_dev_bind_fwdata(ctrl->dev, RT_NULL, ctrl); + + rt_spin_lock(&mbox_ops_lock); + + rt_list_insert_after(&mbox_nodes, &ctrl->list); + + rt_spin_unlock(&mbox_ops_lock); + + return RT_EOK; +} + +rt_err_t rt_mbox_controller_unregister(struct rt_mbox_controller *ctrl) +{ + struct rt_mbox_chan *chan; + + if (!ctrl) + { + return -RT_EINVAL; + } + + rt_spin_lock(&mbox_ops_lock); + + rt_dm_dev_unbind_fwdata(ctrl->dev, RT_NULL); + rt_list_remove(&ctrl->list); + + rt_spin_unlock(&mbox_ops_lock); + + chan = &ctrl->chans[0]; + + for (int i = ctrl->num_chans - 1; i >= 0; --i, ++chan) + { + rt_mbox_release(&ctrl->chans[i]); + } + + rt_free(ctrl->chans); + + return RT_EOK; +} + +rt_err_t rt_mbox_send(struct rt_mbox_chan *chan, const void *data, + rt_uint32_t timeout_ms) +{ + rt_err_t err; + rt_ubase_t level; + rt_bool_t timer_go = RT_FALSE; + struct rt_mbox_client *client; + struct rt_mbox_controller *ctrl; + + if (!chan || !data) + { + return -RT_EINVAL; + } + + ctrl = chan->ctrl; + client = chan->client; + + level = rt_spin_lock_irqsave(&chan->lock); + + if (client->tx_prepare) + { + client->tx_prepare(client, data); + } + + chan->complete = RT_FALSE; + err = ctrl->ops->send(chan, data); + + if (!err) + { + chan->data = (void *)data; + + if (timeout_ms != RT_WAITING_FOREVER) + { + rt_tick_t tick = rt_tick_from_millisecond(timeout_ms); + + rt_timer_control(&chan->timer, RT_TIMER_CTRL_SET_TIME, &tick); + + timer_go = RT_TRUE; + } + } + else + { + chan->complete = RT_TRUE; + } + + rt_spin_unlock_irqrestore(&chan->lock, level); + + if (timer_go) + { + rt_timer_start(&chan->timer); + } + + return err; +} + +void rt_mbox_send_done(struct rt_mbox_chan *chan, rt_err_t err) +{ + void *data; + rt_ubase_t level; + + level = rt_spin_lock_irqsave(&chan->lock); + + data = chan->data; + chan->data = RT_NULL; + + rt_spin_unlock_irqrestore(&chan->lock, level); + + if (chan->client->tx_done) + { + chan->client->tx_done(chan->client, data, err); + } + + chan->complete = RT_TRUE; +} + +static void mbox_chan_timeout(void *param) +{ + rt_err_t err = RT_EOK; + struct rt_mbox_chan *chan = param; + + if (!chan->complete) + { + err = -RT_ETIMEOUT; + } + + rt_mbox_send_done(chan, err); +} + +rt_bool_t rt_mbox_peek(struct rt_mbox_chan *chan) +{ + if (chan && chan->ctrl->ops->peek) + { + return chan->ctrl->ops->peek(chan); + } + + return RT_FALSE; +} + +rt_err_t rt_mbox_recv(struct rt_mbox_chan *chan, void *data) +{ + if (!chan || !data) + { + return -RT_EINVAL; + } + + if (chan->client->rx_callback) + { + chan->client->rx_callback(chan->client, data); + } + + return RT_EOK; +} + +static int mbox_controller_ofw_parse_default(struct rt_mbox_controller *ctrl, + struct rt_ofw_cell_args *args) +{ + if (args->args_count != 1) + { + return -RT_EINVAL; + } + + return args->args[0]; +} + +struct rt_mbox_chan *rt_mbox_request_by_index(struct rt_mbox_client *client, int index) +{ + rt_err_t err; + struct rt_ofw_cell_args args; + struct rt_ofw_node *np, *ctrl_np; + struct rt_mbox_controller *ctrl; + struct rt_mbox_chan *chan = RT_NULL; + + if (!client && index < 0) + { + return rt_err_ptr(-RT_EINVAL); + } + + np = client->dev->ofw_node; + + rt_spin_lock(&mbox_ops_lock); + + err = rt_ofw_parse_phandle_cells(np, "mboxes", "#mbox-cells", index, &args); + + if (err) + { + chan = rt_err_ptr(err); + goto _out_lock; + } + + ctrl_np = args.data; + + if (!rt_ofw_data(ctrl_np)) + { + rt_platform_ofw_request(ctrl_np); + } + + ctrl = rt_ofw_data(ctrl_np); + rt_ofw_node_put(ctrl_np); + + if (ctrl) + { + int index; + + if (ctrl->ops->ofw_parse) + { + index = ctrl->ops->ofw_parse(ctrl, &args); + } + else + { + index = mbox_controller_ofw_parse_default(ctrl, &args); + } + + if (index >= 0) + { + chan = &ctrl->chans[index]; + } + else + { + LOG_E("Parse chan from %s error = %s", + rt_dm_dev_get_name(ctrl->dev), rt_strerror(index)); + + chan = rt_err_ptr(index); + goto _out_lock; + } + + if (ctrl->ops->request) + { + rt_err_t err = ctrl->ops->request(chan); + + if (err) + { + LOG_E("Request chan[%d] from %s error = %s", + index, rt_dm_dev_get_name(ctrl->dev), rt_strerror(err)); + + rt_mbox_release(chan); + chan = rt_err_ptr(err); + } + } + + chan->client = client; + } + else + { + chan = rt_err_ptr(-RT_ENOSYS); + } + +_out_lock: + rt_spin_unlock(&mbox_ops_lock); + + return chan; +} + +struct rt_mbox_chan *rt_mbox_request_by_name(struct rt_mbox_client *client, char *name) +{ + int index; + struct rt_ofw_node *np; + + if (!client || !name) + { + return rt_err_ptr(-RT_EINVAL); + } + + np = client->dev->ofw_node; + index = rt_ofw_prop_index_of_string(np, "mbox-names", name); + + if (index < 0) + { + return RT_NULL; + } + + return rt_mbox_request_by_index(client, index); +} + +rt_err_t rt_mbox_release(struct rt_mbox_chan *chan) +{ + if (chan) + { + chan->ctrl->ops->release(chan); + } + else + { + return -RT_EINVAL; + } + + return RT_EOK; +}