123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Texas Instruments' Message Manager Driver
- *
- * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
- * Nishanth Menon
- */
- #define pr_fmt(fmt) "%s: " fmt, __func__
- #include <linux/device.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/mailbox_controller.h>
- #include <linux/module.h>
- #include <linux/of_device.h>
- #include <linux/of.h>
- #include <linux/of_irq.h>
- #include <linux/platform_device.h>
- #include <linux/soc/ti/ti-msgmgr.h>
- #define Q_DATA_OFFSET(proxy, queue, reg) \
- ((0x10000 * (proxy)) + (0x80 * (queue)) + ((reg) * 4))
- #define Q_STATE_OFFSET(queue) ((queue) * 0x4)
- #define Q_STATE_ENTRY_COUNT_MASK (0xFFF000)
- #define SPROXY_THREAD_OFFSET(tid) (0x1000 * (tid))
- #define SPROXY_THREAD_DATA_OFFSET(tid, reg) \
- (SPROXY_THREAD_OFFSET(tid) + ((reg) * 0x4) + 0x4)
- #define SPROXY_THREAD_STATUS_OFFSET(tid) (SPROXY_THREAD_OFFSET(tid))
- #define SPROXY_THREAD_STATUS_COUNT_MASK (0xFF)
- #define SPROXY_THREAD_CTRL_OFFSET(tid) (0x1000 + SPROXY_THREAD_OFFSET(tid))
- #define SPROXY_THREAD_CTRL_DIR_MASK (0x1 << 31)
- /**
- * struct ti_msgmgr_valid_queue_desc - SoC valid queues meant for this processor
- * @queue_id: Queue Number for this path
- * @proxy_id: Proxy ID representing the processor in SoC
- * @is_tx: Is this a receive path?
- */
- struct ti_msgmgr_valid_queue_desc {
- u8 queue_id;
- u8 proxy_id;
- bool is_tx;
- };
- /**
- * struct ti_msgmgr_desc - Description of message manager integration
- * @queue_count: Number of Queues
- * @max_message_size: Message size in bytes
- * @max_messages: Number of messages
- * @data_first_reg: First data register for proxy data region
- * @data_last_reg: Last data register for proxy data region
- * @status_cnt_mask: Mask for getting the status value
- * @status_err_mask: Mask for getting the error value, if applicable
- * @tx_polled: Do I need to use polled mechanism for tx
- * @tx_poll_timeout_ms: Timeout in ms if polled
- * @valid_queues: List of Valid queues that the processor can access
- * @data_region_name: Name of the proxy data region
- * @status_region_name: Name of the proxy status region
- * @ctrl_region_name: Name of the proxy control region
- * @num_valid_queues: Number of valid queues
- * @is_sproxy: Is this an Secure Proxy instance?
- *
- * This structure is used in of match data to describe how integration
- * for a specific compatible SoC is done.
- */
- struct ti_msgmgr_desc {
- u8 queue_count;
- u8 max_message_size;
- u8 max_messages;
- u8 data_first_reg;
- u8 data_last_reg;
- u32 status_cnt_mask;
- u32 status_err_mask;
- bool tx_polled;
- int tx_poll_timeout_ms;
- const struct ti_msgmgr_valid_queue_desc *valid_queues;
- const char *data_region_name;
- const char *status_region_name;
- const char *ctrl_region_name;
- int num_valid_queues;
- bool is_sproxy;
- };
- /**
- * struct ti_queue_inst - Description of a queue instance
- * @name: Queue Name
- * @queue_id: Queue Identifier as mapped on SoC
- * @proxy_id: Proxy Identifier as mapped on SoC
- * @irq: IRQ for Rx Queue
- * @is_tx: 'true' if transmit queue, else, 'false'
- * @queue_buff_start: First register of Data Buffer
- * @queue_buff_end: Last (or confirmation) register of Data buffer
- * @queue_state: Queue status register
- * @queue_ctrl: Queue Control register
- * @chan: Mailbox channel
- * @rx_buff: Receive buffer pointer allocated at probe, max_message_size
- */
- struct ti_queue_inst {
- char name[30];
- u8 queue_id;
- u8 proxy_id;
- int irq;
- bool is_tx;
- void __iomem *queue_buff_start;
- void __iomem *queue_buff_end;
- void __iomem *queue_state;
- void __iomem *queue_ctrl;
- struct mbox_chan *chan;
- u32 *rx_buff;
- };
- /**
- * struct ti_msgmgr_inst - Description of a Message Manager Instance
- * @dev: device pointer corresponding to the Message Manager instance
- * @desc: Description of the SoC integration
- * @queue_proxy_region: Queue proxy region where queue buffers are located
- * @queue_state_debug_region: Queue status register regions
- * @queue_ctrl_region: Queue Control register regions
- * @num_valid_queues: Number of valid queues defined for the processor
- * Note: other queues are probably reserved for other processors
- * in the SoC.
- * @qinsts: Array of valid Queue Instances for the Processor
- * @mbox: Mailbox Controller
- * @chans: Array for channels corresponding to the Queue Instances.
- */
- struct ti_msgmgr_inst {
- struct device *dev;
- const struct ti_msgmgr_desc *desc;
- void __iomem *queue_proxy_region;
- void __iomem *queue_state_debug_region;
- void __iomem *queue_ctrl_region;
- u8 num_valid_queues;
- struct ti_queue_inst *qinsts;
- struct mbox_controller mbox;
- struct mbox_chan *chans;
- };
- /**
- * ti_msgmgr_queue_get_num_messages() - Get the number of pending messages
- * @d: Description of message manager
- * @qinst: Queue instance for which we check the number of pending messages
- *
- * Return: number of messages pending in the queue (0 == no pending messages)
- */
- static inline int
- ti_msgmgr_queue_get_num_messages(const struct ti_msgmgr_desc *d,
- struct ti_queue_inst *qinst)
- {
- u32 val;
- u32 status_cnt_mask = d->status_cnt_mask;
- /*
- * We cannot use relaxed operation here - update may happen
- * real-time.
- */
- val = readl(qinst->queue_state) & status_cnt_mask;
- val >>= __ffs(status_cnt_mask);
- return val;
- }
- /**
- * ti_msgmgr_queue_is_error() - Check to see if there is queue error
- * @d: Description of message manager
- * @qinst: Queue instance for which we check the number of pending messages
- *
- * Return: true if error, else false
- */
- static inline bool ti_msgmgr_queue_is_error(const struct ti_msgmgr_desc *d,
- struct ti_queue_inst *qinst)
- {
- u32 val;
- /* Msgmgr has no error detection */
- if (!d->is_sproxy)
- return false;
- /*
- * We cannot use relaxed operation here - update may happen
- * real-time.
- */
- val = readl(qinst->queue_state) & d->status_err_mask;
- return val ? true : false;
- }
- /**
- * ti_msgmgr_queue_rx_interrupt() - Interrupt handler for receive Queue
- * @irq: Interrupt number
- * @p: Channel Pointer
- *
- * Return: -EINVAL if there is no instance
- * IRQ_NONE if the interrupt is not ours.
- * IRQ_HANDLED if the rx interrupt was successfully handled.
- */
- static irqreturn_t ti_msgmgr_queue_rx_interrupt(int irq, void *p)
- {
- struct mbox_chan *chan = p;
- struct device *dev = chan->mbox->dev;
- struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
- struct ti_queue_inst *qinst = chan->con_priv;
- const struct ti_msgmgr_desc *desc;
- int msg_count, num_words;
- struct ti_msgmgr_message message;
- void __iomem *data_reg;
- u32 *word_data;
- if (WARN_ON(!inst)) {
- dev_err(dev, "no platform drv data??\n");
- return -EINVAL;
- }
- /* Do I have an invalid interrupt source? */
- if (qinst->is_tx) {
- dev_err(dev, "Cannot handle rx interrupt on tx channel %s\n",
- qinst->name);
- return IRQ_NONE;
- }
- desc = inst->desc;
- if (ti_msgmgr_queue_is_error(desc, qinst)) {
- dev_err(dev, "Error on Rx channel %s\n", qinst->name);
- return IRQ_NONE;
- }
- /* Do I actually have messages to read? */
- msg_count = ti_msgmgr_queue_get_num_messages(desc, qinst);
- if (!msg_count) {
- /* Shared IRQ? */
- dev_dbg(dev, "Spurious event - 0 pending data!\n");
- return IRQ_NONE;
- }
- /*
- * I have no idea about the protocol being used to communicate with the
- * remote producer - 0 could be valid data, so I wont make a judgement
- * of how many bytes I should be reading. Let the client figure this
- * out.. I just read the full message and pass it on..
- */
- message.len = desc->max_message_size;
- message.buf = (u8 *)qinst->rx_buff;
- /*
- * NOTE about register access involved here:
- * the hardware block is implemented with 32bit access operations and no
- * support for data splitting. We don't want the hardware to misbehave
- * with sub 32bit access - For example: if the last register read is
- * split into byte wise access, it can result in the queue getting
- * stuck or indeterminate behavior. An out of order read operation may
- * result in weird data results as well.
- * Hence, we do not use memcpy_fromio or __ioread32_copy here, instead
- * we depend on readl for the purpose.
- *
- * Also note that the final register read automatically marks the
- * queue message as read.
- */
- for (data_reg = qinst->queue_buff_start, word_data = qinst->rx_buff,
- num_words = (desc->max_message_size / sizeof(u32));
- num_words; num_words--, data_reg += sizeof(u32), word_data++)
- *word_data = readl(data_reg);
- /*
- * Last register read automatically clears the IRQ if only 1 message
- * is pending - so send the data up the stack..
- * NOTE: Client is expected to be as optimal as possible, since
- * we invoke the handler in IRQ context.
- */
- mbox_chan_received_data(chan, (void *)&message);
- return IRQ_HANDLED;
- }
- /**
- * ti_msgmgr_queue_peek_data() - Peek to see if there are any rx messages.
- * @chan: Channel Pointer
- *
- * Return: 'true' if there is pending rx data, 'false' if there is none.
- */
- static bool ti_msgmgr_queue_peek_data(struct mbox_chan *chan)
- {
- struct ti_queue_inst *qinst = chan->con_priv;
- struct device *dev = chan->mbox->dev;
- struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
- const struct ti_msgmgr_desc *desc = inst->desc;
- int msg_count;
- if (qinst->is_tx)
- return false;
- if (ti_msgmgr_queue_is_error(desc, qinst)) {
- dev_err(dev, "Error on channel %s\n", qinst->name);
- return false;
- }
- msg_count = ti_msgmgr_queue_get_num_messages(desc, qinst);
- return msg_count ? true : false;
- }
- /**
- * ti_msgmgr_last_tx_done() - See if all the tx messages are sent
- * @chan: Channel pointer
- *
- * Return: 'true' is no pending tx data, 'false' if there are any.
- */
- static bool ti_msgmgr_last_tx_done(struct mbox_chan *chan)
- {
- struct ti_queue_inst *qinst = chan->con_priv;
- struct device *dev = chan->mbox->dev;
- struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
- const struct ti_msgmgr_desc *desc = inst->desc;
- int msg_count;
- if (!qinst->is_tx)
- return false;
- if (ti_msgmgr_queue_is_error(desc, qinst)) {
- dev_err(dev, "Error on channel %s\n", qinst->name);
- return false;
- }
- msg_count = ti_msgmgr_queue_get_num_messages(desc, qinst);
- if (desc->is_sproxy) {
- /* In secure proxy, msg_count indicates how many we can send */
- return msg_count ? true : false;
- }
- /* if we have any messages pending.. */
- return msg_count ? false : true;
- }
- /**
- * ti_msgmgr_send_data() - Send data
- * @chan: Channel Pointer
- * @data: ti_msgmgr_message * Message Pointer
- *
- * Return: 0 if all goes good, else appropriate error messages.
- */
- static int ti_msgmgr_send_data(struct mbox_chan *chan, void *data)
- {
- struct device *dev = chan->mbox->dev;
- struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
- const struct ti_msgmgr_desc *desc;
- struct ti_queue_inst *qinst = chan->con_priv;
- int num_words, trail_bytes;
- struct ti_msgmgr_message *message = data;
- void __iomem *data_reg;
- u32 *word_data;
- if (WARN_ON(!inst)) {
- dev_err(dev, "no platform drv data??\n");
- return -EINVAL;
- }
- desc = inst->desc;
- if (ti_msgmgr_queue_is_error(desc, qinst)) {
- dev_err(dev, "Error on channel %s\n", qinst->name);
- return false;
- }
- if (desc->max_message_size < message->len) {
- dev_err(dev, "Queue %s message length %zu > max %d\n",
- qinst->name, message->len, desc->max_message_size);
- return -EINVAL;
- }
- /* NOTE: Constraints similar to rx path exists here as well */
- for (data_reg = qinst->queue_buff_start,
- num_words = message->len / sizeof(u32),
- word_data = (u32 *)message->buf;
- num_words; num_words--, data_reg += sizeof(u32), word_data++)
- writel(*word_data, data_reg);
- trail_bytes = message->len % sizeof(u32);
- if (trail_bytes) {
- u32 data_trail = *word_data;
- /* Ensure all unused data is 0 */
- data_trail &= 0xFFFFFFFF >> (8 * (sizeof(u32) - trail_bytes));
- writel(data_trail, data_reg);
- data_reg++;
- }
- /*
- * 'data_reg' indicates next register to write. If we did not already
- * write on tx complete reg(last reg), we must do so for transmit
- */
- if (data_reg <= qinst->queue_buff_end)
- writel(0, qinst->queue_buff_end);
- return 0;
- }
- /**
- * ti_msgmgr_queue_rx_irq_req() - RX IRQ request
- * @dev: device pointer
- * @d: descriptor for ti_msgmgr
- * @qinst: Queue instance
- * @chan: Channel pointer
- */
- static int ti_msgmgr_queue_rx_irq_req(struct device *dev,
- const struct ti_msgmgr_desc *d,
- struct ti_queue_inst *qinst,
- struct mbox_chan *chan)
- {
- int ret = 0;
- char of_rx_irq_name[7];
- struct device_node *np;
- snprintf(of_rx_irq_name, sizeof(of_rx_irq_name),
- "rx_%03d", d->is_sproxy ? qinst->proxy_id : qinst->queue_id);
- /* Get the IRQ if not found */
- if (qinst->irq < 0) {
- np = of_node_get(dev->of_node);
- if (!np)
- return -ENODATA;
- qinst->irq = of_irq_get_byname(np, of_rx_irq_name);
- of_node_put(np);
- if (qinst->irq < 0) {
- dev_err(dev,
- "QID %d PID %d:No IRQ[%s]: %d\n",
- qinst->queue_id, qinst->proxy_id,
- of_rx_irq_name, qinst->irq);
- return qinst->irq;
- }
- }
- /* With the expectation that the IRQ might be shared in SoC */
- ret = request_irq(qinst->irq, ti_msgmgr_queue_rx_interrupt,
- IRQF_SHARED, qinst->name, chan);
- if (ret) {
- dev_err(dev, "Unable to get IRQ %d on %s(res=%d)\n",
- qinst->irq, qinst->name, ret);
- }
- return ret;
- }
- /**
- * ti_msgmgr_queue_startup() - Startup queue
- * @chan: Channel pointer
- *
- * Return: 0 if all goes good, else return corresponding error message
- */
- static int ti_msgmgr_queue_startup(struct mbox_chan *chan)
- {
- struct device *dev = chan->mbox->dev;
- struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
- struct ti_queue_inst *qinst = chan->con_priv;
- const struct ti_msgmgr_desc *d = inst->desc;
- int ret;
- int msg_count;
- /*
- * If sproxy is starting and can send messages, we are a Tx thread,
- * else Rx
- */
- if (d->is_sproxy) {
- qinst->is_tx = (readl(qinst->queue_ctrl) &
- SPROXY_THREAD_CTRL_DIR_MASK) ? false : true;
- msg_count = ti_msgmgr_queue_get_num_messages(d, qinst);
- if (!msg_count && qinst->is_tx) {
- dev_err(dev, "%s: Cannot transmit with 0 credits!\n",
- qinst->name);
- return -EINVAL;
- }
- }
- if (!qinst->is_tx) {
- /* Allocate usage buffer for rx */
- qinst->rx_buff = kzalloc(d->max_message_size, GFP_KERNEL);
- if (!qinst->rx_buff)
- return -ENOMEM;
- /* Request IRQ */
- ret = ti_msgmgr_queue_rx_irq_req(dev, d, qinst, chan);
- if (ret) {
- kfree(qinst->rx_buff);
- return ret;
- }
- }
- return 0;
- }
- /**
- * ti_msgmgr_queue_shutdown() - Shutdown the queue
- * @chan: Channel pointer
- */
- static void ti_msgmgr_queue_shutdown(struct mbox_chan *chan)
- {
- struct ti_queue_inst *qinst = chan->con_priv;
- if (!qinst->is_tx) {
- free_irq(qinst->irq, chan);
- kfree(qinst->rx_buff);
- }
- }
- /**
- * ti_msgmgr_of_xlate() - Translation of phandle to queue
- * @mbox: Mailbox controller
- * @p: phandle pointer
- *
- * Return: Mailbox channel corresponding to the queue, else return error
- * pointer.
- */
- static struct mbox_chan *ti_msgmgr_of_xlate(struct mbox_controller *mbox,
- const struct of_phandle_args *p)
- {
- struct ti_msgmgr_inst *inst;
- int req_qid, req_pid;
- struct ti_queue_inst *qinst;
- const struct ti_msgmgr_desc *d;
- int i, ncells;
- inst = container_of(mbox, struct ti_msgmgr_inst, mbox);
- if (WARN_ON(!inst))
- return ERR_PTR(-EINVAL);
- d = inst->desc;
- if (d->is_sproxy)
- ncells = 1;
- else
- ncells = 2;
- if (p->args_count != ncells) {
- dev_err(inst->dev, "Invalid arguments in dt[%d]. Must be %d\n",
- p->args_count, ncells);
- return ERR_PTR(-EINVAL);
- }
- if (ncells == 1) {
- req_qid = 0;
- req_pid = p->args[0];
- } else {
- req_qid = p->args[0];
- req_pid = p->args[1];
- }
- if (d->is_sproxy) {
- if (req_pid >= d->num_valid_queues)
- goto err;
- qinst = &inst->qinsts[req_pid];
- return qinst->chan;
- }
- for (qinst = inst->qinsts, i = 0; i < inst->num_valid_queues;
- i++, qinst++) {
- if (req_qid == qinst->queue_id && req_pid == qinst->proxy_id)
- return qinst->chan;
- }
- err:
- dev_err(inst->dev, "Queue ID %d, Proxy ID %d is wrong on %s\n",
- req_qid, req_pid, p->np->name);
- return ERR_PTR(-ENOENT);
- }
- /**
- * ti_msgmgr_queue_setup() - Setup data structures for each queue instance
- * @idx: index of the queue
- * @dev: pointer to the message manager device
- * @np: pointer to the of node
- * @inst: Queue instance pointer
- * @d: Message Manager instance description data
- * @qd: Queue description data
- * @qinst: Queue instance pointer
- * @chan: pointer to mailbox channel
- *
- * Return: 0 if all went well, else return corresponding error
- */
- static int ti_msgmgr_queue_setup(int idx, struct device *dev,
- struct device_node *np,
- struct ti_msgmgr_inst *inst,
- const struct ti_msgmgr_desc *d,
- const struct ti_msgmgr_valid_queue_desc *qd,
- struct ti_queue_inst *qinst,
- struct mbox_chan *chan)
- {
- char *dir;
- qinst->proxy_id = qd->proxy_id;
- qinst->queue_id = qd->queue_id;
- if (qinst->queue_id > d->queue_count) {
- dev_err(dev, "Queue Data [idx=%d] queuid %d > %d\n",
- idx, qinst->queue_id, d->queue_count);
- return -ERANGE;
- }
- if (d->is_sproxy) {
- qinst->queue_buff_start = inst->queue_proxy_region +
- SPROXY_THREAD_DATA_OFFSET(qinst->proxy_id,
- d->data_first_reg);
- qinst->queue_buff_end = inst->queue_proxy_region +
- SPROXY_THREAD_DATA_OFFSET(qinst->proxy_id,
- d->data_last_reg);
- qinst->queue_state = inst->queue_state_debug_region +
- SPROXY_THREAD_STATUS_OFFSET(qinst->proxy_id);
- qinst->queue_ctrl = inst->queue_ctrl_region +
- SPROXY_THREAD_CTRL_OFFSET(qinst->proxy_id);
- /* XXX: DONOT read registers here!.. Some may be unusable */
- dir = "thr";
- snprintf(qinst->name, sizeof(qinst->name), "%s %s_%03d",
- dev_name(dev), dir, qinst->proxy_id);
- } else {
- qinst->queue_buff_start = inst->queue_proxy_region +
- Q_DATA_OFFSET(qinst->proxy_id, qinst->queue_id,
- d->data_first_reg);
- qinst->queue_buff_end = inst->queue_proxy_region +
- Q_DATA_OFFSET(qinst->proxy_id, qinst->queue_id,
- d->data_last_reg);
- qinst->queue_state =
- inst->queue_state_debug_region +
- Q_STATE_OFFSET(qinst->queue_id);
- qinst->is_tx = qd->is_tx;
- dir = qinst->is_tx ? "tx" : "rx";
- snprintf(qinst->name, sizeof(qinst->name), "%s %s_%03d_%03d",
- dev_name(dev), dir, qinst->queue_id, qinst->proxy_id);
- }
- qinst->chan = chan;
- /* Setup an error value for IRQ - Lazy allocation */
- qinst->irq = -EINVAL;
- chan->con_priv = qinst;
- dev_dbg(dev, "[%d] qidx=%d pidx=%d irq=%d q_s=%p q_e = %p\n",
- idx, qinst->queue_id, qinst->proxy_id, qinst->irq,
- qinst->queue_buff_start, qinst->queue_buff_end);
- return 0;
- }
- /* Queue operations */
- static const struct mbox_chan_ops ti_msgmgr_chan_ops = {
- .startup = ti_msgmgr_queue_startup,
- .shutdown = ti_msgmgr_queue_shutdown,
- .peek_data = ti_msgmgr_queue_peek_data,
- .last_tx_done = ti_msgmgr_last_tx_done,
- .send_data = ti_msgmgr_send_data,
- };
- /* Keystone K2G SoC integration details */
- static const struct ti_msgmgr_valid_queue_desc k2g_valid_queues[] = {
- {.queue_id = 0, .proxy_id = 0, .is_tx = true,},
- {.queue_id = 1, .proxy_id = 0, .is_tx = true,},
- {.queue_id = 2, .proxy_id = 0, .is_tx = true,},
- {.queue_id = 3, .proxy_id = 0, .is_tx = true,},
- {.queue_id = 5, .proxy_id = 2, .is_tx = false,},
- {.queue_id = 56, .proxy_id = 1, .is_tx = true,},
- {.queue_id = 57, .proxy_id = 2, .is_tx = false,},
- {.queue_id = 58, .proxy_id = 3, .is_tx = true,},
- {.queue_id = 59, .proxy_id = 4, .is_tx = true,},
- {.queue_id = 60, .proxy_id = 5, .is_tx = true,},
- {.queue_id = 61, .proxy_id = 6, .is_tx = true,},
- };
- static const struct ti_msgmgr_desc k2g_desc = {
- .queue_count = 64,
- .max_message_size = 64,
- .max_messages = 128,
- .data_region_name = "queue_proxy_region",
- .status_region_name = "queue_state_debug_region",
- .data_first_reg = 16,
- .data_last_reg = 31,
- .status_cnt_mask = Q_STATE_ENTRY_COUNT_MASK,
- .tx_polled = false,
- .valid_queues = k2g_valid_queues,
- .num_valid_queues = ARRAY_SIZE(k2g_valid_queues),
- .is_sproxy = false,
- };
- static const struct ti_msgmgr_desc am654_desc = {
- .queue_count = 190,
- .num_valid_queues = 190,
- .max_message_size = 60,
- .data_region_name = "target_data",
- .status_region_name = "rt",
- .ctrl_region_name = "scfg",
- .data_first_reg = 0,
- .data_last_reg = 14,
- .status_cnt_mask = SPROXY_THREAD_STATUS_COUNT_MASK,
- .tx_polled = false,
- .is_sproxy = true,
- };
- static const struct of_device_id ti_msgmgr_of_match[] = {
- {.compatible = "ti,k2g-message-manager", .data = &k2g_desc},
- {.compatible = "ti,am654-secure-proxy", .data = &am654_desc},
- { /* Sentinel */ }
- };
- MODULE_DEVICE_TABLE(of, ti_msgmgr_of_match);
- static int ti_msgmgr_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- const struct of_device_id *of_id;
- struct device_node *np;
- struct resource *res;
- const struct ti_msgmgr_desc *desc;
- struct ti_msgmgr_inst *inst;
- struct ti_queue_inst *qinst;
- struct mbox_controller *mbox;
- struct mbox_chan *chans;
- int queue_count;
- int i;
- int ret = -EINVAL;
- const struct ti_msgmgr_valid_queue_desc *queue_desc;
- if (!dev->of_node) {
- dev_err(dev, "no OF information\n");
- return -EINVAL;
- }
- np = dev->of_node;
- of_id = of_match_device(ti_msgmgr_of_match, dev);
- if (!of_id) {
- dev_err(dev, "OF data missing\n");
- return -EINVAL;
- }
- desc = of_id->data;
- inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL);
- if (!inst)
- return -ENOMEM;
- inst->dev = dev;
- inst->desc = desc;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- desc->data_region_name);
- inst->queue_proxy_region = devm_ioremap_resource(dev, res);
- if (IS_ERR(inst->queue_proxy_region))
- return PTR_ERR(inst->queue_proxy_region);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- desc->status_region_name);
- inst->queue_state_debug_region = devm_ioremap_resource(dev, res);
- if (IS_ERR(inst->queue_state_debug_region))
- return PTR_ERR(inst->queue_state_debug_region);
- if (desc->is_sproxy) {
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- desc->ctrl_region_name);
- inst->queue_ctrl_region = devm_ioremap_resource(dev, res);
- if (IS_ERR(inst->queue_ctrl_region))
- return PTR_ERR(inst->queue_ctrl_region);
- }
- dev_dbg(dev, "proxy region=%p, queue_state=%p\n",
- inst->queue_proxy_region, inst->queue_state_debug_region);
- queue_count = desc->num_valid_queues;
- if (!queue_count || queue_count > desc->queue_count) {
- dev_crit(dev, "Invalid Number of queues %d. Max %d\n",
- queue_count, desc->queue_count);
- return -ERANGE;
- }
- inst->num_valid_queues = queue_count;
- qinst = devm_kcalloc(dev, queue_count, sizeof(*qinst), GFP_KERNEL);
- if (!qinst)
- return -ENOMEM;
- inst->qinsts = qinst;
- chans = devm_kcalloc(dev, queue_count, sizeof(*chans), GFP_KERNEL);
- if (!chans)
- return -ENOMEM;
- inst->chans = chans;
- if (desc->is_sproxy) {
- struct ti_msgmgr_valid_queue_desc sproxy_desc;
- /* All proxies may be valid in Secure Proxy instance */
- for (i = 0; i < queue_count; i++, qinst++, chans++) {
- sproxy_desc.queue_id = 0;
- sproxy_desc.proxy_id = i;
- ret = ti_msgmgr_queue_setup(i, dev, np, inst,
- desc, &sproxy_desc, qinst,
- chans);
- if (ret)
- return ret;
- }
- } else {
- /* Only Some proxies are valid in Message Manager */
- for (i = 0, queue_desc = desc->valid_queues;
- i < queue_count; i++, qinst++, chans++, queue_desc++) {
- ret = ti_msgmgr_queue_setup(i, dev, np, inst,
- desc, queue_desc, qinst,
- chans);
- if (ret)
- return ret;
- }
- }
- mbox = &inst->mbox;
- mbox->dev = dev;
- mbox->ops = &ti_msgmgr_chan_ops;
- mbox->chans = inst->chans;
- mbox->num_chans = inst->num_valid_queues;
- mbox->txdone_irq = false;
- mbox->txdone_poll = desc->tx_polled;
- if (desc->tx_polled)
- mbox->txpoll_period = desc->tx_poll_timeout_ms;
- mbox->of_xlate = ti_msgmgr_of_xlate;
- platform_set_drvdata(pdev, inst);
- ret = mbox_controller_register(mbox);
- if (ret)
- dev_err(dev, "Failed to register mbox_controller(%d)\n", ret);
- return ret;
- }
- static int ti_msgmgr_remove(struct platform_device *pdev)
- {
- struct ti_msgmgr_inst *inst;
- inst = platform_get_drvdata(pdev);
- mbox_controller_unregister(&inst->mbox);
- return 0;
- }
- static struct platform_driver ti_msgmgr_driver = {
- .probe = ti_msgmgr_probe,
- .remove = ti_msgmgr_remove,
- .driver = {
- .name = "ti-msgmgr",
- .of_match_table = of_match_ptr(ti_msgmgr_of_match),
- },
- };
- module_platform_driver(ti_msgmgr_driver);
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("TI message manager driver");
- MODULE_AUTHOR("Nishanth Menon");
- MODULE_ALIAS("platform:ti-msgmgr");
|