1 /*
2 * cyttsp5_spi.c
3 * Parade TrueTouch(TM) Standard Product V5 SPI Module.
4 * For use with Parade touchscreen controllers.
5 * Supported parts include:
6 * CYTMA5XX
7 * CYTMA448
8 * CYTMA445A
9 * CYTT21XXX
10 * CYTT31XXX
11 *
12 * Copyright (C) 2015 Parade Technologies
13 * Copyright (C) 2012-2015 Cypress Semiconductor
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * version 2, and only version 2, as published by the
18 * Free Software Foundation.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
26 *
27 */
28
29 #include "cyttsp5_regs.h"
30
31 #include <linux/spi/spi.h>
32 #include <linux/version.h>
33
34 #define CY_SPI_WR_OP 0x00 /* r/~w */
35 #define CY_SPI_RD_OP 0x01
36 #define CY_SPI_BITS_PER_WORD 8
37 #define CY_SPI_SYNC_ACK 0x62
38
39 #define CY_SPI_CMD_BYTES 0
40 #define CY_SPI_DATA_SIZE (2 * 256)
41 #define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
42
cyttsp5_spi_add_rw_msg(struct spi_message * msg,struct spi_transfer * xfer,u8 * w_header,u8 * r_header,u8 op)43 static void cyttsp5_spi_add_rw_msg(struct spi_message *msg,
44 struct spi_transfer *xfer, u8 *w_header, u8 *r_header, u8 op)
45 {
46 xfer->tx_buf = w_header;
47 xfer->rx_buf = r_header;
48 w_header[0] = op;
49 xfer->len = 1;
50 spi_message_add_tail(xfer, msg);
51 }
52
cyttsp5_spi_xfer(struct device * dev,u8 op,u8 * buf,int length)53 static int cyttsp5_spi_xfer(struct device *dev, u8 op, u8 *buf, int length)
54 {
55 struct spi_device *spi = to_spi_device(dev);
56 struct spi_message msg;
57 struct spi_transfer xfer[2];
58 u8 w_header[2];
59 u8 r_header[2];
60 int rc;
61
62 memset(xfer, 0, sizeof(xfer));
63
64 spi_message_init(&msg);
65 cyttsp5_spi_add_rw_msg(&msg, &xfer[0], w_header, r_header, op);
66
67 switch (op) {
68 case CY_SPI_RD_OP:
69 xfer[1].rx_buf = buf;
70 xfer[1].len = length;
71 spi_message_add_tail(&xfer[1], &msg);
72 break;
73 case CY_SPI_WR_OP:
74 xfer[1].tx_buf = buf;
75 xfer[1].len = length;
76 spi_message_add_tail(&xfer[1], &msg);
77 break;
78 default:
79 rc = -EIO;
80 goto exit;
81 }
82
83 rc = spi_sync(spi, &msg);
84 exit:
85 if (rc < 0)
86 parade_debug(dev, DEBUG_LEVEL_2, "%s: spi_sync() error %d\n",
87 __func__, rc);
88
89 if (r_header[0] != CY_SPI_SYNC_ACK)
90 return -EIO;
91
92 return rc;
93 }
94
cyttsp5_spi_read_default(struct device * dev,void * buf,int size)95 static int cyttsp5_spi_read_default(struct device *dev, void *buf, int size)
96 {
97 if (!buf || !size)
98 return 0;
99
100 return cyttsp5_spi_xfer(dev, CY_SPI_RD_OP, buf, size);
101 }
102
cyttsp5_spi_read_default_nosize(struct device * dev,u8 * buf,u32 max)103 static int cyttsp5_spi_read_default_nosize(struct device *dev, u8 *buf, u32 max)
104 {
105 u32 size;
106 int rc;
107
108 if (!buf)
109 return 0;
110
111 rc = cyttsp5_spi_xfer(dev, CY_SPI_RD_OP, buf, 2);
112 if (rc < 0)
113 return rc;
114
115 size = get_unaligned_le16(&buf[0]);
116 if (!size)
117 return rc;
118
119 if (size > max)
120 return -EINVAL;
121
122 return cyttsp5_spi_read_default(dev, buf, size);
123 }
124
cyttsp5_spi_write_read_specific(struct device * dev,u8 write_len,u8 * write_buf,u8 * read_buf)125 static int cyttsp5_spi_write_read_specific(struct device *dev, u8 write_len,
126 u8 *write_buf, u8 *read_buf)
127 {
128 int rc;
129
130 rc = cyttsp5_spi_xfer(dev, CY_SPI_WR_OP, write_buf, write_len);
131 if (rc < 0)
132 return rc;
133
134 if (read_buf)
135 rc = cyttsp5_spi_read_default_nosize(dev, read_buf,
136 CY_SPI_DATA_SIZE);
137
138 return rc;
139 }
140
141 static struct cyttsp5_bus_ops cyttsp5_spi_bus_ops = {
142 .bustype = BUS_SPI,
143 .read_default = cyttsp5_spi_read_default,
144 .read_default_nosize = cyttsp5_spi_read_default_nosize,
145 .write_read_specific = cyttsp5_spi_write_read_specific,
146 };
147
148 #ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
149 static const struct of_device_id cyttsp5_spi_of_match[] = {
150 { .compatible = "cy,cyttsp5_spi_adapter", },
151 { }
152 };
153 MODULE_DEVICE_TABLE(of, cyttsp5_spi_of_match);
154 #endif
155
cyttsp5_spi_probe(struct spi_device * spi)156 static int cyttsp5_spi_probe(struct spi_device *spi)
157 {
158 struct device *dev = &spi->dev;
159 #ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
160 const struct of_device_id *match;
161 #endif
162 int rc;
163
164 /* Set up SPI*/
165 spi->bits_per_word = CY_SPI_BITS_PER_WORD;
166 spi->mode = SPI_MODE_0;
167 rc = spi_setup(spi);
168 if (rc < 0) {
169 dev_err(dev, "%s: SPI setup error %d\n", __func__, rc);
170 return rc;
171 }
172
173 #ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
174 match = of_match_device(of_match_ptr(cyttsp5_spi_of_match), dev);
175 if (match) {
176 rc = cyttsp5_devtree_create_and_get_pdata(dev);
177 if (rc < 0)
178 return rc;
179 }
180 #endif
181
182 rc = cyttsp5_probe(&cyttsp5_spi_bus_ops, &spi->dev, spi->irq,
183 CY_SPI_DATA_BUF_SIZE);
184
185 #ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
186 if (rc && match)
187 cyttsp5_devtree_clean_pdata(dev);
188 #endif
189
190 return rc;
191 }
192
cyttsp5_spi_remove(struct spi_device * spi)193 static int cyttsp5_spi_remove(struct spi_device *spi)
194 {
195 #ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
196 struct device *dev = &spi->dev;
197 const struct of_device_id *match;
198 #endif
199 struct cyttsp5_core_data *cd = dev_get_drvdata(&spi->dev);
200
201 cyttsp5_release(cd);
202
203 #ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
204 match = of_match_device(of_match_ptr(cyttsp5_spi_of_match), dev);
205 if (match)
206 cyttsp5_devtree_clean_pdata(dev);
207 #endif
208
209 return 0;
210 }
211
212 static const struct spi_device_id cyttsp5_spi_id[] = {
213 { CYTTSP5_SPI_NAME, 0 },
214 { }
215 };
216 MODULE_DEVICE_TABLE(spi, cyttsp5_spi_id);
217
218 static struct spi_driver cyttsp5_spi_driver = {
219 .driver = {
220 .name = CYTTSP5_SPI_NAME,
221 .bus = &spi_bus_type,
222 .owner = THIS_MODULE,
223 .pm = &cyttsp5_pm_ops,
224 #ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
225 .of_match_table = cyttsp5_spi_of_match,
226 #endif
227 },
228 .probe = cyttsp5_spi_probe,
229 .remove = (cyttsp5_spi_remove),
230 .id_table = cyttsp5_spi_id,
231 };
232
233 #if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE)
234 module_spi_driver(cyttsp5_spi_driver);
235 #else
cyttsp5_spi_init(void)236 static int __init cyttsp5_spi_init(void)
237 {
238 int err = spi_register_driver(&cyttsp5_spi_driver);
239
240 pr_info("%s: Parade TTSP SPI Driver (Built %s) rc=%d\n",
241 __func__, CY_DRIVER_VERSION, err);
242 return err;
243 }
244 module_init(cyttsp5_spi_init);
245
cyttsp5_spi_exit(void)246 static void __exit cyttsp5_spi_exit(void)
247 {
248 spi_unregister_driver(&cyttsp5_spi_driver);
249 }
250 module_exit(cyttsp5_spi_exit);
251 #endif
252
253 MODULE_LICENSE("GPL");
254 MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product SPI Driver");
255 MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
256