Merge tag 'Wimplicit-fallthrough-clang-5.14-rc2' of git://git.kernel.org/pub/scm...
[linux-2.6-microblaze.git] / drivers / mtd / hyperbus / rpc-if.c
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         rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent);
128
129         platform_set_drvdata(pdev, hyperbus);
130
131         rpcif_enable_rpm(&hyperbus->rpc);
132
133         rpcif_hw_init(&hyperbus->rpc, true);
134
135         hyperbus->hbdev.map.size = hyperbus->rpc.size;
136         hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap;
137
138         hyperbus->ctlr.dev = dev;
139         hyperbus->ctlr.ops = &rpcif_hb_ops;
140         hyperbus->hbdev.ctlr = &hyperbus->ctlr;
141         hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL);
142         error = hyperbus_register_device(&hyperbus->hbdev);
143         if (error)
144                 rpcif_disable_rpm(&hyperbus->rpc);
145
146         return error;
147 }
148
149 static int rpcif_hb_remove(struct platform_device *pdev)
150 {
151         struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev);
152         int error = hyperbus_unregister_device(&hyperbus->hbdev);
153         struct rpcif *rpc = dev_get_drvdata(pdev->dev.parent);
154
155         rpcif_disable_rpm(rpc);
156         return error;
157 }
158
159 static struct platform_driver rpcif_platform_driver = {
160         .probe  = rpcif_hb_probe,
161         .remove = rpcif_hb_remove,
162         .driver = {
163                 .name   = "rpc-if-hyperflash",
164         },
165 };
166
167 module_platform_driver(rpcif_platform_driver);
168
169 MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver");
170 MODULE_LICENSE("GPL v2");