xref: /linux/drivers/mtd/hyperbus/rpc-if.c (revision b04cc0d912eb80d3c438b11d96ca847c3e77e8ab)
15de15b61SSergei Shtylyov // SPDX-License-Identifier: GPL-2.0
25de15b61SSergei Shtylyov /*
35de15b61SSergei Shtylyov  * Linux driver for RPC-IF HyperFlash
45de15b61SSergei Shtylyov  *
55de15b61SSergei Shtylyov  * Copyright (C) 2019-2020 Cogent Embedded, Inc.
65de15b61SSergei Shtylyov  */
75de15b61SSergei Shtylyov 
85de15b61SSergei Shtylyov #include <linux/err.h>
95de15b61SSergei Shtylyov #include <linux/kernel.h>
105de15b61SSergei Shtylyov #include <linux/module.h>
115de15b61SSergei Shtylyov #include <linux/mtd/hyperbus.h>
125de15b61SSergei Shtylyov #include <linux/mtd/mtd.h>
135de15b61SSergei Shtylyov #include <linux/mux/consumer.h>
145de15b61SSergei Shtylyov #include <linux/of.h>
155de15b61SSergei Shtylyov #include <linux/platform_device.h>
165de15b61SSergei Shtylyov #include <linux/types.h>
175de15b61SSergei Shtylyov 
185de15b61SSergei Shtylyov #include <memory/renesas-rpc-if.h>
195de15b61SSergei Shtylyov 
205de15b61SSergei Shtylyov struct	rpcif_hyperbus {
215de15b61SSergei Shtylyov 	struct rpcif rpc;
225de15b61SSergei Shtylyov 	struct hyperbus_ctlr ctlr;
235de15b61SSergei Shtylyov 	struct hyperbus_device hbdev;
245de15b61SSergei Shtylyov };
255de15b61SSergei Shtylyov 
265de15b61SSergei Shtylyov static const struct rpcif_op rpcif_op_tmpl = {
275de15b61SSergei Shtylyov 	.cmd = {
285de15b61SSergei Shtylyov 		.buswidth = 8,
295de15b61SSergei Shtylyov 		.ddr = true,
305de15b61SSergei Shtylyov 	},
315de15b61SSergei Shtylyov 	.ocmd = {
325de15b61SSergei Shtylyov 		.buswidth = 8,
335de15b61SSergei Shtylyov 		.ddr = true,
345de15b61SSergei Shtylyov 	},
355de15b61SSergei Shtylyov 	.addr = {
365de15b61SSergei Shtylyov 		.nbytes = 1,
375de15b61SSergei Shtylyov 		.buswidth = 8,
385de15b61SSergei Shtylyov 		.ddr = true,
395de15b61SSergei Shtylyov 	},
405de15b61SSergei Shtylyov 	.data = {
415de15b61SSergei Shtylyov 		.buswidth = 8,
425de15b61SSergei Shtylyov 		.ddr = true,
435de15b61SSergei Shtylyov 	},
445de15b61SSergei Shtylyov };
455de15b61SSergei Shtylyov 
465de15b61SSergei Shtylyov static void rpcif_hb_prepare_read(struct rpcif *rpc, void *to,
475de15b61SSergei Shtylyov 				  unsigned long from, ssize_t len)
485de15b61SSergei Shtylyov {
495de15b61SSergei Shtylyov 	struct rpcif_op op = rpcif_op_tmpl;
505de15b61SSergei Shtylyov 
515de15b61SSergei Shtylyov 	op.cmd.opcode = HYPERBUS_RW_READ | HYPERBUS_AS_MEM;
525de15b61SSergei Shtylyov 	op.addr.val = from >> 1;
535de15b61SSergei Shtylyov 	op.dummy.buswidth = 1;
545de15b61SSergei Shtylyov 	op.dummy.ncycles = 15;
555de15b61SSergei Shtylyov 	op.data.dir = RPCIF_DATA_IN;
565de15b61SSergei Shtylyov 	op.data.nbytes = len;
575de15b61SSergei Shtylyov 	op.data.buf.in = to;
585de15b61SSergei Shtylyov 
595de15b61SSergei Shtylyov 	rpcif_prepare(rpc, &op, NULL, NULL);
605de15b61SSergei Shtylyov }
615de15b61SSergei Shtylyov 
625de15b61SSergei Shtylyov static void rpcif_hb_prepare_write(struct rpcif *rpc, unsigned long to,
635de15b61SSergei Shtylyov 				   void *from, ssize_t len)
645de15b61SSergei Shtylyov {
655de15b61SSergei Shtylyov 	struct rpcif_op op = rpcif_op_tmpl;
665de15b61SSergei Shtylyov 
675de15b61SSergei Shtylyov 	op.cmd.opcode = HYPERBUS_RW_WRITE | HYPERBUS_AS_MEM;
685de15b61SSergei Shtylyov 	op.addr.val = to >> 1;
695de15b61SSergei Shtylyov 	op.data.dir = RPCIF_DATA_OUT;
705de15b61SSergei Shtylyov 	op.data.nbytes = len;
715de15b61SSergei Shtylyov 	op.data.buf.out = from;
725de15b61SSergei Shtylyov 
735de15b61SSergei Shtylyov 	rpcif_prepare(rpc, &op, NULL, NULL);
745de15b61SSergei Shtylyov }
755de15b61SSergei Shtylyov 
765de15b61SSergei Shtylyov static u16 rpcif_hb_read16(struct hyperbus_device *hbdev, unsigned long addr)
775de15b61SSergei Shtylyov {
785de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus =
795de15b61SSergei Shtylyov 		container_of(hbdev, struct rpcif_hyperbus, hbdev);
805de15b61SSergei Shtylyov 	map_word data;
815de15b61SSergei Shtylyov 
825de15b61SSergei Shtylyov 	rpcif_hb_prepare_read(&hyperbus->rpc, &data, addr, 2);
835de15b61SSergei Shtylyov 
845de15b61SSergei Shtylyov 	rpcif_manual_xfer(&hyperbus->rpc);
855de15b61SSergei Shtylyov 
865de15b61SSergei Shtylyov 	return data.x[0];
875de15b61SSergei Shtylyov }
885de15b61SSergei Shtylyov 
895de15b61SSergei Shtylyov static void rpcif_hb_write16(struct hyperbus_device *hbdev, unsigned long addr,
905de15b61SSergei Shtylyov 			     u16 data)
915de15b61SSergei Shtylyov {
925de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus =
935de15b61SSergei Shtylyov 		container_of(hbdev, struct rpcif_hyperbus, hbdev);
945de15b61SSergei Shtylyov 
955de15b61SSergei Shtylyov 	rpcif_hb_prepare_write(&hyperbus->rpc, addr, &data, 2);
965de15b61SSergei Shtylyov 
975de15b61SSergei Shtylyov 	rpcif_manual_xfer(&hyperbus->rpc);
985de15b61SSergei Shtylyov }
995de15b61SSergei Shtylyov 
1005de15b61SSergei Shtylyov static void rpcif_hb_copy_from(struct hyperbus_device *hbdev, void *to,
1015de15b61SSergei Shtylyov 			       unsigned long from, ssize_t len)
1025de15b61SSergei Shtylyov {
1035de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus =
1045de15b61SSergei Shtylyov 		container_of(hbdev, struct rpcif_hyperbus, hbdev);
1055de15b61SSergei Shtylyov 
1065de15b61SSergei Shtylyov 	rpcif_hb_prepare_read(&hyperbus->rpc, to, from, len);
1075de15b61SSergei Shtylyov 
1085de15b61SSergei Shtylyov 	rpcif_dirmap_read(&hyperbus->rpc, from, len, to);
1095de15b61SSergei Shtylyov }
1105de15b61SSergei Shtylyov 
1115de15b61SSergei Shtylyov static const struct hyperbus_ops rpcif_hb_ops = {
1125de15b61SSergei Shtylyov 	.read16 = rpcif_hb_read16,
1135de15b61SSergei Shtylyov 	.write16 = rpcif_hb_write16,
1145de15b61SSergei Shtylyov 	.copy_from = rpcif_hb_copy_from,
1155de15b61SSergei Shtylyov };
1165de15b61SSergei Shtylyov 
1175de15b61SSergei Shtylyov static int rpcif_hb_probe(struct platform_device *pdev)
1185de15b61SSergei Shtylyov {
1195de15b61SSergei Shtylyov 	struct device *dev = &pdev->dev;
1205de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus;
1215de15b61SSergei Shtylyov 	int error;
1225de15b61SSergei Shtylyov 
1235de15b61SSergei Shtylyov 	hyperbus = devm_kzalloc(dev, sizeof(*hyperbus), GFP_KERNEL);
1245de15b61SSergei Shtylyov 	if (!hyperbus)
1255de15b61SSergei Shtylyov 		return -ENOMEM;
1265de15b61SSergei Shtylyov 
1275de15b61SSergei Shtylyov 	rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent);
1285de15b61SSergei Shtylyov 
1295de15b61SSergei Shtylyov 	platform_set_drvdata(pdev, hyperbus);
1305de15b61SSergei Shtylyov 
1315de15b61SSergei Shtylyov 	rpcif_enable_rpm(&hyperbus->rpc);
1325de15b61SSergei Shtylyov 
133*b04cc0d9SLad Prabhakar 	error = rpcif_hw_init(&hyperbus->rpc, true);
134*b04cc0d9SLad Prabhakar 	if (error)
135*b04cc0d9SLad Prabhakar 		return error;
1365de15b61SSergei Shtylyov 
1375de15b61SSergei Shtylyov 	hyperbus->hbdev.map.size = hyperbus->rpc.size;
1385de15b61SSergei Shtylyov 	hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap;
1395de15b61SSergei Shtylyov 
1405de15b61SSergei Shtylyov 	hyperbus->ctlr.dev = dev;
1415de15b61SSergei Shtylyov 	hyperbus->ctlr.ops = &rpcif_hb_ops;
1425de15b61SSergei Shtylyov 	hyperbus->hbdev.ctlr = &hyperbus->ctlr;
1435de15b61SSergei Shtylyov 	hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL);
1445de15b61SSergei Shtylyov 	error = hyperbus_register_device(&hyperbus->hbdev);
1455de15b61SSergei Shtylyov 	if (error)
1465de15b61SSergei Shtylyov 		rpcif_disable_rpm(&hyperbus->rpc);
1475de15b61SSergei Shtylyov 
1485de15b61SSergei Shtylyov 	return error;
1495de15b61SSergei Shtylyov }
1505de15b61SSergei Shtylyov 
1515de15b61SSergei Shtylyov static int rpcif_hb_remove(struct platform_device *pdev)
1525de15b61SSergei Shtylyov {
1535de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev);
1545de15b61SSergei Shtylyov 	int error = hyperbus_unregister_device(&hyperbus->hbdev);
1555de15b61SSergei Shtylyov 	struct rpcif *rpc = dev_get_drvdata(pdev->dev.parent);
1565de15b61SSergei Shtylyov 
1575de15b61SSergei Shtylyov 	rpcif_disable_rpm(rpc);
1585de15b61SSergei Shtylyov 	return error;
1595de15b61SSergei Shtylyov }
1605de15b61SSergei Shtylyov 
1615de15b61SSergei Shtylyov static struct platform_driver rpcif_platform_driver = {
1625de15b61SSergei Shtylyov 	.probe	= rpcif_hb_probe,
1635de15b61SSergei Shtylyov 	.remove	= rpcif_hb_remove,
1645de15b61SSergei Shtylyov 	.driver	= {
1655de15b61SSergei Shtylyov 		.name	= "rpc-if-hyperflash",
1665de15b61SSergei Shtylyov 	},
1675de15b61SSergei Shtylyov };
1685de15b61SSergei Shtylyov 
1695de15b61SSergei Shtylyov module_platform_driver(rpcif_platform_driver);
1705de15b61SSergei Shtylyov 
1715de15b61SSergei Shtylyov MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver");
1725de15b61SSergei Shtylyov MODULE_LICENSE("GPL v2");
173