cregit-Linux how code gets into the kernel

Release 4.17 drivers/mailbox/hi3660-mailbox.c

Directory: drivers/mailbox
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017-2018 Hisilicon Limited.
// Copyright (c) 2017-2018 Linaro Limited.

#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mailbox_controller.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

#include "mailbox.h"


#define MBOX_CHAN_MAX			32


#define MBOX_RX				0x0

#define MBOX_TX				0x1


#define MBOX_BASE(mbox, ch)		((mbox)->base + ((ch) * 0x40))

#define MBOX_SRC_REG			0x00

#define MBOX_DST_REG			0x04

#define MBOX_DCLR_REG			0x08

#define MBOX_DSTAT_REG			0x0c

#define MBOX_MODE_REG			0x10

#define MBOX_IMASK_REG			0x14

#define MBOX_ICLR_REG			0x18

#define MBOX_SEND_REG			0x1c

#define MBOX_DATA_REG			0x20


#define MBOX_IPC_LOCK_REG		0xa00

#define MBOX_IPC_UNLOCK			0x1acce551


#define MBOX_AUTOMATIC_ACK		1


#define MBOX_STATE_IDLE			BIT(4)

#define MBOX_STATE_ACK			BIT(7)


#define MBOX_MSG_LEN			8

/**
 * Hi3660 mailbox channel information
 *
 * A channel can be used for TX or RX, it can trigger remote
 * processor interrupt to notify remote processor and can receive
 * interrupt if has incoming message.
 *
 * @dst_irq:    Interrupt vector for remote processor
 * @ack_irq:    Interrupt vector for local processor
 */

struct hi3660_chan_info {
	
unsigned int dst_irq;
	
unsigned int ack_irq;
};

/**
 * Hi3660 mailbox controller data
 *
 * Mailbox controller includes 32 channels and can allocate
 * channel for message transferring.
 *
 * @dev:        Device to which it is attached
 * @base:       Base address of the register mapping region
 * @chan:       Representation of channels in mailbox controller
 * @mchan:      Representation of channel info
 * @controller: Representation of a communication channel controller
 */

struct hi3660_mbox {
	
struct device *dev;
	
void __iomem *base;
	
struct mbox_chan chan[MBOX_CHAN_MAX];
	
struct hi3660_chan_info mchan[MBOX_CHAN_MAX];
	
struct mbox_controller controller;
};


static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox) { return container_of(mbox, struct hi3660_mbox, controller); }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua24100.00%1100.00%
Total24100.00%1100.00%


static int hi3660_mbox_check_state(struct mbox_chan *chan) { unsigned long ch = (unsigned long)chan->con_priv; struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); struct hi3660_chan_info *mchan = &mbox->mchan[ch]; void __iomem *base = MBOX_BASE(mbox, ch); unsigned long val; unsigned int ret; /* Mailbox is idle so directly bail out */ if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) return 0; /* Wait for acknowledge from remote */ ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG, val, (val & MBOX_STATE_ACK), 1000, 300000); if (ret) { dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__); return ret; } /* Ensure channel is released */ writel(0xffffffff, base + MBOX_IMASK_REG); writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua154100.00%1100.00%
Total154100.00%1100.00%


static int hi3660_mbox_unlock(struct mbox_chan *chan) { struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); unsigned int val, retry = 3; do { writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG); val = readl(mbox->base + MBOX_IPC_LOCK_REG); if (!val) break; udelay(10); } while (retry--); if (val) dev_err(mbox->dev, "%s: failed to unlock mailbox\n", __func__); return (!val) ? 0 : -ETIMEDOUT; }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua99100.00%1100.00%
Total99100.00%1100.00%


static int hi3660_mbox_acquire_channel(struct mbox_chan *chan) { unsigned long ch = (unsigned long)chan->con_priv; struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); struct hi3660_chan_info *mchan = &mbox->mchan[ch]; void __iomem *base = MBOX_BASE(mbox, ch); unsigned int val, retry; for (retry = 10; retry; retry--) { /* Check if channel is in idle state */ if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) { writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG); /* Check ack bit has been set successfully */ val = readl(base + MBOX_SRC_REG); if (val & BIT(mchan->ack_irq)) break; } } if (!retry) dev_err(mbox->dev, "%s: failed to acquire channel\n", __func__); return retry ? 0 : -ETIMEDOUT; }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua153100.00%1100.00%
Total153100.00%1100.00%


