brintos

brintos / linux-shallow public Read only

0
0
Text · 3.9 KiB · b22aa57 Raw
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