1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Source for: |
4 | * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. |
5 | * For use with Cypress Txx3xx parts. |
6 | * Supported parts include: |
7 | * CY8CTST341 |
8 | * CY8CTMA340 |
9 | * |
10 | * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. |
11 | * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> |
12 | * Copyright (C) 2013 Cypress Semiconductor |
13 | * |
14 | * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> |
15 | */ |
16 | |
17 | #include "cyttsp_core.h" |
18 | |
19 | #include <linux/delay.h> |
20 | #include <linux/input.h> |
21 | #include <linux/spi/spi.h> |
22 | |
23 | #define CY_SPI_NAME "cyttsp-spi" |
24 | |
25 | #define CY_SPI_WR_OP 0x00 /* r/~w */ |
26 | #define CY_SPI_RD_OP 0x01 |
27 | #define CY_SPI_CMD_BYTES 4 |
28 | #define CY_SPI_SYNC_BYTE 2 |
29 | #define CY_SPI_SYNC_ACK1 0x62 /* from protocol v.2 */ |
30 | #define CY_SPI_SYNC_ACK2 0x9D /* from protocol v.2 */ |
31 | #define CY_SPI_DATA_SIZE 128 |
32 | #define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) |
33 | #define CY_SPI_BITS_PER_WORD 8 |
34 | |
35 | static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf, |
36 | u8 op, u16 reg, u8 *buf, int length) |
37 | { |
38 | struct spi_device *spi = to_spi_device(dev); |
39 | struct spi_message msg; |
40 | struct spi_transfer xfer[2]; |
41 | u8 *wr_buf = &xfer_buf[0]; |
42 | u8 *rd_buf = &xfer_buf[CY_SPI_DATA_BUF_SIZE]; |
43 | int retval; |
44 | int i; |
45 | |
46 | if (length > CY_SPI_DATA_SIZE) { |
47 | dev_err(dev, "%s: length %d is too big.\n" , |
48 | __func__, length); |
49 | return -EINVAL; |
50 | } |
51 | |
52 | memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); |
53 | memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE); |
54 | |
55 | wr_buf[0] = 0x00; /* header byte 0 */ |
56 | wr_buf[1] = 0xFF; /* header byte 1 */ |
57 | wr_buf[2] = reg; /* reg index */ |
58 | wr_buf[3] = op; /* r/~w */ |
59 | if (op == CY_SPI_WR_OP) |
60 | memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); |
61 | |
62 | memset(xfer, 0, sizeof(xfer)); |
63 | spi_message_init(m: &msg); |
64 | |
65 | /* |
66 | We set both TX and RX buffers because Cypress TTSP |
67 | requires full duplex operation. |
68 | */ |
69 | xfer[0].tx_buf = wr_buf; |
70 | xfer[0].rx_buf = rd_buf; |
71 | switch (op) { |
72 | case CY_SPI_WR_OP: |
73 | xfer[0].len = length + CY_SPI_CMD_BYTES; |
74 | spi_message_add_tail(t: &xfer[0], m: &msg); |
75 | break; |
76 | |
77 | case CY_SPI_RD_OP: |
78 | xfer[0].len = CY_SPI_CMD_BYTES; |
79 | spi_message_add_tail(t: &xfer[0], m: &msg); |
80 | |
81 | xfer[1].rx_buf = buf; |
82 | xfer[1].len = length; |
83 | spi_message_add_tail(t: &xfer[1], m: &msg); |
84 | break; |
85 | |
86 | default: |
87 | dev_err(dev, "%s: bad operation code=%d\n" , __func__, op); |
88 | return -EINVAL; |
89 | } |
90 | |
91 | retval = spi_sync(spi, message: &msg); |
92 | if (retval < 0) { |
93 | dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n" , |
94 | __func__, retval, xfer[1].len, op); |
95 | |
96 | /* |
97 | * do not return here since was a bad ACK sequence |
98 | * let the following ACK check handle any errors and |
99 | * allow silent retries |
100 | */ |
101 | } |
102 | |
103 | if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 || |
104 | rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) { |
105 | dev_dbg(dev, "%s: operation %d failed\n" , __func__, op); |
106 | |
107 | for (i = 0; i < CY_SPI_CMD_BYTES; i++) |
108 | dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n" , |
109 | __func__, i, rd_buf[i]); |
110 | for (i = 0; i < length; i++) |
111 | dev_dbg(dev, "%s: test buf[%d]:0x%02x\n" , |
112 | __func__, i, buf[i]); |
113 | |
114 | return -EIO; |
115 | } |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf, |
121 | u16 addr, u8 length, void *data) |
122 | { |
123 | return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, reg: addr, buf: data, |
124 | length); |
125 | } |
126 | |
127 | static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf, |
128 | u16 addr, u8 length, const void *data) |
129 | { |
130 | return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, reg: addr, buf: (void *)data, |
131 | length); |
132 | } |
133 | |
134 | static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = { |
135 | .bustype = BUS_SPI, |
136 | .write = cyttsp_spi_write_block_data, |
137 | .read = cyttsp_spi_read_block_data, |
138 | }; |
139 | |
140 | static int cyttsp_spi_probe(struct spi_device *spi) |
141 | { |
142 | struct cyttsp *ts; |
143 | int error; |
144 | |
145 | /* Set up SPI*/ |
146 | spi->bits_per_word = CY_SPI_BITS_PER_WORD; |
147 | spi->mode = SPI_MODE_0; |
148 | error = spi_setup(spi); |
149 | if (error < 0) { |
150 | dev_err(&spi->dev, "%s: SPI setup error %d\n" , |
151 | __func__, error); |
152 | return error; |
153 | } |
154 | |
155 | ts = cyttsp_probe(bus_ops: &cyttsp_spi_bus_ops, dev: &spi->dev, irq: spi->irq, |
156 | CY_SPI_DATA_BUF_SIZE * 2); |
157 | if (IS_ERR(ptr: ts)) |
158 | return PTR_ERR(ptr: ts); |
159 | |
160 | spi_set_drvdata(spi, data: ts); |
161 | |
162 | return 0; |
163 | } |
164 | |
165 | static const struct of_device_id cyttsp_of_spi_match[] = { |
166 | { .compatible = "cypress,cy8ctma340" , }, |
167 | { .compatible = "cypress,cy8ctst341" , }, |
168 | { /* sentinel */ } |
169 | }; |
170 | MODULE_DEVICE_TABLE(of, cyttsp_of_spi_match); |
171 | |
172 | static struct spi_driver cyttsp_spi_driver = { |
173 | .driver = { |
174 | .name = CY_SPI_NAME, |
175 | .pm = pm_sleep_ptr(&cyttsp_pm_ops), |
176 | .of_match_table = cyttsp_of_spi_match, |
177 | }, |
178 | .probe = cyttsp_spi_probe, |
179 | }; |
180 | |
181 | module_spi_driver(cyttsp_spi_driver); |
182 | |
183 | MODULE_LICENSE("GPL" ); |
184 | MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver" ); |
185 | MODULE_AUTHOR("Cypress" ); |
186 | MODULE_ALIAS("spi:cyttsp" ); |
187 | |