xref: /openbmc/linux/drivers/mtd/hyperbus/rpc-if.c (revision 6562c9ac)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Linux driver for RPC-IF HyperFlash
4  *
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 
20 struct	rpcif_hyperbus {
21 	struct rpcif rpc;
22 	struct hyperbus_ctlr ctlr;
23 	struct hyperbus_device hbdev;
24 };
25 
26 static 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 
46 static 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, &op, NULL, NULL);
60 }
61 
62 static 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, &op, NULL, NULL);
74 }
75 
76 static 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);
85 
86 	return data.x[0];
87 }
88 
89 static 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);
98 }
99 
100 static 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, from, len, to);
109 }
110 
111 static 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 
117 static 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 	rpcif_enable_rpm(&hyperbus->rpc);
134 
135 	error = rpcif_hw_init(&hyperbus->rpc, 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 
152 out_disable_rpm:
153 	rpcif_disable_rpm(&hyperbus->rpc);
154 	return error;
155 }
156 
157 static int 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 	rpcif_disable_rpm(&hyperbus->rpc);
164 
165 	return 0;
166 }
167 
168 static struct platform_driver rpcif_platform_driver = {
169 	.probe	= rpcif_hb_probe,
170 	.remove	= rpcif_hb_remove,
171 	.driver	= {
172 		.name	= "rpc-if-hyperflash",
173 	},
174 };
175 
176 module_platform_driver(rpcif_platform_driver);
177 
178 MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver");
179 MODULE_LICENSE("GPL v2");
180