| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * (C) Copyright 2002 | |
| * Rich Ireland, Enterasys Networks, rireland@enterasys.com. | |
| */ | |
| /* Generic FPGA support */ | |
| #include <init.h> | |
| #include <log.h> | |
| #include <xilinx.h> /* xilinx specific definitions */ | |
| #include <altera.h> /* altera specific definitions */ | |
| #include <lattice.h> | |
| #include <dm/device_compat.h> | |
| /* Local static data */ | |
| static int next_desc = FPGA_INVALID_DEVICE; | |
| static fpga_desc desc_table[CONFIG_MAX_FPGA_DEVICES]; | |
| /* | |
| * fpga_no_sup | |
| * 'no support' message function | |
| */ | |
| static void fpga_no_sup(char *fn, char *msg) | |
| { | |
| if (fn && msg) | |
| printf("%s: No support for %s.\n", fn, msg); | |
| else if (msg) | |
| printf("No support for %s.\n", msg); | |
| else | |
| printf("No FPGA support!\n"); | |
| } | |
| /* fpga_get_desc | |
| * map a device number to a descriptor | |
| */ | |
| const fpga_desc *const fpga_get_desc(int devnum) | |
| { | |
| fpga_desc *desc = (fpga_desc *)NULL; | |
| if ((devnum >= 0) && (devnum < next_desc)) { | |
| desc = &desc_table[devnum]; | |
| debug("%s: found fpga descriptor #%d @ 0x%p\n", | |
| __func__, devnum, desc); | |
| } | |
| return desc; | |
| } | |
| /* | |
| * fpga_validate | |
| * generic parameter checking code | |
| */ | |
| const fpga_desc *const fpga_validate(int devnum, const void *buf, | |
| size_t bsize, char *fn) | |
| { | |
| const fpga_desc *desc = fpga_get_desc(devnum); | |
| if (!desc) | |
| printf("%s: Invalid device number %d\n", fn, devnum); | |
| if (!buf) { | |
| printf("%s: Null buffer.\n", fn); | |
| return (fpga_desc * const)NULL; | |
| } | |
| return desc; | |
| } | |
| /* | |
| * fpga_dev_info | |
| * generic multiplexing code | |
| */ | |
| static int fpga_dev_info(int devnum) | |
| { | |
| int ret_val = FPGA_FAIL; /* assume failure */ | |
| const fpga_desc * const desc = fpga_get_desc(devnum); | |
| if (desc) { | |
| debug("%s: Device Descriptor @ 0x%p\n", | |
| __func__, desc->devdesc); | |
| switch (desc->devtype) { | |
| case fpga_xilinx: | |
| #if defined(CONFIG_FPGA_XILINX) | |
| printf("Xilinx Device\nDescriptor @ 0x%p\n", desc); | |
| ret_val = xilinx_info(desc->devdesc); | |
| #else | |
| fpga_no_sup((char *)__func__, "Xilinx devices"); | |
| #endif | |
| break; | |
| case fpga_altera: | |
| #if defined(CONFIG_FPGA_ALTERA) | |
| printf("Altera Device\nDescriptor @ 0x%p\n", desc); | |
| ret_val = altera_info(desc->devdesc); | |
| #else | |
| fpga_no_sup((char *)__func__, "Altera devices"); | |
| #endif | |
| break; | |
| case fpga_lattice: | |
| #if defined(CONFIG_FPGA_LATTICE) | |
| printf("Lattice Device\nDescriptor @ 0x%p\n", desc); | |
| ret_val = lattice_info(desc->devdesc); | |
| #else | |
| fpga_no_sup((char *)__func__, "Lattice devices"); | |
| #endif | |
| break; | |
| default: | |
| printf("%s: Invalid or unsupported device type %d\n", | |
| __func__, desc->devtype); | |
| } | |
| } else { | |
| printf("%s: Invalid device number %d\n", __func__, devnum); | |
| } | |
| return ret_val; | |
| } | |
| /* | |
| * fpga_init is usually called from misc_init_r() and MUST be called | |
| * before any of the other fpga functions are used. | |
| */ | |
| void fpga_init(void) | |
| { | |
| next_desc = 0; | |
| memset(desc_table, 0, sizeof(desc_table)); | |
| debug("%s\n", __func__); | |
| } | |
| /* | |
| * fpga_count | |
| * Basic interface function to get the current number of devices available. | |
| */ | |
| int fpga_count(void) | |
| { | |
| return next_desc; | |
| } | |
| /* | |
| * fpga_add | |
| * Add the device descriptor to the device table. | |
| */ | |
| int fpga_add(fpga_type devtype, void *desc) | |
| { | |
| int devnum = FPGA_INVALID_DEVICE; | |
| if (!desc) { | |
| printf("%s: NULL device descriptor\n", __func__); | |
| return devnum; | |
| } | |
| if (next_desc < 0) { | |
| printf("%s: FPGA support not initialized!\n", __func__); | |
| } else if ((devtype > fpga_min_type) && (devtype < fpga_undefined)) { | |
| if (next_desc < CONFIG_MAX_FPGA_DEVICES) { | |
| devnum = next_desc; | |
| desc_table[next_desc].devtype = devtype; | |
| desc_table[next_desc++].devdesc = desc; | |
| } else { | |
| printf("%s: Exceeded Max FPGA device count\n", | |
| __func__); | |
| } | |
| } else { | |
| printf("%s: Unsupported FPGA type %d\n", __func__, devtype); | |
| } | |
| return devnum; | |
| } | |
| /* | |
| * Return 1 if the fpga data is partial. | |
| * This is only required for fpga drivers that support bitstream_type. | |
| */ | |
| int __weak fpga_is_partial_data(int devnum, size_t img_len) | |
| { | |
| return 0; | |
| } | |
| /* | |
| * Convert bitstream data and load into the fpga | |
| */ | |
| int __weak fpga_loadbitstream(int devnum, char *fpgadata, size_t size, | |
| bitstream_type bstype) | |
| { | |
| printf("Bitstream support not implemented for this FPGA device\n"); | |
| return FPGA_FAIL; | |
| } | |
| #if defined(CONFIG_CMD_FPGA_LOADFS) | |
| int fpga_fsload(int devnum, const void *buf, size_t size, | |
| fpga_fs_info *fpga_fsinfo) | |
| { | |
| int ret_val = FPGA_FAIL; /* assume failure */ | |
| const fpga_desc *desc = fpga_validate(devnum, buf, size, | |
| (char *)__func__); | |
| if (desc) { | |
| switch (desc->devtype) { | |
| case fpga_xilinx: | |
| #if defined(CONFIG_FPGA_XILINX) | |
| ret_val = xilinx_loadfs(desc->devdesc, buf, size, | |
| fpga_fsinfo); | |
| #else | |
| fpga_no_sup((char *)__func__, "Xilinx devices"); | |
| #endif | |
| break; | |
| default: | |
| printf("%s: Invalid or unsupported device type %d\n", | |
| __func__, desc->devtype); | |
| } | |
| } | |
| return ret_val; | |
| } | |
| #endif | |
| #if CONFIG_IS_ENABLED(FPGA_LOAD_SECURE) | |
| int fpga_loads(int devnum, const void *buf, size_t size, | |
| struct fpga_secure_info *fpga_sec_info) | |
| { | |
| int ret_val = FPGA_FAIL; | |
| const fpga_desc *desc = fpga_validate(devnum, buf, size, | |
| (char *)__func__); | |
| if (desc) { | |
| switch (desc->devtype) { | |
| case fpga_xilinx: | |
| #if defined(CONFIG_FPGA_XILINX) | |
| ret_val = xilinx_loads(desc->devdesc, buf, size, | |
| fpga_sec_info); | |
| #else | |
| fpga_no_sup((char *)__func__, "Xilinx devices"); | |
| #endif | |
| break; | |
| default: | |
| printf("%s: Invalid or unsupported device type %d\n", | |
| __func__, desc->devtype); | |
| } | |
| } | |
| return ret_val; | |
| } | |
| #endif | |
| static int fpga_load_event_notify(const void *buf, size_t bsize, int result) | |
| { | |
| if (CONFIG_IS_ENABLED(EVENT)) { | |
| struct event_fpga_load load = { | |
| .buf = buf, | |
| .bsize = bsize, | |
| .result = result | |
| }; | |
| return event_notify(EVT_FPGA_LOAD, &load, sizeof(load)); | |
| } | |
| return 0; | |
| } | |
| /* | |
| * Generic multiplexing code | |
| */ | |
| int fpga_load(int devnum, const void *buf, size_t bsize, bitstream_type bstype, | |
| int flags) | |
| { | |
| int ret_val = FPGA_FAIL; /* assume failure */ | |
| int ret_notify; | |
| const fpga_desc *desc = fpga_validate(devnum, buf, bsize, | |
| (char *)__func__); | |
| if (desc) { | |
| switch (desc->devtype) { | |
| case fpga_xilinx: | |
| #if defined(CONFIG_FPGA_XILINX) | |
| ret_val = xilinx_load(desc->devdesc, buf, bsize, | |
| bstype, flags); | |
| #else | |
| fpga_no_sup((char *)__func__, "Xilinx devices"); | |
| #endif | |
| break; | |
| case fpga_altera: | |
| #if defined(CONFIG_FPGA_ALTERA) | |
| ret_val = altera_load(desc->devdesc, buf, bsize); | |
| #else | |
| fpga_no_sup((char *)__func__, "Altera devices"); | |
| #endif | |
| break; | |
| case fpga_lattice: | |
| #if defined(CONFIG_FPGA_LATTICE) | |
| ret_val = lattice_load(desc->devdesc, buf, bsize); | |
| #else | |
| fpga_no_sup((char *)__func__, "Lattice devices"); | |
| #endif | |
| break; | |
| default: | |
| printf("%s: Invalid or unsupported device type %d\n", | |
| __func__, desc->devtype); | |
| } | |
| } | |
| ret_notify = fpga_load_event_notify(buf, bsize, ret_val); | |
| if (ret_notify) | |
| return ret_notify; | |
| return ret_val; | |
| } | |
| /* | |
| * fpga_dump | |
| * generic multiplexing code | |
| */ | |
| int fpga_dump(int devnum, const void *buf, size_t bsize) | |
| { | |
| int ret_val = FPGA_FAIL; /* assume failure */ | |
| const fpga_desc *desc = fpga_validate(devnum, buf, bsize, | |
| (char *)__func__); | |
| if (desc) { | |
| switch (desc->devtype) { | |
| case fpga_xilinx: | |
| #if defined(CONFIG_FPGA_XILINX) | |
| ret_val = xilinx_dump(desc->devdesc, buf, bsize); | |
| #else | |
| fpga_no_sup((char *)__func__, "Xilinx devices"); | |
| #endif | |
| break; | |
| case fpga_altera: | |
| #if defined(CONFIG_FPGA_ALTERA) | |
| ret_val = altera_dump(desc->devdesc, buf, bsize); | |
| #else | |
| fpga_no_sup((char *)__func__, "Altera devices"); | |
| #endif | |
| break; | |
| case fpga_lattice: | |
| #if defined(CONFIG_FPGA_LATTICE) | |
| ret_val = lattice_dump(desc->devdesc, buf, bsize); | |
| #else | |
| fpga_no_sup((char *)__func__, "Lattice devices"); | |
| #endif | |
| break; | |
| default: | |
| printf("%s: Invalid or unsupported device type %d\n", | |
| __func__, desc->devtype); | |
| } | |
| } | |
| return ret_val; | |
| } | |
| /* | |
| * fpga_info | |
| * front end to fpga_dev_info. If devnum is invalid, report on all | |
| * available devices. | |
| */ | |
| int fpga_info(int devnum) | |
| { | |
| if (devnum == FPGA_INVALID_DEVICE) { | |
| if (next_desc > 0) { | |
| int dev; | |
| for (dev = 0; dev < next_desc; dev++) | |
| fpga_dev_info(dev); | |
| return FPGA_SUCCESS; | |
| } else { | |
| printf("%s: No FPGA devices available.\n", __func__); | |
| return FPGA_FAIL; | |
| } | |
| } | |
| return fpga_dev_info(devnum); | |
| } | |
| #if CONFIG_IS_ENABLED(FPGA_LOAD_SECURE) | |
| int fpga_compatible2flag(int devnum, const char *compatible) | |
| { | |
| const fpga_desc * const desc = fpga_get_desc(devnum); | |
| if (!desc) | |
| return 0; | |
| switch (desc->devtype) { | |
| #if defined(CONFIG_FPGA_XILINX) | |
| case fpga_xilinx: | |
| { | |
| xilinx_desc *xdesc = (xilinx_desc *)desc->devdesc; | |
| if (xdesc->operations && xdesc->operations->str2flag) | |
| return xdesc->operations->str2flag(xdesc, compatible); | |
| } | |
| #endif | |
| default: | |
| break; | |
| } | |
| return 0; | |
| } | |
| #endif |