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