18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * STi Mailbox 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 ST Microelectronics 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Lee Jones <lee.jones@linaro.org> for ST Microelectronics 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on the original driver written by; 108c2ecf20Sopenharmony_ci * Alexandre Torgue, Olivier Lebreton and Loic Pallardy 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/of_device.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "mailbox.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define STI_MBOX_INST_MAX 4 /* RAM saving: Max supported instances */ 278c2ecf20Sopenharmony_ci#define STI_MBOX_CHAN_MAX 20 /* RAM saving: Max supported channels */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define STI_IRQ_VAL_OFFSET 0x04 /* Read interrupt status */ 308c2ecf20Sopenharmony_ci#define STI_IRQ_SET_OFFSET 0x24 /* Generate a Tx channel interrupt */ 318c2ecf20Sopenharmony_ci#define STI_IRQ_CLR_OFFSET 0x44 /* Clear pending Rx interrupts */ 328c2ecf20Sopenharmony_ci#define STI_ENA_VAL_OFFSET 0x64 /* Read enable status */ 338c2ecf20Sopenharmony_ci#define STI_ENA_SET_OFFSET 0x84 /* Enable a channel */ 348c2ecf20Sopenharmony_ci#define STI_ENA_CLR_OFFSET 0xa4 /* Disable a channel */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define MBOX_BASE(mdev, inst) ((mdev)->base + ((inst) * 4)) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/** 398c2ecf20Sopenharmony_ci * STi Mailbox device data 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * An IP Mailbox is currently composed of 4 instances 428c2ecf20Sopenharmony_ci * Each instance is currently composed of 32 channels 438c2ecf20Sopenharmony_ci * This means that we have 128 channels per Mailbox 448c2ecf20Sopenharmony_ci * A channel an be used for TX or RX 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * @dev: Device to which it is attached 478c2ecf20Sopenharmony_ci * @mbox: Representation of a communication channel controller 488c2ecf20Sopenharmony_ci * @base: Base address of the register mapping region 498c2ecf20Sopenharmony_ci * @name: Name of the mailbox 508c2ecf20Sopenharmony_ci * @enabled: Local copy of enabled channels 518c2ecf20Sopenharmony_ci * @lock: Mutex protecting enabled status 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistruct sti_mbox_device { 548c2ecf20Sopenharmony_ci struct device *dev; 558c2ecf20Sopenharmony_ci struct mbox_controller *mbox; 568c2ecf20Sopenharmony_ci void __iomem *base; 578c2ecf20Sopenharmony_ci const char *name; 588c2ecf20Sopenharmony_ci u32 enabled[STI_MBOX_INST_MAX]; 598c2ecf20Sopenharmony_ci spinlock_t lock; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/** 638c2ecf20Sopenharmony_ci * STi Mailbox platform specific configuration 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * @num_inst: Maximum number of instances in one HW Mailbox 668c2ecf20Sopenharmony_ci * @num_chan: Maximum number of channel per instance 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistruct sti_mbox_pdata { 698c2ecf20Sopenharmony_ci unsigned int num_inst; 708c2ecf20Sopenharmony_ci unsigned int num_chan; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/** 748c2ecf20Sopenharmony_ci * STi Mailbox allocated channel information 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * @mdev: Pointer to parent Mailbox device 778c2ecf20Sopenharmony_ci * @instance: Instance number channel resides in 788c2ecf20Sopenharmony_ci * @channel: Channel number pertaining to this container 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistruct sti_channel { 818c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev; 828c2ecf20Sopenharmony_ci unsigned int instance; 838c2ecf20Sopenharmony_ci unsigned int channel; 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic inline bool sti_mbox_channel_is_enabled(struct mbox_chan *chan) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct sti_channel *chan_info = chan->con_priv; 898c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev = chan_info->mdev; 908c2ecf20Sopenharmony_ci unsigned int instance = chan_info->instance; 918c2ecf20Sopenharmony_ci unsigned int channel = chan_info->channel; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return mdev->enabled[instance] & BIT(channel); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline 978c2ecf20Sopenharmony_cistruct mbox_chan *sti_mbox_to_channel(struct mbox_controller *mbox, 988c2ecf20Sopenharmony_ci unsigned int instance, 998c2ecf20Sopenharmony_ci unsigned int channel) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct sti_channel *chan_info; 1028c2ecf20Sopenharmony_ci int i; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci for (i = 0; i < mbox->num_chans; i++) { 1058c2ecf20Sopenharmony_ci chan_info = mbox->chans[i].con_priv; 1068c2ecf20Sopenharmony_ci if (chan_info && 1078c2ecf20Sopenharmony_ci chan_info->instance == instance && 1088c2ecf20Sopenharmony_ci chan_info->channel == channel) 1098c2ecf20Sopenharmony_ci return &mbox->chans[i]; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci dev_err(mbox->dev, 1138c2ecf20Sopenharmony_ci "Channel not registered: instance: %d channel: %d\n", 1148c2ecf20Sopenharmony_ci instance, channel); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return NULL; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void sti_mbox_enable_channel(struct mbox_chan *chan) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct sti_channel *chan_info = chan->con_priv; 1228c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev = chan_info->mdev; 1238c2ecf20Sopenharmony_ci unsigned int instance = chan_info->instance; 1248c2ecf20Sopenharmony_ci unsigned int channel = chan_info->channel; 1258c2ecf20Sopenharmony_ci unsigned long flags; 1268c2ecf20Sopenharmony_ci void __iomem *base = MBOX_BASE(mdev, instance); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci spin_lock_irqsave(&mdev->lock, flags); 1298c2ecf20Sopenharmony_ci mdev->enabled[instance] |= BIT(channel); 1308c2ecf20Sopenharmony_ci writel_relaxed(BIT(channel), base + STI_ENA_SET_OFFSET); 1318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mdev->lock, flags); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void sti_mbox_disable_channel(struct mbox_chan *chan) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct sti_channel *chan_info = chan->con_priv; 1378c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev = chan_info->mdev; 1388c2ecf20Sopenharmony_ci unsigned int instance = chan_info->instance; 1398c2ecf20Sopenharmony_ci unsigned int channel = chan_info->channel; 1408c2ecf20Sopenharmony_ci unsigned long flags; 1418c2ecf20Sopenharmony_ci void __iomem *base = MBOX_BASE(mdev, instance); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci spin_lock_irqsave(&mdev->lock, flags); 1448c2ecf20Sopenharmony_ci mdev->enabled[instance] &= ~BIT(channel); 1458c2ecf20Sopenharmony_ci writel_relaxed(BIT(channel), base + STI_ENA_CLR_OFFSET); 1468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mdev->lock, flags); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void sti_mbox_clear_irq(struct mbox_chan *chan) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct sti_channel *chan_info = chan->con_priv; 1528c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev = chan_info->mdev; 1538c2ecf20Sopenharmony_ci unsigned int instance = chan_info->instance; 1548c2ecf20Sopenharmony_ci unsigned int channel = chan_info->channel; 1558c2ecf20Sopenharmony_ci void __iomem *base = MBOX_BASE(mdev, instance); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci writel_relaxed(BIT(channel), base + STI_IRQ_CLR_OFFSET); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic struct mbox_chan *sti_mbox_irq_to_channel(struct sti_mbox_device *mdev, 1618c2ecf20Sopenharmony_ci unsigned int instance) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct mbox_controller *mbox = mdev->mbox; 1648c2ecf20Sopenharmony_ci struct mbox_chan *chan = NULL; 1658c2ecf20Sopenharmony_ci unsigned int channel; 1668c2ecf20Sopenharmony_ci unsigned long bits; 1678c2ecf20Sopenharmony_ci void __iomem *base = MBOX_BASE(mdev, instance); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci bits = readl_relaxed(base + STI_IRQ_VAL_OFFSET); 1708c2ecf20Sopenharmony_ci if (!bits) 1718c2ecf20Sopenharmony_ci /* No IRQs fired in specified instance */ 1728c2ecf20Sopenharmony_ci return NULL; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* An IRQ has fired, find the associated channel */ 1758c2ecf20Sopenharmony_ci for (channel = 0; bits; channel++) { 1768c2ecf20Sopenharmony_ci if (!test_and_clear_bit(channel, &bits)) 1778c2ecf20Sopenharmony_ci continue; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci chan = sti_mbox_to_channel(mbox, instance, channel); 1808c2ecf20Sopenharmony_ci if (chan) { 1818c2ecf20Sopenharmony_ci dev_dbg(mbox->dev, 1828c2ecf20Sopenharmony_ci "IRQ fired on instance: %d channel: %d\n", 1838c2ecf20Sopenharmony_ci instance, channel); 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return chan; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic irqreturn_t sti_mbox_thread_handler(int irq, void *data) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev = data; 1948c2ecf20Sopenharmony_ci struct sti_mbox_pdata *pdata = dev_get_platdata(mdev->dev); 1958c2ecf20Sopenharmony_ci struct mbox_chan *chan; 1968c2ecf20Sopenharmony_ci unsigned int instance; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci for (instance = 0; instance < pdata->num_inst; instance++) { 1998c2ecf20Sopenharmony_cikeep_looking: 2008c2ecf20Sopenharmony_ci chan = sti_mbox_irq_to_channel(mdev, instance); 2018c2ecf20Sopenharmony_ci if (!chan) 2028c2ecf20Sopenharmony_ci continue; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci mbox_chan_received_data(chan, NULL); 2058c2ecf20Sopenharmony_ci sti_mbox_clear_irq(chan); 2068c2ecf20Sopenharmony_ci sti_mbox_enable_channel(chan); 2078c2ecf20Sopenharmony_ci goto keep_looking; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic irqreturn_t sti_mbox_irq_handler(int irq, void *data) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev = data; 2168c2ecf20Sopenharmony_ci struct sti_mbox_pdata *pdata = dev_get_platdata(mdev->dev); 2178c2ecf20Sopenharmony_ci struct sti_channel *chan_info; 2188c2ecf20Sopenharmony_ci struct mbox_chan *chan; 2198c2ecf20Sopenharmony_ci unsigned int instance; 2208c2ecf20Sopenharmony_ci int ret = IRQ_NONE; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci for (instance = 0; instance < pdata->num_inst; instance++) { 2238c2ecf20Sopenharmony_ci chan = sti_mbox_irq_to_channel(mdev, instance); 2248c2ecf20Sopenharmony_ci if (!chan) 2258c2ecf20Sopenharmony_ci continue; 2268c2ecf20Sopenharmony_ci chan_info = chan->con_priv; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (!sti_mbox_channel_is_enabled(chan)) { 2298c2ecf20Sopenharmony_ci dev_warn(mdev->dev, 2308c2ecf20Sopenharmony_ci "Unexpected IRQ: %s\n" 2318c2ecf20Sopenharmony_ci " instance: %d: channel: %d [enabled: %x]\n", 2328c2ecf20Sopenharmony_ci mdev->name, chan_info->instance, 2338c2ecf20Sopenharmony_ci chan_info->channel, mdev->enabled[instance]); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Only handle IRQ if no other valid IRQs were found */ 2368c2ecf20Sopenharmony_ci if (ret == IRQ_NONE) 2378c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 2388c2ecf20Sopenharmony_ci continue; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci sti_mbox_disable_channel(chan); 2428c2ecf20Sopenharmony_ci ret = IRQ_WAKE_THREAD; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (ret == IRQ_NONE) 2468c2ecf20Sopenharmony_ci dev_err(mdev->dev, "Spurious IRQ - was a channel requested?\n"); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return ret; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic bool sti_mbox_tx_is_ready(struct mbox_chan *chan) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct sti_channel *chan_info = chan->con_priv; 2548c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev = chan_info->mdev; 2558c2ecf20Sopenharmony_ci unsigned int instance = chan_info->instance; 2568c2ecf20Sopenharmony_ci unsigned int channel = chan_info->channel; 2578c2ecf20Sopenharmony_ci void __iomem *base = MBOX_BASE(mdev, instance); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!(readl_relaxed(base + STI_ENA_VAL_OFFSET) & BIT(channel))) { 2608c2ecf20Sopenharmony_ci dev_dbg(mdev->dev, "Mbox: %s: inst: %d, chan: %d disabled\n", 2618c2ecf20Sopenharmony_ci mdev->name, instance, channel); 2628c2ecf20Sopenharmony_ci return false; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (readl_relaxed(base + STI_IRQ_VAL_OFFSET) & BIT(channel)) { 2668c2ecf20Sopenharmony_ci dev_dbg(mdev->dev, "Mbox: %s: inst: %d, chan: %d not ready\n", 2678c2ecf20Sopenharmony_ci mdev->name, instance, channel); 2688c2ecf20Sopenharmony_ci return false; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return true; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int sti_mbox_send_data(struct mbox_chan *chan, void *data) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct sti_channel *chan_info = chan->con_priv; 2778c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev = chan_info->mdev; 2788c2ecf20Sopenharmony_ci unsigned int instance = chan_info->instance; 2798c2ecf20Sopenharmony_ci unsigned int channel = chan_info->channel; 2808c2ecf20Sopenharmony_ci void __iomem *base = MBOX_BASE(mdev, instance); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Send event to co-processor */ 2838c2ecf20Sopenharmony_ci writel_relaxed(BIT(channel), base + STI_IRQ_SET_OFFSET); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci dev_dbg(mdev->dev, 2868c2ecf20Sopenharmony_ci "Sent via Mailbox %s: instance: %d channel: %d\n", 2878c2ecf20Sopenharmony_ci mdev->name, instance, channel); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int sti_mbox_startup_chan(struct mbox_chan *chan) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci sti_mbox_clear_irq(chan); 2958c2ecf20Sopenharmony_ci sti_mbox_enable_channel(chan); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic void sti_mbox_shutdown_chan(struct mbox_chan *chan) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct sti_channel *chan_info = chan->con_priv; 3038c2ecf20Sopenharmony_ci struct mbox_controller *mbox = chan_info->mdev->mbox; 3048c2ecf20Sopenharmony_ci int i; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci for (i = 0; i < mbox->num_chans; i++) 3078c2ecf20Sopenharmony_ci if (chan == &mbox->chans[i]) 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (mbox->num_chans == i) { 3118c2ecf20Sopenharmony_ci dev_warn(mbox->dev, "Request to free non-existent channel\n"); 3128c2ecf20Sopenharmony_ci return; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Reset channel */ 3168c2ecf20Sopenharmony_ci sti_mbox_disable_channel(chan); 3178c2ecf20Sopenharmony_ci sti_mbox_clear_irq(chan); 3188c2ecf20Sopenharmony_ci chan->con_priv = NULL; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic struct mbox_chan *sti_mbox_xlate(struct mbox_controller *mbox, 3228c2ecf20Sopenharmony_ci const struct of_phandle_args *spec) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev = dev_get_drvdata(mbox->dev); 3258c2ecf20Sopenharmony_ci struct sti_mbox_pdata *pdata = dev_get_platdata(mdev->dev); 3268c2ecf20Sopenharmony_ci struct sti_channel *chan_info; 3278c2ecf20Sopenharmony_ci struct mbox_chan *chan = NULL; 3288c2ecf20Sopenharmony_ci unsigned int instance = spec->args[0]; 3298c2ecf20Sopenharmony_ci unsigned int channel = spec->args[1]; 3308c2ecf20Sopenharmony_ci int i; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Bounds checking */ 3338c2ecf20Sopenharmony_ci if (instance >= pdata->num_inst || channel >= pdata->num_chan) { 3348c2ecf20Sopenharmony_ci dev_err(mbox->dev, 3358c2ecf20Sopenharmony_ci "Invalid channel requested instance: %d channel: %d\n", 3368c2ecf20Sopenharmony_ci instance, channel); 3378c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci for (i = 0; i < mbox->num_chans; i++) { 3418c2ecf20Sopenharmony_ci chan_info = mbox->chans[i].con_priv; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Is requested channel free? */ 3448c2ecf20Sopenharmony_ci if (chan_info && 3458c2ecf20Sopenharmony_ci mbox->dev == chan_info->mdev->dev && 3468c2ecf20Sopenharmony_ci instance == chan_info->instance && 3478c2ecf20Sopenharmony_ci channel == chan_info->channel) { 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci dev_err(mbox->dev, "Channel in use\n"); 3508c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * Find the first free slot, then continue checking 3558c2ecf20Sopenharmony_ci * to see if requested channel is in use 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci if (!chan && !chan_info) 3588c2ecf20Sopenharmony_ci chan = &mbox->chans[i]; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (!chan) { 3628c2ecf20Sopenharmony_ci dev_err(mbox->dev, "No free channels left\n"); 3638c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci chan_info = devm_kzalloc(mbox->dev, sizeof(*chan_info), GFP_KERNEL); 3678c2ecf20Sopenharmony_ci if (!chan_info) 3688c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci chan_info->mdev = mdev; 3718c2ecf20Sopenharmony_ci chan_info->instance = instance; 3728c2ecf20Sopenharmony_ci chan_info->channel = channel; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci chan->con_priv = chan_info; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci dev_info(mbox->dev, 3778c2ecf20Sopenharmony_ci "Mbox: %s: Created channel: instance: %d channel: %d\n", 3788c2ecf20Sopenharmony_ci mdev->name, instance, channel); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return chan; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops sti_mbox_ops = { 3848c2ecf20Sopenharmony_ci .startup = sti_mbox_startup_chan, 3858c2ecf20Sopenharmony_ci .shutdown = sti_mbox_shutdown_chan, 3868c2ecf20Sopenharmony_ci .send_data = sti_mbox_send_data, 3878c2ecf20Sopenharmony_ci .last_tx_done = sti_mbox_tx_is_ready, 3888c2ecf20Sopenharmony_ci}; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic const struct sti_mbox_pdata mbox_stih407_pdata = { 3918c2ecf20Sopenharmony_ci .num_inst = 4, 3928c2ecf20Sopenharmony_ci .num_chan = 32, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const struct of_device_id sti_mailbox_match[] = { 3968c2ecf20Sopenharmony_ci { 3978c2ecf20Sopenharmony_ci .compatible = "st,stih407-mailbox", 3988c2ecf20Sopenharmony_ci .data = (void *)&mbox_stih407_pdata 3998c2ecf20Sopenharmony_ci }, 4008c2ecf20Sopenharmony_ci { } 4018c2ecf20Sopenharmony_ci}; 4028c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sti_mailbox_match); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int sti_mbox_probe(struct platform_device *pdev) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci const struct of_device_id *match; 4078c2ecf20Sopenharmony_ci struct mbox_controller *mbox; 4088c2ecf20Sopenharmony_ci struct sti_mbox_device *mdev; 4098c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 4108c2ecf20Sopenharmony_ci struct mbox_chan *chans; 4118c2ecf20Sopenharmony_ci struct resource *res; 4128c2ecf20Sopenharmony_ci int irq; 4138c2ecf20Sopenharmony_ci int ret; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci match = of_match_device(sti_mailbox_match, &pdev->dev); 4168c2ecf20Sopenharmony_ci if (!match) { 4178c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No configuration found\n"); 4188c2ecf20Sopenharmony_ci return -ENODEV; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci pdev->dev.platform_data = (struct sti_mbox_pdata *) match->data; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL); 4238c2ecf20Sopenharmony_ci if (!mdev) 4248c2ecf20Sopenharmony_ci return -ENOMEM; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mdev); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4298c2ecf20Sopenharmony_ci mdev->base = devm_ioremap_resource(&pdev->dev, res); 4308c2ecf20Sopenharmony_ci if (IS_ERR(mdev->base)) 4318c2ecf20Sopenharmony_ci return PTR_ERR(mdev->base); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ret = of_property_read_string(np, "mbox-name", &mdev->name); 4348c2ecf20Sopenharmony_ci if (ret) 4358c2ecf20Sopenharmony_ci mdev->name = np->full_name; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); 4388c2ecf20Sopenharmony_ci if (!mbox) 4398c2ecf20Sopenharmony_ci return -ENOMEM; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci chans = devm_kcalloc(&pdev->dev, 4428c2ecf20Sopenharmony_ci STI_MBOX_CHAN_MAX, sizeof(*chans), GFP_KERNEL); 4438c2ecf20Sopenharmony_ci if (!chans) 4448c2ecf20Sopenharmony_ci return -ENOMEM; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci mdev->dev = &pdev->dev; 4478c2ecf20Sopenharmony_ci mdev->mbox = mbox; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci spin_lock_init(&mdev->lock); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* STi Mailbox does not have a Tx-Done or Tx-Ready IRQ */ 4528c2ecf20Sopenharmony_ci mbox->txdone_irq = false; 4538c2ecf20Sopenharmony_ci mbox->txdone_poll = true; 4548c2ecf20Sopenharmony_ci mbox->txpoll_period = 100; 4558c2ecf20Sopenharmony_ci mbox->ops = &sti_mbox_ops; 4568c2ecf20Sopenharmony_ci mbox->dev = mdev->dev; 4578c2ecf20Sopenharmony_ci mbox->of_xlate = sti_mbox_xlate; 4588c2ecf20Sopenharmony_ci mbox->chans = chans; 4598c2ecf20Sopenharmony_ci mbox->num_chans = STI_MBOX_CHAN_MAX; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci ret = devm_mbox_controller_register(&pdev->dev, mbox); 4628c2ecf20Sopenharmony_ci if (ret) 4638c2ecf20Sopenharmony_ci return ret; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* It's okay for Tx Mailboxes to not supply IRQs */ 4668c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 4678c2ecf20Sopenharmony_ci if (irq < 0) { 4688c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 4698c2ecf20Sopenharmony_ci "%s: Registered Tx only Mailbox\n", mdev->name); 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, irq, 4748c2ecf20Sopenharmony_ci sti_mbox_irq_handler, 4758c2ecf20Sopenharmony_ci sti_mbox_thread_handler, 4768c2ecf20Sopenharmony_ci IRQF_ONESHOT, mdev->name, mdev); 4778c2ecf20Sopenharmony_ci if (ret) { 4788c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't claim IRQ %d\n", irq); 4798c2ecf20Sopenharmony_ci return -EINVAL; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s: Registered Tx/Rx Mailbox\n", mdev->name); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic struct platform_driver sti_mbox_driver = { 4888c2ecf20Sopenharmony_ci .probe = sti_mbox_probe, 4898c2ecf20Sopenharmony_ci .driver = { 4908c2ecf20Sopenharmony_ci .name = "sti-mailbox", 4918c2ecf20Sopenharmony_ci .of_match_table = sti_mailbox_match, 4928c2ecf20Sopenharmony_ci }, 4938c2ecf20Sopenharmony_ci}; 4948c2ecf20Sopenharmony_cimodule_platform_driver(sti_mbox_driver); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics Mailbox Controller"); 4988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lee Jones <lee.jones@linaro.org"); 4998c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mailbox-sti"); 500