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 Txx4xx parts. |
6 | * Supported parts include: |
7 | * TMA4XX |
8 | * TMA1036 |
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 "cyttsp4_core.h" |
18 | |
19 | #include <linux/delay.h> |
20 | #include <linux/input.h> |
21 | #include <linux/spi/spi.h> |
22 | |
23 | #define CY_SPI_WR_OP 0x00 /* r/~w */ |
24 | #define CY_SPI_RD_OP 0x01 |
25 | #define CY_SPI_BITS_PER_WORD 8 |
26 | #define CY_SPI_A8_BIT 0x02 |
27 | #define 2 |
28 | #define 1 |
29 | #define CY_SPI_CMD_BYTES 2 |
30 | #define CY_SPI_SYNC_BYTE 0 |
31 | #define CY_SPI_SYNC_ACK 0x62 /* from TRM *A protocol */ |
32 | #define CY_SPI_DATA_SIZE (2 * 256) |
33 | |
34 | #define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) |
35 | |
36 | static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf, |
37 | u8 op, u16 reg, u8 *buf, int length) |
38 | { |
39 | struct spi_device *spi = to_spi_device(dev); |
40 | struct spi_message msg; |
41 | struct spi_transfer xfer[2]; |
42 | u8 *wr_buf = &xfer_buf[0]; |
43 | u8 rd_buf[CY_SPI_CMD_BYTES]; |
44 | int retval; |
45 | int i; |
46 | |
47 | if (length > CY_SPI_DATA_SIZE) { |
48 | dev_err(dev, "%s: length %d is too big.\n" , |
49 | __func__, length); |
50 | return -EINVAL; |
51 | } |
52 | |
53 | memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); |
54 | memset(rd_buf, 0, CY_SPI_CMD_BYTES); |
55 | |
56 | wr_buf[0] = op + (((reg >> 8) & 0x1) ? CY_SPI_A8_BIT : 0); |
57 | if (op == CY_SPI_WR_OP) { |
58 | wr_buf[1] = reg & 0xFF; |
59 | if (length > 0) |
60 | memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); |
61 | } |
62 | |
63 | memset(xfer, 0, sizeof(xfer)); |
64 | spi_message_init(m: &msg); |
65 | |
66 | /* |
67 | We set both TX and RX buffers because Cypress TTSP |
68 | requires full duplex operation. |
69 | */ |
70 | xfer[0].tx_buf = wr_buf; |
71 | xfer[0].rx_buf = rd_buf; |
72 | switch (op) { |
73 | case CY_SPI_WR_OP: |
74 | xfer[0].len = length + CY_SPI_CMD_BYTES; |
75 | spi_message_add_tail(t: &xfer[0], m: &msg); |
76 | break; |
77 | |
78 | case CY_SPI_RD_OP: |
79 | xfer[0].len = CY_SPI_RD_HEADER_BYTES; |
80 | spi_message_add_tail(t: &xfer[0], m: &msg); |
81 | |
82 | xfer[1].rx_buf = buf; |
83 | xfer[1].len = length; |
84 | spi_message_add_tail(t: &xfer[1], m: &msg); |
85 | break; |
86 | |
87 | default: |
88 | dev_err(dev, "%s: bad operation code=%d\n" , __func__, op); |
89 | return -EINVAL; |
90 | } |
91 | |
92 | retval = spi_sync(spi, message: &msg); |
93 | if (retval < 0) { |
94 | dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n" , |
95 | __func__, retval, xfer[1].len, op); |
96 | |
97 | /* |
98 | * do not return here since was a bad ACK sequence |
99 | * let the following ACK check handle any errors and |
100 | * allow silent retries |
101 | */ |
102 | } |
103 | |
104 | if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK) { |
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 | int rc; |
124 | |
125 | rc = cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, reg: addr, NULL, length: 0); |
126 | if (rc) |
127 | return rc; |
128 | else |
129 | return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, reg: addr, buf: data, |
130 | length); |
131 | } |
132 | |
133 | static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf, |
134 | u16 addr, u8 length, const void *data) |
135 | { |
136 | return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, reg: addr, buf: (void *)data, |
137 | length); |
138 | } |
139 | |
140 | static const struct cyttsp4_bus_ops cyttsp_spi_bus_ops = { |
141 | .bustype = BUS_SPI, |
142 | .write = cyttsp_spi_write_block_data, |
143 | .read = cyttsp_spi_read_block_data, |
144 | }; |
145 | |
146 | static int cyttsp4_spi_probe(struct spi_device *spi) |
147 | { |
148 | struct cyttsp4 *ts; |
149 | int error; |
150 | |
151 | /* Set up SPI*/ |
152 | spi->bits_per_word = CY_SPI_BITS_PER_WORD; |
153 | spi->mode = SPI_MODE_0; |
154 | error = spi_setup(spi); |
155 | if (error < 0) { |
156 | dev_err(&spi->dev, "%s: SPI setup error %d\n" , |
157 | __func__, error); |
158 | return error; |
159 | } |
160 | |
161 | ts = cyttsp4_probe(ops: &cyttsp_spi_bus_ops, dev: &spi->dev, irq: spi->irq, |
162 | CY_SPI_DATA_BUF_SIZE); |
163 | |
164 | return PTR_ERR_OR_ZERO(ptr: ts); |
165 | } |
166 | |
167 | static void cyttsp4_spi_remove(struct spi_device *spi) |
168 | { |
169 | struct cyttsp4 *ts = spi_get_drvdata(spi); |
170 | cyttsp4_remove(ts); |
171 | } |
172 | |
173 | static struct spi_driver cyttsp4_spi_driver = { |
174 | .driver = { |
175 | .name = CYTTSP4_SPI_NAME, |
176 | .pm = pm_ptr(&cyttsp4_pm_ops), |
177 | }, |
178 | .probe = cyttsp4_spi_probe, |
179 | .remove = cyttsp4_spi_remove, |
180 | }; |
181 | |
182 | module_spi_driver(cyttsp4_spi_driver); |
183 | |
184 | MODULE_LICENSE("GPL" ); |
185 | MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver" ); |
186 | MODULE_AUTHOR("Cypress" ); |
187 | MODULE_ALIAS("spi:cyttsp4" ); |
188 | |