linux/drivers/remoteproc/wkup_m3_rproc.c
<<
>>
Prefs
   1/*
   2 * TI AMx3 Wakeup M3 Remote Processor driver
   3 *
   4 * Copyright (C) 2014-2015 Texas Instruments, Inc.
   5 *
   6 * Dave Gerlach <d-gerlach@ti.com>
   7 * Suman Anna <s-anna@ti.com>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License
  11 * version 2 as published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18
  19#include <linux/err.h>
  20#include <linux/interrupt.h>
  21#include <linux/kernel.h>
  22#include <linux/module.h>
  23#include <linux/of_device.h>
  24#include <linux/of_address.h>
  25#include <linux/platform_device.h>
  26#include <linux/pm_runtime.h>
  27#include <linux/remoteproc.h>
  28
  29#include <linux/platform_data/wkup_m3.h>
  30
  31#include "remoteproc_internal.h"
  32
  33#define WKUPM3_MEM_MAX  2
  34
  35/**
  36 * struct wkup_m3_mem - WkupM3 internal memory structure
  37 * @cpu_addr: MPU virtual address of the memory region
  38 * @bus_addr: Bus address used to access the memory region
  39 * @dev_addr: Device address from Wakeup M3 view
  40 * @size: Size of the memory region
  41 */
  42struct wkup_m3_mem {
  43        void __iomem *cpu_addr;
  44        phys_addr_t bus_addr;
  45        u32 dev_addr;
  46        size_t size;
  47};
  48
  49/**
  50 * struct wkup_m3_rproc - WkupM3 remote processor state
  51 * @rproc: rproc handle
  52 * @pdev: pointer to platform device
  53 * @mem: WkupM3 memory information
  54 */
  55struct wkup_m3_rproc {
  56        struct rproc *rproc;
  57        struct platform_device *pdev;
  58        struct wkup_m3_mem mem[WKUPM3_MEM_MAX];
  59};
  60
  61static int wkup_m3_rproc_start(struct rproc *rproc)
  62{
  63        struct wkup_m3_rproc *wkupm3 = rproc->priv;
  64        struct platform_device *pdev = wkupm3->pdev;
  65        struct device *dev = &pdev->dev;
  66        struct wkup_m3_platform_data *pdata = dev_get_platdata(dev);
  67
  68        if (pdata->deassert_reset(pdev, pdata->reset_name)) {
  69                dev_err(dev, "Unable to reset wkup_m3!\n");
  70                return -ENODEV;
  71        }
  72
  73        return 0;
  74}
  75
  76static int wkup_m3_rproc_stop(struct rproc *rproc)
  77{
  78        struct wkup_m3_rproc *wkupm3 = rproc->priv;
  79        struct platform_device *pdev = wkupm3->pdev;
  80        struct device *dev = &pdev->dev;
  81        struct wkup_m3_platform_data *pdata = dev_get_platdata(dev);
  82
  83        if (pdata->assert_reset(pdev, pdata->reset_name)) {
  84                dev_err(dev, "Unable to assert reset of wkup_m3!\n");
  85                return -ENODEV;
  86        }
  87
  88        return 0;
  89}
  90
  91static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
  92{
  93        struct wkup_m3_rproc *wkupm3 = rproc->priv;
  94        void *va = NULL;
  95        int i;
  96        u32 offset;
  97
  98        if (len <= 0)
  99                return NULL;
 100
 101        for (i = 0; i < WKUPM3_MEM_MAX; i++) {
 102                if (da >= wkupm3->mem[i].dev_addr && da + len <=
 103                    wkupm3->mem[i].dev_addr +  wkupm3->mem[i].size) {
 104                        offset = da -  wkupm3->mem[i].dev_addr;
 105                        /* __force to make sparse happy with type conversion */
 106                        va = (__force void *)(wkupm3->mem[i].cpu_addr + offset);
 107                        break;
 108                }
 109        }
 110
 111        return va;
 112}
 113
 114static struct rproc_ops wkup_m3_rproc_ops = {
 115        .start          = wkup_m3_rproc_start,
 116        .stop           = wkup_m3_rproc_stop,
 117        .da_to_va       = wkup_m3_rproc_da_to_va,
 118};
 119
 120static const struct of_device_id wkup_m3_rproc_of_match[] = {
 121        { .compatible = "ti,am3352-wkup-m3", },
 122        { .compatible = "ti,am4372-wkup-m3", },
 123        {},
 124};
 125MODULE_DEVICE_TABLE(of, wkup_m3_rproc_of_match);
 126
 127static int wkup_m3_rproc_probe(struct platform_device *pdev)
 128{
 129        struct device *dev = &pdev->dev;
 130        struct wkup_m3_platform_data *pdata = dev->platform_data;
 131        /* umem always needs to be processed first */
 132        const char *mem_names[WKUPM3_MEM_MAX] = { "umem", "dmem" };
 133        struct wkup_m3_rproc *wkupm3;
 134        const char *fw_name;
 135        struct rproc *rproc;
 136        struct resource *res;
 137        const __be32 *addrp;
 138        u32 l4_offset = 0;
 139        u64 size;
 140        int ret;
 141        int i;
 142
 143        if (!(pdata && pdata->deassert_reset && pdata->assert_reset &&
 144              pdata->reset_name)) {
 145                dev_err(dev, "Platform data missing!\n");
 146                return -ENODEV;
 147        }
 148
 149        ret = of_property_read_string(dev->of_node, "ti,pm-firmware",
 150                                      &fw_name);
 151        if (ret) {
 152                dev_err(dev, "No firmware filename given\n");
 153                return -ENODEV;
 154        }
 155
 156        pm_runtime_enable(&pdev->dev);
 157        ret = pm_runtime_get_sync(&pdev->dev);
 158        if (ret < 0) {
 159                dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
 160                goto err;
 161        }
 162
 163        rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops,
 164                            fw_name, sizeof(*wkupm3));
 165        if (!rproc) {
 166                ret = -ENOMEM;
 167                goto err;
 168        }
 169
 170        wkupm3 = rproc->priv;
 171        wkupm3->rproc = rproc;
 172        wkupm3->pdev = pdev;
 173
 174        for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
 175                res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 176                                                   mem_names[i]);
 177                wkupm3->mem[i].cpu_addr = devm_ioremap_resource(dev, res);
 178                if (IS_ERR(wkupm3->mem[i].cpu_addr)) {
 179                        dev_err(&pdev->dev, "devm_ioremap_resource failed for resource %d\n",
 180                                i);
 181                        ret = PTR_ERR(wkupm3->mem[i].cpu_addr);
 182                        goto err;
 183                }
 184                wkupm3->mem[i].bus_addr = res->start;
 185                wkupm3->mem[i].size = resource_size(res);
 186                addrp = of_get_address(dev->of_node, i, &size, NULL);
 187                /*
 188                 * The wkupm3 has umem at address 0 in its view, so the device
 189                 * addresses for each memory region is computed as a relative
 190                 * offset of the bus address for umem, and therefore needs to be
 191                 * processed first.
 192                 */
 193                if (!strcmp(mem_names[i], "umem"))
 194                        l4_offset = be32_to_cpu(*addrp);
 195                wkupm3->mem[i].dev_addr = be32_to_cpu(*addrp) - l4_offset;
 196        }
 197
 198        dev_set_drvdata(dev, rproc);
 199
 200        ret = rproc_add(rproc);
 201        if (ret) {
 202                dev_err(dev, "rproc_add failed\n");
 203                goto err_put_rproc;
 204        }
 205
 206        return 0;
 207
 208err_put_rproc:
 209        rproc_put(rproc);
 210err:
 211        pm_runtime_put_noidle(dev);
 212        pm_runtime_disable(dev);
 213        return ret;
 214}
 215
 216static int wkup_m3_rproc_remove(struct platform_device *pdev)
 217{
 218        struct rproc *rproc = platform_get_drvdata(pdev);
 219
 220        rproc_del(rproc);
 221        rproc_put(rproc);
 222        pm_runtime_put_sync(&pdev->dev);
 223        pm_runtime_disable(&pdev->dev);
 224
 225        return 0;
 226}
 227
 228#ifdef CONFIG_PM
 229static int wkup_m3_rpm_suspend(struct device *dev)
 230{
 231        return -EBUSY;
 232}
 233
 234static int wkup_m3_rpm_resume(struct device *dev)
 235{
 236        return 0;
 237}
 238#endif
 239
 240static const struct dev_pm_ops wkup_m3_rproc_pm_ops = {
 241        SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL)
 242};
 243
 244static struct platform_driver wkup_m3_rproc_driver = {
 245        .probe = wkup_m3_rproc_probe,
 246        .remove = wkup_m3_rproc_remove,
 247        .driver = {
 248                .name = "wkup_m3_rproc",
 249                .of_match_table = wkup_m3_rproc_of_match,
 250                .pm = &wkup_m3_rproc_pm_ops,
 251        },
 252};
 253
 254module_platform_driver(wkup_m3_rproc_driver);
 255
 256MODULE_LICENSE("GPL v2");
 257MODULE_DESCRIPTION("TI Wakeup M3 remote processor control driver");
 258MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
 259