[PATCH v4 2/2] mailbox: exynos: Add support for Exynos850 mailbox
Alexey Klimov <[email protected]>
| Newsgroups | org.kernel.vger.linux-samsung-soc,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Exynos850-based platforms support ACPM and has similar workflow of communicating with ACPM via mailbox, however mailbox controller registers are located at different offsets and writes/reads could be different. To distinguish between such different behaviours, the registers offsets for Exynos850 and the platform-specific data structs are introduced and configuration is described in such structs for gs101 and exynos850 based SoCs. Probe routine now selects the corresponding platform-specific data via device_get_match_data(). Reviewed-by: Krzysztof Kozlowski <[email protected]> Signed-off-by: Alexey Klimov <[email protected]> --- drivers/mailbox/exynos-mailbox.c | 72 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/drivers/mailbox/exynos-mailbox.c b/drivers/mailbox/exynos-mailbox.c index fa02f18948cf..ff75c1c7633c 100644 --- a/drivers/mailbox/exynos-mailbox.c +++ b/drivers/mailbox/exynos-mailbox.c @@ -24,14 +24,60 @@ #define EXYNOS_MBOX_CHAN_COUNT HWEIGHT32(EXYNOS_MBOX_INTGR1_MASK) +#define EXYNOS850_MBOX_INTGR0 0x8 /* Interrupt Generation Register 0 */ +#define EXYNOS850_MBOX_INTMR1 0x24 /* Interrupt Mask Register 1 */ + +#define EXYNOS850_MBOX_INTMR1_MASK GENMASK(15, 0) +#define EXYNOS850_MBOX_INTGR0_MASK GENMASK(31, 16) + +#define EXYNOS850_MBOX_CHAN_COUNT HWEIGHT32(EXYNOS850_MBOX_INTGR0_MASK) + +/** + * struct exynos_mbox_driver_data - platform-specific mailbox configuration. + * @intgr: offset to the IRQ generation register, doorbell + * to APM co-processor. + * @intgr_shift: shift to apply to the value written to IRQ generation + * register. + * @intmr: offset to the IRQ mask register. + * @intmr_mask: value to write to the mask register to mask out all + * interrupts. + * @num_chans: number of channels the mailbox can support (hardware + * capability). + */ +struct exynos_mbox_driver_data { + u32 intgr; + u32 intgr_shift; + u32 intmr; + u32 intmr_mask; + int num_chans; +}; + /** * struct exynos_mbox - driver's private data. * @regs: mailbox registers base address. * @mbox: pointer to the mailbox controller. + * @data: pointer to driver platform-specific data. */ struct exynos_mbox { void __iomem *regs; struct mbox_controller *mbox; + const struct exynos_mbox_driver_data *data; +}; + +static const struct exynos_mbox_driver_data exynos850_mbox_data = { + .intgr = EXYNOS850_MBOX_INTGR0, + .intgr_shift = 16, + .intmr = EXYNOS850_MBOX_INTMR1, + .intmr_mask = EXYNOS850_MBOX_INTMR1_MASK, + .num_chans = EXYNOS850_MBOX_CHAN_COUNT, +}; + +static const struct exynos_mbox_driver_data exynos_gs101_mbox_data = { + .intgr = EXYNOS_MBOX_INTGR1, + .intgr_shift = 0, + .intmr = EXYNOS_MBOX_INTMR0, + .intmr_mask = EXYNOS_MBOX_INTMR0_MASK, + .num_chans = EXYNOS_MBOX_CHAN_COUNT, }; static int exynos_mbox_send_data(struct mbox_chan *chan, void *data) @@ -50,7 +96,9 @@ static int exynos_mbox_send_data(struct mbox_chan *chan, void *data) return -EINVAL; } - writel(BIT(msg->chan_id), exynos_mbox->regs + EXYNOS_MBOX_INTGR1); + /* Ring the doorbell */ + writel(BIT(msg->chan_id) << exynos_mbox->data->intgr_shift, + exynos_mbox->regs + exynos_mbox->data->intgr); return 0; } @@ -80,19 +128,31 @@ static struct mbox_chan *exynos_mbox_of_xlate(struct mbox_controller *mbox, } static const struct of_device_id exynos_mbox_match[] = { - { .compatible = "google,gs101-mbox" }, + { + .compatible = "google,gs101-mbox", + .data = &exynos_gs101_mbox_data + }, + { + .compatible = "samsung,exynos850-mbox", + .data = &exynos850_mbox_data + }, {}, }; MODULE_DEVICE_TABLE(of, exynos_mbox_match); static int exynos_mbox_probe(struct platform_device *pdev) { + const struct exynos_mbox_driver_data *data; struct device *dev = &pdev->dev; struct exynos_mbox *exynos_mbox; struct mbox_controller *mbox; struct mbox_chan *chans; struct clk *pclk; + data = device_get_match_data(&pdev->dev); + if (!data) + return -ENODEV; + exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL); if (!exynos_mbox) return -ENOMEM; @@ -101,8 +161,7 @@ static int exynos_mbox_probe(struct platform_device *pdev) if (!mbox) return -ENOMEM; - chans = devm_kcalloc(dev, EXYNOS_MBOX_CHAN_COUNT, sizeof(*chans), - GFP_KERNEL); + chans = devm_kcalloc(dev, data->num_chans, sizeof(*chans), GFP_KERNEL); if (!chans) return -ENOMEM; @@ -115,7 +174,8 @@ static int exynos_mbox_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(pclk), "Failed to enable clock.\n"); - mbox->num_chans = EXYNOS_MBOX_CHAN_COUNT; + exynos_mbox->data = data; + mbox->num_chans = data->num_chans; mbox->chans = chans; mbox->dev = dev; mbox->ops = &exynos_mbox_chan_ops; @@ -126,7 +186,7 @@ static int exynos_mbox_probe(struct platform_device *pdev) platform_set_drvdata(pdev, exynos_mbox); /* Mask out all interrupts. We support just polling channels for now. */ - writel(EXYNOS_MBOX_INTMR0_MASK, exynos_mbox->regs + EXYNOS_MBOX_INTMR0); + writel(data->intmr_mask, exynos_mbox->regs + data->intmr); return devm_mbox_controller_register(dev, mbox); } -- 2.51.0