xref: /linux/drivers/mtd/hyperbus/rpc-if.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
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 
rpcif_hb_prepare_read(struct rpcif * rpc,void * to,unsigned long from,ssize_t len)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 
59a198fcd1SGeert Uytterhoeven 	rpcif_prepare(rpc->dev, &op, NULL, NULL);
605de15b61SSergei Shtylyov }
615de15b61SSergei Shtylyov 
rpcif_hb_prepare_write(struct rpcif * rpc,unsigned long to,void * from,ssize_t len)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 
73a198fcd1SGeert Uytterhoeven 	rpcif_prepare(rpc->dev, &op, NULL, NULL);
745de15b61SSergei Shtylyov }
755de15b61SSergei Shtylyov 
rpcif_hb_read16(struct hyperbus_device * hbdev,unsigned long addr)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 
84a198fcd1SGeert Uytterhoeven 	rpcif_manual_xfer(hyperbus->rpc.dev);
855de15b61SSergei Shtylyov 
865de15b61SSergei Shtylyov 	return data.x[0];
875de15b61SSergei Shtylyov }
885de15b61SSergei Shtylyov 
rpcif_hb_write16(struct hyperbus_device * hbdev,unsigned long addr,u16 data)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 
97a198fcd1SGeert Uytterhoeven 	rpcif_manual_xfer(hyperbus->rpc.dev);
985de15b61SSergei Shtylyov }
995de15b61SSergei Shtylyov 
rpcif_hb_copy_from(struct hyperbus_device * hbdev,void * to,unsigned long from,ssize_t len)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 
108a198fcd1SGeert Uytterhoeven 	rpcif_dirmap_read(hyperbus->rpc.dev, 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 
rpcif_hb_probe(struct platform_device * pdev)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 
127981387edSLad Prabhakar 	error = rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent);
128981387edSLad Prabhakar 	if (error)
129981387edSLad Prabhakar 		return error;
1305de15b61SSergei Shtylyov 
1315de15b61SSergei Shtylyov 	platform_set_drvdata(pdev, hyperbus);
1325de15b61SSergei Shtylyov 
13327e5f98cSGeert Uytterhoeven 	pm_runtime_enable(hyperbus->rpc.dev);
1345de15b61SSergei Shtylyov 
135a198fcd1SGeert Uytterhoeven 	error = rpcif_hw_init(hyperbus->rpc.dev, true);
136b04cc0d9SLad Prabhakar 	if (error)
137c223a38dSGeert Uytterhoeven 		goto out_disable_rpm;
1385de15b61SSergei Shtylyov 
1395de15b61SSergei Shtylyov 	hyperbus->hbdev.map.size = hyperbus->rpc.size;
1405de15b61SSergei Shtylyov 	hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap;
1415de15b61SSergei Shtylyov 
1425de15b61SSergei Shtylyov 	hyperbus->ctlr.dev = dev;
1435de15b61SSergei Shtylyov 	hyperbus->ctlr.ops = &rpcif_hb_ops;
1445de15b61SSergei Shtylyov 	hyperbus->hbdev.ctlr = &hyperbus->ctlr;
1455de15b61SSergei Shtylyov 	hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL);
1465de15b61SSergei Shtylyov 	error = hyperbus_register_device(&hyperbus->hbdev);
1475de15b61SSergei Shtylyov 	if (error)
148c223a38dSGeert Uytterhoeven 		goto out_disable_rpm;
1495de15b61SSergei Shtylyov 
150c223a38dSGeert Uytterhoeven 	return 0;
151c223a38dSGeert Uytterhoeven 
152c223a38dSGeert Uytterhoeven out_disable_rpm:
15327e5f98cSGeert Uytterhoeven 	pm_runtime_disable(hyperbus->rpc.dev);
1545de15b61SSergei Shtylyov 	return error;
1555de15b61SSergei Shtylyov }
1565de15b61SSergei Shtylyov 
rpcif_hb_remove(struct platform_device * pdev)157*baaa90c1SUwe Kleine-König static void rpcif_hb_remove(struct platform_device *pdev)
1585de15b61SSergei Shtylyov {
1595de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev);
1600c90466aSUwe Kleine-König 
1610c90466aSUwe Kleine-König 	hyperbus_unregister_device(&hyperbus->hbdev);
1625de15b61SSergei Shtylyov 
16327e5f98cSGeert Uytterhoeven 	pm_runtime_disable(hyperbus->rpc.dev);
1645de15b61SSergei Shtylyov }
1655de15b61SSergei Shtylyov 
1665de15b61SSergei Shtylyov static struct platform_driver rpcif_platform_driver = {
1675de15b61SSergei Shtylyov 	.probe	= rpcif_hb_probe,
168*baaa90c1SUwe Kleine-König 	.remove_new = rpcif_hb_remove,
1695de15b61SSergei Shtylyov 	.driver	= {
1705de15b61SSergei Shtylyov 		.name	= "rpc-if-hyperflash",
1715de15b61SSergei Shtylyov 	},
1725de15b61SSergei Shtylyov };
1735de15b61SSergei Shtylyov 
1745de15b61SSergei Shtylyov module_platform_driver(rpcif_platform_driver);
1755de15b61SSergei Shtylyov 
1765de15b61SSergei Shtylyov MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver");
1775de15b61SSergei Shtylyov MODULE_LICENSE("GPL v2");
178