linux/drivers/mtd/hyperbus/rpc-if.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Linux driver for RPC-IF HyperFlash
   4 *
   5 * Copyright (C) 2019-2020 Cogent Embedded, Inc.
   6 */
   7
   8#include <linux/err.h>
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/mtd/hyperbus.h>
  12#include <linux/mtd/mtd.h>
  13#include <linux/mux/consumer.h>
  14#include <linux/of.h>
  15#include <linux/platform_device.h>
  16#include <linux/types.h>
  17
  18#include <memory/renesas-rpc-if.h>
  19
  20struct  rpcif_hyperbus {
  21        struct rpcif rpc;
  22        struct hyperbus_ctlr ctlr;
  23        struct hyperbus_device hbdev;
  24};
  25
  26static const struct rpcif_op rpcif_op_tmpl = {
  27        .cmd = {
  28                .buswidth = 8,
  29                .ddr = true,
  30        },
  31        .ocmd = {
  32                .buswidth = 8,
  33                .ddr = true,
  34        },
  35        .addr = {
  36                .nbytes = 1,
  37                .buswidth = 8,
  38                .ddr = true,
  39        },
  40        .data = {
  41                .buswidth = 8,
  42                .ddr = true,
  43        },
  44};
  45
  46static void rpcif_hb_prepare_read(struct rpcif *rpc, void *to,
  47                                  unsigned long from, ssize_t len)
  48{
  49        struct rpcif_op op = rpcif_op_tmpl;
  50
  51        op.cmd.opcode = HYPERBUS_RW_READ | HYPERBUS_AS_MEM;
  52        op.addr.val = from >> 1;
  53        op.dummy.buswidth = 1;
  54        op.dummy.ncycles = 15;
  55        op.data.dir = RPCIF_DATA_IN;
  56        op.data.nbytes = len;
  57        op.data.buf.in = to;
  58
  59        rpcif_prepare(rpc, &op, NULL, NULL);
  60}
  61
  62static void rpcif_hb_prepare_write(struct rpcif *rpc, unsigned long to,
  63                                   void *from, ssize_t len)
  64{
  65        struct rpcif_op op = rpcif_op_tmpl;
  66
  67        op.cmd.opcode = HYPERBUS_RW_WRITE | HYPERBUS_AS_MEM;
  68        op.addr.val = to >> 1;
  69        op.data.dir = RPCIF_DATA_OUT;
  70        op.data.nbytes = len;
  71        op.data.buf.out = from;
  72
  73        rpcif_prepare(rpc, &op, NULL, NULL);
  74}
  75
  76static u16 rpcif_hb_read16(struct hyperbus_device *hbdev, unsigned long addr)
  77{
  78        struct rpcif_hyperbus *hyperbus =
  79                container_of(hbdev, struct rpcif_hyperbus, hbdev);
  80        map_word data;
  81
  82        rpcif_hb_prepare_read(&hyperbus->rpc, &data, addr, 2);
  83
  84        rpcif_manual_xfer(&hyperbus->rpc);
  85
  86        return data.x[0];
  87}
  88
  89static void rpcif_hb_write16(struct hyperbus_device *hbdev, unsigned long addr,
  90                             u16 data)
  91{
  92        struct rpcif_hyperbus *hyperbus =
  93                container_of(hbdev, struct rpcif_hyperbus, hbdev);
  94
  95        rpcif_hb_prepare_write(&hyperbus->rpc, addr, &data, 2);
  96
  97        rpcif_manual_xfer(&hyperbus->rpc);
  98}
  99
 100static void rpcif_hb_copy_from(struct hyperbus_device *hbdev, void *to,
 101                               unsigned long from, ssize_t len)
 102{
 103        struct rpcif_hyperbus *hyperbus =
 104                container_of(hbdev, struct rpcif_hyperbus, hbdev);
 105
 106        rpcif_hb_prepare_read(&hyperbus->rpc, to, from, len);
 107
 108        rpcif_dirmap_read(&hyperbus->rpc, from, len, to);
 109}
 110
 111static const struct hyperbus_ops rpcif_hb_ops = {
 112        .read16 = rpcif_hb_read16,
 113        .write16 = rpcif_hb_write16,
 114        .copy_from = rpcif_hb_copy_from,
 115};
 116
 117static int rpcif_hb_probe(struct platform_device *pdev)
 118{
 119        struct device *dev = &pdev->dev;
 120        struct rpcif_hyperbus *hyperbus;
 121        int error;
 122
 123        hyperbus = devm_kzalloc(dev, sizeof(*hyperbus), GFP_KERNEL);
 124        if (!hyperbus)
 125                return -ENOMEM;
 126
 127        rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent);
 128
 129        platform_set_drvdata(pdev, hyperbus);
 130
 131        rpcif_enable_rpm(&hyperbus->rpc);
 132
 133        rpcif_hw_init(&hyperbus->rpc, true);
 134
 135        hyperbus->hbdev.map.size = hyperbus->rpc.size;
 136        hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap;
 137
 138        hyperbus->ctlr.dev = dev;
 139        hyperbus->ctlr.ops = &rpcif_hb_ops;
 140        hyperbus->hbdev.ctlr = &hyperbus->ctlr;
 141        hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL);
 142        error = hyperbus_register_device(&hyperbus->hbdev);
 143        if (error)
 144                rpcif_disable_rpm(&hyperbus->rpc);
 145
 146        return error;
 147}
 148
 149static int rpcif_hb_remove(struct platform_device *pdev)
 150{
 151        struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev);
 152        int error = hyperbus_unregister_device(&hyperbus->hbdev);
 153        struct rpcif *rpc = dev_get_drvdata(pdev->dev.parent);
 154
 155        rpcif_disable_rpm(rpc);
 156        return error;
 157}
 158
 159static struct platform_driver rpcif_platform_driver = {
 160        .probe  = rpcif_hb_probe,
 161        .remove = rpcif_hb_remove,
 162        .driver = {
 163                .name   = "rpc-if-hyperflash",
 164        },
 165};
 166
 167module_platform_driver(rpcif_platform_driver);
 168
 169MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver");
 170MODULE_LICENSE("GPL v2");
 171