blob: 94f58a9b7ff1f0a291c5ef19270e4393879271d0 [file] [log] [blame]
/*
<:copyright-BRCM:2016:DUAL/GPL:standard
Broadcom Proprietary and Confidential.(c) 2016 Broadcom
All Rights Reserved
Unless you and Broadcom execute a separate written software license
agreement governing use of this software, this software is licensed
to you under the terms of the GNU General Public License version 2
(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
with the following added to such license:
As a special exception, the copyright holders of this software give
you permission to link this software with independent modules, and
to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent
module, the terms and conditions of the license of that module.
An independent module is a module which is not derived from this
software. The special exception does not apply to any modifications
of the software.
Not withstanding the above, under no circumstances may you combine
this software in any way with any other Broadcom software provided
under a license other than the GPL, without Broadcom's express prior
written consent.
:>
*/
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include <linux/reboot.h>
#include <linux/pci.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <bcmos_system.h>
#include <bcm_config.h>
#include <bcmolt_msg.h>
#include <bcmolt_fld.h>
#include <bcmolt_llpcie.h>
#include <bcmolt_conv.h>
#include <bcmolt_i2c_devs.h>
#include <bcmolt_board_selector.h>
#include "bcmolt_user_utils.h"
#define BCM_I2C_DEV_ADDR_START typedef enum {
#define BCM_I2C_DEV_ADDR(name, desc, val) name = val,
#define BCM_I2C_DEV_ADDR_END } bcm_i2c_dev_addr;
#include <bcmolt_i2c_devs_addr.h>
#define BOOT_FILE "/opt/bcm68620/bcm68620_boot.bin"
#define APPL_FILE "/opt/bcm68620/bcm68620_appl.bin"
#define ONU_FW_FILE "/opt/bcm68620/dummy_onu_firmware.bin"
#define FPGA_REG_FPGA_VERSION 0x0
#define FPGA_REG_BOARD_CONF 0x1
#define FPGA_REG_RESETS 0x2
#define FPGA_RST_BIT_DEVICE 0x0
#define FPGA_DEVICE_SWITCH 4
#define FPGA_ENABLE_ALL_DEVICES 0x1f
#define FPGA_ENABLE_SWITCH (1 << FPGA_DEVICE_SWITCH)
/* For now swap devices for consistency with PCI enumeration */
#define FPGA_DEVICE_RESET_BIT(dev) (1 << (FPGA_RST_BIT_DEVICE + dev))
/* Minimum required FPGA version for reading default system mode via BOARD_CONF register. */
#define FPGA_VER_FOR_SVK_CONFIG 6
/* Lock that protects critical sections */
static bcmos_mutex user_lock;
static bcmos_bool user_lock_initialized;
typedef enum
{
SVK_BOARD_ID_1 = 0x7, /* SVK #1 - BCM968620S: all PON flavors supported, SFP+ */
SVK_BOARD_ID_2_XG = 0x6, /* SVK #2 - BCM968620XG: XGPON */
SVK_BOARD_ID_2_XE = 0x5, /* SVK #2 - BCM968620XE: 10G EPON */
SVK_BOARD_ID_3 = 0x4, /* SVK #3 - BCM968620K: all PON flavors supported, XFP */
DVT_BOARD_ID = 0x3, /* DVT board */
} svk_board_id;
typedef enum
{
SVK_MODE_GPON_16X = 0xF, /* SVK #1/3: 16x GPON mode, SVK #2: only valid mode */
SVK_MODE_EPON_1G_16X = 0xE, /* SVK #1/3: 16x 1G EPON mode */
SVK_MODE_XGPON = 0xD, /* SVK #1/3: 8x XGPON mode */
SVK_MODE_EPON_10G_8X = 0xC, /* SVK #1/3: 8x 10G EPON mode */
SVK_MODE_EPON_GPON_8X = 0xB, /* SVK #1/3: 8x 10G EPON + 8x GPON coexistence mode */
} svk_mode;
#define CHECK_RETURN_ERROR(cond,rc) \
do { \
if (cond) {\
bcmos_printf("%s#%d: failed with rc=%d\n", __FUNCTION__, __LINE__, rc);\
return rc;\
}\
} while (0)
static void userlock_lock(void)
{
if (!user_lock_initialized)
{
bcmos_mutex_create(&user_lock, 0, "reset_lock");
user_lock_initialized = BCMOS_TRUE;
}
bcmos_mutex_lock(&user_lock);
}
static void userlock_unlock(void)
{
bcmos_mutex_unlock(&user_lock);
}
static struct file *file_open(const char *path, int flags)
{
struct file *fd = NULL;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
fd = filp_open(path, flags, mode);
if(IS_ERR(fd))
return NULL;
return fd;
}
static void file_close(struct file *file)
{
filp_close(file, NULL);
}
static int file_read(struct file *file, uint32_t offset, uint8_t *data, unsigned int size)
{
unsigned long long o = offset;
mm_segment_t oldfs;
int bytes_read;
oldfs = get_fs();
set_fs(get_ds());
bytes_read = vfs_read(file, data, size, &o);
set_fs(oldfs);
if (bytes_read < 0)
{
BCMOS_TRACE_ERR("vfs_read returned error %d\n", bytes_read);
return (int)BCM_ERR_IO;
}
return bytes_read;
}
static int file_write(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = vfs_write(file, data, size, &offset);
set_fs(oldfs);
return ret;
}
static bcmos_errno file_write_1(const char *path)
{
struct file *fd;
char to_write[] = { '1' };
loff_t pos = 0;
ssize_t bytes_written;
fd = file_open(path, O_WRONLY);
if ((fd == NULL) || (fd == (void *)-ENOENT)) /* -ENOENT; No such file or directory */
{
BCMOS_TRACE_ERR("Can't open file %s for writing. Error %s\n",
path, bcmos_strerror(fd == NULL ? BCM_ERR_NULL : BCM_ERR_NOENT));
return (fd == NULL ? BCM_ERR_NULL : BCM_ERR_NOENT);
}
bytes_written = file_write(fd, pos, to_write, sizeof(to_write));
if (bytes_written != sizeof(to_write))
{
BCMOS_TRACE_ERR("vfs_write returned incorrect number of characters written: %d\n", bytes_written);
}
file_close(fd);
return BCM_ERR_OK;
}
static bcmos_errno i2c_fpga_read(uint32_t reg, uint32_t *val)
{
return maple_i2c_read_fpga(maple_i2c_get_client(FPGA_I2C_ADDR), reg, val);
}
static bcmos_errno i2c_fpga_write(uint32_t reg, uint32_t val)
{
return maple_i2c_write_fpga(maple_i2c_get_client(FPGA_I2C_ADDR), reg, val);
}
static bcmos_errno i2c_config_switch(void)
{
uint8_t to_write = 1;
int ret;
ret = maple_i2c_write(maple_i2c_get_client(I2C_SW0_I2C_ADDR), &to_write, 1);
if (ret != 0)
{
BCMOS_TRACE_ERR("Failed to write switch: %d\n", ret);
return BCM_ERR_INTERNAL;
}
return BCM_ERR_OK;
}
static bcmos_errno i2c_fpga_version_get(uint32_t *version)
{
return i2c_fpga_read(FPGA_REG_FPGA_VERSION, version);
}
static bcmos_errno i2c_svk_config_get(svk_board_id *board_id, svk_mode *mode)
{
uint32_t val;
bcmos_errno rc;
rc = i2c_fpga_read(FPGA_REG_BOARD_CONF, &val);
if (rc == BCM_ERR_OK)
{
*board_id = (svk_board_id)(val & 0x7);
*mode = (svk_mode)((val >> 4) & 0xF);
}
return rc;
}
static bcmos_errno i2c_device_cpu_state_set(bcmolt_devid device, bcmos_bool is_on)
{
bcmos_errno rc;
uint32_t reset_values;
/* Prevent race condition when this function is called for 2 devices simultaneously */
userlock_lock();
do
{
rc = i2c_config_switch();
if (rc)
break;
/* Read-modify-write so we can set only the relevant bit */
rc = i2c_fpga_read(FPGA_REG_RESETS, &reset_values);
if (rc)
break;
if (is_on)
reset_values |= FPGA_DEVICE_RESET_BIT(device);
else
reset_values &= ~FPGA_DEVICE_RESET_BIT(device);
rc = i2c_fpga_write(FPGA_REG_RESETS, reset_values);
} while (0);
userlock_unlock();
CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
bcmos_usleep(1000);
return BCM_ERR_OK;
}
static bcmos_bool bcmuser_system_mode_is_valid(bcmolt_system_mode system_mode, svk_board_id board_id, svk_mode svk_mode)
{
switch (board_id)
{
case SVK_BOARD_ID_1:
case SVK_BOARD_ID_3:
switch (svk_mode)
{
case SVK_MODE_GPON_16X:
return (system_mode == BCMOLT_SYSTEM_MODE_GPON__4_X ||
system_mode == BCMOLT_SYSTEM_MODE_GPON__8_X ||
system_mode == BCMOLT_SYSTEM_MODE_GPON__16_X ||
system_mode == BCMOLT_SYSTEM_MODE_GPON_8_XGPON_4_X_COEXISTENCE ||
system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__8_X ||
system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__4_X ||
system_mode == BCMOLT_SYSTEM_MODE_NGPON2__8_X_2_P_5_G);
case SVK_MODE_EPON_1G_16X:
return (system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X ||
system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X ||
system_mode == BCMOLT_SYSTEM_MODE_EPON__16_X ||
system_mode == BCMOLT_SYSTEM_MODE_EPON__2_X_10_G ||
system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_10_G ||
system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_10_G);
case SVK_MODE_XGPON:
return (system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__8_X ||
system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__4_X ||
system_mode == BCMOLT_SYSTEM_MODE_NGPON2__8_X_2_P_5_G);
case SVK_MODE_EPON_10G_8X:
return
(system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_COEXISTENCE_TDMA) ||
(system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_COEXISTENCE_TDMA) ||
(system_mode == BCMOLT_SYSTEM_MODE_EPON__2_X_10_G) ||
(system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_10_G) ||
(system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_10_G);
default:
return BCMOS_FALSE;
}
case SVK_BOARD_ID_2_XG:
return (system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__8_X ||
system_mode == BCMOLT_SYSTEM_MODE_XGPON_1__4_X ||
system_mode == BCMOLT_SYSTEM_MODE_NGPON2__8_X_2_P_5_G ||
system_mode == BCMOLT_SYSTEM_MODE_XGS__2_X_10_G ||
system_mode == BCMOLT_SYSTEM_MODE_NGPON2__2_X_10_G);
case SVK_BOARD_ID_2_XE:
return
(system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_COEXISTENCE_TDMA) ||
(system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_COEXISTENCE_TDMA) ||
(system_mode == BCMOLT_SYSTEM_MODE_AE_8_X) ||
(system_mode == BCMOLT_SYSTEM_MODE_EPON__2_X_10_G) ||
(system_mode == BCMOLT_SYSTEM_MODE_EPON__4_X_10_G) ||
(system_mode == BCMOLT_SYSTEM_MODE_EPON__8_X_10_G);
case DVT_BOARD_ID:
return BCMOS_TRUE;
default:
return BCMOS_FALSE;
}
}
bcmos_errno bcmuser_system_mode_validate(bcmolt_devid device, bcmolt_system_mode system_mode)
{
bcmos_errno rc;
uint32_t version;
svk_board_id board_id;
svk_mode svk_mode;
rc = i2c_config_switch();
CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
rc = i2c_fpga_version_get(&version);
if (rc != BCM_ERR_OK)
{
BCMOS_TRACE_INFO("Failed to read FPGA version\n");
return rc;
}
if (version < FPGA_VER_FOR_SVK_CONFIG)
{
/* this FPGA version don't support reading the SVK version, so we must assume it's OK */
return BCM_ERR_OK;
}
rc = i2c_svk_config_get(&board_id, &svk_mode);
if (rc != BCM_ERR_OK)
{
BCMOS_TRACE_INFO("Failed to read SVK config\n");
return rc;
}
if (bcmuser_system_mode_is_valid(system_mode, board_id, svk_mode))
{
return BCM_ERR_OK;
}
else
{
BCMOS_TRACE_INFO(
"System mode %s is not supported for board ID %d, SVK mode %d\n",
bcmolt_system_mode_name(system_mode),
board_id,
svk_mode);
return BCM_ERR_NOT_SUPPORTED;
}
}
int bcmuser_image_read(bcmolt_devid device, bcmolt_device_image_type image_type,
uint32_t offset, uint8_t *buf, uint32_t buf_size)
{
struct file *fd;
int bytes_read;
bcmos_errno rc;
static int2str_t image_type2str[] =
{
{BCMOLT_DEVICE_IMAGE_TYPE_BOOTLOADER, BOOT_FILE},
{BCMOLT_DEVICE_IMAGE_TYPE_APPLICATION, APPL_FILE},
{BCMOLT_DEVICE_IMAGE_TYPE_ITU_PON_ONU_FIRMWARE, ONU_FW_FILE},
{-1}
};
const char *filename;
filename = int2str(image_type2str, image_type);
fd = file_open(filename, O_RDONLY);
if ((fd == NULL) || (fd == (void *)-ENOENT)) /* -ENOENT; No such file or directory */
{
rc = (fd == NULL ? BCM_ERR_NULL : BCM_ERR_NOENT);
BCMOS_TRACE_ERR("filp_open returned error %s (%d)\n", bcmos_strerror(rc), rc);
return rc;
}
bytes_read = file_read(fd, offset, buf, buf_size);
file_close(fd);
if (bytes_read < 0)
{
return (int)BCM_ERR_IO;
}
return bytes_read;
}
bcmos_errno bcmuser_device_off(bcmolt_devid device)
{
/* Ignore request for non-existing device */
if (device >= bcmolt_board_num_maple_devices())
return BCM_ERR_OK;
BCMOS_TRACE_INFO("device=%u\n", device);
return i2c_device_cpu_state_set(device, BCMOS_FALSE);
}
bcmos_errno bcmuser_device_on(bcmolt_devid device)
{
bcmos_errno rc;
BCMOS_TRACE_INFO("device=%u\n", device);
if (device >= bcmolt_board_num_maple_devices())
return BCM_ERR_RANGE;
rc = i2c_device_cpu_state_set(device, BCMOS_TRUE);
/* Wait a moment to make sure the device is discoverable in case it just booted. */
bcmos_usleep(50 * 1000);
return rc;
}
bcmos_errno bcmuser_device_is_running(bcmolt_devid device, bcmos_bool *is_running)
{
bcmos_errno rc;
uint32_t reset_values;
uint32_t version;
rc = i2c_config_switch();
CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
rc = i2c_fpga_version_get(&version);
if (rc != BCM_ERR_OK)
{
BCMOS_TRACE_INFO("Failed to read FPGA version\n");
return rc;
}
if (!bcmolt_board_supports_standalone(version))
{
/* For older FPGAs that don't support standalone mode, the device is taken out of reset at boot. We need to lie
and tell device control that it's not running, so it will be programmed from scratch. */
BCMOS_TRACE_INFO("FPGA version %d doesn't support standalone mode\n", version);
*is_running = BCMOS_FALSE;
return BCM_ERR_OK;
}
rc = i2c_fpga_read(FPGA_REG_RESETS, &reset_values);
CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
*is_running = ((reset_values & FPGA_DEVICE_RESET_BIT(device)) != 0);
return BCM_ERR_OK;
}
bcmos_errno bcmuser_host_reset(void)
{
bcm_ll_pcie_cleanup();
kernel_restart(NULL);
return BCM_ERR_OK; /* will never be reached */
}
bcmos_errno bcmuser_pcie_prepare(void)
{
bcmos_errno rc;
uint32_t reset_values;
const char *pcie_rescan_file = bcmolt_board_pci_rescan_string_get();
int i;
/* - Take all devices out of reset
* - rescan bus
* - restore reset value
*/
rc = i2c_config_switch();
CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
/* Read-modify-write so we can set only the relevant bit */
rc = i2c_fpga_read(FPGA_REG_RESETS, &reset_values);
CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
rc = i2c_fpga_write(FPGA_REG_RESETS, FPGA_ENABLE_ALL_DEVICES);
CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
bcmos_usleep(50000);
/* Now recan PCI bus */
/* Now re-enumerate the PCI bus by writing a 1 to the 'rescan' file. */
rc = file_write_1(pcie_rescan_file);
if (rc != BCM_ERR_OK)
BCMOS_TRACE_ERR("file write returned error %s (%d)\n", bcmos_strerror(rc), rc);
bcmos_usleep(100 * 1000);
/* Now initialize ll_pcie driver and map maple devices */
rc = bcm_ll_pcie_init();
CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
/* Now restore rest values. Keep switch out of reset */
for (i = 0; i < bcmolt_board_num_maple_devices(); i++)
{
if ((FPGA_DEVICE_RESET_BIT(i) & reset_values) == 0)
bcmuser_pcie_channel_remove(i);
}
rc = i2c_fpga_write(FPGA_REG_RESETS, reset_values | FPGA_ENABLE_SWITCH);
CHECK_RETURN_ERROR(rc != BCM_ERR_OK, rc);
return 0;
}
bcmos_errno bcmuser_pcie_channel_prepare(bcmolt_devid device)
{
bcmos_errno rc;
rc = bcm_ll_pcie_dev_enable(device);
if (rc)
BCMOS_TRACE_ERR("Couldn't prepare device %d. Error %s\n", device, bcmos_strerror(rc));
return rc;
}
bcmos_errno bcmuser_pcie_channel_remove(bcmolt_devid device)
{
bcm_ll_pcie_dev_disable(device);
return BCM_ERR_OK;
}