linux/drivers/remoteproc/st_remoteproc.c
<<
>>
Prefs
   1/*
   2 * ST's Remote Processor Control Driver
   3 *
   4 * Copyright (C) 2015 STMicroelectronics - All Rights Reserved
   5 *
   6 * Author: Ludovic Barre <ludovic.barre@st.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/clk.h>
  14#include <linux/dma-mapping.h>
  15#include <linux/err.h>
  16#include <linux/interrupt.h>
  17#include <linux/kernel.h>
  18#include <linux/mailbox_client.h>
  19#include <linux/mfd/syscon.h>
  20#include <linux/module.h>
  21#include <linux/of.h>
  22#include <linux/of_device.h>
  23#include <linux/of_reserved_mem.h>
  24#include <linux/platform_device.h>
  25#include <linux/regmap.h>
  26#include <linux/remoteproc.h>
  27#include <linux/reset.h>
  28
  29#include "remoteproc_internal.h"
  30
  31#define ST_RPROC_VQ0            0
  32#define ST_RPROC_VQ1            1
  33#define ST_RPROC_MAX_VRING      2
  34
  35#define MBOX_RX                 0
  36#define MBOX_TX                 1
  37#define MBOX_MAX                2
  38
  39struct st_rproc_config {
  40        bool                    sw_reset;
  41        bool                    pwr_reset;
  42        unsigned long           bootaddr_mask;
  43};
  44
  45struct st_rproc {
  46        struct st_rproc_config  *config;
  47        struct reset_control    *sw_reset;
  48        struct reset_control    *pwr_reset;
  49        struct clk              *clk;
  50        u32                     clk_rate;
  51        struct regmap           *boot_base;
  52        u32                     boot_offset;
  53        struct mbox_chan        *mbox_chan[ST_RPROC_MAX_VRING * MBOX_MAX];
  54        struct mbox_client mbox_client_vq0;
  55        struct mbox_client mbox_client_vq1;
  56};
  57
  58static void st_rproc_mbox_callback(struct device *dev, u32 msg)
  59{
  60        struct rproc *rproc = dev_get_drvdata(dev);
  61
  62        if (rproc_vq_interrupt(rproc, msg) == IRQ_NONE)
  63                dev_dbg(dev, "no message was found in vqid %d\n", msg);
  64}
  65
  66static
  67void st_rproc_mbox_callback_vq0(struct mbox_client *mbox_client, void *data)
  68{
  69        st_rproc_mbox_callback(mbox_client->dev, 0);
  70}
  71
  72static
  73void st_rproc_mbox_callback_vq1(struct mbox_client *mbox_client, void *data)
  74{
  75        st_rproc_mbox_callback(mbox_client->dev, 1);
  76}
  77
  78static void st_rproc_kick(struct rproc *rproc, int vqid)
  79{
  80        struct st_rproc *ddata = rproc->priv;
  81        struct device *dev = rproc->dev.parent;
  82        int ret;
  83
  84        /* send the index of the triggered virtqueue in the mailbox payload */
  85        if (WARN_ON(vqid >= ST_RPROC_MAX_VRING))
  86                return;
  87
  88        ret = mbox_send_message(ddata->mbox_chan[vqid * MBOX_MAX + MBOX_TX],
  89                                (void *)&vqid);
  90        if (ret < 0)
  91                dev_err(dev, "failed to send message via mbox: %d\n", ret);
  92}
  93
  94static int st_rproc_start(struct rproc *rproc)
  95{
  96        struct st_rproc *ddata = rproc->priv;
  97        int err;
  98
  99        regmap_update_bits(ddata->boot_base, ddata->boot_offset,
 100                           ddata->config->bootaddr_mask, rproc->bootaddr);
 101
 102        err = clk_enable(ddata->clk);
 103        if (err) {
 104                dev_err(&rproc->dev, "Failed to enable clock\n");
 105                return err;
 106        }
 107
 108        if (ddata->config->sw_reset) {
 109                err = reset_control_deassert(ddata->sw_reset);
 110                if (err) {
 111                        dev_err(&rproc->dev, "Failed to deassert S/W Reset\n");
 112                        goto sw_reset_fail;
 113                }
 114        }
 115
 116        if (ddata->config->pwr_reset) {
 117                err = reset_control_deassert(ddata->pwr_reset);
 118                if (err) {
 119                        dev_err(&rproc->dev, "Failed to deassert Power Reset\n");
 120                        goto pwr_reset_fail;
 121                }
 122        }
 123
 124        dev_info(&rproc->dev, "Started from 0x%x\n", rproc->bootaddr);
 125
 126        return 0;
 127
 128
 129pwr_reset_fail:
 130        if (ddata->config->pwr_reset)
 131                reset_control_assert(ddata->sw_reset);
 132sw_reset_fail:
 133        clk_disable(ddata->clk);
 134
 135        return err;
 136}
 137
 138static int st_rproc_stop(struct rproc *rproc)
 139{
 140        struct st_rproc *ddata = rproc->priv;
 141        int sw_err = 0, pwr_err = 0;
 142
 143        if (ddata->config->sw_reset) {
 144                sw_err = reset_control_assert(ddata->sw_reset);
 145                if (sw_err)
 146                        dev_err(&rproc->dev, "Failed to assert S/W Reset\n");
 147        }
 148
 149        if (ddata->config->pwr_reset) {
 150                pwr_err = reset_control_assert(ddata->pwr_reset);
 151                if (pwr_err)
 152                        dev_err(&rproc->dev, "Failed to assert Power Reset\n");
 153        }
 154
 155        clk_disable(ddata->clk);
 156
 157        return sw_err ?: pwr_err;
 158}
 159
 160static const struct rproc_ops st_rproc_ops = {
 161        .kick           = st_rproc_kick,
 162        .start          = st_rproc_start,
 163        .stop           = st_rproc_stop,
 164};
 165
 166/*
 167 * Fetch state of the processor: 0 is off, 1 is on.
 168 */
 169static int st_rproc_state(struct platform_device *pdev)
 170{
 171        struct rproc *rproc = platform_get_drvdata(pdev);
 172        struct st_rproc *ddata = rproc->priv;
 173        int reset_sw = 0, reset_pwr = 0;
 174
 175        if (ddata->config->sw_reset)
 176                reset_sw = reset_control_status(ddata->sw_reset);
 177
 178        if (ddata->config->pwr_reset)
 179                reset_pwr = reset_control_status(ddata->pwr_reset);
 180
 181        if (reset_sw < 0 || reset_pwr < 0)
 182                return -EINVAL;
 183
 184        return !reset_sw && !reset_pwr;
 185}
 186
 187static const struct st_rproc_config st40_rproc_cfg = {
 188        .sw_reset = true,
 189        .pwr_reset = true,
 190        .bootaddr_mask = GENMASK(28, 1),
 191};
 192
 193static const struct st_rproc_config st231_rproc_cfg = {
 194        .sw_reset = true,
 195        .pwr_reset = false,
 196        .bootaddr_mask = GENMASK(31, 6),
 197};
 198
 199static const struct of_device_id st_rproc_match[] = {
 200        { .compatible = "st,st40-rproc", .data = &st40_rproc_cfg },
 201        { .compatible = "st,st231-rproc", .data = &st231_rproc_cfg },
 202        {},
 203};
 204MODULE_DEVICE_TABLE(of, st_rproc_match);
 205
 206static int st_rproc_parse_dt(struct platform_device *pdev)
 207{
 208        struct device *dev = &pdev->dev;
 209        struct rproc *rproc = platform_get_drvdata(pdev);
 210        struct st_rproc *ddata = rproc->priv;
 211        struct device_node *np = dev->of_node;
 212        int err;
 213
 214        if (ddata->config->sw_reset) {
 215                ddata->sw_reset = devm_reset_control_get(dev, "sw_reset");
 216                if (IS_ERR(ddata->sw_reset)) {
 217                        dev_err(dev, "Failed to get S/W Reset\n");
 218                        return PTR_ERR(ddata->sw_reset);
 219                }
 220        }
 221
 222        if (ddata->config->pwr_reset) {
 223                ddata->pwr_reset = devm_reset_control_get(dev, "pwr_reset");
 224                if (IS_ERR(ddata->pwr_reset)) {
 225                        dev_err(dev, "Failed to get Power Reset\n");
 226                        return PTR_ERR(ddata->pwr_reset);
 227                }
 228        }
 229
 230        ddata->clk = devm_clk_get(dev, NULL);
 231        if (IS_ERR(ddata->clk)) {
 232                dev_err(dev, "Failed to get clock\n");
 233                return PTR_ERR(ddata->clk);
 234        }
 235
 236        err = of_property_read_u32(np, "clock-frequency", &ddata->clk_rate);
 237        if (err) {
 238                dev_err(dev, "failed to get clock frequency\n");
 239                return err;
 240        }
 241
 242        ddata->boot_base = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
 243        if (IS_ERR(ddata->boot_base)) {
 244                dev_err(dev, "Boot base not found\n");
 245                return PTR_ERR(ddata->boot_base);
 246        }
 247
 248        err = of_property_read_u32_index(np, "st,syscfg", 1,
 249                                         &ddata->boot_offset);
 250        if (err) {
 251                dev_err(dev, "Boot offset not found\n");
 252                return -EINVAL;
 253        }
 254
 255        err = of_reserved_mem_device_init(dev);
 256        if (err) {
 257                dev_err(dev, "Failed to obtain shared memory\n");
 258                return err;
 259        }
 260
 261        err = clk_prepare(ddata->clk);
 262        if (err)
 263                dev_err(dev, "failed to get clock\n");
 264
 265        return err;
 266}
 267
 268static int st_rproc_probe(struct platform_device *pdev)
 269{
 270        struct device *dev = &pdev->dev;
 271        const struct of_device_id *match;
 272        struct st_rproc *ddata;
 273        struct device_node *np = dev->of_node;
 274        struct rproc *rproc;
 275        struct mbox_chan *chan;
 276        int enabled;
 277        int ret, i;
 278
 279        match = of_match_device(st_rproc_match, dev);
 280        if (!match || !match->data) {
 281                dev_err(dev, "No device match found\n");
 282                return -ENODEV;
 283        }
 284
 285        rproc = rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata));
 286        if (!rproc)
 287                return -ENOMEM;
 288
 289        rproc->has_iommu = false;
 290        ddata = rproc->priv;
 291        ddata->config = (struct st_rproc_config *)match->data;
 292
 293        platform_set_drvdata(pdev, rproc);
 294
 295        ret = st_rproc_parse_dt(pdev);
 296        if (ret)
 297                goto free_rproc;
 298
 299        enabled = st_rproc_state(pdev);
 300        if (enabled < 0) {
 301                ret = enabled;
 302                goto free_clk;
 303        }
 304
 305        if (enabled) {
 306                atomic_inc(&rproc->power);
 307                rproc->state = RPROC_RUNNING;
 308        } else {
 309                clk_set_rate(ddata->clk, ddata->clk_rate);
 310        }
 311
 312        if (of_get_property(np, "mbox-names", NULL)) {
 313                ddata->mbox_client_vq0.dev              = dev;
 314                ddata->mbox_client_vq0.tx_done          = NULL;
 315                ddata->mbox_client_vq0.tx_block = false;
 316                ddata->mbox_client_vq0.knows_txdone     = false;
 317                ddata->mbox_client_vq0.rx_callback      = st_rproc_mbox_callback_vq0;
 318
 319                ddata->mbox_client_vq1.dev              = dev;
 320                ddata->mbox_client_vq1.tx_done          = NULL;
 321                ddata->mbox_client_vq1.tx_block = false;
 322                ddata->mbox_client_vq1.knows_txdone     = false;
 323                ddata->mbox_client_vq1.rx_callback      = st_rproc_mbox_callback_vq1;
 324
 325                /*
 326                 * To control a co-processor without IPC mechanism.
 327                 * This driver can be used without mbox and rpmsg.
 328                 */
 329                chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_rx");
 330                if (IS_ERR(chan)) {
 331                        dev_err(&rproc->dev, "failed to request mbox chan 0\n");
 332                        ret = PTR_ERR(chan);
 333                        goto free_clk;
 334                }
 335                ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_RX] = chan;
 336
 337                chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_tx");
 338                if (IS_ERR(chan)) {
 339                        dev_err(&rproc->dev, "failed to request mbox chan 0\n");
 340                        ret = PTR_ERR(chan);
 341                        goto free_mbox;
 342                }
 343                ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_TX] = chan;
 344
 345                chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_rx");
 346                if (IS_ERR(chan)) {
 347                        dev_err(&rproc->dev, "failed to request mbox chan 1\n");
 348                        ret = PTR_ERR(chan);
 349                        goto free_mbox;
 350                }
 351                ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_RX] = chan;
 352
 353                chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_tx");
 354                if (IS_ERR(chan)) {
 355                        dev_err(&rproc->dev, "failed to request mbox chan 1\n");
 356                        ret = PTR_ERR(chan);
 357                        goto free_mbox;
 358                }
 359                ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_TX] = chan;
 360        }
 361
 362        ret = rproc_add(rproc);
 363        if (ret)
 364                goto free_mbox;
 365
 366        return 0;
 367
 368free_mbox:
 369        for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++)
 370                mbox_free_channel(ddata->mbox_chan[i]);
 371free_clk:
 372        clk_unprepare(ddata->clk);
 373free_rproc:
 374        rproc_free(rproc);
 375        return ret;
 376}
 377
 378static int st_rproc_remove(struct platform_device *pdev)
 379{
 380        struct rproc *rproc = platform_get_drvdata(pdev);
 381        struct st_rproc *ddata = rproc->priv;
 382        int i;
 383
 384        rproc_del(rproc);
 385
 386        clk_disable_unprepare(ddata->clk);
 387
 388        of_reserved_mem_device_release(&pdev->dev);
 389
 390        for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++)
 391                mbox_free_channel(ddata->mbox_chan[i]);
 392
 393        rproc_free(rproc);
 394
 395        return 0;
 396}
 397
 398static struct platform_driver st_rproc_driver = {
 399        .probe = st_rproc_probe,
 400        .remove = st_rproc_remove,
 401        .driver = {
 402                .name = "st-rproc",
 403                .of_match_table = of_match_ptr(st_rproc_match),
 404        },
 405};
 406module_platform_driver(st_rproc_driver);
 407
 408MODULE_DESCRIPTION("ST Remote Processor Control Driver");
 409MODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>");
 410MODULE_LICENSE("GPL v2");
 411