summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVignesh Raghavendra <vigneshr@ti.com>2019-06-25 03:57:44 -0400
committerMiquel Raynal <miquel.raynal@bootlin.com>2019-06-27 13:47:58 -0400
commitdcc7d3446a0fa19bd7e8074920b8f9ef3b7ec00c (patch)
tree9d531f74c08690557d9a126aebd5709ab73051de
parent89ebf2b8501c41b16b093fbf8b617fb5630445d9 (diff)
mtd: Add support for HyperBus memory devices
Cypress' HyperBus is Low Signal Count, High Performance Double Data Rate Bus interface between a host system master and one or more slave interfaces. HyperBus is used to connect microprocessor, microcontroller, or ASIC devices with random access NOR flash memory (called HyperFlash) or self refresh DRAM (called HyperRAM). Its a 8-bit data bus (DQ[7:0]) with Read-Write Data Strobe (RWDS) signal and either Single-ended clock(3.0V parts) or Differential clock (1.8V parts). It uses ChipSelect lines to select b/w multiple slaves. At bus level, it follows a separate protocol described in HyperBus specification[1]. HyperFlash follows CFI AMD/Fujitsu Extended Command Set (0x0002) similar to that of existing parallel NORs. Since HyperBus is x8 DDR bus, its equivalent to x16 parallel NOR flash with respect to bits per clock cycle. But HyperBus operates at >166MHz frequencies. HyperRAM provides direct random read/write access to flash memory array. But, HyperBus memory controllers seem to abstract implementation details and expose a simple MMIO interface to access connected flash. Add support for registering HyperFlash devices with MTD framework. MTD maps framework along with CFI chip support framework are used to support communicating with flash. Framework is modelled along the lines of spi-nor framework. HyperBus memory controller (HBMC) drivers calls hyperbus_register_device() to register a single HyperFlash device. HyperFlash core parses MMIO access information from DT, sets up the map_info struct, probes CFI flash and registers it with MTD framework. Some HBMC masters need calibration/training sequence[3] to be carried out, in order for DLL inside the controller to lock, by reading a known string/pattern. This is done by repeatedly reading CFI Query Identification String. Calibration needs to be done before trying to detect flash as part of CFI flash probe. HyperRAM is not supported at the moment. HyperBus specification can be found at[1] HyperFlash datasheet can be found at[2] [1] https://www.cypress.com/file/213356/download [2] https://www.cypress.com/file/213346/download [3] http://www.ti.com/lit/ug/spruid7b/spruid7b.pdf Table 12-5741. HyperFlash Access Sequence Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/mtd/Kconfig2
-rw-r--r--drivers/mtd/Makefile1
-rw-r--r--drivers/mtd/hyperbus/Kconfig11
-rw-r--r--drivers/mtd/hyperbus/Makefile3
-rw-r--r--drivers/mtd/hyperbus/hyperbus-core.c153
-rw-r--r--include/linux/mtd/hyperbus.h84
7 files changed, 261 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 5cfbea4ce575..f1253adb8cf6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7305,6 +7305,13 @@ F: include/uapi/linux/hyperv.h
7305F: tools/hv/ 7305F: tools/hv/
7306F: Documentation/ABI/stable/sysfs-bus-vmbus 7306F: Documentation/ABI/stable/sysfs-bus-vmbus
7307 7307
7308HYPERBUS SUPPORT
7309M: Vignesh Raghavendra <vigneshr@ti.com>
7310S: Supported
7311F: drivers/mtd/hyperbus/
7312F: include/linux/mtd/hyperbus.h
7313F: Documentation/devicetree/bindings/mtd/cypress,hyperflash.txt
7314
7308HYPERVISOR VIRTUAL CONSOLE DRIVER 7315HYPERVISOR VIRTUAL CONSOLE DRIVER
7309L: linuxppc-dev@lists.ozlabs.org 7316L: linuxppc-dev@lists.ozlabs.org
7310S: Odd Fixes 7317S: Odd Fixes
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index fb31a7f649a3..80a6e2dcd085 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -274,4 +274,6 @@ source "drivers/mtd/spi-nor/Kconfig"
274 274
275source "drivers/mtd/ubi/Kconfig" 275source "drivers/mtd/ubi/Kconfig"
276 276
277source "drivers/mtd/hyperbus/Kconfig"
278
277endif # MTD 279endif # MTD
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 806287e80e84..62d649a959e2 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -34,3 +34,4 @@ obj-y += chips/ lpddr/ maps/ devices/ nand/ tests/
34 34
35obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ 35obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
36obj-$(CONFIG_MTD_UBI) += ubi/ 36obj-$(CONFIG_MTD_UBI) += ubi/
37obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/
diff --git a/drivers/mtd/hyperbus/Kconfig b/drivers/mtd/hyperbus/Kconfig
new file mode 100644
index 000000000000..98147e28caa0
--- /dev/null
+++ b/drivers/mtd/hyperbus/Kconfig
@@ -0,0 +1,11 @@
1menuconfig MTD_HYPERBUS
2 tristate "HyperBus support"
3 select MTD_CFI
4 select MTD_MAP_BANK_WIDTH_2
5 select MTD_CFI_AMDSTD
6 select MTD_COMPLEX_MAPPINGS
7 help
8 This is the framework for the HyperBus which can be used by
9 the HyperBus Controller driver to communicate with
10 HyperFlash. See Cypress HyperBus specification for more
11 details
diff --git a/drivers/mtd/hyperbus/Makefile b/drivers/mtd/hyperbus/Makefile
new file mode 100644
index 000000000000..ca61dedd730d
--- /dev/null
+++ b/drivers/mtd/hyperbus/Makefile
@@ -0,0 +1,3 @@
1# SPDX-License-Identifier: GPL-2.0
2
3obj-$(CONFIG_MTD_HYPERBUS) += hyperbus-core.o
diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c
new file mode 100644
index 000000000000..6af9ea34117d
--- /dev/null
+++ b/drivers/mtd/hyperbus/hyperbus-core.c
@@ -0,0 +1,153 @@
1// SPDX-License-Identifier: GPL-2.0
2//
3// Copyright (C) 2019 Texas Instruments Incorporated - http://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/of_address.h>
14#include <linux/types.h>
15
16static struct hyperbus_device *map_to_hbdev(struct map_info *map)
17{
18 return container_of(map, struct hyperbus_device, map);
19}
20
21static map_word hyperbus_read16(struct map_info *map, unsigned long addr)
22{
23 struct hyperbus_device *hbdev = map_to_hbdev(map);
24 struct hyperbus_ctlr *ctlr = hbdev->ctlr;
25 map_word read_data;
26
27 read_data.x[0] = ctlr->ops->read16(hbdev, addr);
28
29 return read_data;
30}
31
32static void hyperbus_write16(struct map_info *map, map_word d,
33 unsigned long addr)
34{
35 struct hyperbus_device *hbdev = map_to_hbdev(map);
36 struct hyperbus_ctlr *ctlr = hbdev->ctlr;
37
38 ctlr->ops->write16(hbdev, addr, d.x[0]);
39}
40
41static void hyperbus_copy_from(struct map_info *map, void *to,
42 unsigned long from, ssize_t len)
43{
44 struct hyperbus_device *hbdev = map_to_hbdev(map);
45 struct hyperbus_ctlr *ctlr = hbdev->ctlr;
46
47 ctlr->ops->copy_from(hbdev, to, from, len);
48}
49
50static void hyperbus_copy_to(struct map_info *map, unsigned long to,
51 const void *from, ssize_t len)
52{
53 struct hyperbus_device *hbdev = map_to_hbdev(map);
54 struct hyperbus_ctlr *ctlr = hbdev->ctlr;
55
56 ctlr->ops->copy_to(hbdev, to, from, len);
57}
58
59int hyperbus_register_device(struct hyperbus_device *hbdev)
60{
61 const struct hyperbus_ops *ops;
62 struct hyperbus_ctlr *ctlr;
63 struct device_node *np;
64 struct map_info *map;
65 struct resource res;
66 struct device *dev;
67 int ret;
68
69 if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) {
70 pr_err("hyperbus: please fill all the necessary fields!\n");
71 return -EINVAL;
72 }
73
74 np = hbdev->np;
75 ctlr = hbdev->ctlr;
76 if (!of_device_is_compatible(np, "cypress,hyperflash"))
77 return -ENODEV;
78
79 hbdev->memtype = HYPERFLASH;
80
81 ret = of_address_to_resource(np, 0, &res);
82 if (ret)
83 return ret;
84
85 dev = ctlr->dev;
86 map = &hbdev->map;
87 map->size = resource_size(&res);
88 map->virt = devm_ioremap_resource(dev, &res);
89 if (IS_ERR(map->virt))
90 return PTR_ERR(map->virt);
91
92 map->name = dev_name(dev);
93 map->bankwidth = 2;
94 map->device_node = np;
95
96 simple_map_init(map);
97 ops = ctlr->ops;
98 if (ops) {
99 if (ops->read16)
100 map->read = hyperbus_read16;
101 if (ops->write16)
102 map->write = hyperbus_write16;
103 if (ops->copy_to)
104 map->copy_to = hyperbus_copy_to;
105 if (ops->copy_from)
106 map->copy_from = hyperbus_copy_from;
107
108 if (ops->calibrate && !ctlr->calibrated) {
109 ret = ops->calibrate(hbdev);
110 if (!ret) {
111 dev_err(dev, "Calibration failed\n");
112 return -ENODEV;
113 }
114 ctlr->calibrated = true;
115 }
116 }
117
118 hbdev->mtd = do_map_probe("cfi_probe", map);
119 if (!hbdev->mtd) {
120 dev_err(dev, "probing of hyperbus device failed\n");
121 return -ENODEV;
122 }
123
124 hbdev->mtd->dev.parent = dev;
125 mtd_set_of_node(hbdev->mtd, np);
126
127 ret = mtd_device_register(hbdev->mtd, NULL, 0);
128 if (ret) {
129 dev_err(dev, "failed to register mtd device\n");
130 map_destroy(hbdev->mtd);
131 return ret;
132 }
133
134 return 0;
135}
136EXPORT_SYMBOL_GPL(hyperbus_register_device);
137
138int hyperbus_unregister_device(struct hyperbus_device *hbdev)
139{
140 int ret = 0;
141
142 if (hbdev && hbdev->mtd) {
143 ret = mtd_device_unregister(hbdev->mtd);
144 map_destroy(hbdev->mtd);
145 }
146
147 return ret;
148}
149EXPORT_SYMBOL_GPL(hyperbus_unregister_device);
150
151MODULE_DESCRIPTION("HyperBus Framework");
152MODULE_LICENSE("GPL v2");
153MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>");
diff --git a/include/linux/mtd/hyperbus.h b/include/linux/mtd/hyperbus.h
new file mode 100644
index 000000000000..2dfe65964f6e
--- /dev/null
+++ b/include/linux/mtd/hyperbus.h
@@ -0,0 +1,84 @@
1/* SPDX-License-Identifier: GPL-2.0
2 *
3 * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
4 */
5
6#ifndef __LINUX_MTD_HYPERBUS_H__
7#define __LINUX_MTD_HYPERBUS_H__
8
9#include <linux/mtd/map.h>
10
11enum hyperbus_memtype {
12 HYPERFLASH,
13 HYPERRAM,
14};
15
16/**
17 * struct hyperbus_device - struct representing HyperBus slave device
18 * @map: map_info struct for accessing MMIO HyperBus flash memory
19 * @np: pointer to HyperBus slave device node
20 * @mtd: pointer to MTD struct
21 * @ctlr: pointer to HyperBus controller struct
22 * @memtype: type of memory device: HyperFlash or HyperRAM
23 */
24
25struct hyperbus_device {
26 struct map_info map;
27 struct device_node *np;
28 struct mtd_info *mtd;
29 struct hyperbus_ctlr *ctlr;
30 enum hyperbus_memtype memtype;
31};
32
33/**
34 * struct hyperbus_ops - struct representing custom HyperBus operations
35 * @read16: read 16 bit of data from flash in a single burst. Used to read
36 * from non default address space, such as ID/CFI space
37 * @write16: write 16 bit of data to flash in a single burst. Used to
38 * send cmd to flash or write single 16 bit word at a time.
39 * @copy_from: copy data from flash memory
40 * @copy_to: copy data to flash memory
41 * @calibrate: calibrate HyperBus controller
42 */
43
44struct hyperbus_ops {
45 u16 (*read16)(struct hyperbus_device *hbdev, unsigned long addr);
46 void (*write16)(struct hyperbus_device *hbdev,
47 unsigned long addr, u16 val);
48 void (*copy_from)(struct hyperbus_device *hbdev, void *to,
49 unsigned long from, ssize_t len);
50 void (*copy_to)(struct hyperbus_device *dev, unsigned long to,
51 const void *from, ssize_t len);
52 int (*calibrate)(struct hyperbus_device *dev);
53};
54
55/**
56 * struct hyperbus_ctlr - struct representing HyperBus controller
57 * @dev: pointer to HyperBus controller device
58 * @calibrated: flag to indicate ctlr calibration sequence is complete
59 * @ops: HyperBus controller ops
60 */
61struct hyperbus_ctlr {
62 struct device *dev;
63 bool calibrated;
64
65 const struct hyperbus_ops *ops;
66};
67
68/**
69 * hyperbus_register_device - probe and register a HyperBus slave memory device
70 * @hbdev: hyperbus_device struct with dev, np and ctlr field populated
71 *
72 * Return: 0 for success, others for failure.
73 */
74int hyperbus_register_device(struct hyperbus_device *hbdev);
75
76/**
77 * hyperbus_unregister_device - deregister HyperBus slave memory device
78 * @hbdev: hyperbus_device to be unregistered
79 *
80 * Return: 0 for success, others for failure.
81 */
82int hyperbus_unregister_device(struct hyperbus_device *hbdev);
83
84#endif /* __LINUX_MTD_HYPERBUS_H__ */