162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Linux driver for RPC-IF HyperFlash 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019-2020 Cogent Embedded, Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/mtd/hyperbus.h> 1262306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1362306a36Sopenharmony_ci#include <linux/mux/consumer.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <memory/renesas-rpc-if.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct rpcif_hyperbus { 2162306a36Sopenharmony_ci struct rpcif rpc; 2262306a36Sopenharmony_ci struct hyperbus_ctlr ctlr; 2362306a36Sopenharmony_ci struct hyperbus_device hbdev; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct rpcif_op rpcif_op_tmpl = { 2762306a36Sopenharmony_ci .cmd = { 2862306a36Sopenharmony_ci .buswidth = 8, 2962306a36Sopenharmony_ci .ddr = true, 3062306a36Sopenharmony_ci }, 3162306a36Sopenharmony_ci .ocmd = { 3262306a36Sopenharmony_ci .buswidth = 8, 3362306a36Sopenharmony_ci .ddr = true, 3462306a36Sopenharmony_ci }, 3562306a36Sopenharmony_ci .addr = { 3662306a36Sopenharmony_ci .nbytes = 1, 3762306a36Sopenharmony_ci .buswidth = 8, 3862306a36Sopenharmony_ci .ddr = true, 3962306a36Sopenharmony_ci }, 4062306a36Sopenharmony_ci .data = { 4162306a36Sopenharmony_ci .buswidth = 8, 4262306a36Sopenharmony_ci .ddr = true, 4362306a36Sopenharmony_ci }, 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void rpcif_hb_prepare_read(struct rpcif *rpc, void *to, 4762306a36Sopenharmony_ci unsigned long from, ssize_t len) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct rpcif_op op = rpcif_op_tmpl; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci op.cmd.opcode = HYPERBUS_RW_READ | HYPERBUS_AS_MEM; 5262306a36Sopenharmony_ci op.addr.val = from >> 1; 5362306a36Sopenharmony_ci op.dummy.buswidth = 1; 5462306a36Sopenharmony_ci op.dummy.ncycles = 15; 5562306a36Sopenharmony_ci op.data.dir = RPCIF_DATA_IN; 5662306a36Sopenharmony_ci op.data.nbytes = len; 5762306a36Sopenharmony_ci op.data.buf.in = to; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci rpcif_prepare(rpc->dev, &op, NULL, NULL); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void rpcif_hb_prepare_write(struct rpcif *rpc, unsigned long to, 6362306a36Sopenharmony_ci void *from, ssize_t len) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct rpcif_op op = rpcif_op_tmpl; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci op.cmd.opcode = HYPERBUS_RW_WRITE | HYPERBUS_AS_MEM; 6862306a36Sopenharmony_ci op.addr.val = to >> 1; 6962306a36Sopenharmony_ci op.data.dir = RPCIF_DATA_OUT; 7062306a36Sopenharmony_ci op.data.nbytes = len; 7162306a36Sopenharmony_ci op.data.buf.out = from; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci rpcif_prepare(rpc->dev, &op, NULL, NULL); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic u16 rpcif_hb_read16(struct hyperbus_device *hbdev, unsigned long addr) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct rpcif_hyperbus *hyperbus = 7962306a36Sopenharmony_ci container_of(hbdev, struct rpcif_hyperbus, hbdev); 8062306a36Sopenharmony_ci map_word data; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci rpcif_hb_prepare_read(&hyperbus->rpc, &data, addr, 2); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci rpcif_manual_xfer(hyperbus->rpc.dev); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return data.x[0]; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void rpcif_hb_write16(struct hyperbus_device *hbdev, unsigned long addr, 9062306a36Sopenharmony_ci u16 data) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct rpcif_hyperbus *hyperbus = 9362306a36Sopenharmony_ci container_of(hbdev, struct rpcif_hyperbus, hbdev); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci rpcif_hb_prepare_write(&hyperbus->rpc, addr, &data, 2); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci rpcif_manual_xfer(hyperbus->rpc.dev); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void rpcif_hb_copy_from(struct hyperbus_device *hbdev, void *to, 10162306a36Sopenharmony_ci unsigned long from, ssize_t len) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct rpcif_hyperbus *hyperbus = 10462306a36Sopenharmony_ci container_of(hbdev, struct rpcif_hyperbus, hbdev); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci rpcif_hb_prepare_read(&hyperbus->rpc, to, from, len); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci rpcif_dirmap_read(hyperbus->rpc.dev, from, len, to); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const struct hyperbus_ops rpcif_hb_ops = { 11262306a36Sopenharmony_ci .read16 = rpcif_hb_read16, 11362306a36Sopenharmony_ci .write16 = rpcif_hb_write16, 11462306a36Sopenharmony_ci .copy_from = rpcif_hb_copy_from, 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int rpcif_hb_probe(struct platform_device *pdev) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 12062306a36Sopenharmony_ci struct rpcif_hyperbus *hyperbus; 12162306a36Sopenharmony_ci int error; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci hyperbus = devm_kzalloc(dev, sizeof(*hyperbus), GFP_KERNEL); 12462306a36Sopenharmony_ci if (!hyperbus) 12562306a36Sopenharmony_ci return -ENOMEM; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci error = rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent); 12862306a36Sopenharmony_ci if (error) 12962306a36Sopenharmony_ci return error; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci platform_set_drvdata(pdev, hyperbus); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci pm_runtime_enable(hyperbus->rpc.dev); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci error = rpcif_hw_init(hyperbus->rpc.dev, true); 13662306a36Sopenharmony_ci if (error) 13762306a36Sopenharmony_ci goto out_disable_rpm; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci hyperbus->hbdev.map.size = hyperbus->rpc.size; 14062306a36Sopenharmony_ci hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci hyperbus->ctlr.dev = dev; 14362306a36Sopenharmony_ci hyperbus->ctlr.ops = &rpcif_hb_ops; 14462306a36Sopenharmony_ci hyperbus->hbdev.ctlr = &hyperbus->ctlr; 14562306a36Sopenharmony_ci hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL); 14662306a36Sopenharmony_ci error = hyperbus_register_device(&hyperbus->hbdev); 14762306a36Sopenharmony_ci if (error) 14862306a36Sopenharmony_ci goto out_disable_rpm; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciout_disable_rpm: 15362306a36Sopenharmony_ci pm_runtime_disable(hyperbus->rpc.dev); 15462306a36Sopenharmony_ci return error; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int rpcif_hb_remove(struct platform_device *pdev) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci hyperbus_unregister_device(&hyperbus->hbdev); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci pm_runtime_disable(hyperbus->rpc.dev); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic struct platform_driver rpcif_platform_driver = { 16962306a36Sopenharmony_ci .probe = rpcif_hb_probe, 17062306a36Sopenharmony_ci .remove = rpcif_hb_remove, 17162306a36Sopenharmony_ci .driver = { 17262306a36Sopenharmony_ci .name = "rpc-if-hyperflash", 17362306a36Sopenharmony_ci }, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cimodule_platform_driver(rpcif_platform_driver); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ciMODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver"); 17962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 180