1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * Defines the PN80T spidev device and platform wrappers consumed in
17 * the common code.
18 */
19
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <linux/spi/spidev.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/ioctl.h>
28 #include <unistd.h>
29
30 #include "../include/ese/hw/nxp/pn80t/common.h"
31 #include "../include/ese/hw/nxp/spi_board.h"
32
33 struct Handle {
34 int spi_fd;
35 struct NxpSpiBoard *board;
36 };
37
gpio_set(int num,int val)38 int gpio_set(int num, int val) {
39 char val_path[256];
40 char val_chr = (val ? '1' : '0');
41 int fd;
42 if (num < 0) {
43 return 0;
44 }
45 if (snprintf(val_path, sizeof(val_path), "/sys/class/gpio/gpio%d/value",
46 num) >= (int)sizeof(val_path)) {
47 return -1;
48 }
49 printf("Gpio @ %s\n", val_path);
50 fd = open(val_path, O_WRONLY);
51 if (fd < 0) {
52 return -1;
53 }
54 if (write(fd, &val_chr, 1) < 0) {
55 close(fd);
56 return -1;
57 }
58 close(fd);
59 return 0;
60 }
61
platform_toggle_ven(void * blob,int val)62 int platform_toggle_ven(void *blob, int val) {
63 struct Handle *handle = blob;
64 printf("Toggling VEN: %d\n", val);
65 return gpio_set(handle->board->gpios[kBoardGpioNfcVen], val);
66 }
67
platform_toggle_reset(void * blob,int val)68 int platform_toggle_reset(void *blob, int val) {
69 struct Handle *handle = blob;
70 printf("Toggling RST: %d\n", val);
71 return gpio_set(handle->board->gpios[kBoardGpioEseRst], val);
72 }
73
platform_toggle_power_req(void * blob,int val)74 int platform_toggle_power_req(void *blob, int val) {
75 struct Handle *handle = blob;
76 printf("Toggling SVDD_PWR_REQ: %d\n", val);
77 return gpio_set(handle->board->gpios[kBoardGpioEseSvddPwrReq], val);
78 }
79
gpio_configure(int num,int out,int val)80 int gpio_configure(int num, int out, int val) {
81 char dir_path[256];
82 char numstr[8];
83 char dir[5];
84 int fd;
85 /* <0 is unmapped. No work to do! */
86 if (num < 0) {
87 return 0;
88 }
89 if (snprintf(dir, sizeof(dir), "%s", (out ? "out" : "in")) >=
90 (int)sizeof(dir)) {
91 return -1;
92 }
93 if (snprintf(dir_path, sizeof(dir_path), "/sys/class/gpio/gpio%d/direction",
94 num) >= (int)sizeof(dir_path)) {
95 return -1;
96 }
97 if (snprintf(numstr, sizeof(numstr), "%d", num) >= (int)sizeof(numstr)) {
98 return -1;
99 }
100 fd = open("/sys/class/gpio/export", O_WRONLY);
101 if (fd < 0) {
102 return -1;
103 }
104 /* Exporting can only happen once, so instead of stat()ing, just ignore
105 * errors. */
106 (void)write(fd, numstr, strlen(numstr));
107 close(fd);
108
109 fd = open(dir_path, O_WRONLY);
110 if (fd < 0) {
111 return -1;
112 }
113 if (write(fd, dir, strlen(dir)) < 0) {
114 close(fd);
115 return -1;
116 }
117 close(fd);
118 return gpio_set(num, val);
119 }
120
platform_init(void * hwopts)121 void *platform_init(void *hwopts) {
122 struct NxpSpiBoard *board = hwopts;
123 struct Handle *handle;
124 int gpio = 0;
125
126 handle = malloc(sizeof(*handle));
127 if (!handle) {
128 return NULL;
129 }
130 handle->board = board;
131
132 /* Initialize the mapped GPIOs */
133 for (; gpio < kBoardGpioMax; ++gpio) {
134 if (gpio_configure(board->gpios[gpio], 1, 1) < 0) {
135 free(handle);
136 return NULL;
137 }
138 }
139
140 handle->spi_fd = open(board->dev_path, O_RDWR);
141 if (handle->spi_fd < 0) {
142 free(handle);
143 return NULL;
144 }
145 /* If we need anything fancier, we'll need MODE32 in the headers. */
146 if (ioctl(handle->spi_fd, SPI_IOC_WR_MODE, &board->mode) < 0) {
147 close(handle->spi_fd);
148 free(handle);
149 return NULL;
150 }
151 if (ioctl(handle->spi_fd, SPI_IOC_WR_BITS_PER_WORD, &board->bits) < 0) {
152 close(handle->spi_fd);
153 free(handle);
154 return NULL;
155 }
156 if (ioctl(handle->spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &board->speed) < 0) {
157 close(handle->spi_fd);
158 free(handle);
159 return NULL;
160 }
161 printf("Linux SPIDev initialized\n");
162 return (void *)handle;
163 }
164
platform_release(void * blob)165 int platform_release(void *blob) {
166 struct Handle *handle = blob;
167 close(handle->spi_fd);
168 free(handle);
169 /* Note, we don't unconfigure the GPIOs. */
170 return 0;
171 }
172
platform_wait(void * blob,long usec)173 int platform_wait(void *blob __attribute__((unused)), long usec) {
174 return usleep((useconds_t)usec);
175 }
176
spidev_transmit(struct EseInterface * ese,const uint8_t * buf,uint32_t len,int complete)177 uint32_t spidev_transmit(struct EseInterface *ese, const uint8_t *buf,
178 uint32_t len, int complete) {
179 struct NxpState *ns = NXP_PN80T_STATE(ese);
180 struct Handle *handle = ns->handle;
181 struct spi_ioc_transfer tr = {
182 .tx_buf = (unsigned long)buf,
183 .rx_buf = 0,
184 .len = (uint32_t)len,
185 .delay_usecs = 0,
186 .speed_hz = 0,
187 .bits_per_word = 0,
188 .cs_change = !!complete,
189 };
190 ssize_t ret = -1;
191 ALOGV("spidev:%s: called [%d]", __func__, len);
192 if (len > INT_MAX) {
193 ese_set_error(ese, kNxpPn80tErrorTransmitSize);
194 ALOGE("Unexpectedly large transfer attempted: %u", len);
195 return 0;
196 }
197 ret = ioctl(handle->spi_fd, SPI_IOC_MESSAGE(1), &tr);
198 if (ret < 1) {
199 ese_set_error(ese, kNxpPn80tErrorTransmit);
200 ALOGE("%s: failed to write to hw (ret=%zd)", __func__, ret);
201 return 0;
202 }
203 return len;
204 }
205
spidev_receive(struct EseInterface * ese,uint8_t * buf,uint32_t len,int complete)206 uint32_t spidev_receive(struct EseInterface *ese, uint8_t *buf, uint32_t len,
207 int complete) {
208 struct NxpState *ns = NXP_PN80T_STATE(ese);
209 struct Handle *handle = ns->handle;
210 ssize_t ret = -1;
211 struct spi_ioc_transfer tr = {
212 .tx_buf = 0,
213 .rx_buf = (unsigned long)buf,
214 .len = (uint32_t)len,
215 .delay_usecs = 0,
216 .speed_hz = 0,
217 .bits_per_word = 0,
218 .cs_change = !!complete,
219 };
220 ALOGV("spidev:%s: called [%d]", __func__, len);
221 if (len > INT_MAX) {
222 ese_set_error(ese, kNxpPn80tErrorReceiveSize);
223 ALOGE("Unexpectedly large receive attempted: %u", len);
224 return 0;
225 }
226 ret = ioctl(handle->spi_fd, SPI_IOC_MESSAGE(1), &tr);
227 if (ret < 1) {
228 ALOGE("%s: failed to read from hw (ret=%zd)", __func__, ret);
229 ese_set_error(ese, kNxpPn80tErrorReceive);
230 return 0;
231 }
232 ALOGV("%s: read bytes: %zd", __func__, len);
233 return len;
234 }
235
236 static const struct Pn80tPlatform kPn80tLinuxSpidevPlatform = {
237 .initialize = &platform_init,
238 .release = &platform_release,
239 .toggle_reset = &platform_toggle_reset,
240 .toggle_ven = &platform_toggle_ven,
241 .toggle_power_req = &platform_toggle_power_req,
242 .toggle_bootloader = NULL,
243 .wait = &platform_wait,
244 };
245
246 static const struct EseOperations ops = {
247 .name = "NXP PN80T/PN81A (PN553)",
248 .open = &nxp_pn80t_open,
249 .hw_receive = &spidev_receive,
250 .hw_transmit = &spidev_transmit,
251 .hw_reset = &nxp_pn80t_reset,
252 .transceive = &nxp_pn80t_transceive,
253 .poll = &nxp_pn80t_poll,
254 .close = &nxp_pn80t_close,
255 .opts = &kPn80tLinuxSpidevPlatform,
256 .errors = kNxpPn80tErrorMessages,
257 .errors_count = kNxpPn80tErrorMax,
258 };
259 __attribute__((visibility("default")))
260 ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_SPIDEV, ops);
261