xref: /linux/drivers/mtd/hyperbus/rpc-if.c (revision cbecf716ca618fd44feda6bd9a64a8179d031fc5)
1*5de15b61SSergei Shtylyov // SPDX-License-Identifier: GPL-2.0
2*5de15b61SSergei Shtylyov /*
3*5de15b61SSergei Shtylyov  * Linux driver for RPC-IF HyperFlash
4*5de15b61SSergei Shtylyov  *
5*5de15b61SSergei Shtylyov  * Copyright (C) 2019-2020 Cogent Embedded, Inc.
6*5de15b61SSergei Shtylyov  */
7*5de15b61SSergei Shtylyov 
8*5de15b61SSergei Shtylyov #include <linux/err.h>
9*5de15b61SSergei Shtylyov #include <linux/kernel.h>
10*5de15b61SSergei Shtylyov #include <linux/module.h>
11*5de15b61SSergei Shtylyov #include <linux/mtd/hyperbus.h>
12*5de15b61SSergei Shtylyov #include <linux/mtd/mtd.h>
13*5de15b61SSergei Shtylyov #include <linux/mux/consumer.h>
14*5de15b61SSergei Shtylyov #include <linux/of.h>
15*5de15b61SSergei Shtylyov #include <linux/platform_device.h>
16*5de15b61SSergei Shtylyov #include <linux/types.h>
17*5de15b61SSergei Shtylyov 
18*5de15b61SSergei Shtylyov #include <memory/renesas-rpc-if.h>
19*5de15b61SSergei Shtylyov 
20*5de15b61SSergei Shtylyov struct	rpcif_hyperbus {
21*5de15b61SSergei Shtylyov 	struct rpcif rpc;
22*5de15b61SSergei Shtylyov 	struct hyperbus_ctlr ctlr;
23*5de15b61SSergei Shtylyov 	struct hyperbus_device hbdev;
24*5de15b61SSergei Shtylyov };
25*5de15b61SSergei Shtylyov 
26*5de15b61SSergei Shtylyov static const struct rpcif_op rpcif_op_tmpl = {
27*5de15b61SSergei Shtylyov 	.cmd = {
28*5de15b61SSergei Shtylyov 		.buswidth = 8,
29*5de15b61SSergei Shtylyov 		.ddr = true,
30*5de15b61SSergei Shtylyov 	},
31*5de15b61SSergei Shtylyov 	.ocmd = {
32*5de15b61SSergei Shtylyov 		.buswidth = 8,
33*5de15b61SSergei Shtylyov 		.ddr = true,
34*5de15b61SSergei Shtylyov 	},
35*5de15b61SSergei Shtylyov 	.addr = {
36*5de15b61SSergei Shtylyov 		.nbytes = 1,
37*5de15b61SSergei Shtylyov 		.buswidth = 8,
38*5de15b61SSergei Shtylyov 		.ddr = true,
39*5de15b61SSergei Shtylyov 	},
40*5de15b61SSergei Shtylyov 	.data = {
41*5de15b61SSergei Shtylyov 		.buswidth = 8,
42*5de15b61SSergei Shtylyov 		.ddr = true,
43*5de15b61SSergei Shtylyov 	},
44*5de15b61SSergei Shtylyov };
45*5de15b61SSergei Shtylyov 
46*5de15b61SSergei Shtylyov static void rpcif_hb_prepare_read(struct rpcif *rpc, void *to,
47*5de15b61SSergei Shtylyov 				  unsigned long from, ssize_t len)
48*5de15b61SSergei Shtylyov {
49*5de15b61SSergei Shtylyov 	struct rpcif_op op = rpcif_op_tmpl;
50*5de15b61SSergei Shtylyov 
51*5de15b61SSergei Shtylyov 	op.cmd.opcode = HYPERBUS_RW_READ | HYPERBUS_AS_MEM;
52*5de15b61SSergei Shtylyov 	op.addr.val = from >> 1;
53*5de15b61SSergei Shtylyov 	op.dummy.buswidth = 1;
54*5de15b61SSergei Shtylyov 	op.dummy.ncycles = 15;
55*5de15b61SSergei Shtylyov 	op.data.dir = RPCIF_DATA_IN;
56*5de15b61SSergei Shtylyov 	op.data.nbytes = len;
57*5de15b61SSergei Shtylyov 	op.data.buf.in = to;
58*5de15b61SSergei Shtylyov 
59*5de15b61SSergei Shtylyov 	rpcif_prepare(rpc, &op, NULL, NULL);
60*5de15b61SSergei Shtylyov }
61*5de15b61SSergei Shtylyov 
62*5de15b61SSergei Shtylyov static void rpcif_hb_prepare_write(struct rpcif *rpc, unsigned long to,
63*5de15b61SSergei Shtylyov 				   void *from, ssize_t len)
64*5de15b61SSergei Shtylyov {
65*5de15b61SSergei Shtylyov 	struct rpcif_op op = rpcif_op_tmpl;
66*5de15b61SSergei Shtylyov 
67*5de15b61SSergei Shtylyov 	op.cmd.opcode = HYPERBUS_RW_WRITE | HYPERBUS_AS_MEM;
68*5de15b61SSergei Shtylyov 	op.addr.val = to >> 1;
69*5de15b61SSergei Shtylyov 	op.data.dir = RPCIF_DATA_OUT;
70*5de15b61SSergei Shtylyov 	op.data.nbytes = len;
71*5de15b61SSergei Shtylyov 	op.data.buf.out = from;
72*5de15b61SSergei Shtylyov 
73*5de15b61SSergei Shtylyov 	rpcif_prepare(rpc, &op, NULL, NULL);
74*5de15b61SSergei Shtylyov }
75*5de15b61SSergei Shtylyov 
76*5de15b61SSergei Shtylyov static u16 rpcif_hb_read16(struct hyperbus_device *hbdev, unsigned long addr)
77*5de15b61SSergei Shtylyov {
78*5de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus =
79*5de15b61SSergei Shtylyov 		container_of(hbdev, struct rpcif_hyperbus, hbdev);
80*5de15b61SSergei Shtylyov 	map_word data;
81*5de15b61SSergei Shtylyov 
82*5de15b61SSergei Shtylyov 	rpcif_hb_prepare_read(&hyperbus->rpc, &data, addr, 2);
83*5de15b61SSergei Shtylyov 
84*5de15b61SSergei Shtylyov 	rpcif_manual_xfer(&hyperbus->rpc);
85*5de15b61SSergei Shtylyov 
86*5de15b61SSergei Shtylyov 	return data.x[0];
87*5de15b61SSergei Shtylyov }
88*5de15b61SSergei Shtylyov 
89*5de15b61SSergei Shtylyov static void rpcif_hb_write16(struct hyperbus_device *hbdev, unsigned long addr,
90*5de15b61SSergei Shtylyov 			     u16 data)
91*5de15b61SSergei Shtylyov {
92*5de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus =
93*5de15b61SSergei Shtylyov 		container_of(hbdev, struct rpcif_hyperbus, hbdev);
94*5de15b61SSergei Shtylyov 
95*5de15b61SSergei Shtylyov 	rpcif_hb_prepare_write(&hyperbus->rpc, addr, &data, 2);
96*5de15b61SSergei Shtylyov 
97*5de15b61SSergei Shtylyov 	rpcif_manual_xfer(&hyperbus->rpc);
98*5de15b61SSergei Shtylyov }
99*5de15b61SSergei Shtylyov 
100*5de15b61SSergei Shtylyov static void rpcif_hb_copy_from(struct hyperbus_device *hbdev, void *to,
101*5de15b61SSergei Shtylyov 			       unsigned long from, ssize_t len)
102*5de15b61SSergei Shtylyov {
103*5de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus =
104*5de15b61SSergei Shtylyov 		container_of(hbdev, struct rpcif_hyperbus, hbdev);
105*5de15b61SSergei Shtylyov 
106*5de15b61SSergei Shtylyov 	rpcif_hb_prepare_read(&hyperbus->rpc, to, from, len);
107*5de15b61SSergei Shtylyov 
108*5de15b61SSergei Shtylyov 	rpcif_dirmap_read(&hyperbus->rpc, from, len, to);
109*5de15b61SSergei Shtylyov }
110*5de15b61SSergei Shtylyov 
111*5de15b61SSergei Shtylyov static const struct hyperbus_ops rpcif_hb_ops = {
112*5de15b61SSergei Shtylyov 	.read16 = rpcif_hb_read16,
113*5de15b61SSergei Shtylyov 	.write16 = rpcif_hb_write16,
114*5de15b61SSergei Shtylyov 	.copy_from = rpcif_hb_copy_from,
115*5de15b61SSergei Shtylyov };
116*5de15b61SSergei Shtylyov 
117*5de15b61SSergei Shtylyov static int rpcif_hb_probe(struct platform_device *pdev)
118*5de15b61SSergei Shtylyov {
119*5de15b61SSergei Shtylyov 	struct device *dev = &pdev->dev;
120*5de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus;
121*5de15b61SSergei Shtylyov 	int error;
122*5de15b61SSergei Shtylyov 
123*5de15b61SSergei Shtylyov 	hyperbus = devm_kzalloc(dev, sizeof(*hyperbus), GFP_KERNEL);
124*5de15b61SSergei Shtylyov 	if (!hyperbus)
125*5de15b61SSergei Shtylyov 		return -ENOMEM;
126*5de15b61SSergei Shtylyov 
127*5de15b61SSergei Shtylyov 	rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent);
128*5de15b61SSergei Shtylyov 
129*5de15b61SSergei Shtylyov 	platform_set_drvdata(pdev, hyperbus);
130*5de15b61SSergei Shtylyov 
131*5de15b61SSergei Shtylyov 	rpcif_enable_rpm(&hyperbus->rpc);
132*5de15b61SSergei Shtylyov 
133*5de15b61SSergei Shtylyov 	rpcif_hw_init(&hyperbus->rpc, true);
134*5de15b61SSergei Shtylyov 
135*5de15b61SSergei Shtylyov 	hyperbus->hbdev.map.size = hyperbus->rpc.size;
136*5de15b61SSergei Shtylyov 	hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap;
137*5de15b61SSergei Shtylyov 
138*5de15b61SSergei Shtylyov 	hyperbus->ctlr.dev = dev;
139*5de15b61SSergei Shtylyov 	hyperbus->ctlr.ops = &rpcif_hb_ops;
140*5de15b61SSergei Shtylyov 	hyperbus->hbdev.ctlr = &hyperbus->ctlr;
141*5de15b61SSergei Shtylyov 	hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL);
142*5de15b61SSergei Shtylyov 	error = hyperbus_register_device(&hyperbus->hbdev);
143*5de15b61SSergei Shtylyov 	if (error)
144*5de15b61SSergei Shtylyov 		rpcif_disable_rpm(&hyperbus->rpc);
145*5de15b61SSergei Shtylyov 
146*5de15b61SSergei Shtylyov 	return error;
147*5de15b61SSergei Shtylyov }
148*5de15b61SSergei Shtylyov 
149*5de15b61SSergei Shtylyov static int rpcif_hb_remove(struct platform_device *pdev)
150*5de15b61SSergei Shtylyov {
151*5de15b61SSergei Shtylyov 	struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev);
152*5de15b61SSergei Shtylyov 	int error = hyperbus_unregister_device(&hyperbus->hbdev);
153*5de15b61SSergei Shtylyov 	struct rpcif *rpc = dev_get_drvdata(pdev->dev.parent);
154*5de15b61SSergei Shtylyov 
155*5de15b61SSergei Shtylyov 	rpcif_disable_rpm(rpc);
156*5de15b61SSergei Shtylyov 	return error;
157*5de15b61SSergei Shtylyov }
158*5de15b61SSergei Shtylyov 
159*5de15b61SSergei Shtylyov static struct platform_driver rpcif_platform_driver = {
160*5de15b61SSergei Shtylyov 	.probe	= rpcif_hb_probe,
161*5de15b61SSergei Shtylyov 	.remove	= rpcif_hb_remove,
162*5de15b61SSergei Shtylyov 	.driver	= {
163*5de15b61SSergei Shtylyov 		.name	= "rpc-if-hyperflash",
164*5de15b61SSergei Shtylyov 	},
165*5de15b61SSergei Shtylyov };
166*5de15b61SSergei Shtylyov 
167*5de15b61SSergei Shtylyov module_platform_driver(rpcif_platform_driver);
168*5de15b61SSergei Shtylyov 
169*5de15b61SSergei Shtylyov MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver");
170*5de15b61SSergei Shtylyov MODULE_LICENSE("GPL v2");
171