xref: /linux/drivers/mtd/hyperbus/hbmc-am654.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
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