static int hi3660_mbox_startup(struct mbox_chan *chan) { int ret; ret = hi3660_mbox_check_state(chan); if (ret) return ret; ret = hi3660_mbox_unlock(chan); if (ret) return ret; ret = hi3660_mbox_acquire_channel(chan); if (ret) return ret; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua59100.00%1100.00%
Total59100.00%1100.00%


static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg) { unsigned long ch = (unsigned long)chan->con_priv; struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); struct hi3660_chan_info *mchan = &mbox->mchan[ch]; void __iomem *base = MBOX_BASE(mbox, ch); u32 *buf = msg; unsigned int i; /* Ensure channel is released */ writel_relaxed(0xffffffff, base + MBOX_IMASK_REG); writel_relaxed(BIT(mchan->ack_irq), base + MBOX_SRC_REG); /* Clear mask for destination interrupt */ writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG); /* Config destination for interrupt vector */ writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG); /* Automatic acknowledge mode */ writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG); /* Fill message data */ for (i = 0; i < MBOX_MSG_LEN; i++) writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4); /* Trigger data transferring */ writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua187100.00%1100.00%
Total187100.00%1100.00%

static struct mbox_chan_ops hi3660_mbox_ops = { .startup = hi3660_mbox_startup, .send_data = hi3660_mbox_send_data, };
static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller, const struct of_phandle_args *spec) { struct hi3660_mbox *mbox = to_hi3660_mbox(controller); struct hi3660_chan_info *mchan; unsigned int ch = spec->args[0]; if (ch >= MBOX_CHAN_MAX) { dev_err(mbox->dev, "Invalid channel idx %d\n", ch); return ERR_PTR(-EINVAL); } mchan = &mbox->mchan[ch]; mchan->dst_irq = spec->args[1]; mchan->ack_irq = spec->args[2]; return &mbox->chan[ch]; }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua112100.00%1100.00%
Total112100.00%1100.00%

static const struct of_device_id hi3660_mbox_of_match[] = { { .compatible = "hisilicon,hi3660-mbox", }, {}, }; MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match);
static int hi3660_mbox_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct hi3660_mbox *mbox; struct mbox_chan *chan; struct resource *res; unsigned long ch; int err; mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); if (!mbox) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mbox->base = devm_ioremap_resource(dev, res); if (IS_ERR(mbox->base)) return PTR_ERR(mbox->base); mbox->dev = dev; mbox->controller.dev = dev; mbox->controller.chans = mbox->chan; mbox->controller.num_chans = MBOX_CHAN_MAX; mbox->controller.ops = &hi3660_mbox_ops; mbox->controller.of_xlate = hi3660_mbox_xlate; /* Initialize mailbox channel data */ chan = mbox->chan; for (ch = 0; ch < MBOX_CHAN_MAX; ch++) chan[ch].con_priv = (void *)ch; err = mbox_controller_register(&mbox->controller); if (err) { dev_err(dev, "Failed to register mailbox %d\n", err); return err; } platform_set_drvdata(pdev, mbox); dev_info(dev, "Mailbox enabled\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua233100.00%1100.00%
Total233100.00%1100.00%


static int hi3660_mbox_remove(struct platform_device *pdev) { struct hi3660_mbox *mbox = platform_get_drvdata(pdev); mbox_controller_unregister(&mbox->controller); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua32100.00%1100.00%
Total32100.00%1100.00%

static struct platform_driver hi3660_mbox_driver = { .probe = hi3660_mbox_probe, .remove = hi3660_mbox_remove, .driver = { .name = "hi3660-mbox", .of_match_table = hi3660_mbox_of_match, }, };
static int __init hi3660_mbox_init(void) { return platform_driver_register(&hi3660_mbox_driver); }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua16100.00%1100.00%
Total16100.00%1100.00%

core_initcall(hi3660_mbox_init);
static void __exit hi3660_mbox_exit(void) { platform_driver_unregister(&hi3660_mbox_driver); }

Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua15100.00%1100.00%
Total15100.00%1100.00%

module_exit(hi3660_mbox_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller"); MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");

Overall Contributors

PersonTokensPropCommitsCommitProp
Zhong Kaihua1351100.00%1100.00%
Total1351100.00%1100.00%
Directory: drivers/mailbox
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.