1 // SPDX-License-Identifier: GPL-2.0
3 // Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/
4 // Author: Vignesh Raghavendra <vigneshr@ti.com>
6 #include <linux/completion.h>
7 #include <linux/dma-direction.h>
8 #include <linux/dma-mapping.h>
9 #include <linux/dmaengine.h>
10 #include <linux/err.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/mtd/cfi.h>
14 #include <linux/mtd/hyperbus.h>
15 #include <linux/mtd/mtd.h>
16 #include <linux/mux/consumer.h>
18 #include <linux/of_address.h>
19 #include <linux/platform_device.h>
20 #include <linux/sched/task_stack.h>
21 #include <linux/types.h>
23 #define AM654_HBMC_CALIB_COUNT 25
25 struct am654_hbmc_device_priv
{
26 struct completion rx_dma_complete
;
27 phys_addr_t device_base
;
28 struct hyperbus_ctlr
*ctlr
;
29 struct dma_chan
*rx_chan
;
32 struct am654_hbmc_priv
{
33 struct hyperbus_ctlr ctlr
;
34 struct hyperbus_device hbdev
;
35 struct mux_control
*mux_ctrl
;
38 static int am654_hbmc_calibrate(struct hyperbus_device
*hbdev
)
40 struct map_info
*map
= &hbdev
->map
;
41 struct cfi_private cfi
;
42 int count
= AM654_HBMC_CALIB_COUNT
;
47 cfi
.device_type
= CFI_DEVICETYPE_X16
;
48 cfi_send_gen_cmd(0xF0, 0, 0, map
, &cfi
, cfi
.device_type
, NULL
);
49 cfi_send_gen_cmd(0x98, 0x55, 0, map
, &cfi
, cfi
.device_type
, NULL
);
52 ret
= cfi_qry_present(map
, 0, &cfi
);
61 cfi_qry_mode_off(0, map
, &cfi
);
66 static void am654_hbmc_dma_callback(void *param
)
68 struct am654_hbmc_device_priv
*priv
= param
;
70 complete(&priv
->rx_dma_complete
);
73 static int am654_hbmc_dma_read(struct am654_hbmc_device_priv
*priv
, void *to
,
74 unsigned long from
, ssize_t len
)
77 enum dma_ctrl_flags flags
= DMA_CTRL_ACK
| DMA_PREP_INTERRUPT
;
78 struct dma_chan
*rx_chan
= priv
->rx_chan
;
79 struct dma_async_tx_descriptor
*tx
;
80 dma_addr_t dma_dst
, dma_src
;
84 if (!priv
->rx_chan
|| !virt_addr_valid(to
) || object_is_on_stack(to
))
87 dma_dst
= dma_map_single(rx_chan
->device
->dev
, to
, len
, DMA_FROM_DEVICE
);
88 if (dma_mapping_error(rx_chan
->device
->dev
, dma_dst
)) {
89 dev_dbg(priv
->ctlr
->dev
, "DMA mapping failed\n");
93 dma_src
= priv
->device_base
+ from
;
94 tx
= dmaengine_prep_dma_memcpy(rx_chan
, dma_dst
, dma_src
, len
, flags
);
96 dev_err(priv
->ctlr
->dev
, "device_prep_dma_memcpy error\n");
101 reinit_completion(&priv
->rx_dma_complete
);
102 tx
->callback
= am654_hbmc_dma_callback
;
103 tx
->callback_param
= priv
;
104 cookie
= dmaengine_submit(tx
);
106 ret
= dma_submit_error(cookie
);
108 dev_err(priv
->ctlr
->dev
, "dma_submit_error %d\n", cookie
);
112 dma_async_issue_pending(rx_chan
);
113 if (!wait_for_completion_timeout(&priv
->rx_dma_complete
, msecs_to_jiffies(len
+ 1000))) {
114 dmaengine_terminate_sync(rx_chan
);
115 dev_err(priv
->ctlr
->dev
, "DMA wait_for_completion_timeout\n");
120 dma_unmap_single(rx_chan
->device
->dev
, dma_dst
, len
, DMA_FROM_DEVICE
);
124 static void am654_hbmc_read(struct hyperbus_device
*hbdev
, void *to
,
125 unsigned long from
, ssize_t len
)
127 struct am654_hbmc_device_priv
*priv
= hbdev
->priv
;
129 if (len
< SZ_1K
|| am654_hbmc_dma_read(priv
, to
, from
, len
))
130 memcpy_fromio(to
, hbdev
->map
.virt
+ from
, len
);
133 static const struct hyperbus_ops am654_hbmc_ops
= {
134 .calibrate
= am654_hbmc_calibrate
,
135 .copy_from
= am654_hbmc_read
,
138 static int am654_hbmc_request_mmap_dma(struct am654_hbmc_device_priv
*priv
)
140 struct dma_chan
*rx_chan
;
144 dma_cap_set(DMA_MEMCPY
, mask
);
146 rx_chan
= dma_request_chan_by_mask(&mask
);
147 if (IS_ERR(rx_chan
)) {
148 if (PTR_ERR(rx_chan
) == -EPROBE_DEFER
)
149 return -EPROBE_DEFER
;
150 dev_dbg(priv
->ctlr
->dev
, "No DMA channel available\n");
153 priv
->rx_chan
= rx_chan
;
154 init_completion(&priv
->rx_dma_complete
);
159 static int am654_hbmc_probe(struct platform_device
*pdev
)
161 struct device_node
*np
= pdev
->dev
.of_node
;
162 struct am654_hbmc_device_priv
*dev_priv
;
163 struct device
*dev
= &pdev
->dev
;
164 struct am654_hbmc_priv
*priv
;
168 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
172 platform_set_drvdata(pdev
, priv
);
174 priv
->hbdev
.np
= of_get_next_child(np
, NULL
);
175 ret
= of_address_to_resource(priv
->hbdev
.np
, 0, &res
);
179 if (of_property_read_bool(dev
->of_node
, "mux-controls")) {
180 struct mux_control
*control
= devm_mux_control_get(dev
, NULL
);
183 return PTR_ERR(control
);
185 ret
= mux_control_select(control
, 1);
187 dev_err(dev
, "Failed to select HBMC mux\n");
190 priv
->mux_ctrl
= control
;
193 priv
->hbdev
.map
.size
= resource_size(&res
);
194 priv
->hbdev
.map
.virt
= devm_ioremap_resource(dev
, &res
);
195 if (IS_ERR(priv
->hbdev
.map
.virt
))
196 return PTR_ERR(priv
->hbdev
.map
.virt
);
198 priv
->ctlr
.dev
= dev
;
199 priv
->ctlr
.ops
= &am654_hbmc_ops
;
200 priv
->hbdev
.ctlr
= &priv
->ctlr
;
202 dev_priv
= devm_kzalloc(dev
, sizeof(*dev_priv
), GFP_KERNEL
);
208 priv
->hbdev
.priv
= dev_priv
;
209 dev_priv
->device_base
= res
.start
;
210 dev_priv
->ctlr
= &priv
->ctlr
;
212 ret
= am654_hbmc_request_mmap_dma(dev_priv
);
216 ret
= hyperbus_register_device(&priv
->hbdev
);
218 dev_err(dev
, "failed to register controller\n");
224 if (dev_priv
->rx_chan
)
225 dma_release_channel(dev_priv
->rx_chan
);
228 mux_control_deselect(priv
->mux_ctrl
);
232 static int am654_hbmc_remove(struct platform_device
*pdev
)
234 struct am654_hbmc_priv
*priv
= platform_get_drvdata(pdev
);
235 struct am654_hbmc_device_priv
*dev_priv
= priv
->hbdev
.priv
;
238 ret
= hyperbus_unregister_device(&priv
->hbdev
);
240 mux_control_deselect(priv
->mux_ctrl
);
242 if (dev_priv
->rx_chan
)
243 dma_release_channel(dev_priv
->rx_chan
);
248 static const struct of_device_id am654_hbmc_dt_ids
[] = {
250 .compatible
= "ti,am654-hbmc",
252 { /* end of table */ }
255 MODULE_DEVICE_TABLE(of
, am654_hbmc_dt_ids
);
257 static struct platform_driver am654_hbmc_platform_driver
= {
258 .probe
= am654_hbmc_probe
,
259 .remove
= am654_hbmc_remove
,
261 .name
= "hbmc-am654",
262 .of_match_table
= am654_hbmc_dt_ids
,
266 module_platform_driver(am654_hbmc_platform_driver
);
268 MODULE_DESCRIPTION("HBMC driver for AM654 SoC");
269 MODULE_LICENSE("GPL v2");
270 MODULE_ALIAS("platform:hbmc-am654");
271 MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>");