We don't remove the legacy methods here, but we mark them as deprecated in the hopes that people with the ability to properly test modifications can adapt its users. Signed-off-by: Courtney Cavin <courtney.cavin@xxxxxxxxxxxxxx> --- drivers/mailbox/pl320-ipc.c | 258 ++++++++++++++++++++++++++++++++++---------- include/linux/mailbox.h | 29 ++++- 2 files changed, 225 insertions(+), 62 deletions(-) diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c index d873cba..b8da247 100644 --- a/drivers/mailbox/pl320-ipc.c +++ b/drivers/mailbox/pl320-ipc.c @@ -15,7 +15,6 @@ */ #include <linux/types.h> #include <linux/err.h> -#include <linux/delay.h> #include <linux/export.h> #include <linux/io.h> #include <linux/interrupt.h> @@ -27,6 +26,7 @@ #include <linux/amba/bus.h> #include <linux/mailbox.h> +#include <linux/mbox.h> #define IPCMxSOURCE(m) ((m) * 0x40) #define IPCMxDSET(m) (((m) * 0x40) + 0x004) @@ -50,131 +50,162 @@ #define A9_SOURCE 1 #define M3_SOURCE 0 -static void __iomem *ipc_base; -static int ipc_irq; -static DEFINE_MUTEX(ipc_m1_lock); -static DECLARE_COMPLETION(ipc_completion); -static ATOMIC_NOTIFIER_HEAD(ipc_notifier); +struct pl320 { + struct mbox_adapter adapter; + void __iomem *base; + int irq; + struct completion completion; +}; -static inline void set_destination(int source, int mbox) +static inline void set_destination(struct pl320 *pl, int source, int mbox) { - __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); - __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); + __raw_writel(CHAN_MASK(source), pl->base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), pl->base + IPCMxMSET(mbox)); } -static inline void clear_destination(int source, int mbox) +static inline void clear_destination(struct pl320 *pl, int source, int mbox) { - __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); - __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), pl->base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), pl->base + IPCMxMCLEAR(mbox)); } -static void __ipc_send(int mbox, u32 *data) +static void __ipc_send(struct pl320 *pl, int mbox, const u32 *data) { int i; for (i = 0; i < 7; i++) - __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); - __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); + __raw_writel(data[i], pl->base + IPCMxDR(mbox, i)); + __raw_writel(0x1, pl->base + IPCMxSEND(mbox)); } -static u32 __ipc_rcv(int mbox, u32 *data) +static u32 __ipc_rcv(struct pl320 *pl, int mbox, u32 *data) { int i; for (i = 0; i < 7; i++) - data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + data[i] = __raw_readl(pl->base + IPCMxDR(mbox, i)); return data[1]; } /* blocking implmentation from the A9 side, not usuable in interrupts! */ -int pl320_ipc_transmit(u32 *data) +static int pl320_ipc_put_message(struct mbox_adapter *adap, + struct mbox_channel *chan, const void *data, unsigned int len) { + struct pl320 *pl; + u32 repl[7]; int ret; - mutex_lock(&ipc_m1_lock); + if (len != 28 || chan->id != 0) + return -EINVAL; - init_completion(&ipc_completion); - __ipc_send(IPC_TX_MBOX, data); - ret = wait_for_completion_timeout(&ipc_completion, + pl = container_of(adap, struct pl320, adapter); + reinit_completion(&pl->completion); + __ipc_send(pl, IPC_TX_MBOX, data); + ret = wait_for_completion_timeout(&pl->completion, msecs_to_jiffies(1000)); - if (ret == 0) { - ret = -ETIMEDOUT; - goto out; - } + if (ret == 0) + return -ETIMEDOUT; + + ret = __ipc_rcv(pl, IPC_TX_MBOX, repl); - ret = __ipc_rcv(IPC_TX_MBOX, data); -out: - mutex_unlock(&ipc_m1_lock); return ret; } -EXPORT_SYMBOL_GPL(pl320_ipc_transmit); static irqreturn_t ipc_handler(int irq, void *dev) { + struct pl320 *pl = dev; u32 irq_stat; u32 data[7]; - irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + irq_stat = __raw_readl(pl->base + IPCMMIS(1)); if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { - __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); - complete(&ipc_completion); + __raw_writel(0, pl->base + IPCMxSEND(IPC_TX_MBOX)); + complete(&pl->completion); } if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { - __ipc_rcv(IPC_RX_MBOX, data); - atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); - __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + __ipc_rcv(pl, IPC_RX_MBOX, data); + mbox_channel_notify(&pl->adapter.channels[0], data, 28); + __raw_writel(2, pl->base + IPCMxSEND(IPC_RX_MBOX)); } return IRQ_HANDLED; } -int pl320_ipc_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&ipc_notifier, nb); -} -EXPORT_SYMBOL_GPL(pl320_ipc_register_notifier); - -int pl320_ipc_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&ipc_notifier, nb); -} -EXPORT_SYMBOL_GPL(pl320_ipc_unregister_notifier); +static const struct mbox_adapter_ops pl320_mbox_ops = { + .owner = THIS_MODULE, + .put_message = pl320_ipc_put_message, +}; static int pl320_probe(struct amba_device *adev, const struct amba_id *id) { + struct pl320 *pl; int ret; - ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); - if (ipc_base == NULL) + pl = devm_kzalloc(&adev->dev, sizeof(*pl), GFP_KERNEL); + if (pl == NULL) + return -ENOMEM; + pl->base = ioremap(adev->res.start, resource_size(&adev->res)); + if (pl->base == NULL) return -ENOMEM; - __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + init_completion(&pl->completion); - ipc_irq = adev->irq[0]; - ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + pl->adapter.dev = &adev->dev; + pl->adapter.ops = &pl320_mbox_ops; + pl->adapter.nchannels = 1; + + ret = mbox_adapter_add(&pl->adapter); + if (ret) + goto err; + + __raw_writel(0, pl->base + IPCMxSEND(IPC_TX_MBOX)); + + pl->irq = adev->irq[0]; + ret = request_irq(pl->irq, ipc_handler, 0, dev_name(&adev->dev), pl); if (ret < 0) goto err; /* Init slow mailbox */ __raw_writel(CHAN_MASK(A9_SOURCE), - ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); + pl->base + IPCMxSOURCE(IPC_TX_MBOX)); __raw_writel(CHAN_MASK(M3_SOURCE), - ipc_base + IPCMxDSET(IPC_TX_MBOX)); + pl->base + IPCMxDSET(IPC_TX_MBOX)); __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), - ipc_base + IPCMxMSET(IPC_TX_MBOX)); + pl->base + IPCMxMSET(IPC_TX_MBOX)); /* Init receive mailbox */ __raw_writel(CHAN_MASK(M3_SOURCE), - ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + pl->base + IPCMxSOURCE(IPC_RX_MBOX)); __raw_writel(CHAN_MASK(A9_SOURCE), - ipc_base + IPCMxDSET(IPC_RX_MBOX)); + pl->base + IPCMxDSET(IPC_RX_MBOX)); __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), - ipc_base + IPCMxMSET(IPC_RX_MBOX)); + pl->base + IPCMxMSET(IPC_RX_MBOX)); + amba_set_drvdata(adev, pl); return 0; err: - iounmap(ipc_base); + iounmap(pl->base); return ret; } +static int pl320_remove(struct amba_device *adev) +{ + struct pl320 *pl; + int ret; + + pl = amba_get_drvdata(adev); + + disable_irq(pl->irq); + + ret = mbox_adapter_remove(&pl->adapter); + if (ret) { + enable_irq(pl->irq); + return ret; + } + + free_irq(pl->irq, pl); + iounmap(pl->base); + return 0; +} + static struct amba_id pl320_ids[] = { { .id = 0x00041320, @@ -189,6 +220,7 @@ static struct amba_driver pl320_driver = { }, .id_table = pl320_ids, .probe = pl320_probe, + .remove = pl320_remove, }; static int __init ipc_init(void) @@ -196,3 +228,111 @@ static int __init ipc_init(void) return amba_driver_register(&pl320_driver); } module_init(ipc_init); + +/* Legacy API */ +static struct mbox *pl320_mbox; +static struct notifier_block *pl320_notifier; +static DEFINE_SPINLOCK(pl320_legacy_lock); +static DEFINE_MUTEX(pl320_mutex); + +static int __pl320_notify(struct notifier_block *nb, + unsigned long len, void *data) +{ + unsigned long flags; + u32 *mdata = data; + int rc; + + spin_lock_irqsave(&pl320_legacy_lock, flags); + if (!pl320_notifier) { + spin_unlock_irqrestore(&pl320_legacy_lock, flags); + return NOTIFY_DONE; + } + + rc = pl320_notifier->notifier_call(pl320_notifier, + mdata[0], mdata + 1); + spin_unlock_irqrestore(&pl320_legacy_lock, flags); + return rc; +} + +static void __pl320_set_notifier(struct notifier_block *nb) +{ + unsigned long flags; + + spin_lock_irqsave(&pl320_legacy_lock, flags); + pl320_notifier = nb; + spin_unlock_irqrestore(&pl320_legacy_lock, flags); +} + +static struct notifier_block pl320_nb = { + .notifier_call = __pl320_notify, +}; + +static int __pl320_legacy_setup(struct notifier_block *nb, bool exist_ok) +{ + int rc = 0; + + if (WARN_ON(!exist_ok && pl320_mbox)) + return -EBUSY; + + if (pl320_mbox) + return 0; + + __pl320_set_notifier(nb); + + pl320_mbox = mbox_request(NULL, "pl320", &pl320_nb); + if (IS_ERR(pl320_mbox)) { + rc = PTR_ERR(pl320_mbox); + pl320_mbox = NULL; + __pl320_set_notifier(NULL); + } + + return rc; +} + +int __pl320_legacy_ipc_transmit(u32 *data) +{ + int rc; + + mutex_lock(&pl320_mutex); + rc = __pl320_legacy_setup(NULL, true); + if (rc) + goto out; + + rc = mbox_put_message(pl320_mbox, data, 7 * sizeof(*data)); +out: + mutex_unlock(&pl320_mutex); + + return rc; +} +EXPORT_SYMBOL(__pl320_legacy_ipc_transmit); + +int __pl320_legacy_ipc_register_notifier(struct notifier_block *nb) +{ + int rc; + + mutex_lock(&pl320_mutex); + rc = __pl320_legacy_setup(nb, false); + mutex_unlock(&pl320_mutex); + + return rc; +} +EXPORT_SYMBOL(__pl320_legacy_ipc_register_notifier); + +int __pl320_legacy_ipc_unregister_notifier(struct notifier_block *nb) +{ + mutex_lock(&pl320_mutex); + + if (WARN_ON(!pl320_mbox)) { + mutex_unlock(&pl320_mutex); + return -EINVAL; + } + + mbox_release(pl320_mbox); + __pl320_set_notifier(NULL); + pl320_mbox = NULL; + + mutex_unlock(&pl320_mutex); + + return 0; +} +EXPORT_SYMBOL(__pl320_legacy_ipc_unregister_notifier); diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h index 5161f63..2330954 100644 --- a/include/linux/mailbox.h +++ b/include/linux/mailbox.h @@ -12,6 +12,29 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ -int pl320_ipc_transmit(u32 *data); -int pl320_ipc_register_notifier(struct notifier_block *nb); -int pl320_ipc_unregister_notifier(struct notifier_block *nb); +#ifndef PL320_MAILBOX_H +#define PL320_MAILBOX_H + +#include <linux/compiler.h> +#include <linux/mbox.h> + +int __pl320_legacy_ipc_transmit(u32 *data); +int __pl320_legacy_ipc_register_notifier(struct notifier_block *nb); +int __pl320_legacy_ipc_unregister_notifier(struct notifier_block *nb); + +static inline int __deprecated pl320_ipc_transmit(u32 *data) +{ + return __pl320_legacy_ipc_transmit(data); +} +static inline int __deprecated +pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return __pl320_legacy_ipc_register_notifier(nb); +} +static inline int __deprecated +pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return __pl320_legacy_ipc_unregister_notifier(nb); +} + +#endif -- 1.8.1.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html