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