xref: /linux/drivers/mtd/hyperbus/hyperbus-core.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1dcc7d344SVignesh Raghavendra // SPDX-License-Identifier: GPL-2.0
2dcc7d344SVignesh Raghavendra //
3*614a895fSAlexander A. Klimov // Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/
4dcc7d344SVignesh Raghavendra // Author: Vignesh Raghavendra <vigneshr@ti.com>
5dcc7d344SVignesh Raghavendra 
6dcc7d344SVignesh Raghavendra #include <linux/err.h>
7dcc7d344SVignesh Raghavendra #include <linux/kernel.h>
8dcc7d344SVignesh Raghavendra #include <linux/module.h>
9dcc7d344SVignesh Raghavendra #include <linux/mtd/hyperbus.h>
10dcc7d344SVignesh Raghavendra #include <linux/mtd/map.h>
11dcc7d344SVignesh Raghavendra #include <linux/mtd/mtd.h>
12dcc7d344SVignesh Raghavendra #include <linux/of.h>
13dcc7d344SVignesh Raghavendra #include <linux/types.h>
14dcc7d344SVignesh Raghavendra 
15dcc7d344SVignesh Raghavendra static struct hyperbus_device *map_to_hbdev(struct map_info *map)
16dcc7d344SVignesh Raghavendra {
17dcc7d344SVignesh Raghavendra 	return container_of(map, struct hyperbus_device, map);
18dcc7d344SVignesh Raghavendra }
19dcc7d344SVignesh Raghavendra 
20dcc7d344SVignesh Raghavendra static map_word hyperbus_read16(struct map_info *map, unsigned long addr)
21dcc7d344SVignesh Raghavendra {
22dcc7d344SVignesh Raghavendra 	struct hyperbus_device *hbdev = map_to_hbdev(map);
23dcc7d344SVignesh Raghavendra 	struct hyperbus_ctlr *ctlr = hbdev->ctlr;
24dcc7d344SVignesh Raghavendra 	map_word read_data;
25dcc7d344SVignesh Raghavendra 
26dcc7d344SVignesh Raghavendra 	read_data.x[0] = ctlr->ops->read16(hbdev, addr);
27dcc7d344SVignesh Raghavendra 
28dcc7d344SVignesh Raghavendra 	return read_data;
29dcc7d344SVignesh Raghavendra }
30dcc7d344SVignesh Raghavendra 
31dcc7d344SVignesh Raghavendra static void hyperbus_write16(struct map_info *map, map_word d,
32dcc7d344SVignesh Raghavendra 			     unsigned long addr)
33dcc7d344SVignesh Raghavendra {
34dcc7d344SVignesh Raghavendra 	struct hyperbus_device *hbdev = map_to_hbdev(map);
35dcc7d344SVignesh Raghavendra 	struct hyperbus_ctlr *ctlr = hbdev->ctlr;
36dcc7d344SVignesh Raghavendra 
37dcc7d344SVignesh Raghavendra 	ctlr->ops->write16(hbdev, addr, d.x[0]);
38dcc7d344SVignesh Raghavendra }
39dcc7d344SVignesh Raghavendra 
40dcc7d344SVignesh Raghavendra static void hyperbus_copy_from(struct map_info *map, void *to,
41dcc7d344SVignesh Raghavendra 			       unsigned long from, ssize_t len)
42dcc7d344SVignesh Raghavendra {
43dcc7d344SVignesh Raghavendra 	struct hyperbus_device *hbdev = map_to_hbdev(map);
44dcc7d344SVignesh Raghavendra 	struct hyperbus_ctlr *ctlr = hbdev->ctlr;
45dcc7d344SVignesh Raghavendra 
46dcc7d344SVignesh Raghavendra 	ctlr->ops->copy_from(hbdev, to, from, len);
47dcc7d344SVignesh Raghavendra }
48dcc7d344SVignesh Raghavendra 
49dcc7d344SVignesh Raghavendra static void hyperbus_copy_to(struct map_info *map, unsigned long to,
50dcc7d344SVignesh Raghavendra 			     const void *from, ssize_t len)
51dcc7d344SVignesh Raghavendra {
52dcc7d344SVignesh Raghavendra 	struct hyperbus_device *hbdev = map_to_hbdev(map);
53dcc7d344SVignesh Raghavendra 	struct hyperbus_ctlr *ctlr = hbdev->ctlr;
54dcc7d344SVignesh Raghavendra 
55dcc7d344SVignesh Raghavendra 	ctlr->ops->copy_to(hbdev, to, from, len);
56dcc7d344SVignesh Raghavendra }
57dcc7d344SVignesh Raghavendra 
58dcc7d344SVignesh Raghavendra int hyperbus_register_device(struct hyperbus_device *hbdev)
59dcc7d344SVignesh Raghavendra {
60dcc7d344SVignesh Raghavendra 	const struct hyperbus_ops *ops;
61dcc7d344SVignesh Raghavendra 	struct hyperbus_ctlr *ctlr;
62dcc7d344SVignesh Raghavendra 	struct device_node *np;
63dcc7d344SVignesh Raghavendra 	struct map_info *map;
64dcc7d344SVignesh Raghavendra 	struct device *dev;
65dcc7d344SVignesh Raghavendra 	int ret;
66dcc7d344SVignesh Raghavendra 
67dcc7d344SVignesh Raghavendra 	if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) {
68dcc7d344SVignesh Raghavendra 		pr_err("hyperbus: please fill all the necessary fields!\n");
69dcc7d344SVignesh Raghavendra 		return -EINVAL;
70dcc7d344SVignesh Raghavendra 	}
71dcc7d344SVignesh Raghavendra 
72dcc7d344SVignesh Raghavendra 	np = hbdev->np;
73dcc7d344SVignesh Raghavendra 	ctlr = hbdev->ctlr;
74cb6176efSDirk Behme 	if (!of_device_is_compatible(np, "cypress,hyperflash")) {
75cb6176efSDirk Behme 		dev_err(ctlr->dev, "\"cypress,hyperflash\" compatible missing\n");
76dcc7d344SVignesh Raghavendra 		return -ENODEV;
77cb6176efSDirk Behme 	}
78dcc7d344SVignesh Raghavendra 
79dcc7d344SVignesh Raghavendra 	hbdev->memtype = HYPERFLASH;
80dcc7d344SVignesh Raghavendra 
81dcc7d344SVignesh Raghavendra 	dev = ctlr->dev;
82dcc7d344SVignesh Raghavendra 	map = &hbdev->map;
83dcc7d344SVignesh Raghavendra 	map->name = dev_name(dev);
84dcc7d344SVignesh Raghavendra 	map->bankwidth = 2;
85dcc7d344SVignesh Raghavendra 	map->device_node = np;
86dcc7d344SVignesh Raghavendra 
87dcc7d344SVignesh Raghavendra 	simple_map_init(map);
88dcc7d344SVignesh Raghavendra 	ops = ctlr->ops;
89dcc7d344SVignesh Raghavendra 	if (ops) {
90dcc7d344SVignesh Raghavendra 		if (ops->read16)
91dcc7d344SVignesh Raghavendra 			map->read = hyperbus_read16;
92dcc7d344SVignesh Raghavendra 		if (ops->write16)
93dcc7d344SVignesh Raghavendra 			map->write = hyperbus_write16;
94dcc7d344SVignesh Raghavendra 		if (ops->copy_to)
95dcc7d344SVignesh Raghavendra 			map->copy_to = hyperbus_copy_to;
96dcc7d344SVignesh Raghavendra 		if (ops->copy_from)
97dcc7d344SVignesh Raghavendra 			map->copy_from = hyperbus_copy_from;
98dcc7d344SVignesh Raghavendra 
99dcc7d344SVignesh Raghavendra 		if (ops->calibrate && !ctlr->calibrated) {
100dcc7d344SVignesh Raghavendra 			ret = ops->calibrate(hbdev);
101dcc7d344SVignesh Raghavendra 			if (!ret) {
102dcc7d344SVignesh Raghavendra 				dev_err(dev, "Calibration failed\n");
103dcc7d344SVignesh Raghavendra 				return -ENODEV;
104dcc7d344SVignesh Raghavendra 			}
105dcc7d344SVignesh Raghavendra 			ctlr->calibrated = true;
106dcc7d344SVignesh Raghavendra 		}
107dcc7d344SVignesh Raghavendra 	}
108dcc7d344SVignesh Raghavendra 
109dcc7d344SVignesh Raghavendra 	hbdev->mtd = do_map_probe("cfi_probe", map);
110dcc7d344SVignesh Raghavendra 	if (!hbdev->mtd) {
111dcc7d344SVignesh Raghavendra 		dev_err(dev, "probing of hyperbus device failed\n");
112dcc7d344SVignesh Raghavendra 		return -ENODEV;
113dcc7d344SVignesh Raghavendra 	}
114dcc7d344SVignesh Raghavendra 
115dcc7d344SVignesh Raghavendra 	hbdev->mtd->dev.parent = dev;
116dcc7d344SVignesh Raghavendra 	mtd_set_of_node(hbdev->mtd, np);
117dcc7d344SVignesh Raghavendra 
118dcc7d344SVignesh Raghavendra 	ret = mtd_device_register(hbdev->mtd, NULL, 0);
119dcc7d344SVignesh Raghavendra 	if (ret) {
120dcc7d344SVignesh Raghavendra 		dev_err(dev, "failed to register mtd device\n");
121dcc7d344SVignesh Raghavendra 		map_destroy(hbdev->mtd);
122dcc7d344SVignesh Raghavendra 		return ret;
123dcc7d344SVignesh Raghavendra 	}
124dcc7d344SVignesh Raghavendra 
125dcc7d344SVignesh Raghavendra 	return 0;
126dcc7d344SVignesh Raghavendra }
127dcc7d344SVignesh Raghavendra EXPORT_SYMBOL_GPL(hyperbus_register_device);
128dcc7d344SVignesh Raghavendra 
129dcc7d344SVignesh Raghavendra int hyperbus_unregister_device(struct hyperbus_device *hbdev)
130dcc7d344SVignesh Raghavendra {
131dcc7d344SVignesh Raghavendra 	int ret = 0;
132dcc7d344SVignesh Raghavendra 
133dcc7d344SVignesh Raghavendra 	if (hbdev && hbdev->mtd) {
134dcc7d344SVignesh Raghavendra 		ret = mtd_device_unregister(hbdev->mtd);
135dcc7d344SVignesh Raghavendra 		map_destroy(hbdev->mtd);
136dcc7d344SVignesh Raghavendra 	}
137dcc7d344SVignesh Raghavendra 
138dcc7d344SVignesh Raghavendra 	return ret;
139dcc7d344SVignesh Raghavendra }
140dcc7d344SVignesh Raghavendra EXPORT_SYMBOL_GPL(hyperbus_unregister_device);
141dcc7d344SVignesh Raghavendra 
142dcc7d344SVignesh Raghavendra MODULE_DESCRIPTION("HyperBus Framework");
143dcc7d344SVignesh Raghavendra MODULE_LICENSE("GPL v2");
144dcc7d344SVignesh Raghavendra MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>");
145