net/enetfec: support UIO
[dpdk.git] / drivers / net / enetfec / enet_uio.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2021 NXP
3  */
4
5 #include <stdbool.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <dirent.h>
11 #include <string.h>
12 #include <sys/mman.h>
13 #include <errno.h>
14 #include <fcntl.h>
15
16 #include <rte_common.h>
17 #include <rte_malloc.h>
18 #include "enet_pmd_logs.h"
19 #include "enet_uio.h"
20
21 static struct uio_job enetfec_uio_job;
22 static int enetfec_count;
23
24 /** @brief Checks if a file name contains a certain substring.
25  * This function assumes a filename format of: [text][number].
26  * @param [in]  filename    File name
27  * @param [in]  match       String to match in file name
28  *
29  * @retval true if file name matches the criteria
30  * @retval false if file name does not match the criteria
31  */
32 static bool
33 file_name_match_extract(const char filename[], const char match[])
34 {
35         char *substr = NULL;
36
37         substr = strstr(filename, match);
38         if (substr == NULL)
39                 return false;
40
41         return true;
42 }
43
44 /*
45  * @brief Reads first line from a file.
46  * Composes file name as: root/subdir/filename
47  *
48  * @param [in]  root     Root path
49  * @param [in]  subdir   Subdirectory name
50  * @param [in]  filename File name
51  * @param [out] line     The first line read from file.
52  *
53  * @retval 0 for success
54  * @retval other value for error
55  */
56 static int
57 file_read_first_line(const char root[], const char subdir[],
58                         const char filename[], char *line)
59 {
60         char absolute_file_name[FEC_UIO_MAX_ATTR_FILE_NAME];
61         int fd = 0, ret = 0;
62
63         /*compose the file name: root/subdir/filename */
64         memset(absolute_file_name, 0, sizeof(absolute_file_name));
65         snprintf(absolute_file_name, FEC_UIO_MAX_ATTR_FILE_NAME,
66                 "%s/%s/%s", root, subdir, filename);
67
68         fd = open(absolute_file_name, O_RDONLY);
69         if (fd <= 0)
70                 ENETFEC_PMD_ERR("Error opening file %s", absolute_file_name);
71
72         /* read UIO device name from first line in file */
73         ret = read(fd, line, FEC_UIO_MAX_DEVICE_FILE_NAME_LENGTH);
74         if (ret <= 0) {
75                 ENETFEC_PMD_ERR("Error reading file %s", absolute_file_name);
76                 return ret;
77         }
78         close(fd);
79
80         /* NULL-ify string */
81         line[ret] = '\0';
82
83         return 0;
84 }
85
86 /*
87  * @brief Maps rx-tx bd range assigned for a bd ring.
88  *
89  * @param [in] uio_device_fd    UIO device file descriptor
90  * @param [in] uio_device_id    UIO device id
91  * @param [in] uio_map_id       UIO allows maximum 5 different mapping for
92                                 each device. Maps start with id 0.
93  * @param [out] map_size        Map size.
94  * @param [out] map_addr        Map physical address
95  *
96  * @retval  NULL if failed to map registers
97  * @retval  Virtual address for mapped register address range
98  */
99 static void *
100 uio_map_mem(int uio_device_fd, int uio_device_id,
101                 int uio_map_id, int *map_size, uint64_t *map_addr)
102 {
103         void *mapped_address = NULL;
104         unsigned int uio_map_size = 0;
105         unsigned int uio_map_p_addr = 0;
106         char uio_sys_root[FEC_UIO_MAX_ATTR_FILE_NAME];
107         char uio_sys_map_subdir[FEC_UIO_MAX_ATTR_FILE_NAME];
108         char uio_map_size_str[FEC_UIO_MAX_DEVICE_FILE_NAME_LENGTH + 1];
109         char uio_map_p_addr_str[32];
110         int ret = 0;
111
112         /* compose the file name: root/subdir/filename */
113         memset(uio_sys_root, 0, sizeof(uio_sys_root));
114         memset(uio_sys_map_subdir, 0, sizeof(uio_sys_map_subdir));
115         memset(uio_map_size_str, 0, sizeof(uio_map_size_str));
116         memset(uio_map_p_addr_str, 0, sizeof(uio_map_p_addr_str));
117
118         /* Compose string: /sys/class/uio/uioX */
119         snprintf(uio_sys_root, sizeof(uio_sys_root), "%s/%s%d",
120                         FEC_UIO_DEVICE_SYS_ATTR_PATH, "uio", uio_device_id);
121         /* Compose string: maps/mapY */
122         snprintf(uio_sys_map_subdir, sizeof(uio_sys_map_subdir), "%s%d",
123                         FEC_UIO_DEVICE_SYS_MAP_ATTR, uio_map_id);
124
125         /* Read first (and only) line from file
126          * /sys/class/uio/uioX/maps/mapY/size
127          */
128         ret = file_read_first_line(uio_sys_root, uio_sys_map_subdir,
129                                 "size", uio_map_size_str);
130         if (ret < 0) {
131                 ENETFEC_PMD_ERR("file_read_first_line() failed");
132                 return NULL;
133         }
134         ret = file_read_first_line(uio_sys_root, uio_sys_map_subdir,
135                                 "addr", uio_map_p_addr_str);
136         if (ret < 0) {
137                 ENETFEC_PMD_ERR("file_read_first_line() failed");
138                 return NULL;
139         }
140         /* Read mapping size and physical address expressed in hexa(base 16) */
141         uio_map_size = strtol(uio_map_size_str, NULL, 16);
142         uio_map_p_addr = strtol(uio_map_p_addr_str, NULL, 16);
143
144         if (uio_map_id == 0) {
145                 /* Map the register address in user space when map_id is 0 */
146                 mapped_address = mmap(0 /*dynamically choose virtual address */,
147                                 uio_map_size, PROT_READ | PROT_WRITE,
148                                 MAP_SHARED, uio_device_fd, 0);
149         } else {
150                 /* Map the BD memory in user space */
151                 mapped_address = mmap(NULL, uio_map_size,
152                                 PROT_READ | PROT_WRITE,
153                                 MAP_SHARED, uio_device_fd, (1 * MAP_PAGE_SIZE));
154         }
155
156         if (mapped_address == MAP_FAILED) {
157                 ENETFEC_PMD_ERR("Failed to map! errno = %d uio job fd = %d,"
158                         "uio device id = %d, uio map id = %d", errno,
159                         uio_device_fd, uio_device_id, uio_map_id);
160                 return NULL;
161         }
162
163         /* Save the map size to use it later on for munmap-ing */
164         *map_size = uio_map_size;
165         *map_addr = uio_map_p_addr;
166         ENETFEC_PMD_INFO("UIO dev[%d] mapped region [id =%d] size 0x%x at %p",
167                 uio_device_id, uio_map_id, uio_map_size, mapped_address);
168
169         return mapped_address;
170 }
171
172 int
173 config_enetfec_uio(struct enetfec_private *fep)
174 {
175         char uio_device_file_name[32];
176         struct uio_job *uio_job = NULL;
177
178         /* Mapping is done only one time */
179         if (enetfec_count > 0) {
180                 ENETFEC_PMD_INFO("Mapped!\n");
181                 return 0;
182         }
183
184         uio_job = &enetfec_uio_job;
185
186         /* Find UIO device created by ENETFEC-UIO kernel driver */
187         memset(uio_device_file_name, 0, sizeof(uio_device_file_name));
188         snprintf(uio_device_file_name, sizeof(uio_device_file_name), "%s%d",
189                         FEC_UIO_DEVICE_FILE_NAME, uio_job->uio_minor_number);
190
191         /* Open device file */
192         uio_job->uio_fd = open(uio_device_file_name, O_RDWR);
193         if (uio_job->uio_fd < 0) {
194                 ENETFEC_PMD_WARN("Unable to open ENETFEC_UIO file\n");
195                 return -1;
196         }
197
198         ENETFEC_PMD_INFO("US_UIO: Open device(%s) file with uio_fd = %d",
199                         uio_device_file_name, uio_job->uio_fd);
200
201         fep->hw_baseaddr_v = uio_map_mem(uio_job->uio_fd,
202                 uio_job->uio_minor_number, FEC_UIO_REG_MAP_ID,
203                 &uio_job->map_size, &uio_job->map_addr);
204         if (fep->hw_baseaddr_v == NULL)
205                 return -ENOMEM;
206         fep->hw_baseaddr_p = uio_job->map_addr;
207         fep->reg_size = uio_job->map_size;
208
209         fep->bd_addr_v = uio_map_mem(uio_job->uio_fd,
210                 uio_job->uio_minor_number, FEC_UIO_BD_MAP_ID,
211                 &uio_job->map_size, &uio_job->map_addr);
212         if (fep->hw_baseaddr_v == NULL)
213                 return -ENOMEM;
214         fep->bd_addr_p = (uint32_t)uio_job->map_addr;
215         fep->bd_size = uio_job->map_size;
216
217         enetfec_count++;
218
219         return 0;
220 }
221
222 int
223 enetfec_configure(void)
224 {
225         char uio_name[32];
226         int uio_minor_number = -1;
227         int ret;
228         DIR *d = NULL;
229         struct dirent *dir;
230
231         d = opendir(FEC_UIO_DEVICE_SYS_ATTR_PATH);
232         if (d == NULL) {
233                 ENETFEC_PMD_ERR("\nError opening directory '%s': %s\n",
234                         FEC_UIO_DEVICE_SYS_ATTR_PATH, strerror(errno));
235                 return -1;
236         }
237
238         /* Iterate through all subdirs */
239         while ((dir = readdir(d)) != NULL) {
240                 if (!strncmp(dir->d_name, ".", 1) ||
241                                 !strncmp(dir->d_name, "..", 2))
242                         continue;
243
244                 if (file_name_match_extract(dir->d_name, "uio")) {
245                         /*
246                          * As substring <uio> was found in <d_name>
247                          * read number following <uio> substring in <d_name>
248                          */
249                         ret = sscanf(dir->d_name + strlen("uio"), "%d",
250                                                         &uio_minor_number);
251                         if (ret < 0)
252                                 ENETFEC_PMD_ERR("Error: not find minor number\n");
253                         /*
254                          * Open file uioX/name and read first line which
255                          * contains the name for the device. Based on the
256                          * name check if this UIO device is for enetfec.
257                          */
258                         memset(uio_name, 0, sizeof(uio_name));
259                         ret = file_read_first_line(FEC_UIO_DEVICE_SYS_ATTR_PATH,
260                                         dir->d_name, "name", uio_name);
261                         if (ret != 0) {
262                                 ENETFEC_PMD_INFO("file_read_first_line failed\n");
263                                 closedir(d);
264                                 return -1;
265                         }
266
267                         if (file_name_match_extract(uio_name,
268                                                 FEC_UIO_DEVICE_NAME)) {
269                                 enetfec_uio_job.uio_minor_number =
270                                                         uio_minor_number;
271                                 ENETFEC_PMD_INFO("enetfec device uio name: %s",
272                                                  uio_name);
273                         }
274                 }
275         }
276         closedir(d);
277         return 0;
278 }
279
280 void
281 enetfec_cleanup(struct enetfec_private *fep)
282 {
283         munmap(fep->hw_baseaddr_v, fep->cbus_size);
284 }