blob: 94f58a9b7ff1f0a291c5ef19270e4393879271d0 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30#include <linux/fs.h>
31#include <linux/buffer_head.h>
32#include <linux/reboot.h>
33#include <linux/pci.h>
34#include <asm/segment.h>
35#include <asm/uaccess.h>
36#include <bcmos_system.h>
37#include <bcm_config.h>
38#include <bcmolt_msg.h>
39#include <bcmolt_fld.h>
40#include <bcmolt_llpcie.h>
41#include <bcmolt_conv.h>
42#include <bcmolt_i2c_devs.h>
43#include <bcmolt_board_selector.h>
44#include "bcmolt_user_utils.h"
45
46#define BCM_I2C_DEV_ADDR_START typedef enum {
47#define BCM_I2C_DEV_ADDR(name, desc, val) name = val,
48#define BCM_I2C_DEV_ADDR_END } bcm_i2c_dev_addr;
49#include <bcmolt_i2c_devs_addr.h>
50
51#define BOOT_FILE "/opt/bcm68620/bcm68620_boot.bin"
52#define APPL_FILE "/opt/bcm68620/bcm68620_appl.bin"
53#define ONU_FW_FILE "/opt/bcm68620/dummy_onu_firmware.bin"
54
55#define FPGA_REG_FPGA_VERSION 0x0
56#define FPGA_REG_BOARD_CONF 0x1
57#define FPGA_REG_RESETS 0x2
58
59#define FPGA_RST_BIT_DEVICE 0x0
60#define FPGA_DEVICE_SWITCH 4
61
62#define FPGA_ENABLE_ALL_DEVICES 0x1f
63#define FPGA_ENABLE_SWITCH (1 << FPGA_DEVICE_SWITCH)
64/* For now swap devices for consistency with PCI enumeration */
65#define FPGA_DEVICE_RESET_BIT(dev) (1 << (FPGA_RST_BIT_DEVICE + dev))
66
67/* Minimum required FPGA version for reading default system mode via BOARD_CONF register. */
68#define FPGA_VER_FOR_SVK_CONFIG 6
69
70/* Lock that protects critical sections */
71static bcmos_mutex user_lock;
72static bcmos_bool user_lock_initialized;
73
74typedef enum
75{
76 SVK_BOARD_ID_1 = 0x7, /* SVK #1 - BCM968620S: all PON flavors supported, SFP+ */
77 SVK_BOARD_ID_2_XG = 0x6, /* SVK #2 - BCM968620XG: XGPON */
78 SVK_BOARD_ID_2_XE = 0x5, /* SVK #2 - BCM968620XE: 10G EPON */
79 SVK_BOARD_ID_3 = 0x4, /* SVK #3 - BCM968620K: all PON flavors supported, XFP */
80 DVT_BOARD_ID = 0x3, /* DVT board */
81} svk_board_id;
82
83typedef enum
84{
85 SVK_MODE_GPON_16X = 0xF, /* SVK #1/3: 16x GPON mode, SVK #2: only valid mode */
86 SVK_MODE_EPON_1G_16X = 0xE, /* SVK #1/3: 16x 1G EPON mode */
87 SVK_MODE_XGPON = 0xD, /* SVK #1/3: 8x XGPON mode */
88 SVK_MODE_EPON_10G_8X = 0xC, /* SVK #1/3: 8x 10G EPON mode */
89 SVK_MODE_EPON_GPON_8X = 0xB, /* SVK #1/3: 8x 10G EPON + 8x GPON coexistence mode */
90} svk_mode;
91
92
93#define CHECK_RETURN_ERROR(cond,rc) \
94 do { \
95 if (cond) {\
96 bcmos_printf("%s#%d: failed with rc=%d\n", __FUNCTION__, __LINE__, rc);\
97 return rc;\
98 }\
99 } while (0)
100
101
102static void userlock_lock(void)
103{
104 if (!user_lock_initialized)
105 {
106 bcmos_mutex_create(&user_lock, 0, "reset_lock");
107 user_lock_initialized = BCMOS_TRUE;
108 }
109 bcmos_mutex_lock(&user_lock);
110}
111
112
113static void userlock_unlock(void)
114{
115 bcmos_mutex_unlock(&user_lock);
116}
117
118static struct file *file_open(const char *path, int flags)
119{
120 struct file *fd = NULL;
121 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
122
123 fd = filp_open(path, flags, mode);
124 if(IS_ERR(fd))
125 return NULL;
126 return fd;
127}
128
129static void file_close(struct file *file)
130{
131 filp_close(file, NULL);
132}
133
134static int file_read(struct file *file, uint32_t offset, uint8_t *data, unsigned int size)
135{
136 unsigned long long o = offset;
137 mm_segment_t oldfs;
138 int bytes_read;
139
140 oldfs = get_fs();
141 set_fs(get_ds());
142 bytes_read = vfs_read(file, data, size, &o);
143 set_fs(oldfs);
144 if (bytes_read < 0)
145 {
146 BCMOS_TRACE_ERR("vfs_read returned error %d\n", bytes_read);
147 return (int)BCM_ERR_IO;
148 }
149 return bytes_read;
150}
151
152static int file_write(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size)
153{
154 mm_segment_t oldfs;
155 int ret;
156
157 oldfs = get_fs();
158 set_fs(get_ds());
159
160 ret = vfs_write(file, data, size, &offset);
161
162 set_fs(oldfs);
163 return ret;
164}
165
166static bcmos_errno file_write_1(const char *path)
167{
168 struct file *fd;
169 char to_write[] = { '1' };
170 loff_t pos = 0;
171 ssize_t bytes_written;
172
173 fd = file_open(path, O_WRONLY);
174 if ((fd == NULL) || (fd == (void *)-ENOENT)) /* -ENOENT; No such file or directory */
175 {
176 BCMOS_TRACE_ERR("Can't open file %s for writing. Error %s\n",
177 path, bcmos_strerror(fd == NULL ? BCM_ERR_NULL : BCM_ERR_NOENT));
178 return (fd == NULL ? BCM_ERR_NULL : BCM_ERR_NOENT);
179 }
180
181 bytes_written = file_write(fd, pos, to_write, sizeof(to_write));
182 if (bytes_written != sizeof(to_write))
183 {
184 BCMOS_TRACE_ERR("vfs_write returned incorrect number of characters written: %d\n", bytes_written);
185 }
186
187 file_close(fd);
188 return BCM_ERR_OK;
189}
190
191static bcmos_errno i2c_fpga_read(uint32_t reg, uint32_t *val)
192{
193 return maple_i2c_read_fpga(maple_i2c_get_client(FPGA_I2C_ADDR), reg, val);
194}
195
196static bcmos_errno i2c_fpga_write(uint32_t reg, uint32_t val)
197{
198 return maple_i2c_write_fpga(maple_i2c_get_client(FPGA_I2C_ADDR), reg, val);
199}
200
201static bcmos_errno i2c_config_switch(void)
202{
203 uint8_t to_write = 1;
204 int ret;
205
206 ret = maple_i2c_write(maple_i2c_get_client(I2C_SW0_I2C_ADDR), &to_write, 1);
207 if (ret != 0)
208 {
209 BCMOS_TRACE_ERR("Failed to write switch: %d\n", ret);
210 return BCM_ERR_INTERNAL;
211 }
212
213 return BCM_ERR_OK;
214}
215
216static bcmos_errno i2c_fpga_version_get(uint32_t *version)
217{
218 return i2c_fpga_read(FPGA_REG_FPGA_VERSION, version);
219}
220
221static bcmos_errno i2c_svk_config_get(svk_board_id *board_id, svk_mode *mode)
222{
223 uint32_t val;
224 bcmos_errno rc;
225
226 rc = i2c_fpga_read(FPGA_REG_BOARD_CONF, &val);
227 if (rc == BCM_ERR_OK)
228 {
229 *board_id = (svk_board_id)(val & 0x7);
230 *mode = (svk_mode)((val >> 4) & 0xF);
231 }
232
233 return rc;
234}
235
236static bcmos_errno i2c_device_cpu_state_set(bcmolt_devid device, bcmos_bool is_on)
237{
238 bcmos_errno rc;
239 uint32_t reset_values;
240
241 /* Prevent race condition when this function is called for 2 devices simultaneously */
242 userlock_lock();
243
244 do
245 {
246 rc = i2c_config_switch();
247 if (rc)
248 break;
249
250 /* Read-modify-write so we can set only the relevant bit */
251 rc = i2c_fpga_read(FPGA_REG_RESETS, &reset_values);
252 if (rc)
253 break;
254
255 if (is_on)
256 reset_values |= FPGA_DEVICE_RESET_BIT(device);
257 else
258 reset_values &= ~FPGA_DEVICE_RESET_BIT(device);
259
260 rc = i2c_fpga_write(FPGA_REG_RESETS, reset_values);
261
262 } while (0);
263
264 userlock_unlock();
265
266 CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
267
268 bcmos_usleep(1000);
269
270 return BCM_ERR_OK;
271}
272
273static bcmos_bool bcmuser_system_mode_is_valid(bcmolt_system_mode system_mode, svk_board_id board_id, svk_mode svk_mode)
274{
275 switch (board_id)
276 {
277 case SVK_BOARD_ID_1:
278 case SVK_BOARD_ID_3:
279 switch (svk_mode)
280 {
281 case SVK_MODE_GPON_16X:
282 return (system_mode == BCMOLT_SYSTEM_MODE_GPON__4_X ||
283 system_mode == BCMOLT_SYSTEM_MODE_GPON__8_X ||
284 system_mode == BCMOLT_SYSTEM_MODE_GPON__16_X ||
285 system_mode == BCMOLT_SYSTEM_MODE_GPON_8_XGPON_4_X_COEXISTENCE ||
286 system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__8_X ||
287 system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__4_X ||
288 system_mode == BCMOLT_SYSTEM_MODE_NGPON2__8_X_2_P_5_G);
289 case SVK_MODE_EPON_1G_16X:
290 return (system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X ||
291 system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X ||
292 system_mode == BCMOLT_SYSTEM_MODE_EPON__16_X ||
293 system_mode == BCMOLT_SYSTEM_MODE_EPON__2_X_10_G ||
294 system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_10_G ||
295 system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_10_G);
296 case SVK_MODE_XGPON:
297 return (system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__8_X ||
298 system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__4_X ||
299 system_mode == BCMOLT_SYSTEM_MODE_NGPON2__8_X_2_P_5_G);
300 case SVK_MODE_EPON_10G_8X:
301 return
302 (system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_COEXISTENCE_TDMA) ||
303 (system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_COEXISTENCE_TDMA) ||
304 (system_mode == BCMOLT_SYSTEM_MODE_EPON__2_X_10_G) ||
305 (system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_10_G) ||
306 (system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_10_G);
307 default:
308 return BCMOS_FALSE;
309 }
310 case SVK_BOARD_ID_2_XG:
311 return (system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__8_X ||
312 system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__4_X ||
313 system_mode == BCMOLT_SYSTEM_MODE_NGPON2__8_X_2_P_5_G ||
314 system_mode == BCMOLT_SYSTEM_MODE_XGS__2_X_10_G ||
315 system_mode == BCMOLT_SYSTEM_MODE_NGPON2__2_X_10_G);
316 case SVK_BOARD_ID_2_XE:
317 return
318 (system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_COEXISTENCE_TDMA) ||
319 (system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_COEXISTENCE_TDMA) ||
320 (system_mode == BCMOLT_SYSTEM_MODE_AE_8_X) ||
321 (system_mode == BCMOLT_SYSTEM_MODE_EPON__2_X_10_G) ||
322 (system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_10_G) ||
323 (system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_10_G);
324 case DVT_BOARD_ID:
325 return BCMOS_TRUE;
326 default:
327 return BCMOS_FALSE;
328 }
329}
330
331bcmos_errno bcmuser_system_mode_validate(bcmolt_devid device, bcmolt_system_mode system_mode)
332{
333 bcmos_errno rc;
334 uint32_t version;
335 svk_board_id board_id;
336 svk_mode svk_mode;
337
338 rc = i2c_config_switch();
339 CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
340
341 rc = i2c_fpga_version_get(&version);
342 if (rc != BCM_ERR_OK)
343 {
344 BCMOS_TRACE_INFO("Failed to read FPGA version\n");
345 return rc;
346 }
347
348 if (version < FPGA_VER_FOR_SVK_CONFIG)
349 {
350 /* this FPGA version don't support reading the SVK version, so we must assume it's OK */
351 return BCM_ERR_OK;
352 }
353
354 rc = i2c_svk_config_get(&board_id, &svk_mode);
355 if (rc != BCM_ERR_OK)
356 {
357 BCMOS_TRACE_INFO("Failed to read SVK config\n");
358 return rc;
359 }
360
361 if (bcmuser_system_mode_is_valid(system_mode, board_id, svk_mode))
362 {
363 return BCM_ERR_OK;
364 }
365 else
366 {
367 BCMOS_TRACE_INFO(
368 "System mode %s is not supported for board ID %d, SVK mode %d\n",
369 bcmolt_system_mode_name(system_mode),
370 board_id,
371 svk_mode);
372 return BCM_ERR_NOT_SUPPORTED;
373 }
374}
375
376int bcmuser_image_read(bcmolt_devid device, bcmolt_device_image_type image_type,
377 uint32_t offset, uint8_t *buf, uint32_t buf_size)
378{
379 struct file *fd;
380 int bytes_read;
381 bcmos_errno rc;
382 static int2str_t image_type2str[] =
383 {
384 {BCMOLT_DEVICE_IMAGE_TYPE_BOOTLOADER, BOOT_FILE},
385 {BCMOLT_DEVICE_IMAGE_TYPE_APPLICATION, APPL_FILE},
386 {BCMOLT_DEVICE_IMAGE_TYPE_ITU_PON_ONU_FIRMWARE, ONU_FW_FILE},
387 {-1}
388 };
389 const char *filename;
390
391 filename = int2str(image_type2str, image_type);
392 fd = file_open(filename, O_RDONLY);
393 if ((fd == NULL) || (fd == (void *)-ENOENT)) /* -ENOENT; No such file or directory */
394 {
395 rc = (fd == NULL ? BCM_ERR_NULL : BCM_ERR_NOENT);
396 BCMOS_TRACE_ERR("filp_open returned error %s (%d)\n", bcmos_strerror(rc), rc);
397 return rc;
398 }
399 bytes_read = file_read(fd, offset, buf, buf_size);
400 file_close(fd);
401 if (bytes_read < 0)
402 {
403 return (int)BCM_ERR_IO;
404 }
405 return bytes_read;
406}
407
408bcmos_errno bcmuser_device_off(bcmolt_devid device)
409{
410 /* Ignore request for non-existing device */
411 if (device >= bcmolt_board_num_maple_devices())
412 return BCM_ERR_OK;
413 BCMOS_TRACE_INFO("device=%u\n", device);
414 return i2c_device_cpu_state_set(device, BCMOS_FALSE);
415}
416
417bcmos_errno bcmuser_device_on(bcmolt_devid device)
418{
419 bcmos_errno rc;
420
421 BCMOS_TRACE_INFO("device=%u\n", device);
422 if (device >= bcmolt_board_num_maple_devices())
423 return BCM_ERR_RANGE;
424
425 rc = i2c_device_cpu_state_set(device, BCMOS_TRUE);
426 /* Wait a moment to make sure the device is discoverable in case it just booted. */
427 bcmos_usleep(50 * 1000);
428
429 return rc;
430}
431
432bcmos_errno bcmuser_device_is_running(bcmolt_devid device, bcmos_bool *is_running)
433{
434 bcmos_errno rc;
435 uint32_t reset_values;
436 uint32_t version;
437
438 rc = i2c_config_switch();
439 CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
440
441 rc = i2c_fpga_version_get(&version);
442 if (rc != BCM_ERR_OK)
443 {
444 BCMOS_TRACE_INFO("Failed to read FPGA version\n");
445 return rc;
446 }
447 if (!bcmolt_board_supports_standalone(version))
448 {
449 /* For older FPGAs that don't support standalone mode, the device is taken out of reset at boot. We need to lie
450 and tell device control that it's not running, so it will be programmed from scratch. */
451 BCMOS_TRACE_INFO("FPGA version %d doesn't support standalone mode\n", version);
452 *is_running = BCMOS_FALSE;
453 return BCM_ERR_OK;
454 }
455
456 rc = i2c_fpga_read(FPGA_REG_RESETS, &reset_values);
457 CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
458
459 *is_running = ((reset_values & FPGA_DEVICE_RESET_BIT(device)) != 0);
460
461 return BCM_ERR_OK;
462}
463
464bcmos_errno bcmuser_host_reset(void)
465{
466 bcm_ll_pcie_cleanup();
467 kernel_restart(NULL);
468 return BCM_ERR_OK; /* will never be reached */
469}
470
471bcmos_errno bcmuser_pcie_prepare(void)
472{
473 bcmos_errno rc;
474 uint32_t reset_values;
475 const char *pcie_rescan_file = bcmolt_board_pci_rescan_string_get();
476 int i;
477
478 /* - Take all devices out of reset
479 * - rescan bus
480 * - restore reset value
481 */
482
483 rc = i2c_config_switch();
484 CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
485
486 /* Read-modify-write so we can set only the relevant bit */
487 rc = i2c_fpga_read(FPGA_REG_RESETS, &reset_values);
488 CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
489
490 rc = i2c_fpga_write(FPGA_REG_RESETS, FPGA_ENABLE_ALL_DEVICES);
491 CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
492
493 bcmos_usleep(50000);
494
495 /* Now recan PCI bus */
496 /* Now re-enumerate the PCI bus by writing a 1 to the 'rescan' file. */
497 rc = file_write_1(pcie_rescan_file);
498 if (rc != BCM_ERR_OK)
499 BCMOS_TRACE_ERR("file write returned error %s (%d)\n", bcmos_strerror(rc), rc);
500 bcmos_usleep(100 * 1000);
501
502 /* Now initialize ll_pcie driver and map maple devices */
503 rc = bcm_ll_pcie_init();
504 CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
505
506 /* Now restore rest values. Keep switch out of reset */
507 for (i = 0; i < bcmolt_board_num_maple_devices(); i++)
508 {
509 if ((FPGA_DEVICE_RESET_BIT(i) & reset_values) == 0)
510 bcmuser_pcie_channel_remove(i);
511 }
512 rc = i2c_fpga_write(FPGA_REG_RESETS, reset_values | FPGA_ENABLE_SWITCH);
513 CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
514
515 return 0;
516}
517
518bcmos_errno bcmuser_pcie_channel_prepare(bcmolt_devid device)
519{
520 bcmos_errno rc;
521
522 rc = bcm_ll_pcie_dev_enable(device);
523 if (rc)
524 BCMOS_TRACE_ERR("Couldn't prepare device %d. Error %s\n", device, bcmos_strerror(rc));
525
526 return rc;
527}
528
529bcmos_errno bcmuser_pcie_channel_remove(bcmolt_devid device)
530{
531 bcm_ll_pcie_dev_disable(device);
532 return BCM_ERR_OK;
533}