aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ramips/patches-3.18/0044-mtd-add-chunked-read-io-to-m25p80.patch
blob: 2e81e456d3bcf2ffa50862d5cb0db368a43855cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -19,6 +19,7 @@
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/device.h>
+#include <linux/of.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
@@ -32,6 +33,7 @@ struct m25p {
 	struct spi_device	*spi;
 	struct spi_nor		spi_nor;
 	struct mtd_info		mtd;
+	u16			chunk_size;
 	u8			command[MAX_CMD_SIZE];
 };
 
@@ -117,25 +119,14 @@ static inline unsigned int m25p80_rx_nbi
 	}
 }
 
-/*
- * Read an address range from the nor chip.  The address range
- * may be any size provided it is within the physical boundaries.
- */
-static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
-			size_t *retlen, u_char *buf)
+static int __m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
+			 size_t *retlen, u_char *buf)
 {
 	struct m25p *flash = nor->priv;
 	struct spi_device *spi = flash->spi;
 	struct spi_transfer t[2];
 	struct spi_message m;
 	int dummy = nor->read_dummy;
-	int ret;
-
-	/* Wait till previous write/erase is done. */
-	ret = nor->wait_till_ready(nor);
-	if (ret)
-		return ret;
-
 	spi_message_init(&m);
 	memset(t, 0, (sizeof t));
 
@@ -156,6 +147,84 @@ static int m25p80_read(struct spi_nor *n
 	*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
 	return 0;
 }
+/*
+ * Read an address range from the nor chip.  The address range
+ * may be any size provided it is within the physical boundaries.
+ */
+static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
+			size_t *retlen, u_char *buf)
+{
+	int ret;
+
+	/* Wait till previous write/erase is done. */
+	ret = nor->wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	return __m25p80_read(nor, from, len, retlen, buf);
+}
+
+
+static void m25p80_chunked_write(struct spi_nor *nor, loff_t _from, size_t _len,
+			size_t *_retlen, const u_char *_buf)
+{
+	struct m25p *flash = nor->priv;
+	int chunk_size;
+	int retlen = 0;
+
+	chunk_size = flash->chunk_size;
+	if (!chunk_size)
+		chunk_size = _len;
+
+	if (nor->addr_width > 3)
+		chunk_size -= nor->addr_width - 3;
+
+	while (retlen < _len) {
+		size_t len = min_t(int, chunk_size, _len - retlen);
+		const u_char *buf = _buf + retlen;
+		loff_t from = _from + retlen;
+
+		nor->wait_till_ready(nor);
+		nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
+
+		m25p80_write(nor, from, len, &retlen, buf);
+	}
+	*_retlen += retlen;
+}
+
+static int m25p80_chunked_read(struct spi_nor *nor, loff_t _from, size_t _len,
+			size_t *_retlen, u_char *_buf)
+{
+	struct m25p *flash = nor->priv;
+	int chunk_size;
+	int ret;
+
+	/* Wait till previous write/erase is done. */
+	ret = nor->wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	chunk_size = flash->chunk_size;
+	if (!chunk_size)
+		chunk_size = _len;
+
+	*_retlen = 0;
+
+	while (*_retlen < _len) {
+		size_t len = min_t(int, chunk_size, _len - *_retlen);
+		u_char *buf = _buf + *_retlen;
+		loff_t from = _from + *_retlen;
+		int retlen = 0;
+		int ret = __m25p80_read(nor, from, len, &retlen, buf);
+
+		if (ret)
+			return ret;
+
+		*_retlen += retlen;
+	}
+
+	return 0;
+}
 
 static int m25p80_erase(struct spi_nor *nor, loff_t offset)
 {
@@ -197,6 +266,7 @@ static int m25p_probe(struct spi_device
 	struct spi_nor *nor;
 	enum read_mode mode = SPI_NOR_NORMAL;
 	char *flash_name = NULL;
+	u32 val;
 	int ret;
 
 	data = dev_get_platdata(&spi->dev);
@@ -244,6 +314,14 @@ static int m25p_probe(struct spi_device
 	if (ret)
 		return ret;
 
+	if (spi->dev.of_node &&
+	    !of_property_read_u32(spi->dev.of_node, "m25p,chunked-io", &val)) {
+		dev_warn(&spi->dev, "using chunked io\n");
+		nor->read = m25p80_chunked_read;
+		nor->write = m25p80_chunked_write;
+		flash->chunk_size = val;
+	}
+
 	ppdata.of_node = spi->dev.of_node;
 
 	return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,