1b07079f1SVignesh Raghavendra // SPDX-License-Identifier: GPL-2.0
2b07079f1SVignesh Raghavendra //
3614a895fSAlexander A. Klimov // Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/
4b07079f1SVignesh Raghavendra // Author: Vignesh Raghavendra <vigneshr@ti.com>
5b07079f1SVignesh Raghavendra
600c9cf49SVignesh Raghavendra #include <linux/completion.h>
700c9cf49SVignesh Raghavendra #include <linux/dma-direction.h>
800c9cf49SVignesh Raghavendra #include <linux/dma-mapping.h>
900c9cf49SVignesh Raghavendra #include <linux/dmaengine.h>
10b07079f1SVignesh Raghavendra #include <linux/err.h>
11b07079f1SVignesh Raghavendra #include <linux/kernel.h>
12b07079f1SVignesh Raghavendra #include <linux/module.h>
13b07079f1SVignesh Raghavendra #include <linux/mtd/cfi.h>
14b07079f1SVignesh Raghavendra #include <linux/mtd/hyperbus.h>
15b07079f1SVignesh Raghavendra #include <linux/mtd/mtd.h>
16b07079f1SVignesh Raghavendra #include <linux/mux/consumer.h>
17b07079f1SVignesh Raghavendra #include <linux/of.h>
18b6fe8bc6SSergei Shtylyov #include <linux/of_address.h>
19b07079f1SVignesh Raghavendra #include <linux/platform_device.h>
2000c9cf49SVignesh Raghavendra #include <linux/sched/task_stack.h>
21b07079f1SVignesh Raghavendra #include <linux/types.h>
22b07079f1SVignesh Raghavendra
23b07079f1SVignesh Raghavendra #define AM654_HBMC_CALIB_COUNT 25
24b07079f1SVignesh Raghavendra
2500c9cf49SVignesh Raghavendra struct am654_hbmc_device_priv {
2600c9cf49SVignesh Raghavendra struct completion rx_dma_complete;
2700c9cf49SVignesh Raghavendra phys_addr_t device_base;
2800c9cf49SVignesh Raghavendra struct hyperbus_ctlr *ctlr;
2900c9cf49SVignesh Raghavendra struct dma_chan *rx_chan;
3000c9cf49SVignesh Raghavendra };
3100c9cf49SVignesh Raghavendra
32b07079f1SVignesh Raghavendra struct am654_hbmc_priv {
33b07079f1SVignesh Raghavendra struct hyperbus_ctlr ctlr;
34b07079f1SVignesh Raghavendra struct hyperbus_device hbdev;
35b07079f1SVignesh Raghavendra struct mux_control *mux_ctrl;
36b07079f1SVignesh Raghavendra };
37b07079f1SVignesh Raghavendra
am654_hbmc_calibrate(struct hyperbus_device * hbdev)38b07079f1SVignesh Raghavendra static int am654_hbmc_calibrate(struct hyperbus_device *hbdev)
39b07079f1SVignesh Raghavendra {
40b07079f1SVignesh Raghavendra struct map_info *map = &hbdev->map;
41b07079f1SVignesh Raghavendra struct cfi_private cfi;
42b07079f1SVignesh Raghavendra int count = AM654_HBMC_CALIB_COUNT;
43b07079f1SVignesh Raghavendra int pass_count = 0;
44b07079f1SVignesh Raghavendra int ret;
45b07079f1SVignesh Raghavendra
46b07079f1SVignesh Raghavendra cfi.interleave = 1;
47b07079f1SVignesh Raghavendra cfi.device_type = CFI_DEVICETYPE_X16;
48b07079f1SVignesh Raghavendra cfi_send_gen_cmd(0xF0, 0, 0, map, &cfi, cfi.device_type, NULL);
49b07079f1SVignesh Raghavendra cfi_send_gen_cmd(0x98, 0x55, 0, map, &cfi, cfi.device_type, NULL);
50b07079f1SVignesh Raghavendra
51b07079f1SVignesh Raghavendra while (count--) {
52b07079f1SVignesh Raghavendra ret = cfi_qry_present(map, 0, &cfi);
53b07079f1SVignesh Raghavendra if (ret)
54b07079f1SVignesh Raghavendra pass_count++;
55b07079f1SVignesh Raghavendra else
56b07079f1SVignesh Raghavendra pass_count = 0;
57b07079f1SVignesh Raghavendra if (pass_count == 5)
58b07079f1SVignesh Raghavendra break;
59b07079f1SVignesh Raghavendra }
60b07079f1SVignesh Raghavendra
61b07079f1SVignesh Raghavendra cfi_qry_mode_off(0, map, &cfi);
62b07079f1SVignesh Raghavendra
63b07079f1SVignesh Raghavendra return ret;
64b07079f1SVignesh Raghavendra }
65b07079f1SVignesh Raghavendra
am654_hbmc_dma_callback(void * param)6600c9cf49SVignesh Raghavendra static void am654_hbmc_dma_callback(void *param)
6700c9cf49SVignesh Raghavendra {
6800c9cf49SVignesh Raghavendra struct am654_hbmc_device_priv *priv = param;
6900c9cf49SVignesh Raghavendra
7000c9cf49SVignesh Raghavendra complete(&priv->rx_dma_complete);
7100c9cf49SVignesh Raghavendra }
7200c9cf49SVignesh Raghavendra
am654_hbmc_dma_read(struct am654_hbmc_device_priv * priv,void * to,unsigned long from,ssize_t len)7300c9cf49SVignesh Raghavendra static int am654_hbmc_dma_read(struct am654_hbmc_device_priv *priv, void *to,
7400c9cf49SVignesh Raghavendra unsigned long from, ssize_t len)
7500c9cf49SVignesh Raghavendra
7600c9cf49SVignesh Raghavendra {
7700c9cf49SVignesh Raghavendra enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
7800c9cf49SVignesh Raghavendra struct dma_chan *rx_chan = priv->rx_chan;
7900c9cf49SVignesh Raghavendra struct dma_async_tx_descriptor *tx;
8000c9cf49SVignesh Raghavendra dma_addr_t dma_dst, dma_src;
8100c9cf49SVignesh Raghavendra dma_cookie_t cookie;
8200c9cf49SVignesh Raghavendra int ret;
8300c9cf49SVignesh Raghavendra
8400c9cf49SVignesh Raghavendra if (!priv->rx_chan || !virt_addr_valid(to) || object_is_on_stack(to))
8500c9cf49SVignesh Raghavendra return -EINVAL;
8600c9cf49SVignesh Raghavendra
8700c9cf49SVignesh Raghavendra dma_dst = dma_map_single(rx_chan->device->dev, to, len, DMA_FROM_DEVICE);
8800c9cf49SVignesh Raghavendra if (dma_mapping_error(rx_chan->device->dev, dma_dst)) {
8900c9cf49SVignesh Raghavendra dev_dbg(priv->ctlr->dev, "DMA mapping failed\n");
9000c9cf49SVignesh Raghavendra return -EIO;
9100c9cf49SVignesh Raghavendra }
9200c9cf49SVignesh Raghavendra
9300c9cf49SVignesh Raghavendra dma_src = priv->device_base + from;
9400c9cf49SVignesh Raghavendra tx = dmaengine_prep_dma_memcpy(rx_chan, dma_dst, dma_src, len, flags);
9500c9cf49SVignesh Raghavendra if (!tx) {
9600c9cf49SVignesh Raghavendra dev_err(priv->ctlr->dev, "device_prep_dma_memcpy error\n");
9700c9cf49SVignesh Raghavendra ret = -EIO;
9800c9cf49SVignesh Raghavendra goto unmap_dma;
9900c9cf49SVignesh Raghavendra }
10000c9cf49SVignesh Raghavendra
10100c9cf49SVignesh Raghavendra reinit_completion(&priv->rx_dma_complete);
10200c9cf49SVignesh Raghavendra tx->callback = am654_hbmc_dma_callback;
10300c9cf49SVignesh Raghavendra tx->callback_param = priv;
10400c9cf49SVignesh Raghavendra cookie = dmaengine_submit(tx);
10500c9cf49SVignesh Raghavendra
10600c9cf49SVignesh Raghavendra ret = dma_submit_error(cookie);
10700c9cf49SVignesh Raghavendra if (ret) {
10800c9cf49SVignesh Raghavendra dev_err(priv->ctlr->dev, "dma_submit_error %d\n", cookie);
10900c9cf49SVignesh Raghavendra goto unmap_dma;
11000c9cf49SVignesh Raghavendra }
11100c9cf49SVignesh Raghavendra
11200c9cf49SVignesh Raghavendra dma_async_issue_pending(rx_chan);
11300c9cf49SVignesh Raghavendra if (!wait_for_completion_timeout(&priv->rx_dma_complete, msecs_to_jiffies(len + 1000))) {
11400c9cf49SVignesh Raghavendra dmaengine_terminate_sync(rx_chan);
11500c9cf49SVignesh Raghavendra dev_err(priv->ctlr->dev, "DMA wait_for_completion_timeout\n");
11600c9cf49SVignesh Raghavendra ret = -ETIMEDOUT;
11700c9cf49SVignesh Raghavendra }
11800c9cf49SVignesh Raghavendra
11900c9cf49SVignesh Raghavendra unmap_dma:
12000c9cf49SVignesh Raghavendra dma_unmap_single(rx_chan->device->dev, dma_dst, len, DMA_FROM_DEVICE);
12100c9cf49SVignesh Raghavendra return ret;
12200c9cf49SVignesh Raghavendra }
12300c9cf49SVignesh Raghavendra
am654_hbmc_read(struct hyperbus_device * hbdev,void * to,unsigned long from,ssize_t len)12400c9cf49SVignesh Raghavendra static void am654_hbmc_read(struct hyperbus_device *hbdev, void *to,
12500c9cf49SVignesh Raghavendra unsigned long from, ssize_t len)
12600c9cf49SVignesh Raghavendra {
12700c9cf49SVignesh Raghavendra struct am654_hbmc_device_priv *priv = hbdev->priv;
12800c9cf49SVignesh Raghavendra
12900c9cf49SVignesh Raghavendra if (len < SZ_1K || am654_hbmc_dma_read(priv, to, from, len))
13000c9cf49SVignesh Raghavendra memcpy_fromio(to, hbdev->map.virt + from, len);
13100c9cf49SVignesh Raghavendra }
13200c9cf49SVignesh Raghavendra
133b07079f1SVignesh Raghavendra static const struct hyperbus_ops am654_hbmc_ops = {
134b07079f1SVignesh Raghavendra .calibrate = am654_hbmc_calibrate,
13500c9cf49SVignesh Raghavendra .copy_from = am654_hbmc_read,
136b07079f1SVignesh Raghavendra };
137b07079f1SVignesh Raghavendra
am654_hbmc_request_mmap_dma(struct am654_hbmc_device_priv * priv)13800c9cf49SVignesh Raghavendra static int am654_hbmc_request_mmap_dma(struct am654_hbmc_device_priv *priv)
13900c9cf49SVignesh Raghavendra {
14000c9cf49SVignesh Raghavendra struct dma_chan *rx_chan;
14100c9cf49SVignesh Raghavendra dma_cap_mask_t mask;
14200c9cf49SVignesh Raghavendra
14300c9cf49SVignesh Raghavendra dma_cap_zero(mask);
14400c9cf49SVignesh Raghavendra dma_cap_set(DMA_MEMCPY, mask);
14500c9cf49SVignesh Raghavendra
14600c9cf49SVignesh Raghavendra rx_chan = dma_request_chan_by_mask(&mask);
14700c9cf49SVignesh Raghavendra if (IS_ERR(rx_chan)) {
14800c9cf49SVignesh Raghavendra if (PTR_ERR(rx_chan) == -EPROBE_DEFER)
14900c9cf49SVignesh Raghavendra return -EPROBE_DEFER;
15000c9cf49SVignesh Raghavendra dev_dbg(priv->ctlr->dev, "No DMA channel available\n");
15100c9cf49SVignesh Raghavendra return 0;
15200c9cf49SVignesh Raghavendra }
15300c9cf49SVignesh Raghavendra priv->rx_chan = rx_chan;
15400c9cf49SVignesh Raghavendra init_completion(&priv->rx_dma_complete);
15500c9cf49SVignesh Raghavendra
15600c9cf49SVignesh Raghavendra return 0;
15700c9cf49SVignesh Raghavendra }
15800c9cf49SVignesh Raghavendra
am654_hbmc_probe(struct platform_device * pdev)159b07079f1SVignesh Raghavendra static int am654_hbmc_probe(struct platform_device *pdev)
160b07079f1SVignesh Raghavendra {
161b6fe8bc6SSergei Shtylyov struct device_node *np = pdev->dev.of_node;
16200c9cf49SVignesh Raghavendra struct am654_hbmc_device_priv *dev_priv;
163b07079f1SVignesh Raghavendra struct device *dev = &pdev->dev;
164b07079f1SVignesh Raghavendra struct am654_hbmc_priv *priv;
165b6fe8bc6SSergei Shtylyov struct resource res;
166b07079f1SVignesh Raghavendra int ret;
167b07079f1SVignesh Raghavendra
168b07079f1SVignesh Raghavendra priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
169b07079f1SVignesh Raghavendra if (!priv)
170b07079f1SVignesh Raghavendra return -ENOMEM;
171b07079f1SVignesh Raghavendra
172b07079f1SVignesh Raghavendra platform_set_drvdata(pdev, priv);
173b07079f1SVignesh Raghavendra
174aca31ce9SVignesh Raghavendra priv->hbdev.np = of_get_next_child(np, NULL);
175aca31ce9SVignesh Raghavendra ret = of_address_to_resource(priv->hbdev.np, 0, &res);
176b6fe8bc6SSergei Shtylyov if (ret)
177b6fe8bc6SSergei Shtylyov return ret;
178b6fe8bc6SSergei Shtylyov
179b07079f1SVignesh Raghavendra if (of_property_read_bool(dev->of_node, "mux-controls")) {
180b07079f1SVignesh Raghavendra struct mux_control *control = devm_mux_control_get(dev, NULL);
181b07079f1SVignesh Raghavendra
182b07079f1SVignesh Raghavendra if (IS_ERR(control))
183b07079f1SVignesh Raghavendra return PTR_ERR(control);
184b07079f1SVignesh Raghavendra
185b07079f1SVignesh Raghavendra ret = mux_control_select(control, 1);
186b07079f1SVignesh Raghavendra if (ret) {
187b07079f1SVignesh Raghavendra dev_err(dev, "Failed to select HBMC mux\n");
188b07079f1SVignesh Raghavendra return ret;
189b07079f1SVignesh Raghavendra }
190b07079f1SVignesh Raghavendra priv->mux_ctrl = control;
191b07079f1SVignesh Raghavendra }
192b07079f1SVignesh Raghavendra
193b6fe8bc6SSergei Shtylyov priv->hbdev.map.size = resource_size(&res);
194b6fe8bc6SSergei Shtylyov priv->hbdev.map.virt = devm_ioremap_resource(dev, &res);
195b6fe8bc6SSergei Shtylyov if (IS_ERR(priv->hbdev.map.virt))
196b6fe8bc6SSergei Shtylyov return PTR_ERR(priv->hbdev.map.virt);
197b6fe8bc6SSergei Shtylyov
198b07079f1SVignesh Raghavendra priv->ctlr.dev = dev;
199b07079f1SVignesh Raghavendra priv->ctlr.ops = &am654_hbmc_ops;
200b07079f1SVignesh Raghavendra priv->hbdev.ctlr = &priv->ctlr;
20100c9cf49SVignesh Raghavendra
20200c9cf49SVignesh Raghavendra dev_priv = devm_kzalloc(dev, sizeof(*dev_priv), GFP_KERNEL);
20300c9cf49SVignesh Raghavendra if (!dev_priv) {
20400c9cf49SVignesh Raghavendra ret = -ENOMEM;
205992df3bbSVignesh Raghavendra goto disable_mux;
206b07079f1SVignesh Raghavendra }
207b07079f1SVignesh Raghavendra
20800c9cf49SVignesh Raghavendra priv->hbdev.priv = dev_priv;
20900c9cf49SVignesh Raghavendra dev_priv->device_base = res.start;
21000c9cf49SVignesh Raghavendra dev_priv->ctlr = &priv->ctlr;
21100c9cf49SVignesh Raghavendra
21200c9cf49SVignesh Raghavendra ret = am654_hbmc_request_mmap_dma(dev_priv);
21300c9cf49SVignesh Raghavendra if (ret)
21400c9cf49SVignesh Raghavendra goto disable_mux;
21500c9cf49SVignesh Raghavendra
21600c9cf49SVignesh Raghavendra ret = hyperbus_register_device(&priv->hbdev);
21700c9cf49SVignesh Raghavendra if (ret) {
21800c9cf49SVignesh Raghavendra dev_err(dev, "failed to register controller\n");
21900c9cf49SVignesh Raghavendra goto release_dma;
22000c9cf49SVignesh Raghavendra }
22100c9cf49SVignesh Raghavendra
222b07079f1SVignesh Raghavendra return 0;
22300c9cf49SVignesh Raghavendra release_dma:
22400c9cf49SVignesh Raghavendra if (dev_priv->rx_chan)
22500c9cf49SVignesh Raghavendra dma_release_channel(dev_priv->rx_chan);
226992df3bbSVignesh Raghavendra disable_mux:
227b07079f1SVignesh Raghavendra if (priv->mux_ctrl)
228b07079f1SVignesh Raghavendra mux_control_deselect(priv->mux_ctrl);
229b07079f1SVignesh Raghavendra return ret;
230b07079f1SVignesh Raghavendra }
231b07079f1SVignesh Raghavendra
am654_hbmc_remove(struct platform_device * pdev)232*59bd5676SUwe Kleine-König static void am654_hbmc_remove(struct platform_device *pdev)
233b07079f1SVignesh Raghavendra {
234b07079f1SVignesh Raghavendra struct am654_hbmc_priv *priv = platform_get_drvdata(pdev);
23500c9cf49SVignesh Raghavendra struct am654_hbmc_device_priv *dev_priv = priv->hbdev.priv;
236b07079f1SVignesh Raghavendra
2370c90466aSUwe Kleine-König hyperbus_unregister_device(&priv->hbdev);
2380c90466aSUwe Kleine-König
239b07079f1SVignesh Raghavendra if (priv->mux_ctrl)
240b07079f1SVignesh Raghavendra mux_control_deselect(priv->mux_ctrl);
241b07079f1SVignesh Raghavendra
24200c9cf49SVignesh Raghavendra if (dev_priv->rx_chan)
24300c9cf49SVignesh Raghavendra dma_release_channel(dev_priv->rx_chan);
244b07079f1SVignesh Raghavendra }
245b07079f1SVignesh Raghavendra
246b07079f1SVignesh Raghavendra static const struct of_device_id am654_hbmc_dt_ids[] = {
247b07079f1SVignesh Raghavendra {
248b07079f1SVignesh Raghavendra .compatible = "ti,am654-hbmc",
249b07079f1SVignesh Raghavendra },
250b07079f1SVignesh Raghavendra { /* end of table */ }
251b07079f1SVignesh Raghavendra };
252b07079f1SVignesh Raghavendra
253b07079f1SVignesh Raghavendra MODULE_DEVICE_TABLE(of, am654_hbmc_dt_ids);
254b07079f1SVignesh Raghavendra
255b07079f1SVignesh Raghavendra static struct platform_driver am654_hbmc_platform_driver = {
256b07079f1SVignesh Raghavendra .probe = am654_hbmc_probe,
257*59bd5676SUwe Kleine-König .remove_new = am654_hbmc_remove,
258b07079f1SVignesh Raghavendra .driver = {
259b07079f1SVignesh Raghavendra .name = "hbmc-am654",
260b07079f1SVignesh Raghavendra .of_match_table = am654_hbmc_dt_ids,
261b07079f1SVignesh Raghavendra },
262b07079f1SVignesh Raghavendra };
263b07079f1SVignesh Raghavendra
264b07079f1SVignesh Raghavendra module_platform_driver(am654_hbmc_platform_driver);
265b07079f1SVignesh Raghavendra
266b07079f1SVignesh Raghavendra MODULE_DESCRIPTION("HBMC driver for AM654 SoC");
267b07079f1SVignesh Raghavendra MODULE_LICENSE("GPL v2");
268b07079f1SVignesh Raghavendra MODULE_ALIAS("platform:hbmc-am654");
269b07079f1SVignesh Raghavendra MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>");
270