diff options
author | Guenter Roeck <linux@roeck-us.net> | 2012-08-18 12:06:27 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-08-22 14:34:51 -0400 |
commit | 3ce8859e2e72713d3619285cab609d05c3591fc4 (patch) | |
tree | c5ac44e96198137f34559e1fd493c9d6b02960b1 | |
parent | 21879213652aca6e3fe250fc8e02c68e71c5e18a (diff) |
spi: Master driver for NXP SC18IS602/603
This driver adds support for NXP SC18IS602/603 I2C to SPI bus bridge.
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r-- | Documentation/devicetree/bindings/spi/spi-sc18is602.txt | 23 | ||||
-rw-r--r-- | Documentation/spi/spi-sc18is602 | 36 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-sc18is602.c | 364 | ||||
-rw-r--r-- | include/linux/platform_data/sc18is602.h | 19 |
6 files changed, 449 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/spi/spi-sc18is602.txt b/Documentation/devicetree/bindings/spi/spi-sc18is602.txt new file mode 100644 index 000000000000..02f9033270a2 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-sc18is602.txt | |||
@@ -0,0 +1,23 @@ | |||
1 | NXP SC18IS602/SCIS603 | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : Should be one of | ||
5 | "nxp,sc18is602" | ||
6 | "nxp,sc18is602b" | ||
7 | "nxp,sc18is603" | ||
8 | - reg: I2C bus address | ||
9 | |||
10 | Optional properties: | ||
11 | - clock-frequency : external oscillator clock frequency. If not | ||
12 | specified, the SC18IS602 default frequency (7372000) will be used. | ||
13 | |||
14 | The clock-frequency property is relevant and needed only if the chip has an | ||
15 | external oscillator (SC18IS603). | ||
16 | |||
17 | Example: | ||
18 | |||
19 | sc18is603@28 { | ||
20 | compatible = "nxp,sc18is603"; | ||
21 | reg = <0x28>; | ||
22 | clock-frequency = <14744000>; | ||
23 | } | ||
diff --git a/Documentation/spi/spi-sc18is602 b/Documentation/spi/spi-sc18is602 new file mode 100644 index 000000000000..a45702865a38 --- /dev/null +++ b/Documentation/spi/spi-sc18is602 | |||
@@ -0,0 +1,36 @@ | |||
1 | Kernel driver spi-sc18is602 | ||
2 | =========================== | ||
3 | |||
4 | Supported chips: | ||
5 | * NXP SI18IS602/602B/603 | ||
6 | Datasheet: http://www.nxp.com/documents/data_sheet/SC18IS602_602B_603.pdf | ||
7 | |||
8 | Author: | ||
9 | Guenter Roeck <linux@roeck-us.net> | ||
10 | |||
11 | |||
12 | Description | ||
13 | ----------- | ||
14 | |||
15 | This driver provides connects a NXP SC18IS602/603 I2C-bus to SPI bridge to the | ||
16 | kernel's SPI core subsystem. | ||
17 | |||
18 | The driver does not probe for supported chips, since the SI18IS602/603 does not | ||
19 | support Chip ID registers. You will have to instantiate the devices explicitly. | ||
20 | Please see Documentation/i2c/instantiating-devices for details. | ||
21 | |||
22 | |||
23 | Usage Notes | ||
24 | ----------- | ||
25 | |||
26 | This driver requires the I2C adapter driver to support raw I2C messages. I2C | ||
27 | adapter drivers which can only handle the SMBus protocol are not supported. | ||
28 | |||
29 | The maximum SPI message size supported by SC18IS602/603 is 200 bytes. Attempts | ||
30 | to initiate longer transfers will fail with -EINVAL. EEPROM read operations and | ||
31 | similar large accesses have to be split into multiple chunks of no more than | ||
32 | 200 bytes per SPI message (128 bytes of data per message is recommended). This | ||
33 | means that programs such as "cp" or "od", which automatically use large block | ||
34 | sizes to access a device, can not be used directly to read data from EEPROM. | ||
35 | Programs such as dd, where the block size can be specified, should be used | ||
36 | instead. | ||
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5f84b5563c2d..920bb4d22d40 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig | |||
@@ -325,6 +325,12 @@ config SPI_S3C64XX | |||
325 | help | 325 | help |
326 | SPI driver for Samsung S3C64XX and newer SoCs. | 326 | SPI driver for Samsung S3C64XX and newer SoCs. |
327 | 327 | ||
328 | config SPI_SC18IS602 | ||
329 | tristate "NXP SC18IS602/602B/603 I2C to SPI bridge" | ||
330 | depends on I2C | ||
331 | help | ||
332 | SPI driver for NXP SC18IS602/602B/603 I2C to SPI bridge. | ||
333 | |||
328 | config SPI_SH_MSIOF | 334 | config SPI_SH_MSIOF |
329 | tristate "SuperH MSIOF SPI controller" | 335 | tristate "SuperH MSIOF SPI controller" |
330 | depends on SUPERH && HAVE_CLK | 336 | depends on SUPERH && HAVE_CLK |
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3920dcf4c740..7559c984db77 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile | |||
@@ -51,6 +51,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o | |||
51 | spi-s3c24xx-hw-y := spi-s3c24xx.o | 51 | spi-s3c24xx-hw-y := spi-s3c24xx.o |
52 | spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o | 52 | spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o |
53 | obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o | 53 | obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o |
54 | obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o | ||
54 | obj-$(CONFIG_SPI_SH) += spi-sh.o | 55 | obj-$(CONFIG_SPI_SH) += spi-sh.o |
55 | obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o | 56 | obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o |
56 | obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o | 57 | obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o |
diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c new file mode 100644 index 000000000000..dd9896423f0b --- /dev/null +++ b/drivers/spi/spi-sc18is602.c | |||
@@ -0,0 +1,364 @@ | |||
1 | /* | ||
2 | * NXP SC18IS602/603 SPI driver | ||
3 | * | ||
4 | * Copyright (C) Guenter Roeck <linux@roeck-us.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/err.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/spi/spi.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/pm_runtime.h> | ||
28 | #include <linux/of.h> | ||
29 | #include <linux/platform_data/sc18is602.h> | ||
30 | |||
31 | enum chips { sc18is602, sc18is602b, sc18is603 }; | ||
32 | |||
33 | #define SC18IS602_BUFSIZ 200 | ||
34 | #define SC18IS602_CLOCK 7372000 | ||
35 | |||
36 | #define SC18IS602_MODE_CPHA BIT(2) | ||
37 | #define SC18IS602_MODE_CPOL BIT(3) | ||
38 | #define SC18IS602_MODE_LSB_FIRST BIT(5) | ||
39 | #define SC18IS602_MODE_CLOCK_DIV_4 0x0 | ||
40 | #define SC18IS602_MODE_CLOCK_DIV_16 0x1 | ||
41 | #define SC18IS602_MODE_CLOCK_DIV_64 0x2 | ||
42 | #define SC18IS602_MODE_CLOCK_DIV_128 0x3 | ||
43 | |||
44 | struct sc18is602 { | ||
45 | struct spi_master *master; | ||
46 | struct device *dev; | ||
47 | u8 ctrl; | ||
48 | u32 freq; | ||
49 | u32 speed; | ||
50 | |||
51 | /* I2C data */ | ||
52 | struct i2c_client *client; | ||
53 | enum chips id; | ||
54 | u8 buffer[SC18IS602_BUFSIZ + 1]; | ||
55 | int tlen; /* Data queued for tx in buffer */ | ||
56 | int rindex; /* Receive data index in buffer */ | ||
57 | }; | ||
58 | |||
59 | static int sc18is602_wait_ready(struct sc18is602 *hw, int len) | ||
60 | { | ||
61 | int i, err; | ||
62 | int usecs = 1000000 * len / hw->speed + 1; | ||
63 | u8 dummy[1]; | ||
64 | |||
65 | for (i = 0; i < 10; i++) { | ||
66 | err = i2c_master_recv(hw->client, dummy, 1); | ||
67 | if (err >= 0) | ||
68 | return 0; | ||
69 | usleep_range(usecs, usecs * 2); | ||
70 | } | ||
71 | return -ETIMEDOUT; | ||
72 | } | ||
73 | |||
74 | static int sc18is602_txrx(struct sc18is602 *hw, struct spi_message *msg, | ||
75 | struct spi_transfer *t, bool do_transfer) | ||
76 | { | ||
77 | unsigned int len = t->len; | ||
78 | int ret; | ||
79 | |||
80 | if (hw->tlen == 0) { | ||
81 | /* First byte (I2C command) is chip select */ | ||
82 | hw->buffer[0] = 1 << msg->spi->chip_select; | ||
83 | hw->tlen = 1; | ||
84 | hw->rindex = 0; | ||
85 | } | ||
86 | /* | ||
87 | * We can not immediately send data to the chip, since each I2C message | ||
88 | * resembles a full SPI message (from CS active to CS inactive). | ||
89 | * Enqueue messages up to the first read or until do_transfer is true. | ||
90 | */ | ||
91 | if (t->tx_buf) { | ||
92 | memcpy(&hw->buffer[hw->tlen], t->tx_buf, len); | ||
93 | hw->tlen += len; | ||
94 | if (t->rx_buf) | ||
95 | do_transfer = true; | ||
96 | else | ||
97 | hw->rindex = hw->tlen - 1; | ||
98 | } else if (t->rx_buf) { | ||
99 | /* | ||
100 | * For receive-only transfers we still need to perform a dummy | ||
101 | * write to receive data from the SPI chip. | ||
102 | * Read data starts at the end of transmit data (minus 1 to | ||
103 | * account for CS). | ||
104 | */ | ||
105 | hw->rindex = hw->tlen - 1; | ||
106 | memset(&hw->buffer[hw->tlen], 0, len); | ||
107 | hw->tlen += len; | ||
108 | do_transfer = true; | ||
109 | } | ||
110 | |||
111 | if (do_transfer && hw->tlen > 1) { | ||
112 | ret = sc18is602_wait_ready(hw, SC18IS602_BUFSIZ); | ||
113 | if (ret < 0) | ||
114 | return ret; | ||
115 | ret = i2c_master_send(hw->client, hw->buffer, hw->tlen); | ||
116 | if (ret < 0) | ||
117 | return ret; | ||
118 | if (ret != hw->tlen) | ||
119 | return -EIO; | ||
120 | |||
121 | if (t->rx_buf) { | ||
122 | int rlen = hw->rindex + len; | ||
123 | |||
124 | ret = sc18is602_wait_ready(hw, hw->tlen); | ||
125 | if (ret < 0) | ||
126 | return ret; | ||
127 | ret = i2c_master_recv(hw->client, hw->buffer, rlen); | ||
128 | if (ret < 0) | ||
129 | return ret; | ||
130 | if (ret != rlen) | ||
131 | return -EIO; | ||
132 | memcpy(t->rx_buf, &hw->buffer[hw->rindex], len); | ||
133 | } | ||
134 | hw->tlen = 0; | ||
135 | } | ||
136 | return len; | ||
137 | } | ||
138 | |||
139 | static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode) | ||
140 | { | ||
141 | u8 ctrl = 0; | ||
142 | int ret; | ||
143 | |||
144 | if (mode & SPI_CPHA) | ||
145 | ctrl |= SC18IS602_MODE_CPHA; | ||
146 | if (mode & SPI_CPOL) | ||
147 | ctrl |= SC18IS602_MODE_CPOL; | ||
148 | if (mode & SPI_LSB_FIRST) | ||
149 | ctrl |= SC18IS602_MODE_LSB_FIRST; | ||
150 | |||
151 | /* Find the closest clock speed */ | ||
152 | if (hz >= hw->freq / 4) { | ||
153 | ctrl |= SC18IS602_MODE_CLOCK_DIV_4; | ||
154 | hw->speed = hw->freq / 4; | ||
155 | } else if (hz >= hw->freq / 16) { | ||
156 | ctrl |= SC18IS602_MODE_CLOCK_DIV_16; | ||
157 | hw->speed = hw->freq / 16; | ||
158 | } else if (hz >= hw->freq / 64) { | ||
159 | ctrl |= SC18IS602_MODE_CLOCK_DIV_64; | ||
160 | hw->speed = hw->freq / 64; | ||
161 | } else { | ||
162 | ctrl |= SC18IS602_MODE_CLOCK_DIV_128; | ||
163 | hw->speed = hw->freq / 128; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * Don't do anything if the control value did not change. The initial | ||
168 | * value of 0xff for hw->ctrl ensures that the correct mode will be set | ||
169 | * with the first call to this function. | ||
170 | */ | ||
171 | if (ctrl == hw->ctrl) | ||
172 | return 0; | ||
173 | |||
174 | ret = i2c_smbus_write_byte_data(hw->client, 0xf0, ctrl); | ||
175 | if (ret < 0) | ||
176 | return ret; | ||
177 | |||
178 | hw->ctrl = ctrl; | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static int sc18is602_check_transfer(struct spi_device *spi, | ||
184 | struct spi_transfer *t, int tlen) | ||
185 | { | ||
186 | int bpw; | ||
187 | uint32_t hz; | ||
188 | |||
189 | if (t && t->len + tlen > SC18IS602_BUFSIZ) | ||
190 | return -EINVAL; | ||
191 | |||
192 | bpw = spi->bits_per_word; | ||
193 | if (t && t->bits_per_word) | ||
194 | bpw = t->bits_per_word; | ||
195 | if (bpw != 8) | ||
196 | return -EINVAL; | ||
197 | |||
198 | hz = spi->max_speed_hz; | ||
199 | if (t && t->speed_hz) | ||
200 | hz = t->speed_hz; | ||
201 | if (hz == 0) | ||
202 | return -EINVAL; | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static int sc18is602_transfer_one(struct spi_master *master, | ||
208 | struct spi_message *m) | ||
209 | { | ||
210 | struct sc18is602 *hw = spi_master_get_devdata(master); | ||
211 | struct spi_device *spi = m->spi; | ||
212 | struct spi_transfer *t; | ||
213 | int status = 0; | ||
214 | |||
215 | /* SC18IS602 does not support CS2 */ | ||
216 | if (hw->id == sc18is602 && spi->chip_select == 2) { | ||
217 | status = -ENXIO; | ||
218 | goto error; | ||
219 | } | ||
220 | |||
221 | hw->tlen = 0; | ||
222 | list_for_each_entry(t, &m->transfers, transfer_list) { | ||
223 | u32 hz = t->speed_hz ? : spi->max_speed_hz; | ||
224 | bool do_transfer; | ||
225 | |||
226 | status = sc18is602_check_transfer(spi, t, hw->tlen); | ||
227 | if (status < 0) | ||
228 | break; | ||
229 | |||
230 | status = sc18is602_setup_transfer(hw, hz, spi->mode); | ||
231 | if (status < 0) | ||
232 | break; | ||
233 | |||
234 | do_transfer = t->cs_change || list_is_last(&t->transfer_list, | ||
235 | &m->transfers); | ||
236 | |||
237 | if (t->len) { | ||
238 | status = sc18is602_txrx(hw, m, t, do_transfer); | ||
239 | if (status < 0) | ||
240 | break; | ||
241 | m->actual_length += status; | ||
242 | } | ||
243 | status = 0; | ||
244 | |||
245 | if (t->delay_usecs) | ||
246 | udelay(t->delay_usecs); | ||
247 | } | ||
248 | error: | ||
249 | m->status = status; | ||
250 | spi_finalize_current_message(master); | ||
251 | |||
252 | return status; | ||
253 | } | ||
254 | |||
255 | static int sc18is602_setup(struct spi_device *spi) | ||
256 | { | ||
257 | if (!spi->bits_per_word) | ||
258 | spi->bits_per_word = 8; | ||
259 | |||
260 | if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST)) | ||
261 | return -EINVAL; | ||
262 | |||
263 | return sc18is602_check_transfer(spi, NULL, 0); | ||
264 | } | ||
265 | |||
266 | static int sc18is602_probe(struct i2c_client *client, | ||
267 | const struct i2c_device_id *id) | ||
268 | { | ||
269 | struct device *dev = &client->dev; | ||
270 | struct device_node *np = dev->of_node; | ||
271 | struct sc18is602_platform_data *pdata = dev_get_platdata(dev); | ||
272 | struct sc18is602 *hw; | ||
273 | struct spi_master *master; | ||
274 | int error; | ||
275 | |||
276 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | | ||
277 | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) | ||
278 | return -ENODEV; | ||
279 | |||
280 | master = spi_alloc_master(dev, sizeof(struct sc18is602)); | ||
281 | if (!master) | ||
282 | return -ENOMEM; | ||
283 | |||
284 | hw = spi_master_get_devdata(master); | ||
285 | i2c_set_clientdata(client, hw); | ||
286 | |||
287 | hw->master = master; | ||
288 | hw->client = client; | ||
289 | hw->dev = dev; | ||
290 | hw->ctrl = 0xff; | ||
291 | |||
292 | hw->id = id->driver_data; | ||
293 | |||
294 | switch (hw->id) { | ||
295 | case sc18is602: | ||
296 | case sc18is602b: | ||
297 | master->num_chipselect = 4; | ||
298 | hw->freq = SC18IS602_CLOCK; | ||
299 | break; | ||
300 | case sc18is603: | ||
301 | master->num_chipselect = 2; | ||
302 | if (pdata) { | ||
303 | hw->freq = pdata->clock_frequency; | ||
304 | } else { | ||
305 | const __be32 *val; | ||
306 | int len; | ||
307 | |||
308 | val = of_get_property(np, "clock-frequency", &len); | ||
309 | if (val && len >= sizeof(__be32)) | ||
310 | hw->freq = be32_to_cpup(val); | ||
311 | } | ||
312 | if (!hw->freq) | ||
313 | hw->freq = SC18IS602_CLOCK; | ||
314 | break; | ||
315 | } | ||
316 | master->bus_num = client->adapter->nr; | ||
317 | master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST; | ||
318 | master->setup = sc18is602_setup; | ||
319 | master->transfer_one_message = sc18is602_transfer_one; | ||
320 | master->dev.of_node = np; | ||
321 | |||
322 | error = spi_register_master(master); | ||
323 | if (error) | ||
324 | goto error_reg; | ||
325 | |||
326 | return 0; | ||
327 | |||
328 | error_reg: | ||
329 | spi_master_put(master); | ||
330 | return error; | ||
331 | } | ||
332 | |||
333 | static int sc18is602_remove(struct i2c_client *client) | ||
334 | { | ||
335 | struct sc18is602 *hw = i2c_get_clientdata(client); | ||
336 | struct spi_master *master = hw->master; | ||
337 | |||
338 | spi_unregister_master(master); | ||
339 | |||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | static const struct i2c_device_id sc18is602_id[] = { | ||
344 | { "sc18is602", sc18is602 }, | ||
345 | { "sc18is602b", sc18is602b }, | ||
346 | { "sc18is603", sc18is603 }, | ||
347 | { } | ||
348 | }; | ||
349 | MODULE_DEVICE_TABLE(i2c, sc18is602_id); | ||
350 | |||
351 | static struct i2c_driver sc18is602_driver = { | ||
352 | .driver = { | ||
353 | .name = "sc18is602", | ||
354 | }, | ||
355 | .probe = sc18is602_probe, | ||
356 | .remove = sc18is602_remove, | ||
357 | .id_table = sc18is602_id, | ||
358 | }; | ||
359 | |||
360 | module_i2c_driver(sc18is602_driver); | ||
361 | |||
362 | MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver"); | ||
363 | MODULE_AUTHOR("Guenter Roeck"); | ||
364 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/platform_data/sc18is602.h b/include/linux/platform_data/sc18is602.h new file mode 100644 index 000000000000..997b06634152 --- /dev/null +++ b/include/linux/platform_data/sc18is602.h | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * Platform data for NXP SC18IS602/603 | ||
3 | * | ||
4 | * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * For further information, see the Documentation/spi/sc18is602 file. | ||
11 | */ | ||
12 | |||
13 | /** | ||
14 | * struct sc18is602_platform_data - sc18is602 info | ||
15 | * @clock_frequency SC18IS603 oscillator frequency | ||
16 | */ | ||
17 | struct sc18is602_platform_data { | ||
18 | u32 clock_frequency; | ||
19 | }; | ||