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