178 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Linux driver for RPC-IF HyperFlash4 *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->dev, &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->dev, &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.dev);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.dev);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.dev, 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 error = rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent);128 if (error)129 return error;130 131 platform_set_drvdata(pdev, hyperbus);132 133 pm_runtime_enable(hyperbus->rpc.dev);134 135 error = rpcif_hw_init(hyperbus->rpc.dev, true);136 if (error)137 goto out_disable_rpm;138 139 hyperbus->hbdev.map.size = hyperbus->rpc.size;140 hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap;141 142 hyperbus->ctlr.dev = dev;143 hyperbus->ctlr.ops = &rpcif_hb_ops;144 hyperbus->hbdev.ctlr = &hyperbus->ctlr;145 hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL);146 error = hyperbus_register_device(&hyperbus->hbdev);147 if (error)148 goto out_disable_rpm;149 150 return 0;151 152out_disable_rpm:153 pm_runtime_disable(hyperbus->rpc.dev);154 return error;155}156 157static void rpcif_hb_remove(struct platform_device *pdev)158{159 struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev);160 161 hyperbus_unregister_device(&hyperbus->hbdev);162 163 pm_runtime_disable(hyperbus->rpc.dev);164}165 166static struct platform_driver rpcif_platform_driver = {167 .probe = rpcif_hb_probe,168 .remove_new = rpcif_hb_remove,169 .driver = {170 .name = "rpc-if-hyperflash",171 },172};173 174module_platform_driver(rpcif_platform_driver);175 176MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver");177MODULE_LICENSE("GPL v2");178