diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/io/io_fip.c | 1 | ||||
-rw-r--r-- | drivers/io/io_memmap.c | 3 | ||||
-rw-r--r-- | drivers/io/io_semihosting.c | 3 | ||||
-rw-r--r-- | drivers/io/io_storage.c | 4 | ||||
-rw-r--r-- | drivers/marvell/amb_adec.c | 156 | ||||
-rw-r--r-- | drivers/marvell/cache_llc.c | 109 | ||||
-rw-r--r-- | drivers/marvell/ccu.c | 361 | ||||
-rw-r--r-- | drivers/marvell/comphy.h | 473 | ||||
-rw-r--r-- | drivers/marvell/comphy/comphy-cp110.h | 775 | ||||
-rw-r--r-- | drivers/marvell/comphy/phy-comphy-cp110.c | 2319 | ||||
-rw-r--r-- | drivers/marvell/comphy/phy-comphy-cp110.h | 19 | ||||
-rw-r--r-- | drivers/marvell/gwin.c | 227 | ||||
-rw-r--r-- | drivers/marvell/i2c/a8k_i2c.c | 613 | ||||
-rw-r--r-- | drivers/marvell/io_win.c | 267 | ||||
-rw-r--r-- | drivers/marvell/iob.c | 195 | ||||
-rw-r--r-- | drivers/marvell/mci.c | 832 | ||||
-rw-r--r-- | drivers/marvell/mochi/ap807_setup.c | 237 | ||||
-rw-r--r-- | drivers/marvell/mochi/apn806_setup.c | 251 | ||||
-rw-r--r-- | drivers/marvell/mochi/cp110_setup.c | 429 | ||||
-rw-r--r-- | drivers/marvell/thermal.c | 54 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 714 |
21 files changed, 8035 insertions, 7 deletions
diff --git a/drivers/io/io_fip.c b/drivers/io/io_fip.c index 6e7103db..9d6c763e 100644 --- a/drivers/io/io_fip.c +++ b/drivers/io/io_fip.c @@ -372,7 +372,6 @@ static int fip_file_read(io_entity_t *entity, uintptr_t buffer, size_t length, uintptr_t backend_handle; assert(entity != NULL); - assert(buffer != (uintptr_t)NULL); assert(length_read != NULL); assert(entity->info != (uintptr_t)NULL); diff --git a/drivers/io/io_memmap.c b/drivers/io/io_memmap.c index bf59d6a5..5595e60a 100644 --- a/drivers/io/io_memmap.c +++ b/drivers/io/io_memmap.c @@ -9,6 +9,7 @@ #include <io_driver.h> #include <io_memmap.h> #include <io_storage.h> +#include <platform_def.h> #include <string.h> #include <utils.h> @@ -169,7 +170,6 @@ static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, size_t pos_after; assert(entity != NULL); - assert(buffer != (uintptr_t)NULL); assert(length_read != NULL); fp = (file_state_t *) entity->info; @@ -197,7 +197,6 @@ static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer, size_t pos_after; assert(entity != NULL); - assert(buffer != (uintptr_t)NULL); assert(length_written != NULL); fp = (file_state_t *) entity->info; diff --git a/drivers/io/io_semihosting.c b/drivers/io/io_semihosting.c index 4abf44f7..9ca0a9dc 100644 --- a/drivers/io/io_semihosting.c +++ b/drivers/io/io_semihosting.c @@ -8,6 +8,7 @@ #include <io_driver.h> #include <io_semihosting.h> #include <io_storage.h> +#include <platform_def.h> #include <semihosting.h> @@ -133,7 +134,6 @@ static int sh_file_read(io_entity_t *entity, uintptr_t buffer, size_t length, long file_handle; assert(entity != NULL); - assert(buffer != (uintptr_t)NULL); assert(length_read != NULL); file_handle = (long)entity->info; @@ -158,7 +158,6 @@ static int sh_file_write(io_entity_t *entity, const uintptr_t buffer, size_t bytes = length; assert(entity != NULL); - assert(buffer != (uintptr_t)NULL); assert(length_written != NULL); file_handle = (long)entity->info; diff --git a/drivers/io/io_storage.c b/drivers/io/io_storage.c index 0918de0a..948f8481 100644 --- a/drivers/io/io_storage.c +++ b/drivers/io/io_storage.c @@ -279,7 +279,7 @@ int io_read(uintptr_t handle, size_t *length_read) { int result = -ENODEV; - assert(is_valid_entity(handle) && (buffer != (uintptr_t)NULL)); + assert(is_valid_entity(handle)); io_entity_t *entity = (io_entity_t *)handle; @@ -299,7 +299,7 @@ int io_write(uintptr_t handle, size_t *length_written) { int result = -ENODEV; - assert(is_valid_entity(handle) && (buffer != (uintptr_t)NULL)); + assert(is_valid_entity(handle)); io_entity_t *entity = (io_entity_t *)handle; diff --git a/drivers/marvell/amb_adec.c b/drivers/marvell/amb_adec.c new file mode 100644 index 00000000..06a1957e --- /dev/null +++ b/drivers/marvell/amb_adec.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* AXI to M-Bridge decoding unit driver for Marvell Armada 8K and 8K+ SoCs */ + +#include <a8k_common.h> +#include <debug.h> +#include <mmio.h> +#include <mvebu.h> +#include <mvebu_def.h> + +#if LOG_LEVEL >= LOG_LEVEL_INFO +#define DEBUG_ADDR_MAP +#endif + +/* common defines */ +#define WIN_ENABLE_BIT (0x1) + +#define MVEBU_AMB_ADEC_OFFSET (0x70ff00) + +#define AMB_WIN_CR_OFFSET(win) (amb_base + 0x0 + (0x8 * win)) +#define AMB_ATTR_OFFSET 8 +#define AMB_ATTR_MASK 0xFF +#define AMB_SIZE_OFFSET 16 +#define AMB_SIZE_MASK 0xFF + +#define AMB_WIN_BASE_OFFSET(win) (amb_base + 0x4 + (0x8 * win)) +#define AMB_BASE_OFFSET 16 +#define AMB_BASE_ADDR_MASK ((1 << (32 - AMB_BASE_OFFSET)) - 1) + +#define AMB_WIN_ALIGNMENT_64K (0x10000) +#define AMB_WIN_ALIGNMENT_1M (0x100000) + +uintptr_t amb_base; + +static void amb_check_win(struct addr_map_win *win, uint32_t win_num) +{ + uint32_t base_addr; + + /* make sure the base address is in 16-bit range */ + if (win->base_addr > AMB_BASE_ADDR_MASK) { + WARN("Window %d: base address is too big 0x%llx\n", + win_num, win->base_addr); + win->base_addr = AMB_BASE_ADDR_MASK; + WARN("Set the base address to 0x%llx\n", win->base_addr); + } + + base_addr = win->base_addr << AMB_BASE_OFFSET; + /* for AMB The base is always 1M aligned */ + /* check if address is aligned to 1M */ + if (IS_NOT_ALIGN(base_addr, AMB_WIN_ALIGNMENT_1M)) { + win->base_addr = ALIGN_UP(base_addr, AMB_WIN_ALIGNMENT_1M); + WARN("Window %d: base address unaligned to 0x%x\n", + win_num, AMB_WIN_ALIGNMENT_1M); + WARN("Align up the base address to 0x%llx\n", win->base_addr); + } + + /* size parameter validity check */ + if (!IS_POWER_OF_2(win->win_size)) { + WARN("Window %d: window size is not power of 2 (0x%llx)\n", + win_num, win->win_size); + win->win_size = ROUND_UP_TO_POW_OF_2(win->win_size); + WARN("Rounding size to 0x%llx\n", win->win_size); + } +} + +static void amb_enable_win(struct addr_map_win *win, uint32_t win_num) +{ + uint32_t ctrl, base, size; + + /* + * size is 64KB granularity. + * The number of ones specifies the size of the + * window in 64 KB granularity. 0 is 64KB + */ + size = (win->win_size / AMB_WIN_ALIGNMENT_64K) - 1; + ctrl = (size << AMB_SIZE_OFFSET) | (win->target_id << AMB_ATTR_OFFSET); + base = win->base_addr << AMB_BASE_OFFSET; + + mmio_write_32(AMB_WIN_BASE_OFFSET(win_num), base); + mmio_write_32(AMB_WIN_CR_OFFSET(win_num), ctrl); + + /* enable window after configuring window size (and attributes) */ + ctrl |= WIN_ENABLE_BIT; + mmio_write_32(AMB_WIN_CR_OFFSET(win_num), ctrl); +} + +#ifdef DEBUG_ADDR_MAP +static void dump_amb_adec(void) +{ + uint32_t ctrl, base, win_id, attr; + uint32_t size, size_count; + + /* Dump all AMB windows */ + tf_printf("bank attribute base size\n"); + tf_printf("--------------------------------------------\n"); + for (win_id = 0; win_id < AMB_MAX_WIN_ID; win_id++) { + ctrl = mmio_read_32(AMB_WIN_CR_OFFSET(win_id)); + if (ctrl & WIN_ENABLE_BIT) { + base = mmio_read_32(AMB_WIN_BASE_OFFSET(win_id)); + attr = (ctrl >> AMB_ATTR_OFFSET) & AMB_ATTR_MASK; + size_count = (ctrl >> AMB_SIZE_OFFSET) & AMB_SIZE_MASK; + size = (size_count + 1) * AMB_WIN_ALIGNMENT_64K; + tf_printf("amb 0x%04x 0x%08x 0x%08x\n", + attr, base, size); + } + } +} +#endif + +int init_amb_adec(uintptr_t base) +{ + struct addr_map_win *win; + uint32_t win_id, win_reg; + uint32_t win_count; + + INFO("Initializing AXI to MBus Bridge Address decoding\n"); + + /* Get the base address of the AMB address decoding */ + amb_base = base + MVEBU_AMB_ADEC_OFFSET; + + /* Get the array of the windows and its size */ + marvell_get_amb_memory_map(&win, &win_count, base); + if (win_count <= 0) + INFO("no windows configurations found\n"); + + if (win_count > AMB_MAX_WIN_ID) { + INFO("number of windows is bigger than %d\n", AMB_MAX_WIN_ID); + return 0; + } + + /* disable all AMB windows */ + for (win_id = 0; win_id < AMB_MAX_WIN_ID; win_id++) { + win_reg = mmio_read_32(AMB_WIN_CR_OFFSET(win_id)); + win_reg &= ~WIN_ENABLE_BIT; + mmio_write_32(AMB_WIN_CR_OFFSET(win_id), win_reg); + } + + /* enable relevant windows */ + for (win_id = 0; win_id < win_count; win_id++, win++) { + amb_check_win(win, win_id); + amb_enable_win(win, win_id); + } + +#ifdef DEBUG_ADDR_MAP + dump_amb_adec(); +#endif + + INFO("Done AXI to MBus Bridge Address decoding Initializing\n"); + + return 0; +} diff --git a/drivers/marvell/cache_llc.c b/drivers/marvell/cache_llc.c new file mode 100644 index 00000000..e13e6ce2 --- /dev/null +++ b/drivers/marvell/cache_llc.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* LLC driver is the Last Level Cache (L3C) driver + * for Marvell SoCs in AP806, AP807, and AP810 + */ + +#include <arch_helpers.h> +#include <assert.h> +#include <cache_llc.h> +#include <ccu.h> +#include <mmio.h> +#include <mvebu_def.h> + +#define CCU_HTC_CR(ap_index) (MVEBU_CCU_BASE(ap_index) + 0x200) +#define CCU_SET_POC_OFFSET 5 + +extern void ca72_l2_enable_unique_clean(void); + +void llc_cache_sync(int ap_index) +{ + mmio_write_32(LLC_SYNC(ap_index), 0); + /* Atomic write, no need to wait */ +} + +void llc_flush_all(int ap_index) +{ + mmio_write_32(L2X0_CLEAN_INV_WAY(ap_index), LLC_WAY_MASK); + llc_cache_sync(ap_index); +} + +void llc_clean_all(int ap_index) +{ + mmio_write_32(L2X0_CLEAN_WAY(ap_index), LLC_WAY_MASK); + llc_cache_sync(ap_index); +} + +void llc_inv_all(int ap_index) +{ + mmio_write_32(L2X0_INV_WAY(ap_index), LLC_WAY_MASK); + llc_cache_sync(ap_index); +} + +void llc_disable(int ap_index) +{ + llc_flush_all(ap_index); + mmio_write_32(LLC_CTRL(ap_index), 0); + dsbishst(); +} + +void llc_enable(int ap_index, int excl_mode) +{ + uint32_t val; + + dsbsy(); + llc_inv_all(ap_index); + dsbsy(); + + val = LLC_CTRL_EN; + if (excl_mode) + val |= LLC_EXCLUSIVE_EN; + + mmio_write_32(LLC_CTRL(ap_index), val); + dsbsy(); +} + +int llc_is_exclusive(int ap_index) +{ + uint32_t reg; + + reg = mmio_read_32(LLC_CTRL(ap_index)); + + if ((reg & (LLC_CTRL_EN | LLC_EXCLUSIVE_EN)) == + (LLC_CTRL_EN | LLC_EXCLUSIVE_EN)) + return 1; + + return 0; +} + +void llc_runtime_enable(int ap_index) +{ + uint32_t reg; + + reg = mmio_read_32(LLC_CTRL(ap_index)); + if (reg & LLC_CTRL_EN) + return; + + INFO("Enabling LLC\n"); + + /* + * Enable L2 UniqueClean evictions with data + * Note: this configuration assumes that LLC is configured + * in exclusive mode. + * Later on in the code this assumption will be validated + */ + ca72_l2_enable_unique_clean(); + llc_enable(ap_index, 1); + + /* Set point of coherency to DDR. + * This is required by units which have SW cache coherency + */ + reg = mmio_read_32(CCU_HTC_CR(ap_index)); + reg |= (0x1 << CCU_SET_POC_OFFSET); + mmio_write_32(CCU_HTC_CR(ap_index), reg); +} diff --git a/drivers/marvell/ccu.c b/drivers/marvell/ccu.c new file mode 100644 index 00000000..e478d63a --- /dev/null +++ b/drivers/marvell/ccu.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* CCU unit device driver for Marvell AP807, AP807 and AP810 SoCs */ + +#include <a8k_common.h> +#include <ccu.h> +#include <debug.h> +#include <mmio.h> +#include <mvebu.h> +#include <mvebu_def.h> + +#if LOG_LEVEL >= LOG_LEVEL_INFO +#define DEBUG_ADDR_MAP +#endif + +/* common defines */ +#define WIN_ENABLE_BIT (0x1) +/* Physical address of the base of the window = {AddrLow[19:0],20’h0} */ +#define ADDRESS_SHIFT (20 - 4) +#define ADDRESS_MASK (0xFFFFFFF0) +#define CCU_WIN_ALIGNMENT (0x100000) + +#define IS_DRAM_TARGET(tgt) ((((tgt) == DRAM_0_TID) || \ + ((tgt) == DRAM_1_TID) || \ + ((tgt) == RAR_TID)) ? 1 : 0) + +/* For storage of CR, SCR, ALR, AHR abd GCR */ +static uint32_t ccu_regs_save[MVEBU_CCU_MAX_WINS * 4 + 1]; + +#ifdef DEBUG_ADDR_MAP +static void dump_ccu(int ap_index) +{ + uint32_t win_id, win_cr, alr, ahr; + uint8_t target_id; + uint64_t start, end; + + /* Dump all AP windows */ + tf_printf("\tbank target start end\n"); + tf_printf("\t----------------------------------------------------\n"); + for (win_id = 0; win_id < MVEBU_CCU_MAX_WINS; win_id++) { + win_cr = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id)); + if (win_cr & WIN_ENABLE_BIT) { + target_id = (win_cr >> CCU_TARGET_ID_OFFSET) & + CCU_TARGET_ID_MASK; + alr = mmio_read_32(CCU_WIN_ALR_OFFSET(ap_index, + win_id)); + ahr = mmio_read_32(CCU_WIN_AHR_OFFSET(ap_index, + win_id)); + start = ((uint64_t)alr << ADDRESS_SHIFT); + end = (((uint64_t)ahr + 0x10) << ADDRESS_SHIFT); + tf_printf("\tccu %02x 0x%016llx 0x%016llx\n", + target_id, start, end); + } + } + win_cr = mmio_read_32(CCU_WIN_GCR_OFFSET(ap_index)); + target_id = (win_cr >> CCU_GCR_TARGET_OFFSET) & CCU_GCR_TARGET_MASK; + tf_printf("\tccu GCR %d - all other transactions\n", target_id); +} +#endif + +void ccu_win_check(struct addr_map_win *win) +{ + /* check if address is aligned to 1M */ + if (IS_NOT_ALIGN(win->base_addr, CCU_WIN_ALIGNMENT)) { + win->base_addr = ALIGN_UP(win->base_addr, CCU_WIN_ALIGNMENT); + NOTICE("%s: Align up the base address to 0x%llx\n", + __func__, win->base_addr); + } + + /* size parameter validity check */ + if (IS_NOT_ALIGN(win->win_size, CCU_WIN_ALIGNMENT)) { + win->win_size = ALIGN_UP(win->win_size, CCU_WIN_ALIGNMENT); + NOTICE("%s: Aligning size to 0x%llx\n", + __func__, win->win_size); + } +} + +void ccu_enable_win(int ap_index, struct addr_map_win *win, uint32_t win_id) +{ + uint32_t ccu_win_reg; + uint32_t alr, ahr; + uint64_t end_addr; + + if ((win_id == 0) || (win_id > MVEBU_CCU_MAX_WINS)) { + ERROR("Enabling wrong CCU window %d!\n", win_id); + return; + } + + end_addr = (win->base_addr + win->win_size - 1); + alr = (uint32_t)((win->base_addr >> ADDRESS_SHIFT) & ADDRESS_MASK); + ahr = (uint32_t)((end_addr >> ADDRESS_SHIFT) & ADDRESS_MASK); + + mmio_write_32(CCU_WIN_ALR_OFFSET(ap_index, win_id), alr); + mmio_write_32(CCU_WIN_AHR_OFFSET(ap_index, win_id), ahr); + + ccu_win_reg = WIN_ENABLE_BIT; + ccu_win_reg |= (win->target_id & CCU_TARGET_ID_MASK) + << CCU_TARGET_ID_OFFSET; + mmio_write_32(CCU_WIN_CR_OFFSET(ap_index, win_id), ccu_win_reg); +} + +static void ccu_disable_win(int ap_index, uint32_t win_id) +{ + uint32_t win_reg; + + if ((win_id == 0) || (win_id > MVEBU_CCU_MAX_WINS)) { + ERROR("Disabling wrong CCU window %d!\n", win_id); + return; + } + + win_reg = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id)); + win_reg &= ~WIN_ENABLE_BIT; + mmio_write_32(CCU_WIN_CR_OFFSET(ap_index, win_id), win_reg); +} + +/* Insert/Remove temporary window for using the out-of reset default + * CPx base address to access the CP configuration space prior to + * the further base address update in accordance with address mapping + * design. + * + * NOTE: Use the same window array for insertion and removal of + * temporary windows. + */ +void ccu_temp_win_insert(int ap_index, struct addr_map_win *win, int size) +{ + uint32_t win_id; + + for (int i = 0; i < size; i++) { + win_id = MVEBU_CCU_MAX_WINS - 1 - i; + ccu_win_check(win); + ccu_enable_win(ap_index, win, win_id); + win++; + } +} + +/* + * NOTE: Use the same window array for insertion and removal of + * temporary windows. + */ +void ccu_temp_win_remove(int ap_index, struct addr_map_win *win, int size) +{ + uint32_t win_id; + + for (int i = 0; i < size; i++) { + uint64_t base; + uint32_t target; + + win_id = MVEBU_CCU_MAX_WINS - 1 - i; + + target = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id)); + target >>= CCU_TARGET_ID_OFFSET; + target &= CCU_TARGET_ID_MASK; + + base = mmio_read_32(CCU_WIN_ALR_OFFSET(ap_index, win_id)); + base <<= ADDRESS_SHIFT; + + if ((win->target_id != target) || (win->base_addr != base)) { + ERROR("%s: Trying to remove bad window-%d!\n", + __func__, win_id); + continue; + } + ccu_disable_win(ap_index, win_id); + win++; + } +} + +/* Returns current DRAM window target (DRAM_0_TID, DRAM_1_TID, RAR_TID) + * NOTE: Call only once for each AP. + * The AP0 DRAM window is located at index 2 only at the BL31 execution start. + * Then it relocated to index 1 for matching the rest of APs DRAM settings. + * Calling this function after relocation will produce wrong results on AP0 + */ +static uint32_t ccu_dram_target_get(int ap_index) +{ + /* On BLE stage the AP0 DRAM window is opened by the BootROM at index 2. + * All the rest of detected APs will use window at index 1. + * The AP0 DRAM window is moved from index 2 to 1 during + * init_ccu() execution. + */ + const uint32_t win_id = (ap_index == 0) ? 2 : 1; + uint32_t target; + + target = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id)); + target >>= CCU_TARGET_ID_OFFSET; + target &= CCU_TARGET_ID_MASK; + + return target; +} + +void ccu_dram_target_set(int ap_index, uint32_t target) +{ + /* On BLE stage the AP0 DRAM window is opened by the BootROM at index 2. + * All the rest of detected APs will use window at index 1. + * The AP0 DRAM window is moved from index 2 to 1 + * during init_ccu() execution. + */ + const uint32_t win_id = (ap_index == 0) ? 2 : 1; + uint32_t dram_cr; + + dram_cr = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id)); + dram_cr &= ~(CCU_TARGET_ID_MASK << CCU_TARGET_ID_OFFSET); + dram_cr |= (target & CCU_TARGET_ID_MASK) << CCU_TARGET_ID_OFFSET; + mmio_write_32(CCU_WIN_CR_OFFSET(ap_index, win_id), dram_cr); +} + +/* Setup CCU DRAM window and enable it */ +void ccu_dram_win_config(int ap_index, struct addr_map_win *win) +{ +#if IMAGE_BLE /* BLE */ + /* On BLE stage the AP0 DRAM window is opened by the BootROM at index 2. + * Since the BootROM is not accessing DRAM at BLE stage, + * the DRAM window can be temporarely disabled. + */ + const uint32_t win_id = (ap_index == 0) ? 2 : 1; +#else /* end of BLE */ + /* At the ccu_init() execution stage, DRAM windows of all APs + * are arranged at index 1. + * The AP0 still has the old window BootROM DRAM at index 2, so + * the window-1 can be safely disabled without breaking the DRAM access. + */ + const uint32_t win_id = 1; +#endif + + ccu_disable_win(ap_index, win_id); + /* enable write secure (and clear read secure) */ + mmio_write_32(CCU_WIN_SCR_OFFSET(ap_index, win_id), + CCU_WIN_ENA_WRITE_SECURE); + ccu_win_check(win); + ccu_enable_win(ap_index, win, win_id); +} + +/* Save content of CCU window + GCR */ +static void ccu_save_win_range(int ap_id, int win_first, + int win_last, uint32_t *buffer) +{ + int win_id, idx; + /* Save CCU */ + for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) { + buffer[idx++] = mmio_read_32(CCU_WIN_CR_OFFSET(ap_id, win_id)); + buffer[idx++] = mmio_read_32(CCU_WIN_SCR_OFFSET(ap_id, win_id)); + buffer[idx++] = mmio_read_32(CCU_WIN_ALR_OFFSET(ap_id, win_id)); + buffer[idx++] = mmio_read_32(CCU_WIN_AHR_OFFSET(ap_id, win_id)); + } + buffer[idx] = mmio_read_32(CCU_WIN_GCR_OFFSET(ap_id)); +} + +/* Restore content of CCU window + GCR */ +static void ccu_restore_win_range(int ap_id, int win_first, + int win_last, uint32_t *buffer) +{ + int win_id, idx; + /* Restore CCU */ + for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) { + mmio_write_32(CCU_WIN_CR_OFFSET(ap_id, win_id), buffer[idx++]); + mmio_write_32(CCU_WIN_SCR_OFFSET(ap_id, win_id), buffer[idx++]); + mmio_write_32(CCU_WIN_ALR_OFFSET(ap_id, win_id), buffer[idx++]); + mmio_write_32(CCU_WIN_AHR_OFFSET(ap_id, win_id), buffer[idx++]); + } + mmio_write_32(CCU_WIN_GCR_OFFSET(ap_id), buffer[idx]); +} + +void ccu_save_win_all(int ap_id) +{ + ccu_save_win_range(ap_id, 0, MVEBU_CCU_MAX_WINS - 1, ccu_regs_save); +} + +void ccu_restore_win_all(int ap_id) +{ + ccu_restore_win_range(ap_id, 0, MVEBU_CCU_MAX_WINS - 1, ccu_regs_save); +} + +int init_ccu(int ap_index) +{ + struct addr_map_win *win, *dram_win; + uint32_t win_id, win_reg; + uint32_t win_count, array_id; + uint32_t dram_target; +#if IMAGE_BLE + /* In BootROM context CCU Window-1 + * has SRAM_TID target and should not be disabled + */ + const uint32_t win_start = 2; +#else + const uint32_t win_start = 1; +#endif + + INFO("Initializing CCU Address decoding\n"); + + /* Get the array of the windows and fill the map data */ + marvell_get_ccu_memory_map(ap_index, &win, &win_count); + if (win_count <= 0) { + INFO("No windows configurations found\n"); + } else if (win_count > (MVEBU_CCU_MAX_WINS - 1)) { + ERROR("CCU mem map array > than max available windows (%d)\n", + MVEBU_CCU_MAX_WINS); + win_count = MVEBU_CCU_MAX_WINS; + } + + /* Need to set GCR to DRAM before all CCU windows are disabled for + * securing the normal access to DRAM location, which the ATF is running + * from. Once all CCU windows are set, which have to include the + * dedicated DRAM window as well, the GCR can be switched to the target + * defined by the platform configuration. + */ + dram_target = ccu_dram_target_get(ap_index); + win_reg = (dram_target & CCU_GCR_TARGET_MASK) << CCU_GCR_TARGET_OFFSET; + mmio_write_32(CCU_WIN_GCR_OFFSET(ap_index), win_reg); + + /* If the DRAM window was already configured at the BLE stage, + * only the window target considered valid, the address range should be + * updated according to the platform configuration. + */ + for (dram_win = win, array_id = 0; array_id < win_count; + array_id++, dram_win++) { + if (IS_DRAM_TARGET(dram_win->target_id)) { + dram_win->target_id = dram_target; + break; + } + } + + /* Disable all AP CCU windows + * Window-0 is always bypassed since it already contains + * data allowing the internal configuration space access + */ + for (win_id = win_start; win_id < MVEBU_CCU_MAX_WINS; win_id++) { + ccu_disable_win(ap_index, win_id); + /* enable write secure (and clear read secure) */ + mmio_write_32(CCU_WIN_SCR_OFFSET(ap_index, win_id), + CCU_WIN_ENA_WRITE_SECURE); + } + + /* win_id is the index of the current ccu window + * array_id is the index of the current memory map window entry + */ + for (win_id = win_start, array_id = 0; + ((win_id < MVEBU_CCU_MAX_WINS) && (array_id < win_count)); + win_id++) { + ccu_win_check(win); + ccu_enable_win(ap_index, win, win_id); + win++; + array_id++; + } + + /* Get & set the default target according to board topology */ + win_reg = (marvell_get_ccu_gcr_target(ap_index) & CCU_GCR_TARGET_MASK) + << CCU_GCR_TARGET_OFFSET; + mmio_write_32(CCU_WIN_GCR_OFFSET(ap_index), win_reg); + +#ifdef DEBUG_ADDR_MAP + dump_ccu(ap_index); +#endif + + INFO("Done CCU Address decoding Initializing\n"); + + return 0; +} diff --git a/drivers/marvell/comphy.h b/drivers/marvell/comphy.h new file mode 100644 index 00000000..788b1b60 --- /dev/null +++ b/drivers/marvell/comphy.h @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* Driver for COMPHY unit that is part or Marvell A8K SoCs */ + +#ifndef _COMPHY_H_ +#define _COMPHY_H_ + +/* COMPHY registers */ +#define COMMON_PHY_CFG1_REG 0x0 +#define COMMON_PHY_CFG1_PWR_UP_OFFSET 1 +#define COMMON_PHY_CFG1_PWR_UP_MASK \ + (0x1 << COMMON_PHY_CFG1_PWR_UP_OFFSET) +#define COMMON_PHY_CFG1_PIPE_SELECT_OFFSET 2 +#define COMMON_PHY_CFG1_PIPE_SELECT_MASK \ + (0x1 << COMMON_PHY_CFG1_PIPE_SELECT_OFFSET) +#define COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET 13 +#define COMMON_PHY_CFG1_PWR_ON_RESET_MASK \ + (0x1 << COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET) +#define COMMON_PHY_CFG1_CORE_RSTN_OFFSET 14 +#define COMMON_PHY_CFG1_CORE_RSTN_MASK \ + (0x1 << COMMON_PHY_CFG1_CORE_RSTN_OFFSET) +#define COMMON_PHY_PHY_MODE_OFFSET 15 +#define COMMON_PHY_PHY_MODE_MASK \ + (0x1 << COMMON_PHY_PHY_MODE_OFFSET) + +#define COMMON_SELECTOR_PHY_OFFSET 0x140 +#define COMMON_SELECTOR_PIPE_OFFSET 0x144 + +#define COMMON_PHY_SD_CTRL1 0x148 +#define COMMON_PHY_SD_CTRL1_COMPHY_0_4_PORT_OFFSET 0 +#define COMMON_PHY_SD_CTRL1_COMPHY_0_4_PORT_MASK 0xFFFF +#define COMMON_PHY_SD_CTRL1_PCIE_X4_EN_OFFSET 24 +#define COMMON_PHY_SD_CTRL1_PCIE_X4_EN_MASK \ + (0x1 << COMMON_PHY_SD_CTRL1_PCIE_X4_EN_OFFSET) +#define COMMON_PHY_SD_CTRL1_PCIE_X2_EN_OFFSET 25 +#define COMMON_PHY_SD_CTRL1_PCIE_X2_EN_MASK \ + (0x1 << COMMON_PHY_SD_CTRL1_PCIE_X2_EN_OFFSET) + +#define DFX_DEV_GEN_CTRL12 0x80 +#define DFX_DEV_GEN_PCIE_CLK_SRC_OFFSET 7 +#define DFX_DEV_GEN_PCIE_CLK_SRC_MASK \ + (0x3 << DFX_DEV_GEN_PCIE_CLK_SRC_OFFSET) + +/* HPIPE register */ +#define HPIPE_PWR_PLL_REG 0x4 +#define HPIPE_PWR_PLL_REF_FREQ_OFFSET 0 +#define HPIPE_PWR_PLL_REF_FREQ_MASK \ + (0x1f << HPIPE_PWR_PLL_REF_FREQ_OFFSET) +#define HPIPE_PWR_PLL_PHY_MODE_OFFSET 5 +#define HPIPE_PWR_PLL_PHY_MODE_MASK \ + (0x7 << HPIPE_PWR_PLL_PHY_MODE_OFFSET) + +#define HPIPE_DFE_REG0 0x01C +#define HPIPE_DFE_RES_FORCE_OFFSET 15 +#define HPIPE_DFE_RES_FORCE_MASK \ + (0x1 << HPIPE_DFE_RES_FORCE_OFFSET) + +#define HPIPE_G2_SET_1_REG 0x040 +#define HPIPE_G2_SET_1_G2_RX_SELMUPI_OFFSET 0 +#define HPIPE_G2_SET_1_G2_RX_SELMUPI_MASK \ + (0x7 << HPIPE_G2_SET_1_G2_RX_SELMUPI_OFFSET) +#define HPIPE_G2_SET_1_G2_RX_SELMUPP_OFFSET 3 +#define HPIPE_G2_SET_1_G2_RX_SELMUPP_MASK \ + (0x7 << HPIPE_G2_SET_1_G2_RX_SELMUPP_OFFSET) +#define HPIPE_G2_SET_1_G2_RX_SELMUFI_OFFSET 6 +#define HPIPE_G2_SET_1_G2_RX_SELMUFI_MASK \ + (0x3 << HPIPE_G2_SET_1_G2_RX_SELMUFI_OFFSET) + +#define HPIPE_G3_SETTINGS_1_REG 0x048 +#define HPIPE_G3_RX_SELMUPI_OFFSET 0 +#define HPIPE_G3_RX_SELMUPI_MASK \ + (0x7 << HPIPE_G3_RX_SELMUPI_OFFSET) +#define HPIPE_G3_RX_SELMUPF_OFFSET 3 +#define HPIPE_G3_RX_SELMUPF_MASK \ + (0x7 << HPIPE_G3_RX_SELMUPF_OFFSET) +#define HPIPE_G3_SETTING_BIT_OFFSET 13 +#define HPIPE_G3_SETTING_BIT_MASK \ + (0x1 << HPIPE_G3_SETTING_BIT_OFFSET) + +#define HPIPE_INTERFACE_REG 0x94 +#define HPIPE_INTERFACE_GEN_MAX_OFFSET 10 +#define HPIPE_INTERFACE_GEN_MAX_MASK \ + (0x3 << HPIPE_INTERFACE_GEN_MAX_OFFSET) +#define HPIPE_INTERFACE_DET_BYPASS_OFFSET 12 +#define HPIPE_INTERFACE_DET_BYPASS_MASK \ + (0x1 << HPIPE_INTERFACE_DET_BYPASS_OFFSET) +#define HPIPE_INTERFACE_LINK_TRAIN_OFFSET 14 +#define HPIPE_INTERFACE_LINK_TRAIN_MASK \ + (0x1 << HPIPE_INTERFACE_LINK_TRAIN_OFFSET) + +#define HPIPE_VDD_CAL_CTRL_REG 0x114 +#define HPIPE_EXT_SELLV_RXSAMPL_OFFSET 5 +#define HPIPE_EXT_SELLV_RXSAMPL_MASK \ + (0x1f << HPIPE_EXT_SELLV_RXSAMPL_OFFSET) + +#define HPIPE_PCIE_REG0 0x120 +#define HPIPE_PCIE_IDLE_SYNC_OFFSET 12 +#define HPIPE_PCIE_IDLE_SYNC_MASK \ + (0x1 << HPIPE_PCIE_IDLE_SYNC_OFFSET) +#define HPIPE_PCIE_SEL_BITS_OFFSET 13 +#define HPIPE_PCIE_SEL_BITS_MASK \ + (0x3 << HPIPE_PCIE_SEL_BITS_OFFSET) + +#define HPIPE_LANE_ALIGN_REG 0x124 +#define HPIPE_LANE_ALIGN_OFF_OFFSET 12 +#define HPIPE_LANE_ALIGN_OFF_MASK \ + (0x1 << HPIPE_LANE_ALIGN_OFF_OFFSET) + +#define HPIPE_MISC_REG 0x13C +#define HPIPE_MISC_CLK100M_125M_OFFSET 4 +#define HPIPE_MISC_CLK100M_125M_MASK \ + (0x1 << HPIPE_MISC_CLK100M_125M_OFFSET) +#define HPIPE_MISC_ICP_FORCE_OFFSET 5 +#define HPIPE_MISC_ICP_FORCE_MASK \ + (0x1 << HPIPE_MISC_ICP_FORCE_OFFSET) +#define HPIPE_MISC_TXDCLK_2X_OFFSET 6 +#define HPIPE_MISC_TXDCLK_2X_MASK \ + (0x1 << HPIPE_MISC_TXDCLK_2X_OFFSET) +#define HPIPE_MISC_CLK500_EN_OFFSET 7 +#define HPIPE_MISC_CLK500_EN_MASK \ + (0x1 << HPIPE_MISC_CLK500_EN_OFFSET) +#define HPIPE_MISC_REFCLK_SEL_OFFSET 10 +#define HPIPE_MISC_REFCLK_SEL_MASK \ + (0x1 << HPIPE_MISC_REFCLK_SEL_OFFSET) + +#define HPIPE_SAMPLER_N_PROC_CALIB_CTRL_REG 0x16C +#define HPIPE_SMAPLER_OFFSET 12 +#define HPIPE_SMAPLER_MASK (0x1 << HPIPE_SMAPLER_OFFSET) + +#define HPIPE_PWR_CTR_DTL_REG 0x184 +#define HPIPE_PWR_CTR_DTL_FLOOP_EN_OFFSET 2 +#define HPIPE_PWR_CTR_DTL_FLOOP_EN_MASK \ + (0x1 << HPIPE_PWR_CTR_DTL_FLOOP_EN_OFFSET) + +#define HPIPE_FRAME_DET_CONTROL_REG 0x220 +#define HPIPE_FRAME_DET_LOCK_LOST_TO_OFFSET 12 +#define HPIPE_FRAME_DET_LOCK_LOST_TO_MASK \ + (0x1 << HPIPE_FRAME_DET_LOCK_LOST_TO_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_0_REG 0x268 +#define HPIPE_TX_TRAIN_P2P_HOLD_OFFSET 15 +#define HPIPE_TX_TRAIN_P2P_HOLD_MASK \ + (0x1 << HPIPE_TX_TRAIN_P2P_HOLD_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_REG 0x26C +#define HPIPE_TX_TRAIN_CTRL_G1_OFFSET 0 +#define HPIPE_TX_TRAIN_CTRL_G1_MASK \ + (0x1 << HPIPE_TX_TRAIN_CTRL_G1_OFFSET) +#define HPIPE_TX_TRAIN_CTRL_GN1_OFFSET 1 +#define HPIPE_TX_TRAIN_CTRL_GN1_MASK \ + (0x1 << HPIPE_TX_TRAIN_CTRL_GN1_OFFSET) +#define HPIPE_TX_TRAIN_CTRL_G0_OFFSET 2 +#define HPIPE_TX_TRAIN_CTRL_G0_MASK \ + (0x1 << HPIPE_TX_TRAIN_CTRL_G0_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_4_REG 0x278 +#define HPIPE_TRX_TRAIN_TIMER_OFFSET 0 +#define HPIPE_TRX_TRAIN_TIMER_MASK \ + (0x3FF << HPIPE_TRX_TRAIN_TIMER_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_5_REG 0x2A4 +#define HPIPE_TX_TRAIN_START_SQ_EN_OFFSET 11 +#define HPIPE_TX_TRAIN_START_SQ_EN_MASK \ + (0x1 << HPIPE_TX_TRAIN_START_SQ_EN_OFFSET) +#define HPIPE_TX_TRAIN_START_FRM_DET_EN_OFFSET 12 +#define HPIPE_TX_TRAIN_START_FRM_DET_EN_MASK \ + (0x1 << HPIPE_TX_TRAIN_START_FRM_DET_EN_OFFSET) +#define HPIPE_TX_TRAIN_START_FRM_LOCK_EN_OFFSET 13 +#define HPIPE_TX_TRAIN_START_FRM_LOCK_EN_MASK \ + (0x1 << HPIPE_TX_TRAIN_START_FRM_LOCK_EN_OFFSET) +#define HPIPE_TX_TRAIN_WAIT_TIME_EN_OFFSET 14 +#define HPIPE_TX_TRAIN_WAIT_TIME_EN_MASK \ + (0x1 << HPIPE_TX_TRAIN_WAIT_TIME_EN_OFFSET) + +#define HPIPE_TX_TRAIN_REG 0x31C +#define HPIPE_TX_TRAIN_CHK_INIT_OFFSET 4 +#define HPIPE_TX_TRAIN_CHK_INIT_MASK \ + (0x1 << HPIPE_TX_TRAIN_CHK_INIT_OFFSET) +#define HPIPE_TX_TRAIN_COE_FM_PIN_PCIE3_OFFSET 7 +#define HPIPE_TX_TRAIN_COE_FM_PIN_PCIE3_MASK \ + (0x1 << HPIPE_TX_TRAIN_COE_FM_PIN_PCIE3_OFFSET) + +#define HPIPE_CDR_CONTROL_REG 0x418 +#define HPIPE_CDR_RX_MAX_DFE_ADAPT_0_OFFSET 14 +#define HPIPE_CDR_RX_MAX_DFE_ADAPT_0_MASK \ + (0x3 << HPIPE_CDR_RX_MAX_DFE_ADAPT_0_OFFSET) +#define HPIPE_CDR_RX_MAX_DFE_ADAPT_1_OFFSET 12 +#define HPIPE_CDR_RX_MAX_DFE_ADAPT_1_MASK \ + (0x3 << HPIPE_CDR_RX_MAX_DFE_ADAPT_1_OFFSET) +#define HPIPE_CDR_MAX_DFE_ADAPT_0_OFFSET 9 +#define HPIPE_CDR_MAX_DFE_ADAPT_0_MASK \ + (0x7 << HPIPE_CDR_MAX_DFE_ADAPT_0_OFFSET) +#define HPIPE_CDR_MAX_DFE_ADAPT_1_OFFSET 6 +#define HPIPE_CDR_MAX_DFE_ADAPT_1_MASK \ + (0x7 << HPIPE_CDR_MAX_DFE_ADAPT_1_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_11_REG 0x438 +#define HPIPE_TX_STATUS_CHECK_MODE_OFFSET 6 +#define HPIPE_TX_TX_STATUS_CHECK_MODE_MASK \ + (0x1 << HPIPE_TX_STATUS_CHECK_MODE_OFFSET) +#define HPIPE_TX_NUM_OF_PRESET_OFFSET 10 +#define HPIPE_TX_NUM_OF_PRESET_MASK \ + (0x7 << HPIPE_TX_NUM_OF_PRESET_OFFSET) +#define HPIPE_TX_SWEEP_PRESET_EN_OFFSET 15 +#define HPIPE_TX_SWEEP_PRESET_EN_MASK \ + (0x1 << HPIPE_TX_SWEEP_PRESET_EN_OFFSET) +#define HPIPE_G2_SETTINGS_4_REG 0x44C +#define HPIPE_G2_DFE_RES_OFFSET 8 +#define HPIPE_G2_DFE_RES_MASK (0x3 << HPIPE_G2_DFE_RES_OFFSET) + +#define HPIPE_G3_SETTING_3_REG 0x450 +#define HPIPE_G3_FFE_CAP_SEL_OFFSET 0 +#define HPIPE_G3_FFE_CAP_SEL_MASK \ + (0xf << HPIPE_G3_FFE_CAP_SEL_OFFSET) +#define HPIPE_G3_FFE_RES_SEL_OFFSET 4 +#define HPIPE_G3_FFE_RES_SEL_MASK \ + (0x7 << HPIPE_G3_FFE_RES_SEL_OFFSET) +#define HPIPE_G3_FFE_SETTING_FORCE_OFFSET 7 +#define HPIPE_G3_FFE_SETTING_FORCE_MASK \ + (0x1 << HPIPE_G3_FFE_SETTING_FORCE_OFFSET) +#define HPIPE_G3_FFE_DEG_RES_LEVEL_OFFSET 12 +#define HPIPE_G3_FFE_DEG_RES_LEVEL_MASK \ + (0x3 << HPIPE_G3_FFE_DEG_RES_LEVEL_OFFSET) +#define HPIPE_G3_FFE_LOAD_RES_LEVEL_OFFSET 14 +#define HPIPE_G3_FFE_LOAD_RES_LEVEL_MASK \ + (0x3 << HPIPE_G3_FFE_LOAD_RES_LEVEL_OFFSET) + +#define HPIPE_G3_SETTING_4_REG 0x454 +#define HPIPE_G3_DFE_RES_OFFSET 8 +#define HPIPE_G3_DFE_RES_MASK (0x3 << HPIPE_G3_DFE_RES_OFFSET) + +#define HPIPE_DFE_CONTROL_REG 0x470 +#define HPIPE_DFE_TX_MAX_DFE_ADAPT_OFFSET 14 +#define HPIPE_DFE_TX_MAX_DFE_ADAPT_MASK \ + (0x3 << HPIPE_DFE_TX_MAX_DFE_ADAPT_OFFSET) + +#define HPIPE_DFE_CTRL_28_REG 0x49C +#define HPIPE_DFE_CTRL_28_PIPE4_OFFSET 7 +#define HPIPE_DFE_CTRL_28_PIPE4_MASK \ + (0x1 << HPIPE_DFE_CTRL_28_PIPE4_OFFSET) + +#define HPIPE_G3_SETTING_5_REG 0x548 +#define HPIPE_G3_SETTING_5_G3_ICP_OFFSET 0 +#define HPIPE_G3_SETTING_5_G3_ICP_MASK \ + (0xf << HPIPE_G3_SETTING_5_G3_ICP_OFFSET) + +#define HPIPE_LANE_STATUS1_REG 0x60C +#define HPIPE_LANE_STATUS1_PCLK_EN_OFFSET 0 +#define HPIPE_LANE_STATUS1_PCLK_EN_MASK \ + (0x1 << HPIPE_LANE_STATUS1_PCLK_EN_OFFSET) + +#define HPIPE_LANE_CFG4_REG 0x620 +#define HPIPE_LANE_CFG4_DFE_EN_SEL_OFFSET 3 +#define HPIPE_LANE_CFG4_DFE_EN_SEL_MASK \ + (0x1 << HPIPE_LANE_CFG4_DFE_EN_SEL_OFFSET) + +#define HPIPE_LANE_EQU_CONFIG_0_REG 0x69C +#define HPIPE_CFG_EQ_FS_OFFSET 0 +#define HPIPE_CFG_EQ_FS_MASK (0x3f << HPIPE_CFG_EQ_FS_OFFSET) +#define HPIPE_CFG_EQ_LF_OFFSET 6 +#define HPIPE_CFG_EQ_LF_MASK (0x3f << HPIPE_CFG_EQ_LF_OFFSET) +#define HPIPE_CFG_PHY_RC_EP_OFFSET 12 +#define HPIPE_CFG_PHY_RC_EP_MASK \ + (0x1 << HPIPE_CFG_PHY_RC_EP_OFFSET) + +#define HPIPE_LANE_EQ_CFG1_REG 0x6a0 +#define HPIPE_CFG_UPDATE_POLARITY_OFFSET 12 +#define HPIPE_CFG_UPDATE_POLARITY_MASK \ + (0x1 << HPIPE_CFG_UPDATE_POLARITY_OFFSET) + +#define HPIPE_LANE_EQ_CFG2_REG 0x6a4 +#define HPIPE_CFG_EQ_BUNDLE_DIS_OFFSET 14 +#define HPIPE_CFG_EQ_BUNDLE_DIS_MASK \ + (0x1 << HPIPE_CFG_EQ_BUNDLE_DIS_OFFSET) + +#define HPIPE_LANE_PRESET_CFG0_REG 0x6a8 +#define HPIPE_CFG_CURSOR_PRESET0_OFFSET 0 +#define HPIPE_CFG_CURSOR_PRESET0_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET0_OFFSET) +#define HPIPE_CFG_CURSOR_PRESET1_OFFSET 6 +#define HPIPE_CFG_CURSOR_PRESET1_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET1_OFFSET) + +#define HPIPE_LANE_PRESET_CFG1_REG 0x6ac +#define HPIPE_CFG_CURSOR_PRESET2_OFFSET 0 +#define HPIPE_CFG_CURSOR_PRESET2_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET2_OFFSET) +#define HPIPE_CFG_CURSOR_PRESET3_OFFSET 6 +#define HPIPE_CFG_CURSOR_PRESET3_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET3_OFFSET) + +#define HPIPE_LANE_PRESET_CFG2_REG 0x6b0 +#define HPIPE_CFG_CURSOR_PRESET4_OFFSET 0 +#define HPIPE_CFG_CURSOR_PRESET4_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET4_OFFSET) +#define HPIPE_CFG_CURSOR_PRESET5_OFFSET 6 +#define HPIPE_CFG_CURSOR_PRESET5_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET5_OFFSET) + +#define HPIPE_LANE_PRESET_CFG3_REG 0x6b4 +#define HPIPE_CFG_CURSOR_PRESET6_OFFSET 0 +#define HPIPE_CFG_CURSOR_PRESET6_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET6_OFFSET) +#define HPIPE_CFG_CURSOR_PRESET7_OFFSET 6 +#define HPIPE_CFG_CURSOR_PRESET7_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET7_OFFSET) + +#define HPIPE_LANE_PRESET_CFG4_REG 0x6b8 +#define HPIPE_CFG_CURSOR_PRESET8_OFFSET 0 +#define HPIPE_CFG_CURSOR_PRESET8_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET8_OFFSET) +#define HPIPE_CFG_CURSOR_PRESET9_OFFSET 6 +#define HPIPE_CFG_CURSOR_PRESET9_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET9_OFFSET) + +#define HPIPE_LANE_PRESET_CFG5_REG 0x6bc +#define HPIPE_CFG_CURSOR_PRESET10_OFFSET 0 +#define HPIPE_CFG_CURSOR_PRESET10_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET10_OFFSET) +#define HPIPE_CFG_CURSOR_PRESET11_OFFSET 6 +#define HPIPE_CFG_CURSOR_PRESET11_MASK \ + (0x3f << HPIPE_CFG_CURSOR_PRESET11_OFFSET) + +#define HPIPE_LANE_PRESET_CFG6_REG 0x6c0 +#define HPIPE_CFG_PRE_CURSOR_PRESET0_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET0_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET0_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET0_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET0_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET0_OFFSET) + +#define HPIPE_LANE_PRESET_CFG7_REG 0x6c4 +#define HPIPE_CFG_PRE_CURSOR_PRESET1_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET1_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET1_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET1_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET1_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET1_OFFSET) + +#define HPIPE_LANE_PRESET_CFG8_REG 0x6c8 +#define HPIPE_CFG_PRE_CURSOR_PRESET2_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET2_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET2_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET2_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET2_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET2_OFFSET) + +#define HPIPE_LANE_PRESET_CFG9_REG 0x6cc +#define HPIPE_CFG_PRE_CURSOR_PRESET3_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET3_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET3_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET3_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET3_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET3_OFFSET) + +#define HPIPE_LANE_PRESET_CFG10_REG 0x6d0 +#define HPIPE_CFG_PRE_CURSOR_PRESET4_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET4_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET4_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET4_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET4_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET4_OFFSET) + +#define HPIPE_LANE_PRESET_CFG11_REG 0x6d4 +#define HPIPE_CFG_PRE_CURSOR_PRESET5_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET5_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET5_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET5_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET5_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET5_OFFSET) + +#define HPIPE_LANE_PRESET_CFG12_REG 0x6d8 +#define HPIPE_CFG_PRE_CURSOR_PRESET6_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET6_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET6_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET6_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET6_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET6_OFFSET) + +#define HPIPE_LANE_PRESET_CFG13_REG 0x6dc +#define HPIPE_CFG_PRE_CURSOR_PRESET7_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET7_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET7_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET7_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET7_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET7_OFFSET) + +#define HPIPE_LANE_PRESET_CFG14_REG 0x6e0 +#define HPIPE_CFG_PRE_CURSOR_PRESET8_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET8_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET8_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET8_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET8_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET8_OFFSET) + +#define HPIPE_LANE_PRESET_CFG15_REG 0x6e4 +#define HPIPE_CFG_PRE_CURSOR_PRESET9_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET9_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET9_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET9_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET9_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET9_OFFSET) + +#define HPIPE_LANE_PRESET_CFG16_REG 0x6e8 +#define HPIPE_CFG_PRE_CURSOR_PRESET10_OFFSET 0 +#define HPIPE_CFG_PRE_CURSOR_PRESET10_MASK \ + (0x3f << HPIPE_CFG_PRE_CURSOR_PRESET10_OFFSET) +#define HPIPE_CFG_POST_CURSOR_PRESET10_OFFSET 6 +#define HPIPE_CFG_POST_CURSOR_PRESET10_MASK \ + (0x3f << HPIPE_CFG_POST_CURSOR_PRESET10_OFFSET) + +#define HPIPE_LANE_EQ_REMOTE_SETTING_REG 0x6f8 +#define HPIPE_LANE_CFG_FOM_DIRN_OVERRIDE_OFFSET 0 +#define HPIPE_LANE_CFG_FOM_DIRN_OVERRIDE_MASK \ + (0x1 << HPIPE_LANE_CFG_FOM_DIRN_OVERRIDE_OFFSET) +#define HPIPE_LANE_CFG_FOM_ONLY_MODE_OFFFSET 1 +#define HPIPE_LANE_CFG_FOM_ONLY_MODE_MASK \ + (0x1 << HPIPE_LANE_CFG_FOM_ONLY_MODE_OFFFSET) +#define HPIPE_LANE_CFG_FOM_PRESET_VECTOR_OFFSET 2 +#define HPIPE_LANE_CFG_FOM_PRESET_VECTOR_MASK \ + (0xf << HPIPE_LANE_CFG_FOM_PRESET_VECTOR_OFFSET) + +#define HPIPE_RST_CLK_CTRL_REG 0x704 +#define HPIPE_RST_CLK_CTRL_PIPE_RST_OFFSET 0 +#define HPIPE_RST_CLK_CTRL_PIPE_RST_MASK \ + (0x1 << HPIPE_RST_CLK_CTRL_PIPE_RST_OFFSET) +#define HPIPE_RST_CLK_CTRL_FIXED_PCLK_OFFSET 2 +#define HPIPE_RST_CLK_CTRL_FIXED_PCLK_MASK \ + (0x1 << HPIPE_RST_CLK_CTRL_FIXED_PCLK_OFFSET) +#define HPIPE_RST_CLK_CTRL_PIPE_WIDTH_OFFSET 3 +#define HPIPE_RST_CLK_CTRL_PIPE_WIDTH_MASK \ + (0x1 << HPIPE_RST_CLK_CTRL_PIPE_WIDTH_OFFSET) +#define HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_OFFSET 9 +#define HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_MASK \ + (0x1 << HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_OFFSET) + +#define HPIPE_CLK_SRC_LO_REG 0x70c +#define HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SEL_OFFSET 1 +#define HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SEL_MASK \ + (0x1 << HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SEL_OFFSET) +#define HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SCALE_OFFSET 2 +#define HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SCALE_MASK \ + (0x3 << HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SCALE_OFFSET) +#define HPIPE_CLK_SRC_LO_PLL_RDY_DL_OFFSET 5 +#define HPIPE_CLK_SRC_LO_PLL_RDY_DL_MASK \ + (0x7 << HPIPE_CLK_SRC_LO_PLL_RDY_DL_OFFSET) + +#define HPIPE_CLK_SRC_HI_REG 0x710 +#define HPIPE_CLK_SRC_HI_LANE_STRT_OFFSET 0 +#define HPIPE_CLK_SRC_HI_LANE_STRT_MASK \ + (0x1 << HPIPE_CLK_SRC_HI_LANE_STRT_OFFSET) +#define HPIPE_CLK_SRC_HI_LANE_BREAK_OFFSET 1 +#define HPIPE_CLK_SRC_HI_LANE_BREAK_MASK \ + (0x1 << HPIPE_CLK_SRC_HI_LANE_BREAK_OFFSET) +#define HPIPE_CLK_SRC_HI_LANE_MASTER_OFFSET 2 +#define HPIPE_CLK_SRC_HI_LANE_MASTER_MASK \ + (0x1 << HPIPE_CLK_SRC_HI_LANE_MASTER_OFFSET) +#define HPIPE_CLK_SRC_HI_MODE_PIPE_OFFSET 7 +#define HPIPE_CLK_SRC_HI_MODE_PIPE_MASK \ + (0x1 << HPIPE_CLK_SRC_HI_MODE_PIPE_OFFSET) + +#define HPIPE_GLOBAL_PM_CTRL 0x740 +#define HPIPE_GLOBAL_PM_RXDLOZ_WAIT_OFFSET 0 +#define HPIPE_GLOBAL_PM_RXDLOZ_WAIT_MASK \ + (0xFF << HPIPE_GLOBAL_PM_RXDLOZ_WAIT_OFFSET) + +#endif /* _COMPHY_H_ */ + diff --git a/drivers/marvell/comphy/comphy-cp110.h b/drivers/marvell/comphy/comphy-cp110.h new file mode 100644 index 00000000..925abb5e --- /dev/null +++ b/drivers/marvell/comphy/comphy-cp110.h @@ -0,0 +1,775 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* Marvell CP110 SoC COMPHY unit driver */ + +#ifndef _PHY_COMPHY_CP110_H +#define _PHY_COMPHY_CP110_H + +#define SD_ADDR(base, lane) (base + 0x1000 * lane) +#define HPIPE_ADDR(base, lane) (SD_ADDR(base, lane) + 0x800) +#define COMPHY_ADDR(base, lane) (base + 0x28 * lane) + +#define MAX_NUM_OF_FFE 8 +#define RX_TRAINING_TIMEOUT 500 + +/* Comphy registers */ +#define COMMON_PHY_CFG1_REG 0x0 +#define COMMON_PHY_CFG1_PWR_UP_OFFSET 1 +#define COMMON_PHY_CFG1_PWR_UP_MASK \ + (0x1 << COMMON_PHY_CFG1_PWR_UP_OFFSET) +#define COMMON_PHY_CFG1_PIPE_SELECT_OFFSET 2 +#define COMMON_PHY_CFG1_PIPE_SELECT_MASK \ + (0x1 << COMMON_PHY_CFG1_PIPE_SELECT_OFFSET) +#define COMMON_PHY_CFG1_CORE_RSTN_OFFSET 13 +#define COMMON_PHY_CFG1_CORE_RSTN_MASK \ + (0x1 << COMMON_PHY_CFG1_CORE_RSTN_OFFSET) +#define COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET 14 +#define COMMON_PHY_CFG1_PWR_ON_RESET_MASK \ + (0x1 << COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET) +#define COMMON_PHY_PHY_MODE_OFFSET 15 +#define COMMON_PHY_PHY_MODE_MASK \ + (0x1 << COMMON_PHY_PHY_MODE_OFFSET) + +#define COMMON_PHY_CFG6_REG 0x14 +#define COMMON_PHY_CFG6_IF_40_SEL_OFFSET 18 +#define COMMON_PHY_CFG6_IF_40_SEL_MASK \ + (0x1 << COMMON_PHY_CFG6_IF_40_SEL_OFFSET) + +#define COMMON_PHY_CFG6_REG 0x14 +#define COMMON_PHY_CFG6_IF_40_SEL_OFFSET 18 +#define COMMON_PHY_CFG6_IF_40_SEL_MASK \ + (0x1 << COMMON_PHY_CFG6_IF_40_SEL_OFFSET) + +#define COMMON_SELECTOR_PHY_REG_OFFSET 0x140 +#define COMMON_SELECTOR_PIPE_REG_OFFSET 0x144 +#define COMMON_SELECTOR_COMPHY_MASK 0xf +#define COMMON_SELECTOR_COMPHYN_FIELD_WIDTH 4 +#define COMMON_SELECTOR_COMPHYN_SATA 0x4 +#define COMMON_SELECTOR_PIPE_COMPHY_PCIE 0x4 +#define COMMON_SELECTOR_PIPE_COMPHY_USBH 0x1 +#define COMMON_SELECTOR_PIPE_COMPHY_USBD 0x2 + +/* SGMII/HS-SGMII/SFI/RXAUI */ +#define COMMON_SELECTOR_COMPHY0_1_2_NETWORK 0x1 +#define COMMON_SELECTOR_COMPHY3_RXAUI 0x1 +#define COMMON_SELECTOR_COMPHY3_SGMII 0x2 +#define COMMON_SELECTOR_COMPHY4_PORT1 0x1 +#define COMMON_SELECTOR_COMPHY4_ALL_OTHERS 0x2 +#define COMMON_SELECTOR_COMPHY5_RXAUI 0x2 +#define COMMON_SELECTOR_COMPHY5_SGMII 0x1 + +#define COMMON_PHY_SD_CTRL1 0x148 +#define COMMON_PHY_SD_CTRL1_COMPHY_0_PORT_OFFSET 0 +#define COMMON_PHY_SD_CTRL1_COMPHY_1_PORT_OFFSET 4 +#define COMMON_PHY_SD_CTRL1_COMPHY_2_PORT_OFFSET 8 +#define COMMON_PHY_SD_CTRL1_COMPHY_3_PORT_OFFSET 12 +#define COMMON_PHY_SD_CTRL1_COMPHY_0_3_PORT_MASK 0xFFFF +#define COMMON_PHY_SD_CTRL1_COMPHY_0_1_PORT_MASK 0xFF +#define COMMON_PHY_SD_CTRL1_PCIE_X4_EN_OFFSET 24 +#define COMMON_PHY_SD_CTRL1_PCIE_X4_EN_MASK \ + (0x1 << COMMON_PHY_SD_CTRL1_PCIE_X4_EN_OFFSET) +#define COMMON_PHY_SD_CTRL1_PCIE_X2_EN_OFFSET 25 +#define COMMON_PHY_SD_CTRL1_PCIE_X2_EN_MASK \ + (0x1 << COMMON_PHY_SD_CTRL1_PCIE_X2_EN_OFFSET) +#define COMMON_PHY_SD_CTRL1_RXAUI1_OFFSET 26 +#define COMMON_PHY_SD_CTRL1_RXAUI1_MASK \ + (0x1 << COMMON_PHY_SD_CTRL1_RXAUI1_OFFSET) +#define COMMON_PHY_SD_CTRL1_RXAUI0_OFFSET 27 +#define COMMON_PHY_SD_CTRL1_RXAUI0_MASK \ + (0x1 << COMMON_PHY_SD_CTRL1_RXAUI0_OFFSET) + +/* DFX register */ +#define DFX_BASE (0x400000) +#define DFX_DEV_GEN_CTRL12_REG (0x280) +#define DFX_DEV_GEN_PCIE_CLK_SRC_MUX (0x3) +#define DFX_DEV_GEN_PCIE_CLK_SRC_OFFSET 7 +#define DFX_DEV_GEN_PCIE_CLK_SRC_MASK \ + (0x3 << DFX_DEV_GEN_PCIE_CLK_SRC_OFFSET) + +/* SerDes IP registers */ +#define SD_EXTERNAL_CONFIG0_REG 0 +#define SD_EXTERNAL_CONFIG0_SD_PU_PLL_OFFSET 1 +#define SD_EXTERNAL_CONFIG0_SD_PU_PLL_MASK \ + (1 << SD_EXTERNAL_CONFIG0_SD_PU_PLL_OFFSET) +#define SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_OFFSET 3 +#define SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_MASK \ + (0xf << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_OFFSET) +#define SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_OFFSET 7 +#define SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_MASK \ + (0xf << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_OFFSET) +#define SD_EXTERNAL_CONFIG0_SD_PU_RX_OFFSET 11 +#define SD_EXTERNAL_CONFIG0_SD_PU_RX_MASK \ + (1 << SD_EXTERNAL_CONFIG0_SD_PU_RX_OFFSET) +#define SD_EXTERNAL_CONFIG0_SD_PU_TX_OFFSET 12 +#define SD_EXTERNAL_CONFIG0_SD_PU_TX_MASK \ + (1 << SD_EXTERNAL_CONFIG0_SD_PU_TX_OFFSET) +#define SD_EXTERNAL_CONFIG0_HALF_BUS_MODE_OFFSET 14 +#define SD_EXTERNAL_CONFIG0_HALF_BUS_MODE_MASK \ + (1 << SD_EXTERNAL_CONFIG0_HALF_BUS_MODE_OFFSET) +#define SD_EXTERNAL_CONFIG0_MEDIA_MODE_OFFSET 15 +#define SD_EXTERNAL_CONFIG0_MEDIA_MODE_MASK \ + (0x1 << SD_EXTERNAL_CONFIG0_MEDIA_MODE_OFFSET) + +#define SD_EXTERNAL_CONFIG1_REG 0x4 +#define SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET 3 +#define SD_EXTERNAL_CONFIG1_RESET_IN_MASK \ + (0x1 << SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET) +#define SD_EXTERNAL_CONFIG1_RX_INIT_OFFSET 4 +#define SD_EXTERNAL_CONFIG1_RX_INIT_MASK \ + (0x1 << SD_EXTERNAL_CONFIG1_RX_INIT_OFFSET) +#define SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET 5 +#define SD_EXTERNAL_CONFIG1_RESET_CORE_MASK \ + (0x1 << SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET) +#define SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET 6 +#define SD_EXTERNAL_CONFIG1_RF_RESET_IN_MASK \ + (0x1 << SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET) + +#define SD_EXTERNAL_CONFIG2_REG 0x8 +#define SD_EXTERNAL_CONFIG2_PIN_DFE_EN_OFFSET 4 +#define SD_EXTERNAL_CONFIG2_PIN_DFE_EN_MASK \ + (0x1 << SD_EXTERNAL_CONFIG2_PIN_DFE_EN_OFFSET) +#define SD_EXTERNAL_CONFIG2_SSC_ENABLE_OFFSET 7 +#define SD_EXTERNAL_CONFIG2_SSC_ENABLE_MASK \ + (0x1 << SD_EXTERNAL_CONFIG2_SSC_ENABLE_OFFSET) + +#define SD_EXTERNAL_STATUS_REG 0xc +#define SD_EXTERNAL_STATUS_START_RX_TRAINING_OFFSET 7 +#define SD_EXTERNAL_STATUS_START_RX_TRAINING_MASK \ + (1 << SD_EXTERNAL_STATUS_START_RX_TRAINING_OFFSET) + +#define SD_EXTERNAL_STATUS0_REG 0x18 +#define SD_EXTERNAL_STATUS0_PLL_TX_OFFSET 2 +#define SD_EXTERNAL_STATUS0_PLL_TX_MASK \ + (0x1 << SD_EXTERNAL_STATUS0_PLL_TX_OFFSET) +#define SD_EXTERNAL_STATUS0_PLL_RX_OFFSET 3 +#define SD_EXTERNAL_STATUS0_PLL_RX_MASK \ + (0x1 << SD_EXTERNAL_STATUS0_PLL_RX_OFFSET) +#define SD_EXTERNAL_STATUS0_RX_INIT_OFFSET 4 +#define SD_EXTERNAL_STATUS0_RX_INIT_MASK \ + (0x1 << SD_EXTERNAL_STATUS0_RX_INIT_OFFSET) + +#define SD_EXTERNAL_STATAUS1_REG 0x1c +#define SD_EXTERNAL_STATAUS1_REG_RX_TRAIN_COMP_OFFSET 0 +#define SD_EXTERNAL_STATAUS1_REG_RX_TRAIN_COMP_MASK \ + (1 << SD_EXTERNAL_STATAUS1_REG_RX_TRAIN_COMP_OFFSET) +#define SD_EXTERNAL_STATAUS1_REG_RX_TRAIN_FAILED_OFFSET 1 +#define SD_EXTERNAL_STATAUS1_REG_RX_TRAIN_FAILED_MASK \ + (1 << SD_EXTERNAL_STATAUS1_REG_RX_TRAIN_FAILED_OFFSET) + +/* HPIPE registers */ +#define HPIPE_PWR_PLL_REG 0x4 +#define HPIPE_PWR_PLL_REF_FREQ_OFFSET 0 +#define HPIPE_PWR_PLL_REF_FREQ_MASK \ + (0x1f << HPIPE_PWR_PLL_REF_FREQ_OFFSET) +#define HPIPE_PWR_PLL_PHY_MODE_OFFSET 5 +#define HPIPE_PWR_PLL_PHY_MODE_MASK \ + (0x7 << HPIPE_PWR_PLL_PHY_MODE_OFFSET) + +#define HPIPE_CAL_REG1_REG 0xc +#define HPIPE_CAL_REG_1_EXT_TXIMP_OFFSET 10 +#define HPIPE_CAL_REG_1_EXT_TXIMP_MASK \ + (0x1f << HPIPE_CAL_REG_1_EXT_TXIMP_OFFSET) +#define HPIPE_CAL_REG_1_EXT_TXIMP_EN_OFFSET 15 +#define HPIPE_CAL_REG_1_EXT_TXIMP_EN_MASK \ + (0x1 << HPIPE_CAL_REG_1_EXT_TXIMP_EN_OFFSET) + +#define HPIPE_SQUELCH_FFE_SETTING_REG 0x18 +#define HPIPE_SQUELCH_THRESH_IN_OFFSET 8 +#define HPIPE_SQUELCH_THRESH_IN_MASK \ + (0xf << HPIPE_SQUELCH_THRESH_IN_OFFSET) +#define HPIPE_SQUELCH_DETECTED_OFFSET 14 +#define HPIPE_SQUELCH_DETECTED_MASK \ + (0x1 << HPIPE_SQUELCH_DETECTED_OFFSET) + +#define HPIPE_DFE_REG0 0x1c +#define HPIPE_DFE_RES_FORCE_OFFSET 15 +#define HPIPE_DFE_RES_FORCE_MASK \ + (0x1 << HPIPE_DFE_RES_FORCE_OFFSET) + +#define HPIPE_DFE_F3_F5_REG 0x28 +#define HPIPE_DFE_F3_F5_DFE_EN_OFFSET 14 +#define HPIPE_DFE_F3_F5_DFE_EN_MASK \ + (0x1 << HPIPE_DFE_F3_F5_DFE_EN_OFFSET) +#define HPIPE_DFE_F3_F5_DFE_CTRL_OFFSET 15 +#define HPIPE_DFE_F3_F5_DFE_CTRL_MASK \ + (0x1 << HPIPE_DFE_F3_F5_DFE_CTRL_OFFSET) + +#define HPIPE_G1_SET_0_REG 0x34 +#define HPIPE_G1_SET_0_G1_TX_AMP_OFFSET 1 +#define HPIPE_G1_SET_0_G1_TX_AMP_MASK \ + (0x1f << HPIPE_G1_SET_0_G1_TX_AMP_OFFSET) +#define HPIPE_G1_SET_0_G1_TX_AMP_ADJ_OFFSET 6 +#define HPIPE_G1_SET_0_G1_TX_AMP_ADJ_MASK \ + (0x1 << HPIPE_G1_SET_0_G1_TX_AMP_ADJ_OFFSET) +#define HPIPE_G1_SET_0_G1_TX_EMPH1_OFFSET 7 +#define HPIPE_G1_SET_0_G1_TX_EMPH1_MASK \ + (0xf << HPIPE_G1_SET_0_G1_TX_EMPH1_OFFSET) +#define HPIPE_G1_SET_0_G1_TX_EMPH1_EN_OFFSET 11 +#define HPIPE_G1_SET_0_G1_TX_EMPH1_EN_MASK \ + (0x1 << HPIPE_G1_SET_0_G1_TX_EMPH1_EN_OFFSET) + +#define HPIPE_G1_SET_1_REG 0x38 +#define HPIPE_G1_SET_1_G1_RX_SELMUPI_OFFSET 0 +#define HPIPE_G1_SET_1_G1_RX_SELMUPI_MASK \ + (0x7 << HPIPE_G1_SET_1_G1_RX_SELMUPI_OFFSET) +#define HPIPE_G1_SET_1_G1_RX_SELMUPP_OFFSET 3 +#define HPIPE_G1_SET_1_G1_RX_SELMUPP_MASK \ + (0x7 << HPIPE_G1_SET_1_G1_RX_SELMUPP_OFFSET) +#define HPIPE_G1_SET_1_G1_RX_SELMUFI_OFFSET 6 +#define HPIPE_G1_SET_1_G1_RX_SELMUFI_MASK \ + (0x3 << HPIPE_G1_SET_1_G1_RX_SELMUFI_OFFSET) +#define HPIPE_G1_SET_1_G1_RX_SELMUFF_OFFSET 8 +#define HPIPE_G1_SET_1_G1_RX_SELMUFF_MASK \ + (0x3 << HPIPE_G1_SET_1_G1_RX_SELMUFF_OFFSET) +#define HPIPE_G1_SET_1_G1_RX_DFE_EN_OFFSET 10 +#define HPIPE_G1_SET_1_G1_RX_DFE_EN_MASK \ + (0x1 << HPIPE_G1_SET_1_G1_RX_DFE_EN_OFFSET) +#define HPIPE_G1_SET_1_G1_RX_DIGCK_DIV_OFFSET 11 +#define HPIPE_G1_SET_1_G1_RX_DIGCK_DIV_MASK \ + (0x3 << HPIPE_G1_SET_1_G1_RX_DIGCK_DIV_OFFSET) + +#define HPIPE_G2_SET_0_REG 0x3c +#define HPIPE_G2_SET_0_G2_TX_AMP_OFFSET 1 +#define HPIPE_G2_SET_0_G2_TX_AMP_MASK \ + (0x1f << HPIPE_G2_SET_0_G2_TX_AMP_OFFSET) +#define HPIPE_G2_SET_0_G2_TX_AMP_ADJ_OFFSET 6 +#define HPIPE_G2_SET_0_G2_TX_AMP_ADJ_MASK \ + (0x1 << HPIPE_G2_SET_0_G2_TX_AMP_ADJ_OFFSET) +#define HPIPE_G2_SET_0_G2_TX_EMPH1_OFFSET 7 +#define HPIPE_G2_SET_0_G2_TX_EMPH1_MASK \ + (0xf << HPIPE_G2_SET_0_G2_TX_EMPH1_OFFSET) +#define HPIPE_G2_SET_0_G2_TX_EMPH1_EN_OFFSET 11 +#define HPIPE_G2_SET_0_G2_TX_EMPH1_EN_MASK \ + (0x1 << HPIPE_G2_SET_0_G2_TX_EMPH1_EN_OFFSET) + +#define HPIPE_G2_SET_1_REG 0x40 +#define HPIPE_G2_SET_1_G2_RX_SELMUPI_OFFSET 0 +#define HPIPE_G2_SET_1_G2_RX_SELMUPI_MASK \ + (0x7 << HPIPE_G2_SET_1_G2_RX_SELMUPI_OFFSET) +#define HPIPE_G2_SET_1_G2_RX_SELMUPP_OFFSET 3 +#define HPIPE_G2_SET_1_G2_RX_SELMUPP_MASK \ + (0x7 << HPIPE_G2_SET_1_G2_RX_SELMUPP_OFFSET) +#define HPIPE_G2_SET_1_G2_RX_SELMUFI_OFFSET 6 +#define HPIPE_G2_SET_1_G2_RX_SELMUFI_MASK \ + (0x3 << HPIPE_G2_SET_1_G2_RX_SELMUFI_OFFSET) +#define HPIPE_G2_SET_1_G2_RX_SELMUFF_OFFSET 8 +#define HPIPE_G2_SET_1_G2_RX_SELMUFF_MASK \ + (0x3 << HPIPE_G2_SET_1_G2_RX_SELMUFF_OFFSET) +#define HPIPE_G2_SET_1_G2_RX_DFE_EN_OFFSET 10 +#define HPIPE_G2_SET_1_G2_RX_DFE_EN_MASK \ + (0x1 << HPIPE_G2_SET_1_G2_RX_DFE_EN_OFFSET) +#define HPIPE_G2_SET_1_G2_RX_DIGCK_DIV_OFFSET 11 +#define HPIPE_G2_SET_1_G2_RX_DIGCK_DIV_MASK \ + (0x3 << HPIPE_G2_SET_1_G2_RX_DIGCK_DIV_OFFSET) + +#define HPIPE_G3_SET_0_REG 0x44 +#define HPIPE_G3_SET_0_G3_TX_AMP_OFFSET 1 +#define HPIPE_G3_SET_0_G3_TX_AMP_MASK \ + (0x1f << HPIPE_G3_SET_0_G3_TX_AMP_OFFSET) +#define HPIPE_G3_SET_0_G3_TX_AMP_ADJ_OFFSET 6 +#define HPIPE_G3_SET_0_G3_TX_AMP_ADJ_MASK \ + (0x1 << HPIPE_G3_SET_0_G3_TX_AMP_ADJ_OFFSET) +#define HPIPE_G3_SET_0_G3_TX_EMPH1_OFFSET 7 +#define HPIPE_G3_SET_0_G3_TX_EMPH1_MASK \ + (0xf << HPIPE_G3_SET_0_G3_TX_EMPH1_OFFSET) +#define HPIPE_G3_SET_0_G3_TX_EMPH1_EN_OFFSET 11 +#define HPIPE_G3_SET_0_G3_TX_EMPH1_EN_MASK \ + (0x1 << HPIPE_G3_SET_0_G3_TX_EMPH1_EN_OFFSET) +#define HPIPE_G3_SET_0_G3_TX_SLEW_RATE_SEL_OFFSET 12 +#define HPIPE_G3_SET_0_G3_TX_SLEW_RATE_SEL_MASK \ + (0x7 << HPIPE_G3_SET_0_G3_TX_SLEW_RATE_SEL_OFFSET) +#define HPIPE_G3_SET_0_G3_TX_SLEW_CTRL_EN_OFFSET 15 +#define HPIPE_G3_SET_0_G3_TX_SLEW_CTRL_EN_MASK \ + (0x1 << HPIPE_G3_SET_0_G3_TX_SLEW_CTRL_EN_OFFSET) + +#define HPIPE_G3_SET_1_REG 0x48 +#define HPIPE_G3_SET_1_G3_RX_SELMUPI_OFFSET 0 +#define HPIPE_G3_SET_1_G3_RX_SELMUPI_MASK \ + (0x7 << HPIPE_G3_SET_1_G3_RX_SELMUPI_OFFSET) +#define HPIPE_G3_SET_1_G3_RX_SELMUPF_OFFSET 3 +#define HPIPE_G3_SET_1_G3_RX_SELMUPF_MASK \ + (0x7 << HPIPE_G3_SET_1_G3_RX_SELMUPF_OFFSET) +#define HPIPE_G3_SET_1_G3_RX_SELMUFI_OFFSET 6 +#define HPIPE_G3_SET_1_G3_RX_SELMUFI_MASK \ + (0x3 << HPIPE_G3_SET_1_G3_RX_SELMUFI_OFFSET) +#define HPIPE_G3_SET_1_G3_RX_SELMUFF_OFFSET 8 +#define HPIPE_G3_SET_1_G3_RX_SELMUFF_MASK \ + (0x3 << HPIPE_G3_SET_1_G3_RX_SELMUFF_OFFSET) +#define HPIPE_G3_SET_1_G3_RX_DFE_EN_OFFSET 10 +#define HPIPE_G3_SET_1_G3_RX_DFE_EN_MASK \ + (0x1 << HPIPE_G3_SET_1_G3_RX_DFE_EN_OFFSET) +#define HPIPE_G3_SET_1_G3_RX_DIGCK_DIV_OFFSET 11 +#define HPIPE_G3_SET_1_G3_RX_DIGCK_DIV_MASK \ + (0x3 << HPIPE_G3_SET_1_G3_RX_DIGCK_DIV_OFFSET) +#define HPIPE_G3_SET_1_G3_SAMPLER_INPAIRX2_EN_OFFSET 13 +#define HPIPE_G3_SET_1_G3_SAMPLER_INPAIRX2_EN_MASK \ + (0x1 << HPIPE_G3_SET_1_G3_SAMPLER_INPAIRX2_EN_OFFSET) + +#define HPIPE_PHY_TEST_CONTROL_REG 0x54 +#define HPIPE_PHY_TEST_PATTERN_SEL_OFFSET 4 +#define HPIPE_PHY_TEST_PATTERN_SEL_MASK \ + (0xf << HPIPE_PHY_TEST_PATTERN_SEL_OFFSET) +#define HPIPE_PHY_TEST_RESET_OFFSET 14 +#define HPIPE_PHY_TEST_RESET_MASK \ + (0x1 << HPIPE_PHY_TEST_RESET_OFFSET) +#define HPIPE_PHY_TEST_EN_OFFSET 15 +#define HPIPE_PHY_TEST_EN_MASK \ + (0x1 << HPIPE_PHY_TEST_EN_OFFSET) + +#define HPIPE_PHY_TEST_DATA_REG 0x6c +#define HPIPE_PHY_TEST_DATA_OFFSET 0 +#define HPIPE_PHY_TEST_DATA_MASK \ + (0xffff << HPIPE_PHY_TEST_DATA_OFFSET) + +#define HPIPE_LOOPBACK_REG 0x8c +#define HPIPE_LOOPBACK_SEL_OFFSET 1 +#define HPIPE_LOOPBACK_SEL_MASK \ + (0x7 << HPIPE_LOOPBACK_SEL_OFFSET) +#define HPIPE_CDR_LOCK_OFFSET 7 +#define HPIPE_CDR_LOCK_MASK \ + (0x1 << HPIPE_CDR_LOCK_OFFSET) +#define HPIPE_CDR_LOCK_DET_EN_OFFSET 8 +#define HPIPE_CDR_LOCK_DET_EN_MASK \ + (0x1 << HPIPE_CDR_LOCK_DET_EN_OFFSET) + +#define HPIPE_INTERFACE_REG 0x94 +#define HPIPE_INTERFACE_GEN_MAX_OFFSET 10 +#define HPIPE_INTERFACE_GEN_MAX_MASK \ + (0x3 << HPIPE_INTERFACE_GEN_MAX_OFFSET) +#define HPIPE_INTERFACE_DET_BYPASS_OFFSET 12 +#define HPIPE_INTERFACE_DET_BYPASS_MASK \ + (0x1 << HPIPE_INTERFACE_DET_BYPASS_OFFSET) +#define HPIPE_INTERFACE_LINK_TRAIN_OFFSET 14 +#define HPIPE_INTERFACE_LINK_TRAIN_MASK \ + (0x1 << HPIPE_INTERFACE_LINK_TRAIN_OFFSET) + +#define HPIPE_G1_SET_2_REG 0xf4 +#define HPIPE_G1_SET_2_G1_TX_EMPH0_OFFSET 0 +#define HPIPE_G1_SET_2_G1_TX_EMPH0_MASK \ + (0xf << HPIPE_G1_SET_2_G1_TX_EMPH0_OFFSET) +#define HPIPE_G1_SET_2_G1_TX_EMPH0_EN_OFFSET 4 +#define HPIPE_G1_SET_2_G1_TX_EMPH0_EN_MASK \ + (0x1 << HPIPE_G1_SET_2_G1_TX_EMPH0_EN_OFFSET) + +#define HPIPE_G2_SET_2_REG 0xf8 +#define HPIPE_G2_TX_SSC_AMP_OFFSET 9 +#define HPIPE_G2_TX_SSC_AMP_MASK \ + (0x7f << HPIPE_G2_TX_SSC_AMP_OFFSET) + +#define HPIPE_VDD_CAL_0_REG 0x108 +#define HPIPE_CAL_VDD_CONT_MODE_OFFSET 15 +#define HPIPE_CAL_VDD_CONT_MODE_MASK \ + (0x1 << HPIPE_CAL_VDD_CONT_MODE_OFFSET) + +#define HPIPE_VDD_CAL_CTRL_REG 0x114 +#define HPIPE_EXT_SELLV_RXSAMPL_OFFSET 5 +#define HPIPE_EXT_SELLV_RXSAMPL_MASK \ + (0x1f << HPIPE_EXT_SELLV_RXSAMPL_OFFSET) + +#define HPIPE_PCIE_REG0 0x120 +#define HPIPE_PCIE_IDLE_SYNC_OFFSET 12 +#define HPIPE_PCIE_IDLE_SYNC_MASK \ + (0x1 << HPIPE_PCIE_IDLE_SYNC_OFFSET) +#define HPIPE_PCIE_SEL_BITS_OFFSET 13 +#define HPIPE_PCIE_SEL_BITS_MASK \ + (0x3 << HPIPE_PCIE_SEL_BITS_OFFSET) + +#define HPIPE_LANE_ALIGN_REG 0x124 +#define HPIPE_LANE_ALIGN_OFF_OFFSET 12 +#define HPIPE_LANE_ALIGN_OFF_MASK \ + (0x1 << HPIPE_LANE_ALIGN_OFF_OFFSET) + +#define HPIPE_MISC_REG 0x13C +#define HPIPE_MISC_CLK100M_125M_OFFSET 4 +#define HPIPE_MISC_CLK100M_125M_MASK \ + (0x1 << HPIPE_MISC_CLK100M_125M_OFFSET) +#define HPIPE_MISC_ICP_FORCE_OFFSET 5 +#define HPIPE_MISC_ICP_FORCE_MASK \ + (0x1 << HPIPE_MISC_ICP_FORCE_OFFSET) +#define HPIPE_MISC_TXDCLK_2X_OFFSET 6 +#define HPIPE_MISC_TXDCLK_2X_MASK \ + (0x1 << HPIPE_MISC_TXDCLK_2X_OFFSET) +#define HPIPE_MISC_CLK500_EN_OFFSET 7 +#define HPIPE_MISC_CLK500_EN_MASK \ + (0x1 << HPIPE_MISC_CLK500_EN_OFFSET) +#define HPIPE_MISC_REFCLK_SEL_OFFSET 10 +#define HPIPE_MISC_REFCLK_SEL_MASK \ + (0x1 << HPIPE_MISC_REFCLK_SEL_OFFSET) + +#define HPIPE_RX_CONTROL_1_REG 0x140 +#define HPIPE_RX_CONTROL_1_RXCLK2X_SEL_OFFSET 11 +#define HPIPE_RX_CONTROL_1_RXCLK2X_SEL_MASK \ + (0x1 << HPIPE_RX_CONTROL_1_RXCLK2X_SEL_OFFSET) +#define HPIPE_RX_CONTROL_1_CLK8T_EN_OFFSET 12 +#define HPIPE_RX_CONTROL_1_CLK8T_EN_MASK \ + (0x1 << HPIPE_RX_CONTROL_1_CLK8T_EN_OFFSET) + +#define HPIPE_PWR_CTR_REG 0x148 +#define HPIPE_PWR_CTR_RST_DFE_OFFSET 0 +#define HPIPE_PWR_CTR_RST_DFE_MASK \ + (0x1 << HPIPE_PWR_CTR_RST_DFE_OFFSET) +#define HPIPE_PWR_CTR_SFT_RST_OFFSET 10 +#define HPIPE_PWR_CTR_SFT_RST_MASK \ + (0x1 << HPIPE_PWR_CTR_SFT_RST_OFFSET) + +#define HPIPE_SPD_DIV_FORCE_REG 0x154 +#define HPIPE_TXDIGCK_DIV_FORCE_OFFSET 7 +#define HPIPE_TXDIGCK_DIV_FORCE_MASK \ + (0x1 << HPIPE_TXDIGCK_DIV_FORCE_OFFSET) +#define HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_OFFSET 8 +#define HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_MASK \ + (0x3 << HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_OFFSET) +#define HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_FORCE_OFFSET 10 +#define HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_FORCE_MASK \ + (0x1 << HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_FORCE_OFFSET) +#define HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_OFFSET 13 +#define HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_MASK \ + (0x3 << HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_OFFSET) +#define HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_FORCE_OFFSET 15 +#define HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_FORCE_MASK \ + (0x1 << HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_FORCE_OFFSET) + +#define HPIPE_SAMPLER_N_PROC_CALIB_CTRL_REG 0x16C +#define HPIPE_RX_SAMPLER_OS_GAIN_OFFSET 6 +#define HPIPE_RX_SAMPLER_OS_GAIN_MASK \ + (0x3 << HPIPE_RX_SAMPLER_OS_GAIN_OFFSET) +#define HPIPE_SMAPLER_OFFSET 12 +#define HPIPE_SMAPLER_MASK \ + (0x1 << HPIPE_SMAPLER_OFFSET) + +#define HPIPE_TX_REG1_REG 0x174 +#define HPIPE_TX_REG1_TX_EMPH_RES_OFFSET 5 +#define HPIPE_TX_REG1_TX_EMPH_RES_MASK \ + (0x3 << HPIPE_TX_REG1_TX_EMPH_RES_OFFSET) +#define HPIPE_TX_REG1_SLC_EN_OFFSET 10 +#define HPIPE_TX_REG1_SLC_EN_MASK \ + (0x3f << HPIPE_TX_REG1_SLC_EN_OFFSET) + +#define HPIPE_PWR_CTR_DTL_REG 0x184 +#define HPIPE_PWR_CTR_DTL_SQ_DET_EN_OFFSET 0 +#define HPIPE_PWR_CTR_DTL_SQ_DET_EN_MASK \ + (0x1 << HPIPE_PWR_CTR_DTL_SQ_DET_EN_OFFSET) +#define HPIPE_PWR_CTR_DTL_SQ_PLOOP_EN_OFFSET 1 +#define HPIPE_PWR_CTR_DTL_SQ_PLOOP_EN_MASK \ + (0x1 << HPIPE_PWR_CTR_DTL_SQ_PLOOP_EN_OFFSET) +#define HPIPE_PWR_CTR_DTL_FLOOP_EN_OFFSET 2 +#define HPIPE_PWR_CTR_DTL_FLOOP_EN_MASK \ + (0x1 << HPIPE_PWR_CTR_DTL_FLOOP_EN_OFFSET) +#define HPIPE_PWR_CTR_DTL_CLAMPING_SEL_OFFSET 4 +#define HPIPE_PWR_CTR_DTL_CLAMPING_SEL_MASK \ + (0x7 << HPIPE_PWR_CTR_DTL_CLAMPING_SEL_OFFSET) +#define HPIPE_PWR_CTR_DTL_INTPCLK_DIV_FORCE_OFFSET 10 +#define HPIPE_PWR_CTR_DTL_INTPCLK_DIV_FORCE_MASK \ + (0x1 << HPIPE_PWR_CTR_DTL_INTPCLK_DIV_FORCE_OFFSET) +#define HPIPE_PWR_CTR_DTL_CLK_MODE_OFFSET 12 +#define HPIPE_PWR_CTR_DTL_CLK_MODE_MASK \ + (0x3 << HPIPE_PWR_CTR_DTL_CLK_MODE_OFFSET) +#define HPIPE_PWR_CTR_DTL_CLK_MODE_FORCE_OFFSET 14 +#define HPIPE_PWR_CTR_DTL_CLK_MODE_FORCE_MASK \ + (1 << HPIPE_PWR_CTR_DTL_CLK_MODE_FORCE_OFFSET) + +#define HPIPE_PHASE_CONTROL_REG 0x188 +#define HPIPE_OS_PH_OFFSET_OFFSET 0 +#define HPIPE_OS_PH_OFFSET_MASK \ + (0x7f << HPIPE_OS_PH_OFFSET_OFFSET) +#define HPIPE_OS_PH_OFFSET_FORCE_OFFSET 7 +#define HPIPE_OS_PH_OFFSET_FORCE_MASK \ + (0x1 << HPIPE_OS_PH_OFFSET_FORCE_OFFSET) +#define HPIPE_OS_PH_VALID_OFFSET 8 +#define HPIPE_OS_PH_VALID_MASK \ + (0x1 << HPIPE_OS_PH_VALID_OFFSET) + +#define HPIPE_SQ_GLITCH_FILTER_CTRL 0x1c8 +#define HPIPE_SQ_DEGLITCH_WIDTH_P_OFFSET 0 +#define HPIPE_SQ_DEGLITCH_WIDTH_P_MASK \ + (0xf << HPIPE_SQ_DEGLITCH_WIDTH_P_OFFSET) +#define HPIPE_SQ_DEGLITCH_WIDTH_N_OFFSET 4 +#define HPIPE_SQ_DEGLITCH_WIDTH_N_MASK \ + (0xf << HPIPE_SQ_DEGLITCH_WIDTH_N_OFFSET) +#define HPIPE_SQ_DEGLITCH_EN_OFFSET 8 +#define HPIPE_SQ_DEGLITCH_EN_MASK \ + (0x1 << HPIPE_SQ_DEGLITCH_EN_OFFSET) + +#define HPIPE_FRAME_DETECT_CTRL_0_REG 0x214 +#define HPIPE_TRAIN_PAT_NUM_OFFSET 0x7 +#define HPIPE_TRAIN_PAT_NUM_MASK \ + (0x1FF << HPIPE_TRAIN_PAT_NUM_OFFSET) + +#define HPIPE_FRAME_DETECT_CTRL_3_REG 0x220 +#define HPIPE_PATTERN_LOCK_LOST_TIMEOUT_EN_OFFSET 12 +#define HPIPE_PATTERN_LOCK_LOST_TIMEOUT_EN_MASK \ + (0x1 << HPIPE_PATTERN_LOCK_LOST_TIMEOUT_EN_OFFSET) + +#define HPIPE_DME_REG 0x228 +#define HPIPE_DME_ETHERNET_MODE_OFFSET 7 +#define HPIPE_DME_ETHERNET_MODE_MASK \ + (0x1 << HPIPE_DME_ETHERNET_MODE_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_0_REG 0x268 +#define HPIPE_TX_TRAIN_P2P_HOLD_OFFSET 15 +#define HPIPE_TX_TRAIN_P2P_HOLD_MASK \ + (0x1 << HPIPE_TX_TRAIN_P2P_HOLD_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_REG 0x26C +#define HPIPE_TX_TRAIN_CTRL_G1_OFFSET 0 +#define HPIPE_TX_TRAIN_CTRL_G1_MASK \ + (0x1 << HPIPE_TX_TRAIN_CTRL_G1_OFFSET) +#define HPIPE_TX_TRAIN_CTRL_GN1_OFFSET 1 +#define HPIPE_TX_TRAIN_CTRL_GN1_MASK \ + (0x1 << HPIPE_TX_TRAIN_CTRL_GN1_OFFSET) +#define HPIPE_TX_TRAIN_CTRL_G0_OFFSET 2 +#define HPIPE_TX_TRAIN_CTRL_G0_MASK \ + (0x1 << HPIPE_TX_TRAIN_CTRL_G0_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_4_REG 0x278 +#define HPIPE_TRX_TRAIN_TIMER_OFFSET 0 +#define HPIPE_TRX_TRAIN_TIMER_MASK \ + (0x3FF << HPIPE_TRX_TRAIN_TIMER_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_5_REG 0x2A4 +#define HPIPE_RX_TRAIN_TIMER_OFFSET 0 +#define HPIPE_RX_TRAIN_TIMER_MASK \ + (0x3ff << HPIPE_RX_TRAIN_TIMER_OFFSET) +#define HPIPE_TX_TRAIN_START_SQ_EN_OFFSET 11 +#define HPIPE_TX_TRAIN_START_SQ_EN_MASK \ + (0x1 << HPIPE_TX_TRAIN_START_SQ_EN_OFFSET) +#define HPIPE_TX_TRAIN_START_FRM_DET_EN_OFFSET 12 +#define HPIPE_TX_TRAIN_START_FRM_DET_EN_MASK \ + (0x1 << HPIPE_TX_TRAIN_START_FRM_DET_EN_OFFSET) +#define HPIPE_TX_TRAIN_START_FRM_LOCK_EN_OFFSET 13 +#define HPIPE_TX_TRAIN_START_FRM_LOCK_EN_MASK \ + (0x1 << HPIPE_TX_TRAIN_START_FRM_LOCK_EN_OFFSET) +#define HPIPE_TX_TRAIN_WAIT_TIME_EN_OFFSET 14 +#define HPIPE_TX_TRAIN_WAIT_TIME_EN_MASK \ + (0x1 << HPIPE_TX_TRAIN_WAIT_TIME_EN_OFFSET) + +#define HPIPE_TX_TRAIN_REG 0x31C +#define HPIPE_TX_TRAIN_CHK_INIT_OFFSET 4 +#define HPIPE_TX_TRAIN_CHK_INIT_MASK \ + (0x1 << HPIPE_TX_TRAIN_CHK_INIT_OFFSET) +#define HPIPE_TX_TRAIN_COE_FM_PIN_PCIE3_OFFSET 7 +#define HPIPE_TX_TRAIN_COE_FM_PIN_PCIE3_MASK \ + (0x1 << HPIPE_TX_TRAIN_COE_FM_PIN_PCIE3_OFFSET) +#define HPIPE_TX_TRAIN_16BIT_AUTO_EN_OFFSET 8 +#define HPIPE_TX_TRAIN_16BIT_AUTO_EN_MASK \ + (0x1 << HPIPE_TX_TRAIN_16BIT_AUTO_EN_OFFSET) +#define HPIPE_TX_TRAIN_PAT_SEL_OFFSET 9 +#define HPIPE_TX_TRAIN_PAT_SEL_MASK \ + (0x1 << HPIPE_TX_TRAIN_PAT_SEL_OFFSET) + +#define HPIPE_SAVED_DFE_VALUES_REG 0x328 +#define HPIPE_SAVED_DFE_VALUES_SAV_F0D_OFFSET 10 +#define HPIPE_SAVED_DFE_VALUES_SAV_F0D_MASK \ + (0x3f << HPIPE_SAVED_DFE_VALUES_SAV_F0D_OFFSET) + +#define HPIPE_CDR_CONTROL_REG 0x418 +#define HPIPE_CDR_RX_MAX_DFE_ADAPT_0_OFFSET 14 +#define HPIPE_CDR_RX_MAX_DFE_ADAPT_0_MASK \ + (0x3 << HPIPE_CDR_RX_MAX_DFE_ADAPT_0_OFFSET) +#define HPIPE_CDR_RX_MAX_DFE_ADAPT_1_OFFSET 12 +#define HPIPE_CDR_RX_MAX_DFE_ADAPT_1_MASK \ + (0x3 << HPIPE_CDR_RX_MAX_DFE_ADAPT_1_OFFSET) +#define HPIPE_CDR_MAX_DFE_ADAPT_0_OFFSET 9 +#define HPIPE_CDR_MAX_DFE_ADAPT_0_MASK \ + (0x7 << HPIPE_CDR_MAX_DFE_ADAPT_0_OFFSET) +#define HPIPE_CDR_MAX_DFE_ADAPT_1_OFFSET 6 +#define HPIPE_CDR_MAX_DFE_ADAPT_1_MASK \ + (0x7 << HPIPE_CDR_MAX_DFE_ADAPT_1_OFFSET) + +#define HPIPE_TX_TRAIN_CTRL_11_REG 0x438 +#define HPIPE_TX_STATUS_CHECK_MODE_OFFSET 6 +#define HPIPE_TX_TX_STATUS_CHECK_MODE_MASK \ + (0x1 << HPIPE_TX_STATUS_CHECK_MODE_OFFSET) +#define HPIPE_TX_NUM_OF_PRESET_OFFSET 10 +#define HPIPE_TX_NUM_OF_PRESET_MASK \ + (0x7 << HPIPE_TX_NUM_OF_PRESET_OFFSET) +#define HPIPE_TX_SWEEP_PRESET_EN_OFFSET 15 +#define HPIPE_TX_SWEEP_PRESET_EN_MASK \ + (0x1 << HPIPE_TX_SWEEP_PRESET_EN_OFFSET) + +#define HPIPE_G1_SETTINGS_3_REG 0x440 +#define HPIPE_G1_SETTINGS_3_G1_FFE_CAP_SEL_OFFSET 0 +#define HPIPE_G1_SETTINGS_3_G1_FFE_CAP_SEL_MASK \ + (0xf << HPIPE_G1_SETTINGS_3_G1_FFE_CAP_SEL_OFFSET) +#define HPIPE_G1_SETTINGS_3_G1_FFE_RES_SEL_OFFSET 4 +#define HPIPE_G1_SETTINGS_3_G1_FFE_RES_SEL_MASK \ + (0x7 << HPIPE_G1_SETTINGS_3_G1_FFE_RES_SEL_OFFSET) +#define HPIPE_G1_SETTINGS_3_G1_FFE_SETTING_FORCE_OFFSET 7 +#define HPIPE_G1_SETTINGS_3_G1_FFE_SETTING_FORCE_MASK \ + (0x1 << HPIPE_G1_SETTINGS_3_G1_FFE_SETTING_FORCE_OFFSET) +#define HPIPE_G1_SETTINGS_3_G1_FBCK_SEL_OFFSET 9 +#define HPIPE_G1_SETTINGS_3_G1_FBCK_SEL_MASK \ + (0x1 << HPIPE_G1_SETTINGS_3_G1_FBCK_SEL_OFFSET) +#define HPIPE_G1_SETTINGS_3_G1_FFE_DEG_RES_LEVEL_OFFSET 12 +#define HPIPE_G1_SETTINGS_3_G1_FFE_DEG_RES_LEVEL_MASK \ + (0x3 << HPIPE_G1_SETTINGS_3_G1_FFE_DEG_RES_LEVEL_OFFSET) +#define HPIPE_G1_SETTINGS_3_G1_FFE_LOAD_RES_LEVEL_OFFSET 14 +#define HPIPE_G1_SETTINGS_3_G1_FFE_LOAD_RES_LEVEL_MASK \ + (0x3 << HPIPE_G1_SETTINGS_3_G1_FFE_LOAD_RES_LEVEL_OFFSET) + +#define HPIPE_G1_SETTINGS_4_REG 0x444 +#define HPIPE_G1_SETTINGS_4_G1_DFE_RES_OFFSET 8 +#define HPIPE_G1_SETTINGS_4_G1_DFE_RES_MASK \ + (0x3 << HPIPE_G1_SETTINGS_4_G1_DFE_RES_OFFSET) + +#define HPIPE_G2_SETTINGS_4_REG 0x44c +#define HPIPE_G2_DFE_RES_OFFSET 8 +#define HPIPE_G2_DFE_RES_MASK \ + (0x3 << HPIPE_G2_DFE_RES_OFFSET) + +#define HPIPE_G3_SETTING_3_REG 0x450 +#define HPIPE_G3_FFE_CAP_SEL_OFFSET 0 +#define HPIPE_G3_FFE_CAP_SEL_MASK \ + (0xf << HPIPE_G3_FFE_CAP_SEL_OFFSET) +#define HPIPE_G3_FFE_RES_SEL_OFFSET 4 +#define HPIPE_G3_FFE_RES_SEL_MASK \ + (0x7 << HPIPE_G3_FFE_RES_SEL_OFFSET) +#define HPIPE_G3_FFE_SETTING_FORCE_OFFSET 7 +#define HPIPE_G3_FFE_SETTING_FORCE_MASK \ + (0x1 << HPIPE_G3_FFE_SETTING_FORCE_OFFSET) +#define HPIPE_G3_FFE_DEG_RES_LEVEL_OFFSET 12 +#define HPIPE_G3_FFE_DEG_RES_LEVEL_MASK \ + (0x3 << HPIPE_G3_FFE_DEG_RES_LEVEL_OFFSET) +#define HPIPE_G3_FFE_LOAD_RES_LEVEL_OFFSET 14 +#define HPIPE_G3_FFE_LOAD_RES_LEVEL_MASK \ + (0x3 << HPIPE_G3_FFE_LOAD_RES_LEVEL_OFFSET) + +#define HPIPE_G3_SETTING_4_REG 0x454 +#define HPIPE_G3_DFE_RES_OFFSET 8 +#define HPIPE_G3_DFE_RES_MASK (0x3 << HPIPE_G3_DFE_RES_OFFSET) + +#define HPIPE_TX_PRESET_INDEX_REG 0x468 +#define HPIPE_TX_PRESET_INDEX_OFFSET 0 +#define HPIPE_TX_PRESET_INDEX_MASK \ + (0xf << HPIPE_TX_PRESET_INDEX_OFFSET) + +#define HPIPE_DFE_CONTROL_REG 0x470 +#define HPIPE_DFE_TX_MAX_DFE_ADAPT_OFFSET 14 +#define HPIPE_DFE_TX_MAX_DFE_ADAPT_MASK \ + (0x3 << HPIPE_DFE_TX_MAX_DFE_ADAPT_OFFSET) + +#define HPIPE_DFE_CTRL_28_REG 0x49C +#define HPIPE_DFE_CTRL_28_PIPE4_OFFSET 7 +#define HPIPE_DFE_CTRL_28_PIPE4_MASK \ + (0x1 << HPIPE_DFE_CTRL_28_PIPE4_OFFSET) + +#define HPIPE_G1_SETTING_5_REG 0x538 +#define HPIPE_G1_SETTING_5_G1_ICP_OFFSET 0 +#define HPIPE_G1_SETTING_5_G1_ICP_MASK \ + (0xf << HPIPE_G1_SETTING_5_G1_ICP_OFFSET) + +#define HPIPE_G3_SETTING_5_REG 0x548 +#define HPIPE_G3_SETTING_5_G3_ICP_OFFSET 0 +#define HPIPE_G3_SETTING_5_G3_ICP_MASK \ + (0xf << HPIPE_G3_SETTING_5_G3_ICP_OFFSET) + +#define HPIPE_LANE_CONFIG0_REG 0x600 +#define HPIPE_LANE_CONFIG0_TXDEEMPH0_OFFSET 0 +#define HPIPE_LANE_CONFIG0_TXDEEMPH0_MASK \ + (0x1 << HPIPE_LANE_CONFIG0_TXDEEMPH0_OFFSET) + +#define HPIPE_LANE_STATUS1_REG 0x60C +#define HPIPE_LANE_STATUS1_PCLK_EN_OFFSET 0 +#define HPIPE_LANE_STATUS1_PCLK_EN_MASK \ + (0x1 << HPIPE_LANE_STATUS1_PCLK_EN_OFFSET) + +#define HPIPE_LANE_CFG4_REG 0x620 +#define HPIPE_LANE_CFG4_DFE_CTRL_OFFSET 0 +#define HPIPE_LANE_CFG4_DFE_CTRL_MASK \ + (0x7 << HPIPE_LANE_CFG4_DFE_CTRL_OFFSET) +#define HPIPE_LANE_CFG4_DFE_EN_SEL_OFFSET 3 +#define HPIPE_LANE_CFG4_DFE_EN_SEL_MASK \ + (0x1 << HPIPE_LANE_CFG4_DFE_EN_SEL_OFFSET) +#define HPIPE_LANE_CFG4_DFE_OVER_OFFSET 6 +#define HPIPE_LANE_CFG4_DFE_OVER_MASK \ + (0x1 << HPIPE_LANE_CFG4_DFE_OVER_OFFSET) +#define HPIPE_LANE_CFG4_SSC_CTRL_OFFSET 7 +#define HPIPE_LANE_CFG4_SSC_CTRL_MASK \ + (0x1 << HPIPE_LANE_CFG4_SSC_CTRL_OFFSET) + +#define HPIPE_LANE_EQ_REMOTE_SETTING_REG 0x6f8 +#define HPIPE_LANE_CFG_FOM_DIRN_OVERRIDE_OFFSET 0 +#define HPIPE_LANE_CFG_FOM_DIRN_OVERRIDE_MASK \ + (0x1 << HPIPE_LANE_CFG_FOM_DIRN_OVERRIDE_OFFSET) +#define HPIPE_LANE_CFG_FOM_ONLY_MODE_OFFFSET 1 +#define HPIPE_LANE_CFG_FOM_ONLY_MODE_MASK \ + (0x1 << HPIPE_LANE_CFG_FOM_ONLY_MODE_OFFFSET) +#define HPIPE_LANE_CFG_FOM_PRESET_VECTOR_OFFSET 2 +#define HPIPE_LANE_CFG_FOM_PRESET_VECTOR_MASK \ + (0xf << HPIPE_LANE_CFG_FOM_PRESET_VECTOR_OFFSET) + +#define HPIPE_LANE_EQU_CONFIG_0_REG 0x69C +#define HPIPE_CFG_PHY_RC_EP_OFFSET 12 +#define HPIPE_CFG_PHY_RC_EP_MASK \ + (0x1 << HPIPE_CFG_PHY_RC_EP_OFFSET) + +#define HPIPE_LANE_EQ_CFG1_REG 0x6a0 +#define HPIPE_CFG_UPDATE_POLARITY_OFFSET 12 +#define HPIPE_CFG_UPDATE_POLARITY_MASK \ + (0x1 << HPIPE_CFG_UPDATE_POLARITY_OFFSET) + +#define HPIPE_LANE_EQ_CFG2_REG 0x6a4 +#define HPIPE_CFG_EQ_BUNDLE_DIS_OFFSET 14 +#define HPIPE_CFG_EQ_BUNDLE_DIS_MASK \ + (0x1 << HPIPE_CFG_EQ_BUNDLE_DIS_OFFSET) + +#define HPIPE_RST_CLK_CTRL_REG 0x704 +#define HPIPE_RST_CLK_CTRL_PIPE_RST_OFFSET 0 +#define HPIPE_RST_CLK_CTRL_PIPE_RST_MASK \ + (0x1 << HPIPE_RST_CLK_CTRL_PIPE_RST_OFFSET) +#define HPIPE_RST_CLK_CTRL_FIXED_PCLK_OFFSET 2 +#define HPIPE_RST_CLK_CTRL_FIXED_PCLK_MASK \ + (0x1 << HPIPE_RST_CLK_CTRL_FIXED_PCLK_OFFSET) +#define HPIPE_RST_CLK_CTRL_PIPE_WIDTH_OFFSET 3 +#define HPIPE_RST_CLK_CTRL_PIPE_WIDTH_MASK \ + (0x1 << HPIPE_RST_CLK_CTRL_PIPE_WIDTH_OFFSET) +#define HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_OFFSET 9 +#define HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_MASK \ + (0x1 << HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_OFFSET) + +#define HPIPE_TST_MODE_CTRL_REG 0x708 +#define HPIPE_TST_MODE_CTRL_MODE_MARGIN_OFFSET 2 +#define HPIPE_TST_MODE_CTRL_MODE_MARGIN_MASK \ + (0x1 << HPIPE_TST_MODE_CTRL_MODE_MARGIN_OFFSET) + +#define HPIPE_CLK_SRC_LO_REG 0x70c +#define HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SEL_OFFSET 1 +#define HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SEL_MASK \ + (0x1 << HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SEL_OFFSET) +#define HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SCALE_OFFSET 2 +#define HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SCALE_MASK \ + (0x3 << HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SCALE_OFFSET) +#define HPIPE_CLK_SRC_LO_PLL_RDY_DL_OFFSET 5 +#define HPIPE_CLK_SRC_LO_PLL_RDY_DL_MASK \ + (0x7 << HPIPE_CLK_SRC_LO_PLL_RDY_DL_OFFSET) + +#define HPIPE_CLK_SRC_HI_REG 0x710 +#define HPIPE_CLK_SRC_HI_LANE_STRT_OFFSET 0 +#define HPIPE_CLK_SRC_HI_LANE_STRT_MASK \ + (0x1 << HPIPE_CLK_SRC_HI_LANE_STRT_OFFSET) +#define HPIPE_CLK_SRC_HI_LANE_BREAK_OFFSET 1 +#define HPIPE_CLK_SRC_HI_LANE_BREAK_MASK \ + (0x1 << HPIPE_CLK_SRC_HI_LANE_BREAK_OFFSET) +#define HPIPE_CLK_SRC_HI_LANE_MASTER_OFFSET 2 +#define HPIPE_CLK_SRC_HI_LANE_MASTER_MASK \ + (0x1 << HPIPE_CLK_SRC_HI_LANE_MASTER_OFFSET) +#define HPIPE_CLK_SRC_HI_MODE_PIPE_OFFSET 7 +#define HPIPE_CLK_SRC_HI_MODE_PIPE_MASK \ + (0x1 << HPIPE_CLK_SRC_HI_MODE_PIPE_OFFSET) + +#define HPIPE_GLOBAL_MISC_CTRL 0x718 +#define HPIPE_GLOBAL_PM_CTRL 0x740 +#define HPIPE_GLOBAL_PM_RXDLOZ_WAIT_OFFSET 0 +#define HPIPE_GLOBAL_PM_RXDLOZ_WAIT_MASK \ + (0xFF << HPIPE_GLOBAL_PM_RXDLOZ_WAIT_OFFSET) + +/* General defines */ +#define PLL_LOCK_TIMEOUT 15000 + +#endif /* _PHY_COMPHY_CP110_H */ + diff --git a/drivers/marvell/comphy/phy-comphy-cp110.c b/drivers/marvell/comphy/phy-comphy-cp110.c new file mode 100644 index 00000000..8b78280b --- /dev/null +++ b/drivers/marvell/comphy/phy-comphy-cp110.c @@ -0,0 +1,2319 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* Marvell CP110 SoC COMPHY unit driver */ + +#include <debug.h> +#include <delay_timer.h> +#include <errno.h> +#include <mmio.h> +#include <mvebu_def.h> +#include <spinlock.h> +#include "mvebu.h" +#include "comphy-cp110.h" + +/* #define DEBUG_COMPHY */ +#ifdef DEBUG_COMPHY +#define debug(format...) printf(format) +#else +#define debug(format, arg...) +#endif + +/* A lane is described by 4 fields: + * - bit 1~0 represent comphy polarity invert + * - bit 7~2 represent comphy speed + * - bit 11~8 represent unit index + * - bit 16~12 represent mode + * - bit 17 represent comphy indication of clock source + * - bit 19-18 represents pcie width (in case of pcie comphy config.) + * - bit 31~20 reserved + */ + +#define COMPHY_INVERT_OFFSET 0 +#define COMPHY_INVERT_LEN 2 +#define COMPHY_INVERT_MASK COMPHY_MASK(COMPHY_INVERT_OFFSET, \ + COMPHY_INVERT_LEN) +#define COMPHY_SPEED_OFFSET (COMPHY_INVERT_OFFSET + COMPHY_INVERT_LEN) +#define COMPHY_SPEED_LEN 6 +#define COMPHY_SPEED_MASK COMPHY_MASK(COMPHY_SPEED_OFFSET, \ + COMPHY_SPEED_LEN) +#define COMPHY_UNIT_ID_OFFSET (COMPHY_SPEED_OFFSET + COMPHY_SPEED_LEN) +#define COMPHY_UNIT_ID_LEN 4 +#define COMPHY_UNIT_ID_MASK COMPHY_MASK(COMPHY_UNIT_ID_OFFSET, \ + COMPHY_UNIT_ID_LEN) +#define COMPHY_MODE_OFFSET (COMPHY_UNIT_ID_OFFSET + COMPHY_UNIT_ID_LEN) +#define COMPHY_MODE_LEN 5 +#define COMPHY_MODE_MASK COMPHY_MASK(COMPHY_MODE_OFFSET, COMPHY_MODE_LEN) +#define COMPHY_CLK_SRC_OFFSET (COMPHY_MODE_OFFSET + COMPHY_MODE_LEN) +#define COMPHY_CLK_SRC_LEN 1 +#define COMPHY_CLK_SRC_MASK COMPHY_MASK(COMPHY_CLK_SRC_OFFSET, \ + COMPHY_CLK_SRC_LEN) +#define COMPHY_PCI_WIDTH_OFFSET (COMPHY_CLK_SRC_OFFSET + COMPHY_CLK_SRC_LEN) +#define COMPHY_PCI_WIDTH_LEN 3 +#define COMPHY_PCI_WIDTH_MASK COMPHY_MASK(COMPHY_PCI_WIDTH_OFFSET, \ + COMPHY_PCI_WIDTH_LEN) + +#define COMPHY_MASK(offset, len) (((1 << (len)) - 1) << (offset)) + +/* Macro which extracts mode from lane description */ +#define COMPHY_GET_MODE(x) (((x) & COMPHY_MODE_MASK) >> \ + COMPHY_MODE_OFFSET) +/* Macro which extracts unit index from lane description */ +#define COMPHY_GET_ID(x) (((x) & COMPHY_UNIT_ID_MASK) >> \ + COMPHY_UNIT_ID_OFFSET) +/* Macro which extracts speed from lane description */ +#define COMPHY_GET_SPEED(x) (((x) & COMPHY_SPEED_MASK) >> \ + COMPHY_SPEED_OFFSET) +/* Macro which extracts clock source indication from lane description */ +#define COMPHY_GET_CLK_SRC(x) (((x) & COMPHY_CLK_SRC_MASK) >> \ + COMPHY_CLK_SRC_OFFSET) +/* Macro which extracts pcie width indication from lane description */ +#define COMPHY_GET_PCIE_WIDTH(x) (((x) & COMPHY_PCI_WIDTH_MASK) >> \ + COMPHY_PCI_WIDTH_OFFSET) + +#define COMPHY_SATA_MODE 0x1 +#define COMPHY_SGMII_MODE 0x2 /* SGMII 1G */ +#define COMPHY_HS_SGMII_MODE 0x3 /* SGMII 2.5G */ +#define COMPHY_USB3H_MODE 0x4 +#define COMPHY_USB3D_MODE 0x5 +#define COMPHY_PCIE_MODE 0x6 +#define COMPHY_RXAUI_MODE 0x7 +#define COMPHY_XFI_MODE 0x8 +#define COMPHY_SFI_MODE 0x9 +#define COMPHY_USB3_MODE 0xa +#define COMPHY_AP_MODE 0xb + +/* COMPHY speed macro */ +#define COMPHY_SPEED_1_25G 0 /* SGMII 1G */ +#define COMPHY_SPEED_2_5G 1 +#define COMPHY_SPEED_3_125G 2 /* SGMII 2.5G */ +#define COMPHY_SPEED_5G 3 +#define COMPHY_SPEED_5_15625G 4 /* XFI 5G */ +#define COMPHY_SPEED_6G 5 +#define COMPHY_SPEED_10_3125G 6 /* XFI 10G */ +#define COMPHY_SPEED_MAX 0x3F +/* The default speed for IO with fixed known speed */ +#define COMPHY_SPEED_DEFAULT COMPHY_SPEED_MAX + +/* Commands for comphy driver */ +#define COMPHY_COMMAND_DIGITAL_PWR_OFF 0x00000001 +#define COMPHY_COMMAND_DIGITAL_PWR_ON 0x00000002 + +#define COMPHY_PIPE_FROM_COMPHY_ADDR(x) ((x & ~0xffffff) + 0x120000) + +/* System controller registers */ +#define PCIE_MAC_RESET_MASK_PORT0 BIT(13) +#define PCIE_MAC_RESET_MASK_PORT1 BIT(11) +#define PCIE_MAC_RESET_MASK_PORT2 BIT(12) +#define SYS_CTRL_UINIT_SOFT_RESET_REG 0x268 +#define SYS_CTRL_FROM_COMPHY_ADDR(x) ((x & ~0xffffff) + 0x440000) + +/* DFX register spaces */ +#define SAR_RST_PCIE0_CLOCK_CONFIG_CP1_OFFSET (0) +#define SAR_RST_PCIE0_CLOCK_CONFIG_CP1_MASK (0x1 << \ + SAR_RST_PCIE0_CLOCK_CONFIG_CP1_OFFSET) +#define SAR_RST_PCIE1_CLOCK_CONFIG_CP1_OFFSET (1) +#define SAR_RST_PCIE1_CLOCK_CONFIG_CP1_MASK (0x1 << \ + SAR_RST_PCIE1_CLOCK_CONFIG_CP1_OFFSET) +#define SAR_STATUS_0_REG 200 +#define DFX_FROM_COMPHY_ADDR(x) ((x & ~0xffffff) + DFX_BASE) + +/* The same Units Soft Reset Config register are accessed in all PCIe ports + * initialization, so a spin lock is defined in case when more than 1 CPUs + * resets PCIe MAC and need to access the register in the same time. The spin + * lock is shared by all CP110 units. + */ +spinlock_t cp110_mac_reset_lock; + +enum reg_width_type { + REG_16BIT = 0, + REG_32BIT, +}; + +enum { + COMPHY_LANE0 = 0, + COMPHY_LANE1, + COMPHY_LANE2, + COMPHY_LANE3, + COMPHY_LANE4, + COMPHY_LANE5, + COMPHY_LANE_MAX, +}; + +/* These values come from the PCI Express Spec */ +enum pcie_link_width { + PCIE_LNK_WIDTH_RESRV = 0x00, + PCIE_LNK_X1 = 0x01, + PCIE_LNK_X2 = 0x02, + PCIE_LNK_X4 = 0x04, + PCIE_LNK_X8 = 0x08, + PCIE_LNK_X12 = 0x0C, + PCIE_LNK_X16 = 0x10, + PCIE_LNK_X32 = 0x20, + PCIE_LNK_WIDTH_UNKNOWN = 0xFF, +}; + +static inline uint32_t polling_with_timeout(uintptr_t addr, + uint32_t val, + uint32_t mask, + uint32_t usec_timeout, + enum reg_width_type type) +{ + uint32_t data; + + do { + udelay(1); + if (type == REG_16BIT) + data = mmio_read_16(addr) & mask; + else + data = mmio_read_32(addr) & mask; + } while (data != val && --usec_timeout > 0); + + if (usec_timeout == 0) + return data; + + return 0; +} + +static inline void reg_set(uintptr_t addr, uint32_t data, uint32_t mask) +{ + debug("<atf>: WR to addr = %#010lx, data = %#010x (mask = %#010x) - ", + addr, data, mask); + debug("old value = %#010x ==> ", mmio_read_32(addr)); + mmio_clrsetbits_32(addr, mask, data); + + debug("new val %#010x\n", mmio_read_32(addr)); +} + +/* Clear PIPE selector - avoid collision with previous configuration */ +static void mvebu_cp110_comphy_clr_pipe_selector(uint64_t comphy_base, + uint8_t comphy_index) +{ + uint32_t reg, mask, field; + uint32_t comphy_offset = + COMMON_SELECTOR_COMPHYN_FIELD_WIDTH * comphy_index; + + mask = COMMON_SELECTOR_COMPHY_MASK << comphy_offset; + reg = mmio_read_32(comphy_base + COMMON_SELECTOR_PIPE_REG_OFFSET); + field = reg & mask; + + if (field) { + reg &= ~mask; + mmio_write_32(comphy_base + COMMON_SELECTOR_PIPE_REG_OFFSET, + reg); + } +} + +/* Clear PHY selector - avoid collision with previous configuration */ +static void mvebu_cp110_comphy_clr_phy_selector(uint64_t comphy_base, + uint8_t comphy_index) +{ + uint32_t reg, mask, field; + uint32_t comphy_offset = + COMMON_SELECTOR_COMPHYN_FIELD_WIDTH * comphy_index; + + mask = COMMON_SELECTOR_COMPHY_MASK << comphy_offset; + reg = mmio_read_32(comphy_base + COMMON_SELECTOR_PHY_REG_OFFSET); + field = reg & mask; + + /* Clear comphy selector - if it was already configured. + * (might be that this comphy was configured as PCIe/USB, + * in such case, no need to clear comphy selector because PCIe/USB + * are controlled by hpipe selector). + */ + if (field) { + reg &= ~mask; + mmio_write_32(comphy_base + COMMON_SELECTOR_PHY_REG_OFFSET, + reg); + } +} + +/* PHY selector configures SATA and Network modes */ +static void mvebu_cp110_comphy_set_phy_selector(uint64_t comphy_base, + uint8_t comphy_index, uint32_t comphy_mode) +{ + uint32_t reg, mask; + uint32_t comphy_offset = + COMMON_SELECTOR_COMPHYN_FIELD_WIDTH * comphy_index; + int mode; + + /* If phy selector is used the pipe selector should be marked as + * unconnected. + */ + mvebu_cp110_comphy_clr_pipe_selector(comphy_base, comphy_index); + + /* Comphy mode (compound of the IO mode and id). Here, only the IO mode + * is required to distinguish between SATA and network modes. + */ + mode = COMPHY_GET_MODE(comphy_mode); + + mask = COMMON_SELECTOR_COMPHY_MASK << comphy_offset; + reg = mmio_read_32(comphy_base + COMMON_SELECTOR_PHY_REG_OFFSET); + reg &= ~mask; + + /* SATA port 0/1 require the same configuration */ + if (mode == COMPHY_SATA_MODE) { + /* SATA selector values is always 4 */ + reg |= COMMON_SELECTOR_COMPHYN_SATA << comphy_offset; + } else { + switch (comphy_index) { + case(0): + case(1): + case(2): + /* For comphy 0,1, and 2: + * Network selector value is always 1. + */ + reg |= COMMON_SELECTOR_COMPHY0_1_2_NETWORK << + comphy_offset; + break; + case(3): + /* For comphy 3: + * 0x1 = RXAUI_Lane1 + * 0x2 = SGMII/HS-SGMII Port1 + */ + if (mode == COMPHY_RXAUI_MODE) + reg |= COMMON_SELECTOR_COMPHY3_RXAUI << + comphy_offset; + else + reg |= COMMON_SELECTOR_COMPHY3_SGMII << + comphy_offset; + break; + case(4): + /* For comphy 4: + * 0x1 = SGMII/HS-SGMII Port1, XFI1/SFI1 + * 0x2 = SGMII/HS-SGMII Port0: XFI0/SFI0, RXAUI_Lane0 + * + * We want to check if SGMII1/HS_SGMII1 is the + * requested mode in order to determine which value + * should be set (all other modes use the same value) + * so we need to strip the mode, and check the ID + * because we might handle SGMII0/HS_SGMII0 too. + */ + /* TODO: need to distinguish between CP110 and CP115 + * as SFI1/XFI1 available only for CP115. + */ + if ((mode == COMPHY_SGMII_MODE || + mode == COMPHY_HS_SGMII_MODE || + mode == COMPHY_SFI_MODE) && + COMPHY_GET_ID(comphy_mode) == 1) + reg |= COMMON_SELECTOR_COMPHY4_PORT1 << + comphy_offset; + else + reg |= COMMON_SELECTOR_COMPHY4_ALL_OTHERS << + comphy_offset; + break; + case(5): + /* For comphy 5: + * 0x1 = SGMII/HS-SGMII Port2 + * 0x2 = RXAUI Lane1 + */ + if (mode == COMPHY_RXAUI_MODE) + reg |= COMMON_SELECTOR_COMPHY5_RXAUI << + comphy_offset; + else + reg |= COMMON_SELECTOR_COMPHY5_SGMII << + comphy_offset; + break; + } + } + + mmio_write_32(comphy_base + COMMON_SELECTOR_PHY_REG_OFFSET, reg); +} + +/* PIPE selector configures for PCIe, USB 3.0 Host, and USB 3.0 Device mode */ +static void mvebu_cp110_comphy_set_pipe_selector(uint64_t comphy_base, + uint8_t comphy_index, uint32_t comphy_mode) +{ + uint32_t reg; + uint32_t shift = COMMON_SELECTOR_COMPHYN_FIELD_WIDTH * comphy_index; + int mode = COMPHY_GET_MODE(comphy_mode); + uint32_t mask = COMMON_SELECTOR_COMPHY_MASK << shift; + uint32_t pipe_sel = 0x0; + + /* If pipe selector is used the phy selector should be marked as + * unconnected. + */ + mvebu_cp110_comphy_clr_phy_selector(comphy_base, comphy_index); + + reg = mmio_read_32(comphy_base + COMMON_SELECTOR_PIPE_REG_OFFSET); + reg &= ~mask; + + switch (mode) { + case (COMPHY_PCIE_MODE): + /* For lanes support PCIE, selector value are all same */ + pipe_sel = COMMON_SELECTOR_PIPE_COMPHY_PCIE; + break; + + case (COMPHY_USB3H_MODE): + /* Only lane 1-4 support USB host, selector value is same */ + if (comphy_index == COMPHY_LANE0 || + comphy_index == COMPHY_LANE5) + ERROR("COMPHY[%d] mode[%d] is invalid\n", + comphy_index, mode); + else + pipe_sel = COMMON_SELECTOR_PIPE_COMPHY_USBH; + break; + + case (COMPHY_USB3D_MODE): + /* Lane 1 and 4 support USB device, selector value is same */ + if (comphy_index == COMPHY_LANE1 || + comphy_index == COMPHY_LANE4) + pipe_sel = COMMON_SELECTOR_PIPE_COMPHY_USBD; + else + ERROR("COMPHY[%d] mode[%d] is invalid\n", comphy_index, + mode); + break; + + default: + ERROR("COMPHY[%d] mode[%d] is invalid\n", comphy_index, mode); + break; + } + + mmio_write_32(comphy_base + COMMON_SELECTOR_PIPE_REG_OFFSET, reg | + (pipe_sel << shift)); +} + +int mvebu_cp110_comphy_is_pll_locked(uint64_t comphy_base, uint8_t comphy_index) +{ + uintptr_t sd_ip_addr, addr; + uint32_t mask, data; + int ret = 0; + + debug_enter(); + + sd_ip_addr = SD_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + + addr = sd_ip_addr + SD_EXTERNAL_STATUS0_REG; + data = SD_EXTERNAL_STATUS0_PLL_TX_MASK & + SD_EXTERNAL_STATUS0_PLL_RX_MASK; + mask = data; + data = polling_with_timeout(addr, data, mask, + PLL_LOCK_TIMEOUT, REG_32BIT); + if (data != 0) { + if (data & SD_EXTERNAL_STATUS0_PLL_RX_MASK) + ERROR("RX PLL is not locked\n"); + if (data & SD_EXTERNAL_STATUS0_PLL_TX_MASK) + ERROR("TX PLL is not locked\n"); + + ret = -ETIMEDOUT; + } + + debug_exit(); + + return ret; +} + +static int mvebu_cp110_comphy_sata_power_on(uint64_t comphy_base, + uint8_t comphy_index, uint32_t comphy_mode) +{ + uintptr_t hpipe_addr, sd_ip_addr, comphy_addr; + uint32_t mask, data; + int ret = 0; + + debug_enter(); + + /* configure phy selector for SATA */ + mvebu_cp110_comphy_set_phy_selector(comphy_base, + comphy_index, comphy_mode); + + hpipe_addr = HPIPE_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + sd_ip_addr = SD_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + comphy_addr = COMPHY_ADDR(comphy_base, comphy_index); + + debug(" add hpipe 0x%lx, sd 0x%lx, comphy 0x%lx\n", + hpipe_addr, sd_ip_addr, comphy_addr); + debug("stage: RFU configurations - hard reset comphy\n"); + /* RFU configurations - hard reset comphy */ + mask = COMMON_PHY_CFG1_PWR_UP_MASK; + data = 0x1 << COMMON_PHY_CFG1_PWR_UP_OFFSET; + mask |= COMMON_PHY_CFG1_PIPE_SELECT_MASK; + data |= 0x0 << COMMON_PHY_CFG1_PIPE_SELECT_OFFSET; + mask |= COMMON_PHY_CFG1_PWR_ON_RESET_MASK; + data |= 0x0 << COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET; + mask |= COMMON_PHY_CFG1_CORE_RSTN_MASK; + data |= 0x0 << COMMON_PHY_CFG1_CORE_RSTN_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG1_REG, data, mask); + + /* Set select data width 40Bit - SATA mode only */ + reg_set(comphy_addr + COMMON_PHY_CFG6_REG, + 0x1 << COMMON_PHY_CFG6_IF_40_SEL_OFFSET, + COMMON_PHY_CFG6_IF_40_SEL_MASK); + + /* release from hard reset in SD external */ + mask = SD_EXTERNAL_CONFIG1_RESET_IN_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RESET_CORE_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + /* Wait 1ms - until band gap and ref clock ready */ + mdelay(1); + + debug("stage: Comphy configuration\n"); + /* Start comphy Configuration */ + /* Set reference clock to comes from group 1 - choose 25Mhz */ + reg_set(hpipe_addr + HPIPE_MISC_REG, + 0x0 << HPIPE_MISC_REFCLK_SEL_OFFSET, + HPIPE_MISC_REFCLK_SEL_MASK); + /* Reference frequency select set 1 (for SATA = 25Mhz) */ + mask = HPIPE_PWR_PLL_REF_FREQ_MASK; + data = 0x1 << HPIPE_PWR_PLL_REF_FREQ_OFFSET; + /* PHY mode select (set SATA = 0x0 */ + mask |= HPIPE_PWR_PLL_PHY_MODE_MASK; + data |= 0x0 << HPIPE_PWR_PLL_PHY_MODE_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_PLL_REG, data, mask); + /* Set max PHY generation setting - 6Gbps */ + reg_set(hpipe_addr + HPIPE_INTERFACE_REG, + 0x2 << HPIPE_INTERFACE_GEN_MAX_OFFSET, + HPIPE_INTERFACE_GEN_MAX_MASK); + /* Set select data width 40Bit (SEL_BITS[2:0]) */ + reg_set(hpipe_addr + HPIPE_LOOPBACK_REG, + 0x2 << HPIPE_LOOPBACK_SEL_OFFSET, HPIPE_LOOPBACK_SEL_MASK); + + debug("stage: Analog parameters from ETP(HW)\n"); + /* G1 settings */ + mask = HPIPE_G1_SET_1_G1_RX_SELMUPI_MASK; + data = 0x0 << HPIPE_G1_SET_1_G1_RX_SELMUPI_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_SELMUPP_MASK; + data |= 0x1 << HPIPE_G1_SET_1_G1_RX_SELMUPP_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_SELMUFI_MASK; + data |= 0x0 << HPIPE_G1_SET_1_G1_RX_SELMUFI_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_SELMUFF_MASK; + data |= 0x3 << HPIPE_G1_SET_1_G1_RX_SELMUFF_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_DIGCK_DIV_MASK; + data |= 0x1 << HPIPE_G1_SET_1_G1_RX_DIGCK_DIV_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SET_1_REG, data, mask); + + mask = HPIPE_G1_SETTINGS_3_G1_FFE_CAP_SEL_MASK; + data = 0xf << HPIPE_G1_SETTINGS_3_G1_FFE_CAP_SEL_OFFSET; + mask |= HPIPE_G1_SETTINGS_3_G1_FFE_RES_SEL_MASK; + data |= 0x2 << HPIPE_G1_SETTINGS_3_G1_FFE_RES_SEL_OFFSET; + mask |= HPIPE_G1_SETTINGS_3_G1_FFE_SETTING_FORCE_MASK; + data |= 0x1 << HPIPE_G1_SETTINGS_3_G1_FFE_SETTING_FORCE_OFFSET; + mask |= HPIPE_G1_SETTINGS_3_G1_FFE_DEG_RES_LEVEL_MASK; + data |= 0x1 << HPIPE_G1_SETTINGS_3_G1_FFE_DEG_RES_LEVEL_OFFSET; + mask |= HPIPE_G1_SETTINGS_3_G1_FFE_LOAD_RES_LEVEL_MASK; + data |= 0x1 << HPIPE_G1_SETTINGS_3_G1_FFE_LOAD_RES_LEVEL_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SETTINGS_3_REG, data, mask); + + /* G2 settings */ + mask = HPIPE_G2_SET_1_G2_RX_SELMUPI_MASK; + data = 0x0 << HPIPE_G2_SET_1_G2_RX_SELMUPI_OFFSET; + mask |= HPIPE_G2_SET_1_G2_RX_SELMUPP_MASK; + data |= 0x1 << HPIPE_G2_SET_1_G2_RX_SELMUPP_OFFSET; + mask |= HPIPE_G2_SET_1_G2_RX_SELMUFI_MASK; + data |= 0x0 << HPIPE_G2_SET_1_G2_RX_SELMUFI_OFFSET; + mask |= HPIPE_G2_SET_1_G2_RX_SELMUFF_MASK; + data |= 0x3 << HPIPE_G2_SET_1_G2_RX_SELMUFF_OFFSET; + mask |= HPIPE_G2_SET_1_G2_RX_DIGCK_DIV_MASK; + data |= 0x1 << HPIPE_G2_SET_1_G2_RX_DIGCK_DIV_OFFSET; + reg_set(hpipe_addr + HPIPE_G2_SET_1_REG, data, mask); + + /* G3 settings */ + mask = HPIPE_G3_SET_1_G3_RX_SELMUPI_MASK; + data = 0x2 << HPIPE_G3_SET_1_G3_RX_SELMUPI_OFFSET; + mask |= HPIPE_G3_SET_1_G3_RX_SELMUPF_MASK; + data |= 0x2 << HPIPE_G3_SET_1_G3_RX_SELMUPF_OFFSET; + mask |= HPIPE_G3_SET_1_G3_RX_SELMUFI_MASK; + data |= 0x3 << HPIPE_G3_SET_1_G3_RX_SELMUFI_OFFSET; + mask |= HPIPE_G3_SET_1_G3_RX_SELMUFF_MASK; + data |= 0x3 << HPIPE_G3_SET_1_G3_RX_SELMUFF_OFFSET; + mask |= HPIPE_G3_SET_1_G3_RX_DFE_EN_MASK; + data |= 0x1 << HPIPE_G3_SET_1_G3_RX_DFE_EN_OFFSET; + mask |= HPIPE_G3_SET_1_G3_RX_DIGCK_DIV_MASK; + data |= 0x2 << HPIPE_G3_SET_1_G3_RX_DIGCK_DIV_OFFSET; + mask |= HPIPE_G3_SET_1_G3_SAMPLER_INPAIRX2_EN_MASK; + data |= 0x0 << HPIPE_G3_SET_1_G3_SAMPLER_INPAIRX2_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_G3_SET_1_REG, data, mask); + + /* DTL Control */ + mask = HPIPE_PWR_CTR_DTL_SQ_DET_EN_MASK; + data = 0x1 << HPIPE_PWR_CTR_DTL_SQ_DET_EN_OFFSET; + mask |= HPIPE_PWR_CTR_DTL_SQ_PLOOP_EN_MASK; + data |= 0x1 << HPIPE_PWR_CTR_DTL_SQ_PLOOP_EN_OFFSET; + mask |= HPIPE_PWR_CTR_DTL_FLOOP_EN_MASK; + data |= 0x1 << HPIPE_PWR_CTR_DTL_FLOOP_EN_OFFSET; + mask |= HPIPE_PWR_CTR_DTL_CLAMPING_SEL_MASK; + data |= 0x1 << HPIPE_PWR_CTR_DTL_CLAMPING_SEL_OFFSET; + mask |= HPIPE_PWR_CTR_DTL_INTPCLK_DIV_FORCE_MASK; + data |= 0x1 << HPIPE_PWR_CTR_DTL_INTPCLK_DIV_FORCE_OFFSET; + mask |= HPIPE_PWR_CTR_DTL_CLK_MODE_MASK; + data |= 0x1 << HPIPE_PWR_CTR_DTL_CLK_MODE_OFFSET; + mask |= HPIPE_PWR_CTR_DTL_CLK_MODE_FORCE_MASK; + data |= 0x1 << HPIPE_PWR_CTR_DTL_CLK_MODE_FORCE_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_CTR_DTL_REG, data, mask); + + /* Trigger sampler enable pulse */ + mask = HPIPE_SMAPLER_MASK; + data = 0x1 << HPIPE_SMAPLER_OFFSET; + reg_set(hpipe_addr + HPIPE_SAMPLER_N_PROC_CALIB_CTRL_REG, data, mask); + mask = HPIPE_SMAPLER_MASK; + data = 0x0 << HPIPE_SMAPLER_OFFSET; + reg_set(hpipe_addr + HPIPE_SAMPLER_N_PROC_CALIB_CTRL_REG, data, mask); + + /* VDD Calibration Control 3 */ + mask = HPIPE_EXT_SELLV_RXSAMPL_MASK; + data = 0x10 << HPIPE_EXT_SELLV_RXSAMPL_OFFSET; + reg_set(hpipe_addr + HPIPE_VDD_CAL_CTRL_REG, data, mask); + + /* DFE Resolution Control */ + mask = HPIPE_DFE_RES_FORCE_MASK; + data = 0x1 << HPIPE_DFE_RES_FORCE_OFFSET; + reg_set(hpipe_addr + HPIPE_DFE_REG0, data, mask); + + /* DFE F3-F5 Coefficient Control */ + mask = HPIPE_DFE_F3_F5_DFE_EN_MASK; + data = 0x0 << HPIPE_DFE_F3_F5_DFE_EN_OFFSET; + mask |= HPIPE_DFE_F3_F5_DFE_CTRL_MASK; + data = 0x0 << HPIPE_DFE_F3_F5_DFE_CTRL_OFFSET; + reg_set(hpipe_addr + HPIPE_DFE_F3_F5_REG, data, mask); + + /* G3 Setting 3 */ + mask = HPIPE_G3_FFE_CAP_SEL_MASK; + data = 0xf << HPIPE_G3_FFE_CAP_SEL_OFFSET; + mask |= HPIPE_G3_FFE_RES_SEL_MASK; + data |= 0x4 << HPIPE_G3_FFE_RES_SEL_OFFSET; + mask |= HPIPE_G3_FFE_SETTING_FORCE_MASK; + data |= 0x1 << HPIPE_G3_FFE_SETTING_FORCE_OFFSET; + mask |= HPIPE_G3_FFE_DEG_RES_LEVEL_MASK; + data |= 0x1 << HPIPE_G3_FFE_DEG_RES_LEVEL_OFFSET; + mask |= HPIPE_G3_FFE_LOAD_RES_LEVEL_MASK; + data |= 0x3 << HPIPE_G3_FFE_LOAD_RES_LEVEL_OFFSET; + reg_set(hpipe_addr + HPIPE_G3_SETTING_3_REG, data, mask); + + /* G3 Setting 4 */ + mask = HPIPE_G3_DFE_RES_MASK; + data = 0x1 << HPIPE_G3_DFE_RES_OFFSET; + reg_set(hpipe_addr + HPIPE_G3_SETTING_4_REG, data, mask); + + /* Offset Phase Control */ + mask = HPIPE_OS_PH_OFFSET_MASK; + data = 0x61 << HPIPE_OS_PH_OFFSET_OFFSET; + mask |= HPIPE_OS_PH_OFFSET_FORCE_MASK; + data |= 0x1 << HPIPE_OS_PH_OFFSET_FORCE_OFFSET; + mask |= HPIPE_OS_PH_VALID_MASK; + data |= 0x0 << HPIPE_OS_PH_VALID_OFFSET; + reg_set(hpipe_addr + HPIPE_PHASE_CONTROL_REG, data, mask); + mask = HPIPE_OS_PH_VALID_MASK; + data = 0x1 << HPIPE_OS_PH_VALID_OFFSET; + reg_set(hpipe_addr + HPIPE_PHASE_CONTROL_REG, data, mask); + mask = HPIPE_OS_PH_VALID_MASK; + data = 0x0 << HPIPE_OS_PH_VALID_OFFSET; + reg_set(hpipe_addr + HPIPE_PHASE_CONTROL_REG, data, mask); + + /* Set G1 TX amplitude and TX post emphasis value */ + mask = HPIPE_G1_SET_0_G1_TX_AMP_MASK; + data = 0x8 << HPIPE_G1_SET_0_G1_TX_AMP_OFFSET; + mask |= HPIPE_G1_SET_0_G1_TX_AMP_ADJ_MASK; + data |= 0x1 << HPIPE_G1_SET_0_G1_TX_AMP_ADJ_OFFSET; + mask |= HPIPE_G1_SET_0_G1_TX_EMPH1_MASK; + data |= 0x1 << HPIPE_G1_SET_0_G1_TX_EMPH1_OFFSET; + mask |= HPIPE_G1_SET_0_G1_TX_EMPH1_EN_MASK; + data |= 0x1 << HPIPE_G1_SET_0_G1_TX_EMPH1_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SET_0_REG, data, mask); + + /* Set G2 TX amplitude and TX post emphasis value */ + mask = HPIPE_G2_SET_0_G2_TX_AMP_MASK; + data = 0xa << HPIPE_G2_SET_0_G2_TX_AMP_OFFSET; + mask |= HPIPE_G2_SET_0_G2_TX_AMP_ADJ_MASK; + data |= 0x1 << HPIPE_G2_SET_0_G2_TX_AMP_ADJ_OFFSET; + mask |= HPIPE_G2_SET_0_G2_TX_EMPH1_MASK; + data |= 0x2 << HPIPE_G2_SET_0_G2_TX_EMPH1_OFFSET; + mask |= HPIPE_G2_SET_0_G2_TX_EMPH1_EN_MASK; + data |= 0x1 << HPIPE_G2_SET_0_G2_TX_EMPH1_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_G2_SET_0_REG, data, mask); + + /* Set G3 TX amplitude and TX post emphasis value */ + mask = HPIPE_G3_SET_0_G3_TX_AMP_MASK; + data = 0x1e << HPIPE_G3_SET_0_G3_TX_AMP_OFFSET; + mask |= HPIPE_G3_SET_0_G3_TX_AMP_ADJ_MASK; + data |= 0x1 << HPIPE_G3_SET_0_G3_TX_AMP_ADJ_OFFSET; + mask |= HPIPE_G3_SET_0_G3_TX_EMPH1_MASK; + data |= 0xe << HPIPE_G3_SET_0_G3_TX_EMPH1_OFFSET; + mask |= HPIPE_G3_SET_0_G3_TX_EMPH1_EN_MASK; + data |= 0x1 << HPIPE_G3_SET_0_G3_TX_EMPH1_EN_OFFSET; + mask |= HPIPE_G3_SET_0_G3_TX_SLEW_RATE_SEL_MASK; + data |= 0x4 << HPIPE_G3_SET_0_G3_TX_SLEW_RATE_SEL_OFFSET; + mask |= HPIPE_G3_SET_0_G3_TX_SLEW_CTRL_EN_MASK; + data |= 0x0 << HPIPE_G3_SET_0_G3_TX_SLEW_CTRL_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_G3_SET_0_REG, data, mask); + + /* SERDES External Configuration 2 register */ + mask = SD_EXTERNAL_CONFIG2_SSC_ENABLE_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG2_SSC_ENABLE_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG2_REG, data, mask); + + /* DFE reset sequence */ + reg_set(hpipe_addr + HPIPE_PWR_CTR_REG, + 0x1 << HPIPE_PWR_CTR_RST_DFE_OFFSET, + HPIPE_PWR_CTR_RST_DFE_MASK); + reg_set(hpipe_addr + HPIPE_PWR_CTR_REG, + 0x0 << HPIPE_PWR_CTR_RST_DFE_OFFSET, + HPIPE_PWR_CTR_RST_DFE_MASK); + /* SW reset for interrupt logic */ + reg_set(hpipe_addr + HPIPE_PWR_CTR_REG, + 0x1 << HPIPE_PWR_CTR_SFT_RST_OFFSET, + HPIPE_PWR_CTR_SFT_RST_MASK); + reg_set(hpipe_addr + HPIPE_PWR_CTR_REG, + 0x0 << HPIPE_PWR_CTR_SFT_RST_OFFSET, + HPIPE_PWR_CTR_SFT_RST_MASK); + + debug_exit(); + + return ret; +} + +static int mvebu_cp110_comphy_sgmii_power_on(uint64_t comphy_base, + uint8_t comphy_index, uint32_t comphy_mode) +{ + uintptr_t hpipe_addr, sd_ip_addr, comphy_addr, addr; + uint32_t mask, data, sgmii_speed = COMPHY_GET_SPEED(comphy_mode); + int ret = 0; + + debug_enter(); + + hpipe_addr = HPIPE_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + sd_ip_addr = SD_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + comphy_addr = COMPHY_ADDR(comphy_base, comphy_index); + + /* configure phy selector for SGMII */ + mvebu_cp110_comphy_set_phy_selector(comphy_base, comphy_index, + comphy_mode); + + /* Confiugre the lane */ + debug("stage: RFU configurations - hard reset comphy\n"); + /* RFU configurations - hard reset comphy */ + mask = COMMON_PHY_CFG1_PWR_UP_MASK; + data = 0x1 << COMMON_PHY_CFG1_PWR_UP_OFFSET; + mask |= COMMON_PHY_CFG1_PIPE_SELECT_MASK; + data |= 0x0 << COMMON_PHY_CFG1_PIPE_SELECT_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG1_REG, data, mask); + + /* Select Baud Rate of Comphy And PD_PLL/Tx/Rx */ + mask = SD_EXTERNAL_CONFIG0_SD_PU_PLL_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG0_SD_PU_PLL_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_MASK; + mask |= SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_MASK; + + if (sgmii_speed == COMPHY_SPEED_1_25G) { + /* SGMII 1G, SerDes speed 1.25G */ + data |= 0x6 << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_OFFSET; + data |= 0x6 << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_OFFSET; + } else if (sgmii_speed == COMPHY_SPEED_3_125G) { + /* HS SGMII (2.5G), SerDes speed 3.125G */ + data |= 0x8 << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_OFFSET; + data |= 0x8 << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_OFFSET; + } else { + /* Other rates are not supported */ + ERROR("unsupported SGMII speed on comphy%d\n", comphy_index); + return -EINVAL; + } + + mask |= SD_EXTERNAL_CONFIG0_SD_PU_RX_MASK; + data |= 0 << SD_EXTERNAL_CONFIG0_SD_PU_RX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_TX_MASK; + data |= 0 << SD_EXTERNAL_CONFIG0_SD_PU_TX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_HALF_BUS_MODE_MASK; + data |= 1 << SD_EXTERNAL_CONFIG0_HALF_BUS_MODE_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG0_REG, data, mask); + + /* Set hard reset */ + mask = SD_EXTERNAL_CONFIG1_RESET_IN_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RESET_CORE_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RF_RESET_IN_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + /* Release hard reset */ + mask = SD_EXTERNAL_CONFIG1_RESET_IN_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RESET_CORE_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + /* Wait 1ms - until band gap and ref clock ready */ + mdelay(1); + + /* Make sure that 40 data bits is disabled + * This bit is not cleared by reset + */ + mask = COMMON_PHY_CFG6_IF_40_SEL_MASK; + data = 0 << COMMON_PHY_CFG6_IF_40_SEL_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG6_REG, data, mask); + + /* Start comphy Configuration */ + debug("stage: Comphy configuration\n"); + /* set reference clock */ + mask = HPIPE_MISC_REFCLK_SEL_MASK; + data = 0x0 << HPIPE_MISC_REFCLK_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_MISC_REG, data, mask); + /* Power and PLL Control */ + mask = HPIPE_PWR_PLL_REF_FREQ_MASK; + data = 0x1 << HPIPE_PWR_PLL_REF_FREQ_OFFSET; + mask |= HPIPE_PWR_PLL_PHY_MODE_MASK; + data |= 0x4 << HPIPE_PWR_PLL_PHY_MODE_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_PLL_REG, data, mask); + /* Loopback register */ + mask = HPIPE_LOOPBACK_SEL_MASK; + data = 0x1 << HPIPE_LOOPBACK_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_LOOPBACK_REG, data, mask); + /* rx control 1 */ + mask = HPIPE_RX_CONTROL_1_RXCLK2X_SEL_MASK; + data = 0x1 << HPIPE_RX_CONTROL_1_RXCLK2X_SEL_OFFSET; + mask |= HPIPE_RX_CONTROL_1_CLK8T_EN_MASK; + data |= 0x0 << HPIPE_RX_CONTROL_1_CLK8T_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_RX_CONTROL_1_REG, data, mask); + /* DTL Control */ + mask = HPIPE_PWR_CTR_DTL_FLOOP_EN_MASK; + data = 0x0 << HPIPE_PWR_CTR_DTL_FLOOP_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_CTR_DTL_REG, data, mask); + + /* Set analog parameters from ETP(HW) - for now use the default datas */ + debug("stage: Analog parameters from ETP(HW)\n"); + + reg_set(hpipe_addr + HPIPE_G1_SET_0_REG, + 0x1 << HPIPE_G1_SET_0_G1_TX_EMPH1_OFFSET, + HPIPE_G1_SET_0_G1_TX_EMPH1_MASK); + + debug("stage: RFU configurations- Power Up PLL,Tx,Rx\n"); + /* SERDES External Configuration */ + mask = SD_EXTERNAL_CONFIG0_SD_PU_PLL_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG0_SD_PU_PLL_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_RX_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG0_SD_PU_RX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_TX_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG0_SD_PU_TX_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG0_REG, data, mask); + + ret = mvebu_cp110_comphy_is_pll_locked(comphy_base, comphy_index); + if (ret) + return ret; + + /* RX init */ + mask = SD_EXTERNAL_CONFIG1_RX_INIT_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG1_RX_INIT_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + /* check that RX init done */ + addr = sd_ip_addr + SD_EXTERNAL_STATUS0_REG; + data = SD_EXTERNAL_STATUS0_RX_INIT_MASK; + mask = data; + data = polling_with_timeout(addr, data, mask, 100, REG_32BIT); + if (data != 0) { + ERROR("RX init failed\n"); + ret = -ETIMEDOUT; + } + + debug("stage: RF Reset\n"); + /* RF Reset */ + mask = SD_EXTERNAL_CONFIG1_RX_INIT_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG1_RX_INIT_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RF_RESET_IN_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + debug_exit(); + + return ret; +} + +static int mvebu_cp110_comphy_xfi_power_on(uint64_t comphy_base, + uint8_t comphy_index, + uint32_t comphy_mode) +{ + uintptr_t hpipe_addr, sd_ip_addr, comphy_addr, addr; + uint32_t mask, data, speed = COMPHY_GET_SPEED(comphy_mode); + int ret = 0; + + debug_enter(); + + if ((speed != COMPHY_SPEED_5_15625G) && + (speed != COMPHY_SPEED_10_3125G) && + (speed != COMPHY_SPEED_DEFAULT)) { + ERROR("comphy:%d: unsupported sfi/xfi speed\n", comphy_index); + return -EINVAL; + } + + hpipe_addr = HPIPE_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + sd_ip_addr = SD_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + comphy_addr = COMPHY_ADDR(comphy_base, comphy_index); + + /* configure phy selector for XFI/SFI */ + mvebu_cp110_comphy_set_phy_selector(comphy_base, comphy_index, + comphy_mode); + + debug("stage: RFU configurations - hard reset comphy\n"); + /* RFU configurations - hard reset comphy */ + mask = COMMON_PHY_CFG1_PWR_UP_MASK; + data = 0x1 << COMMON_PHY_CFG1_PWR_UP_OFFSET; + mask |= COMMON_PHY_CFG1_PIPE_SELECT_MASK; + data |= 0x0 << COMMON_PHY_CFG1_PIPE_SELECT_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG1_REG, data, mask); + + /* Make sure that 40 data bits is disabled + * This bit is not cleared by reset + */ + mask = COMMON_PHY_CFG6_IF_40_SEL_MASK; + data = 0 << COMMON_PHY_CFG6_IF_40_SEL_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG6_REG, data, mask); + + /* Select Baud Rate of Comphy And PD_PLL/Tx/Rx */ + mask = SD_EXTERNAL_CONFIG0_SD_PU_PLL_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG0_SD_PU_PLL_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_MASK; + data |= 0xE << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_MASK; + data |= 0xE << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_RX_MASK; + data |= 0 << SD_EXTERNAL_CONFIG0_SD_PU_RX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_TX_MASK; + data |= 0 << SD_EXTERNAL_CONFIG0_SD_PU_TX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_HALF_BUS_MODE_MASK; + data |= 0 << SD_EXTERNAL_CONFIG0_HALF_BUS_MODE_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG0_REG, data, mask); + + /* release from hard reset */ + mask = SD_EXTERNAL_CONFIG1_RESET_IN_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RESET_CORE_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RF_RESET_IN_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + mask = SD_EXTERNAL_CONFIG1_RESET_IN_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RESET_CORE_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + /* Wait 1ms - until band gap and ref clock ready */ + mdelay(1); + + /* Start comphy Configuration */ + debug("stage: Comphy configuration\n"); + /* set reference clock */ + mask = HPIPE_MISC_ICP_FORCE_MASK; + data = (speed == COMPHY_SPEED_5_15625G) ? + (0x0 << HPIPE_MISC_ICP_FORCE_OFFSET) : + (0x1 << HPIPE_MISC_ICP_FORCE_OFFSET); + mask |= HPIPE_MISC_REFCLK_SEL_MASK; + data |= 0x0 << HPIPE_MISC_REFCLK_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_MISC_REG, data, mask); + /* Power and PLL Control */ + mask = HPIPE_PWR_PLL_REF_FREQ_MASK; + data = 0x1 << HPIPE_PWR_PLL_REF_FREQ_OFFSET; + mask |= HPIPE_PWR_PLL_PHY_MODE_MASK; + data |= 0x4 << HPIPE_PWR_PLL_PHY_MODE_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_PLL_REG, data, mask); + /* Loopback register */ + mask = HPIPE_LOOPBACK_SEL_MASK; + data = 0x1 << HPIPE_LOOPBACK_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_LOOPBACK_REG, data, mask); + /* rx control 1 */ + mask = HPIPE_RX_CONTROL_1_RXCLK2X_SEL_MASK; + data = 0x1 << HPIPE_RX_CONTROL_1_RXCLK2X_SEL_OFFSET; + mask |= HPIPE_RX_CONTROL_1_CLK8T_EN_MASK; + data |= 0x1 << HPIPE_RX_CONTROL_1_CLK8T_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_RX_CONTROL_1_REG, data, mask); + /* DTL Control */ + mask = HPIPE_PWR_CTR_DTL_FLOOP_EN_MASK; + data = 0x1 << HPIPE_PWR_CTR_DTL_FLOOP_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_CTR_DTL_REG, data, mask); + + /* Transmitter/Receiver Speed Divider Force */ + if (speed == COMPHY_SPEED_5_15625G) { + mask = HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_MASK; + data = 1 << HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_OFFSET; + mask |= HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_FORCE_MASK; + data |= 1 << HPIPE_SPD_DIV_FORCE_RX_SPD_DIV_FORCE_OFFSET; + mask |= HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_MASK; + data |= 1 << HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_OFFSET; + mask |= HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_FORCE_MASK; + data |= 1 << HPIPE_SPD_DIV_FORCE_TX_SPD_DIV_FORCE_OFFSET; + } else { + mask = HPIPE_TXDIGCK_DIV_FORCE_MASK; + data = 0x1 << HPIPE_TXDIGCK_DIV_FORCE_OFFSET; + } + reg_set(hpipe_addr + HPIPE_SPD_DIV_FORCE_REG, data, mask); + + /* Set analog parameters from ETP(HW) */ + debug("stage: Analog parameters from ETP(HW)\n"); + /* SERDES External Configuration 2 */ + mask = SD_EXTERNAL_CONFIG2_PIN_DFE_EN_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG2_PIN_DFE_EN_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG2_REG, data, mask); + /* 0x7-DFE Resolution control */ + mask = HPIPE_DFE_RES_FORCE_MASK; + data = 0x1 << HPIPE_DFE_RES_FORCE_OFFSET; + reg_set(hpipe_addr + HPIPE_DFE_REG0, data, mask); + /* 0xd-G1_Setting_0 */ + if (speed == COMPHY_SPEED_5_15625G) { + mask = HPIPE_G1_SET_0_G1_TX_EMPH1_MASK; + data = 0x6 << HPIPE_G1_SET_0_G1_TX_EMPH1_OFFSET; + } else { + mask = HPIPE_G1_SET_0_G1_TX_AMP_MASK; + data = 0x1c << HPIPE_G1_SET_0_G1_TX_AMP_OFFSET; + mask |= HPIPE_G1_SET_0_G1_TX_EMPH1_MASK; + data |= 0xe << HPIPE_G1_SET_0_G1_TX_EMPH1_OFFSET; + } + reg_set(hpipe_addr + HPIPE_G1_SET_0_REG, data, mask); + /* Genration 1 setting 2 (G1_Setting_2) */ + mask = HPIPE_G1_SET_2_G1_TX_EMPH0_MASK; + data = 0x0 << HPIPE_G1_SET_2_G1_TX_EMPH0_OFFSET; + mask |= HPIPE_G1_SET_2_G1_TX_EMPH0_EN_MASK; + data |= 0x1 << HPIPE_G1_SET_2_G1_TX_EMPH0_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SET_2_REG, data, mask); + /* Transmitter Slew Rate Control register (tx_reg1) */ + mask = HPIPE_TX_REG1_TX_EMPH_RES_MASK; + data = 0x3 << HPIPE_TX_REG1_TX_EMPH_RES_OFFSET; + mask |= HPIPE_TX_REG1_SLC_EN_MASK; + data |= 0x3f << HPIPE_TX_REG1_SLC_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_REG1_REG, data, mask); + /* Impedance Calibration Control register (cal_reg1) */ + mask = HPIPE_CAL_REG_1_EXT_TXIMP_MASK; + data = 0xe << HPIPE_CAL_REG_1_EXT_TXIMP_OFFSET; + mask |= HPIPE_CAL_REG_1_EXT_TXIMP_EN_MASK; + data |= 0x1 << HPIPE_CAL_REG_1_EXT_TXIMP_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_CAL_REG1_REG, data, mask); + /* Generation 1 Setting 5 (g1_setting_5) */ + mask = HPIPE_G1_SETTING_5_G1_ICP_MASK; + data = 0 << HPIPE_CAL_REG_1_EXT_TXIMP_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SETTING_5_REG, data, mask); + + /* 0xE-G1_Setting_1 */ + mask = HPIPE_G1_SET_1_G1_RX_DFE_EN_MASK; + data = 0x1 << HPIPE_G1_SET_1_G1_RX_DFE_EN_OFFSET; + if (speed == COMPHY_SPEED_5_15625G) { + mask |= HPIPE_G1_SET_1_G1_RX_SELMUPI_MASK; + data |= 0x1 << HPIPE_G1_SET_1_G1_RX_SELMUPI_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_SELMUPP_MASK; + data |= 0x1 << HPIPE_G1_SET_1_G1_RX_SELMUPP_OFFSET; + } else { + mask |= HPIPE_G1_SET_1_G1_RX_SELMUPI_MASK; + data |= 0x2 << HPIPE_G1_SET_1_G1_RX_SELMUPI_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_SELMUPP_MASK; + data |= 0x2 << HPIPE_G1_SET_1_G1_RX_SELMUPP_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_SELMUFI_MASK; + data |= 0x0 << HPIPE_G1_SET_1_G1_RX_SELMUFI_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_SELMUFF_MASK; + data |= 0x1 << HPIPE_G1_SET_1_G1_RX_SELMUFF_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_DIGCK_DIV_MASK; + data |= 0x3 << HPIPE_G1_SET_1_G1_RX_DIGCK_DIV_OFFSET; + } + reg_set(hpipe_addr + HPIPE_G1_SET_1_REG, data, mask); + + /* 0xA-DFE_Reg3 */ + mask = HPIPE_DFE_F3_F5_DFE_EN_MASK; + data = 0x0 << HPIPE_DFE_F3_F5_DFE_EN_OFFSET; + mask |= HPIPE_DFE_F3_F5_DFE_CTRL_MASK; + data |= 0x0 << HPIPE_DFE_F3_F5_DFE_CTRL_OFFSET; + reg_set(hpipe_addr + HPIPE_DFE_F3_F5_REG, data, mask); + + /* 0x111-G1_Setting_4 */ + mask = HPIPE_G1_SETTINGS_4_G1_DFE_RES_MASK; + data = 0x1 << HPIPE_G1_SETTINGS_4_G1_DFE_RES_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SETTINGS_4_REG, data, mask); + /* Genration 1 setting 3 (G1_Setting_3) */ + mask = HPIPE_G1_SETTINGS_3_G1_FBCK_SEL_MASK; + data = 0x1 << HPIPE_G1_SETTINGS_3_G1_FBCK_SEL_OFFSET; + if (speed == COMPHY_SPEED_5_15625G) { + /* Force FFE (Feed Forward Equalization) to 5G */ + mask |= HPIPE_G1_SETTINGS_3_G1_FFE_CAP_SEL_MASK; + data |= 0xf << HPIPE_G1_SETTINGS_3_G1_FFE_CAP_SEL_OFFSET; + mask |= HPIPE_G1_SETTINGS_3_G1_FFE_RES_SEL_MASK; + data |= 0x4 << HPIPE_G1_SETTINGS_3_G1_FFE_RES_SEL_OFFSET; + mask |= HPIPE_G1_SETTINGS_3_G1_FFE_SETTING_FORCE_MASK; + data |= 0x1 << HPIPE_G1_SETTINGS_3_G1_FFE_SETTING_FORCE_OFFSET; + } + reg_set(hpipe_addr + HPIPE_G1_SETTINGS_3_REG, data, mask); + + /* Connfigure RX training timer */ + mask = HPIPE_RX_TRAIN_TIMER_MASK; + data = 0x13 << HPIPE_RX_TRAIN_TIMER_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_CTRL_5_REG, data, mask); + + /* Enable TX train peak to peak hold */ + mask = HPIPE_TX_TRAIN_P2P_HOLD_MASK; + data = 0x1 << HPIPE_TX_TRAIN_P2P_HOLD_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_CTRL_0_REG, data, mask); + + /* Configure TX preset index */ + mask = HPIPE_TX_PRESET_INDEX_MASK; + data = 0x2 << HPIPE_TX_PRESET_INDEX_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_PRESET_INDEX_REG, data, mask); + + /* Disable pattern lock lost timeout */ + mask = HPIPE_PATTERN_LOCK_LOST_TIMEOUT_EN_MASK; + data = 0x0 << HPIPE_PATTERN_LOCK_LOST_TIMEOUT_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_FRAME_DETECT_CTRL_3_REG, data, mask); + + /* Configure TX training pattern and TX training 16bit auto */ + mask = HPIPE_TX_TRAIN_16BIT_AUTO_EN_MASK; + data = 0x1 << HPIPE_TX_TRAIN_16BIT_AUTO_EN_OFFSET; + mask |= HPIPE_TX_TRAIN_PAT_SEL_MASK; + data |= 0x1 << HPIPE_TX_TRAIN_PAT_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_REG, data, mask); + + /* Configure Training patten number */ + mask = HPIPE_TRAIN_PAT_NUM_MASK; + data = 0x88 << HPIPE_TRAIN_PAT_NUM_OFFSET; + reg_set(hpipe_addr + HPIPE_FRAME_DETECT_CTRL_0_REG, data, mask); + + /* Configure differencial manchester encoter to ethernet mode */ + mask = HPIPE_DME_ETHERNET_MODE_MASK; + data = 0x1 << HPIPE_DME_ETHERNET_MODE_OFFSET; + reg_set(hpipe_addr + HPIPE_DME_REG, data, mask); + + /* Configure VDD Continuous Calibration */ + mask = HPIPE_CAL_VDD_CONT_MODE_MASK; + data = 0x1 << HPIPE_CAL_VDD_CONT_MODE_OFFSET; + reg_set(hpipe_addr + HPIPE_VDD_CAL_0_REG, data, mask); + + /* Trigger sampler enable pulse (by toggleing the bit) */ + mask = HPIPE_RX_SAMPLER_OS_GAIN_MASK; + data = 0x3 << HPIPE_RX_SAMPLER_OS_GAIN_OFFSET; + mask |= HPIPE_SMAPLER_MASK; + data |= 0x1 << HPIPE_SMAPLER_OFFSET; + reg_set(hpipe_addr + HPIPE_SAMPLER_N_PROC_CALIB_CTRL_REG, data, mask); + mask = HPIPE_SMAPLER_MASK; + data = 0x0 << HPIPE_SMAPLER_OFFSET; + reg_set(hpipe_addr + HPIPE_SAMPLER_N_PROC_CALIB_CTRL_REG, data, mask); + + /* Set External RX Regulator Control */ + mask = HPIPE_EXT_SELLV_RXSAMPL_MASK; + data = 0x1A << HPIPE_EXT_SELLV_RXSAMPL_OFFSET; + reg_set(hpipe_addr + HPIPE_VDD_CAL_CTRL_REG, data, mask); + + debug("stage: RFU configurations- Power Up PLL,Tx,Rx\n"); + /* SERDES External Configuration */ + mask = SD_EXTERNAL_CONFIG0_SD_PU_PLL_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG0_SD_PU_PLL_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_RX_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG0_SD_PU_RX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_TX_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG0_SD_PU_TX_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG0_REG, data, mask); + + /* check PLL rx & tx ready */ + addr = sd_ip_addr + SD_EXTERNAL_STATUS0_REG; + data = SD_EXTERNAL_STATUS0_PLL_RX_MASK | + SD_EXTERNAL_STATUS0_PLL_TX_MASK; + mask = data; + data = polling_with_timeout(addr, data, mask, + PLL_LOCK_TIMEOUT, REG_32BIT); + if (data != 0) { + if (data & SD_EXTERNAL_STATUS0_PLL_RX_MASK) + ERROR("RX PLL is not locked\n"); + if (data & SD_EXTERNAL_STATUS0_PLL_TX_MASK) + ERROR("TX PLL is not locked\n"); + + ret = -ETIMEDOUT; + } + + /* RX init */ + mask = SD_EXTERNAL_CONFIG1_RX_INIT_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG1_RX_INIT_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + /* check that RX init done */ + addr = sd_ip_addr + SD_EXTERNAL_STATUS0_REG; + data = SD_EXTERNAL_STATUS0_RX_INIT_MASK; + mask = data; + data = polling_with_timeout(addr, data, mask, 100, REG_32BIT); + if (data != 0) { + ERROR("RX init failed\n"); + ret = -ETIMEDOUT; + } + + debug("stage: RF Reset\n"); + /* RF Reset */ + mask = SD_EXTERNAL_CONFIG1_RX_INIT_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG1_RX_INIT_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RF_RESET_IN_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + debug_exit(); + + return ret; +} + +static int mvebu_cp110_comphy_pcie_power_on(uint64_t comphy_base, + uint8_t comphy_index, uint32_t comphy_mode) +{ + int ret = 0; + uint32_t reg, mask, data, pcie_width; + uint32_t clk_dir; + uintptr_t hpipe_addr, comphy_addr, addr; + _Bool clk_src = COMPHY_GET_CLK_SRC(comphy_mode); + + hpipe_addr = HPIPE_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + comphy_addr = COMPHY_ADDR(comphy_base, comphy_index); + pcie_width = COMPHY_GET_PCIE_WIDTH(comphy_mode); + + debug_enter(); + + spin_lock(&cp110_mac_reset_lock); + + reg = mmio_read_32(SYS_CTRL_FROM_COMPHY_ADDR(comphy_base) + + SYS_CTRL_UINIT_SOFT_RESET_REG); + switch (comphy_index) { + case COMPHY_LANE0: + reg |= PCIE_MAC_RESET_MASK_PORT0; + break; + case COMPHY_LANE4: + reg |= PCIE_MAC_RESET_MASK_PORT1; + break; + case COMPHY_LANE5: + reg |= PCIE_MAC_RESET_MASK_PORT2; + break; + } + + mmio_write_32(SYS_CTRL_FROM_COMPHY_ADDR(comphy_base) + + SYS_CTRL_UINIT_SOFT_RESET_REG, reg); + spin_unlock(&cp110_mac_reset_lock); + + /* Configure PIPE selector for PCIE */ + mvebu_cp110_comphy_set_pipe_selector(comphy_base, comphy_index, + comphy_mode); + + /* + * Read SAR (Sample-At-Reset) configuration for the PCIe clock + * direction. + * + * SerDes Lane 4/5 got the PCIe ref-clock #1, + * and SerDes Lane 0 got PCIe ref-clock #0 + */ + reg = mmio_read_32(DFX_FROM_COMPHY_ADDR(comphy_base) + + SAR_STATUS_0_REG); + if (comphy_index == COMPHY_LANE4 || comphy_index == COMPHY_LANE5) + clk_dir = (reg & SAR_RST_PCIE1_CLOCK_CONFIG_CP1_MASK) >> + SAR_RST_PCIE1_CLOCK_CONFIG_CP1_OFFSET; + else + clk_dir = (reg & SAR_RST_PCIE0_CLOCK_CONFIG_CP1_MASK) >> + SAR_RST_PCIE0_CLOCK_CONFIG_CP1_OFFSET; + + debug("On lane %d\n", comphy_index); + debug("PCIe clock direction = %x\n", clk_dir); + debug("PCIe Width = %d\n", pcie_width); + + /* enable PCIe X4 and X2 */ + if (comphy_index == COMPHY_LANE0) { + if (pcie_width == PCIE_LNK_X4) { + data = 0x1 << COMMON_PHY_SD_CTRL1_PCIE_X4_EN_OFFSET; + mask = COMMON_PHY_SD_CTRL1_PCIE_X4_EN_MASK; + reg_set(comphy_base + COMMON_PHY_SD_CTRL1, + data, mask); + } else if (pcie_width == PCIE_LNK_X2) { + data = 0x1 << COMMON_PHY_SD_CTRL1_PCIE_X2_EN_OFFSET; + mask = COMMON_PHY_SD_CTRL1_PCIE_X2_EN_MASK; + reg_set(comphy_base + COMMON_PHY_SD_CTRL1, data, mask); + } + } + + /* If PCIe clock is output and clock source from SerDes lane 5, + * need to configure the clock-source MUX. + * By default, the clock source is from lane 4 + */ + if (clk_dir && clk_src && (comphy_index == COMPHY_LANE5)) { + data = DFX_DEV_GEN_PCIE_CLK_SRC_MUX << + DFX_DEV_GEN_PCIE_CLK_SRC_OFFSET; + mask = DFX_DEV_GEN_PCIE_CLK_SRC_MASK; + reg_set(DFX_FROM_COMPHY_ADDR(comphy_base) + + DFX_DEV_GEN_CTRL12_REG, data, mask); + } + + debug("stage: RFU configurations - hard reset comphy\n"); + /* RFU configurations - hard reset comphy */ + mask = COMMON_PHY_CFG1_PWR_UP_MASK; + data = 0x1 << COMMON_PHY_CFG1_PWR_UP_OFFSET; + mask |= COMMON_PHY_CFG1_PIPE_SELECT_MASK; + data |= 0x1 << COMMON_PHY_CFG1_PIPE_SELECT_OFFSET; + mask |= COMMON_PHY_CFG1_PWR_ON_RESET_MASK; + data |= 0x0 << COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET; + mask |= COMMON_PHY_CFG1_CORE_RSTN_MASK; + data |= 0x0 << COMMON_PHY_CFG1_CORE_RSTN_OFFSET; + mask |= COMMON_PHY_PHY_MODE_MASK; + data |= 0x0 << COMMON_PHY_PHY_MODE_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG1_REG, data, mask); + + /* release from hard reset */ + mask = COMMON_PHY_CFG1_PWR_ON_RESET_MASK; + data = 0x1 << COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET; + mask |= COMMON_PHY_CFG1_CORE_RSTN_MASK; + data |= 0x1 << COMMON_PHY_CFG1_CORE_RSTN_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG1_REG, data, mask); + + /* Wait 1ms - until band gap and ref clock ready */ + mdelay(1); + /* Start comphy Configuration */ + debug("stage: Comphy configuration\n"); + /* Set PIPE soft reset */ + mask = HPIPE_RST_CLK_CTRL_PIPE_RST_MASK; + data = 0x1 << HPIPE_RST_CLK_CTRL_PIPE_RST_OFFSET; + /* Set PHY datapath width mode for V0 */ + mask |= HPIPE_RST_CLK_CTRL_FIXED_PCLK_MASK; + data |= 0x1 << HPIPE_RST_CLK_CTRL_FIXED_PCLK_OFFSET; + /* Set Data bus width USB mode for V0 */ + mask |= HPIPE_RST_CLK_CTRL_PIPE_WIDTH_MASK; + data |= 0x0 << HPIPE_RST_CLK_CTRL_PIPE_WIDTH_OFFSET; + /* Set CORE_CLK output frequency for 250Mhz */ + mask |= HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_MASK; + data |= 0x0 << HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_RST_CLK_CTRL_REG, data, mask); + /* Set PLL ready delay for 0x2 */ + data = 0x2 << HPIPE_CLK_SRC_LO_PLL_RDY_DL_OFFSET; + mask = HPIPE_CLK_SRC_LO_PLL_RDY_DL_MASK; + if (pcie_width != PCIE_LNK_X1) { + data |= 0x1 << HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SEL_OFFSET; + mask |= HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SEL_MASK; + data |= 0x1 << HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SCALE_OFFSET; + mask |= HPIPE_CLK_SRC_LO_BUNDLE_PERIOD_SCALE_MASK; + } + reg_set(hpipe_addr + HPIPE_CLK_SRC_LO_REG, data, mask); + + /* Set PIPE mode interface to PCIe3 - 0x1 & set lane order */ + data = 0x1 << HPIPE_CLK_SRC_HI_MODE_PIPE_OFFSET; + mask = HPIPE_CLK_SRC_HI_MODE_PIPE_MASK; + if (pcie_width != PCIE_LNK_X1) { + mask |= HPIPE_CLK_SRC_HI_LANE_STRT_MASK; + mask |= HPIPE_CLK_SRC_HI_LANE_MASTER_MASK; + mask |= HPIPE_CLK_SRC_HI_LANE_BREAK_MASK; + if (comphy_index == 0) { + data |= 0x1 << HPIPE_CLK_SRC_HI_LANE_STRT_OFFSET; + data |= 0x1 << HPIPE_CLK_SRC_HI_LANE_MASTER_OFFSET; + } else if (comphy_index == (pcie_width - 1)) { + data |= 0x1 << HPIPE_CLK_SRC_HI_LANE_BREAK_OFFSET; + } + } + reg_set(hpipe_addr + HPIPE_CLK_SRC_HI_REG, data, mask); + /* Config update polarity equalization */ + data = 0x1 << HPIPE_CFG_UPDATE_POLARITY_OFFSET; + mask = HPIPE_CFG_UPDATE_POLARITY_MASK; + reg_set(hpipe_addr + HPIPE_LANE_EQ_CFG1_REG, data, mask); + /* Set PIPE version 4 to mode enable */ + data = 0x1 << HPIPE_DFE_CTRL_28_PIPE4_OFFSET; + mask = HPIPE_DFE_CTRL_28_PIPE4_MASK; + reg_set(hpipe_addr + HPIPE_DFE_CTRL_28_REG, data, mask); + /* TODO: check if pcie clock is output/input - for bringup use input*/ + /* Enable PIN clock 100M_125M */ + mask = 0; + data = 0; + /* Only if clock is output, configure the clock-source mux */ + if (clk_dir) { + mask |= HPIPE_MISC_CLK100M_125M_MASK; + data |= 0x1 << HPIPE_MISC_CLK100M_125M_OFFSET; + } + /* Set PIN_TXDCLK_2X Clock Freq. Selection for outputs 500MHz clock */ + mask |= HPIPE_MISC_TXDCLK_2X_MASK; + data |= 0x0 << HPIPE_MISC_TXDCLK_2X_OFFSET; + /* Enable 500MHz Clock */ + mask |= HPIPE_MISC_CLK500_EN_MASK; + data |= 0x1 << HPIPE_MISC_CLK500_EN_OFFSET; + if (clk_dir) { /* output */ + /* Set reference clock comes from group 1 */ + mask |= HPIPE_MISC_REFCLK_SEL_MASK; + data |= 0x0 << HPIPE_MISC_REFCLK_SEL_OFFSET; + } else { + /* Set reference clock comes from group 2 */ + mask |= HPIPE_MISC_REFCLK_SEL_MASK; + data |= 0x1 << HPIPE_MISC_REFCLK_SEL_OFFSET; + } + mask |= HPIPE_MISC_ICP_FORCE_MASK; + data |= 0x1 << HPIPE_MISC_ICP_FORCE_OFFSET; + reg_set(hpipe_addr + HPIPE_MISC_REG, data, mask); + if (clk_dir) { /* output */ + /* Set reference frequcency select - 0x2 for 25MHz*/ + mask = HPIPE_PWR_PLL_REF_FREQ_MASK; + data = 0x2 << HPIPE_PWR_PLL_REF_FREQ_OFFSET; + } else { + /* Set reference frequcency select - 0x0 for 100MHz*/ + mask = HPIPE_PWR_PLL_REF_FREQ_MASK; + data = 0x0 << HPIPE_PWR_PLL_REF_FREQ_OFFSET; + } + /* Set PHY mode to PCIe */ + mask |= HPIPE_PWR_PLL_PHY_MODE_MASK; + data |= 0x3 << HPIPE_PWR_PLL_PHY_MODE_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_PLL_REG, data, mask); + + /* ref clock alignment */ + if (pcie_width != PCIE_LNK_X1) { + mask = HPIPE_LANE_ALIGN_OFF_MASK; + data = 0x0 << HPIPE_LANE_ALIGN_OFF_OFFSET; + reg_set(hpipe_addr + HPIPE_LANE_ALIGN_REG, data, mask); + } + + /* Set the amount of time spent in the LoZ state - set for 0x7 only if + * the PCIe clock is output + */ + if (clk_dir) + reg_set(hpipe_addr + HPIPE_GLOBAL_PM_CTRL, + 0x7 << HPIPE_GLOBAL_PM_RXDLOZ_WAIT_OFFSET, + HPIPE_GLOBAL_PM_RXDLOZ_WAIT_MASK); + + /* Set Maximal PHY Generation Setting(8Gbps) */ + mask = HPIPE_INTERFACE_GEN_MAX_MASK; + data = 0x2 << HPIPE_INTERFACE_GEN_MAX_OFFSET; + /* Bypass frame detection and sync detection for RX DATA */ + mask |= HPIPE_INTERFACE_DET_BYPASS_MASK; + data |= 0x1 << HPIPE_INTERFACE_DET_BYPASS_OFFSET; + /* Set Link Train Mode (Tx training control pins are used) */ + mask |= HPIPE_INTERFACE_LINK_TRAIN_MASK; + data |= 0x1 << HPIPE_INTERFACE_LINK_TRAIN_OFFSET; + reg_set(hpipe_addr + HPIPE_INTERFACE_REG, data, mask); + + /* Set Idle_sync enable */ + mask = HPIPE_PCIE_IDLE_SYNC_MASK; + data = 0x1 << HPIPE_PCIE_IDLE_SYNC_OFFSET; + /* Select bits for PCIE Gen3(32bit) */ + mask |= HPIPE_PCIE_SEL_BITS_MASK; + data |= 0x2 << HPIPE_PCIE_SEL_BITS_OFFSET; + reg_set(hpipe_addr + HPIPE_PCIE_REG0, data, mask); + + /* Enable Tx_adapt_g1 */ + mask = HPIPE_TX_TRAIN_CTRL_G1_MASK; + data = 0x1 << HPIPE_TX_TRAIN_CTRL_G1_OFFSET; + /* Enable Tx_adapt_gn1 */ + mask |= HPIPE_TX_TRAIN_CTRL_GN1_MASK; + data |= 0x1 << HPIPE_TX_TRAIN_CTRL_GN1_OFFSET; + /* Disable Tx_adapt_g0 */ + mask |= HPIPE_TX_TRAIN_CTRL_G0_MASK; + data |= 0x0 << HPIPE_TX_TRAIN_CTRL_G0_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_CTRL_REG, data, mask); + + /* Set reg_tx_train_chk_init */ + mask = HPIPE_TX_TRAIN_CHK_INIT_MASK; + data = 0x0 << HPIPE_TX_TRAIN_CHK_INIT_OFFSET; + /* Enable TX_COE_FM_PIN_PCIE3_EN */ + mask |= HPIPE_TX_TRAIN_COE_FM_PIN_PCIE3_MASK; + data |= 0x1 << HPIPE_TX_TRAIN_COE_FM_PIN_PCIE3_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_REG, data, mask); + + debug("stage: TRx training parameters\n"); + /* Set Preset sweep configurations */ + mask = HPIPE_TX_TX_STATUS_CHECK_MODE_MASK; + data = 0x1 << HPIPE_TX_STATUS_CHECK_MODE_OFFSET; + mask |= HPIPE_TX_NUM_OF_PRESET_MASK; + data |= 0x7 << HPIPE_TX_NUM_OF_PRESET_OFFSET; + mask |= HPIPE_TX_SWEEP_PRESET_EN_MASK; + data |= 0x1 << HPIPE_TX_SWEEP_PRESET_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_CTRL_11_REG, data, mask); + + /* Tx train start configuration */ + mask = HPIPE_TX_TRAIN_START_SQ_EN_MASK; + data = 0x1 << HPIPE_TX_TRAIN_START_SQ_EN_OFFSET; + mask |= HPIPE_TX_TRAIN_START_FRM_DET_EN_MASK; + data |= 0x0 << HPIPE_TX_TRAIN_START_FRM_DET_EN_OFFSET; + mask |= HPIPE_TX_TRAIN_START_FRM_LOCK_EN_MASK; + data |= 0x0 << HPIPE_TX_TRAIN_START_FRM_LOCK_EN_OFFSET; + mask |= HPIPE_TX_TRAIN_WAIT_TIME_EN_MASK; + data |= 0x1 << HPIPE_TX_TRAIN_WAIT_TIME_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_CTRL_5_REG, data, mask); + + /* Enable Tx train P2P */ + mask = HPIPE_TX_TRAIN_P2P_HOLD_MASK; + data = 0x1 << HPIPE_TX_TRAIN_P2P_HOLD_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_CTRL_0_REG, data, mask); + + /* Configure Tx train timeout */ + mask = HPIPE_TRX_TRAIN_TIMER_MASK; + data = 0x17 << HPIPE_TRX_TRAIN_TIMER_OFFSET; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_CTRL_4_REG, data, mask); + + /* Disable G0/G1/GN1 adaptation */ + mask = HPIPE_TX_TRAIN_CTRL_G1_MASK | HPIPE_TX_TRAIN_CTRL_GN1_MASK + | HPIPE_TX_TRAIN_CTRL_G0_OFFSET; + data = 0; + reg_set(hpipe_addr + HPIPE_TX_TRAIN_CTRL_REG, data, mask); + + /* Disable DTL frequency loop */ + mask = HPIPE_PWR_CTR_DTL_FLOOP_EN_MASK; + data = 0x0 << HPIPE_PWR_CTR_DTL_FLOOP_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_CTR_DTL_REG, data, mask); + + /* Configure G3 DFE */ + mask = HPIPE_G3_DFE_RES_MASK; + data = 0x3 << HPIPE_G3_DFE_RES_OFFSET; + reg_set(hpipe_addr + HPIPE_G3_SETTING_4_REG, data, mask); + + /* Use TX/RX training result for DFE */ + mask = HPIPE_DFE_RES_FORCE_MASK; + data = 0x0 << HPIPE_DFE_RES_FORCE_OFFSET; + reg_set(hpipe_addr + HPIPE_DFE_REG0, data, mask); + + /* Configure initial and final coefficient value for receiver */ + mask = HPIPE_G3_SET_1_G3_RX_SELMUPI_MASK; + data = 0x1 << HPIPE_G3_SET_1_G3_RX_SELMUPI_OFFSET; + + mask |= HPIPE_G3_SET_1_G3_RX_SELMUPF_MASK; + data |= 0x1 << HPIPE_G3_SET_1_G3_RX_SELMUPF_OFFSET; + + mask |= HPIPE_G3_SET_1_G3_SAMPLER_INPAIRX2_EN_MASK; + data |= 0x0 << HPIPE_G3_SET_1_G3_SAMPLER_INPAIRX2_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_G3_SET_1_REG, data, mask); + + /* Trigger sampler enable pulse */ + mask = HPIPE_SMAPLER_MASK; + data = 0x1 << HPIPE_SMAPLER_OFFSET; + reg_set(hpipe_addr + HPIPE_SAMPLER_N_PROC_CALIB_CTRL_REG, data, mask); + udelay(5); + reg_set(hpipe_addr + HPIPE_SAMPLER_N_PROC_CALIB_CTRL_REG, 0, mask); + + /* FFE resistor tuning for different bandwidth */ + mask = HPIPE_G3_FFE_DEG_RES_LEVEL_MASK; + data = 0x1 << HPIPE_G3_FFE_DEG_RES_LEVEL_OFFSET; + mask |= HPIPE_G3_FFE_LOAD_RES_LEVEL_MASK; + data |= 0x3 << HPIPE_G3_FFE_LOAD_RES_LEVEL_OFFSET; + reg_set(hpipe_addr + HPIPE_G3_SETTING_3_REG, data, mask); + + /* Pattern lock lost timeout disable */ + mask = HPIPE_PATTERN_LOCK_LOST_TIMEOUT_EN_MASK; + data = 0x0 << HPIPE_PATTERN_LOCK_LOST_TIMEOUT_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_FRAME_DETECT_CTRL_3_REG, data, mask); + + /* Configure DFE adaptations */ + mask = HPIPE_CDR_RX_MAX_DFE_ADAPT_0_MASK; + data = 0x0 << HPIPE_CDR_RX_MAX_DFE_ADAPT_0_OFFSET; + mask |= HPIPE_CDR_RX_MAX_DFE_ADAPT_1_MASK; + data |= 0x0 << HPIPE_CDR_RX_MAX_DFE_ADAPT_1_OFFSET; + mask |= HPIPE_CDR_MAX_DFE_ADAPT_0_MASK; + data |= 0x0 << HPIPE_CDR_MAX_DFE_ADAPT_0_OFFSET; + mask |= HPIPE_CDR_MAX_DFE_ADAPT_1_MASK; + data |= 0x1 << HPIPE_CDR_MAX_DFE_ADAPT_1_OFFSET; + reg_set(hpipe_addr + HPIPE_CDR_CONTROL_REG, data, mask); + + mask = HPIPE_DFE_TX_MAX_DFE_ADAPT_MASK; + data = 0x0 << HPIPE_DFE_TX_MAX_DFE_ADAPT_OFFSET; + reg_set(hpipe_addr + HPIPE_DFE_CONTROL_REG, data, mask); + + /* Genration 2 setting 1*/ + mask = HPIPE_G2_SET_1_G2_RX_SELMUPI_MASK; + data = 0x0 << HPIPE_G2_SET_1_G2_RX_SELMUPI_OFFSET; + mask |= HPIPE_G2_SET_1_G2_RX_SELMUPP_MASK; + data |= 0x1 << HPIPE_G2_SET_1_G2_RX_SELMUPP_OFFSET; + mask |= HPIPE_G2_SET_1_G2_RX_SELMUFI_MASK; + data |= 0x0 << HPIPE_G2_SET_1_G2_RX_SELMUFI_OFFSET; + reg_set(hpipe_addr + HPIPE_G2_SET_1_REG, data, mask); + + /* DFE enable */ + mask = HPIPE_G2_DFE_RES_MASK; + data = 0x3 << HPIPE_G2_DFE_RES_OFFSET; + reg_set(hpipe_addr + HPIPE_G2_SETTINGS_4_REG, data, mask); + + /* Configure DFE Resolution */ + mask = HPIPE_LANE_CFG4_DFE_EN_SEL_MASK; + data = 0x1 << HPIPE_LANE_CFG4_DFE_EN_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_LANE_CFG4_REG, data, mask); + + /* VDD calibration control */ + mask = HPIPE_EXT_SELLV_RXSAMPL_MASK; + data = 0x16 << HPIPE_EXT_SELLV_RXSAMPL_OFFSET; + reg_set(hpipe_addr + HPIPE_VDD_CAL_CTRL_REG, data, mask); + + /* Set PLL Charge-pump Current Control */ + mask = HPIPE_G3_SETTING_5_G3_ICP_MASK; + data = 0x4 << HPIPE_G3_SETTING_5_G3_ICP_OFFSET; + reg_set(hpipe_addr + HPIPE_G3_SETTING_5_REG, data, mask); + + /* Set lane rqualization remote setting */ + mask = HPIPE_LANE_CFG_FOM_DIRN_OVERRIDE_MASK; + data = 0x1 << HPIPE_LANE_CFG_FOM_DIRN_OVERRIDE_OFFSET; + mask |= HPIPE_LANE_CFG_FOM_ONLY_MODE_MASK; + data |= 0x1 << HPIPE_LANE_CFG_FOM_ONLY_MODE_OFFFSET; + mask |= HPIPE_LANE_CFG_FOM_PRESET_VECTOR_MASK; + data |= 0x6 << HPIPE_LANE_CFG_FOM_PRESET_VECTOR_OFFSET; + reg_set(hpipe_addr + HPIPE_LANE_EQ_REMOTE_SETTING_REG, data, mask); + + mask = HPIPE_CFG_EQ_BUNDLE_DIS_MASK; + data = 0x1 << HPIPE_CFG_EQ_BUNDLE_DIS_OFFSET; + reg_set(hpipe_addr + HPIPE_LANE_EQ_CFG2_REG, data, mask); + + debug("stage: Comphy power up\n"); + + /* For PCIe X4 or X2: + * release from reset only after finish to configure all lanes + */ + if ((pcie_width == PCIE_LNK_X1) || (comphy_index == (pcie_width - 1))) { + uint32_t i, start_lane, end_lane; + + if (pcie_width != PCIE_LNK_X1) { + /* allows writing to all lanes in one write */ + data = 0x0; + if (pcie_width == PCIE_LNK_X2) + mask = COMMON_PHY_SD_CTRL1_COMPHY_0_1_PORT_MASK; + else if (pcie_width == PCIE_LNK_X4) + mask = COMMON_PHY_SD_CTRL1_COMPHY_0_3_PORT_MASK; + reg_set(comphy_base + COMMON_PHY_SD_CTRL1, data, mask); + start_lane = 0; + end_lane = pcie_width; + + /* Release from PIPE soft reset + * For PCIe by4 or by2: + * release from soft reset all lanes - can't use + * read modify write + */ + reg_set(HPIPE_ADDR( + COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), 0) + + HPIPE_RST_CLK_CTRL_REG, 0x24, 0xffffffff); + } else { + start_lane = comphy_index; + end_lane = comphy_index + 1; + + /* Release from PIPE soft reset + * for PCIe by4 or by2: + * release from soft reset all lanes + */ + reg_set(hpipe_addr + HPIPE_RST_CLK_CTRL_REG, + 0x0 << HPIPE_RST_CLK_CTRL_PIPE_RST_OFFSET, + HPIPE_RST_CLK_CTRL_PIPE_RST_MASK); + } + + if (pcie_width != PCIE_LNK_X1) { + /* disable writing to all lanes with one write */ + if (pcie_width == PCIE_LNK_X2) { + data = (COMPHY_LANE0 << + COMMON_PHY_SD_CTRL1_COMPHY_0_PORT_OFFSET) | + (COMPHY_LANE1 << + COMMON_PHY_SD_CTRL1_COMPHY_1_PORT_OFFSET); + mask = COMMON_PHY_SD_CTRL1_COMPHY_0_1_PORT_MASK; + } else if (pcie_width == PCIE_LNK_X4) { + data = (COMPHY_LANE0 << + COMMON_PHY_SD_CTRL1_COMPHY_0_PORT_OFFSET) | + (COMPHY_LANE1 << + COMMON_PHY_SD_CTRL1_COMPHY_1_PORT_OFFSET) | + (COMPHY_LANE2 << + COMMON_PHY_SD_CTRL1_COMPHY_2_PORT_OFFSET) | + (COMPHY_LANE3 << + COMMON_PHY_SD_CTRL1_COMPHY_3_PORT_OFFSET); + mask = COMMON_PHY_SD_CTRL1_COMPHY_0_3_PORT_MASK; + } + reg_set(comphy_base + COMMON_PHY_SD_CTRL1, + data, mask); + } + + debug("stage: Check PLL\n"); + /* Read lane status */ + for (i = start_lane; i < end_lane; i++) { + addr = HPIPE_ADDR( + COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), i) + + HPIPE_LANE_STATUS1_REG; + data = HPIPE_LANE_STATUS1_PCLK_EN_MASK; + mask = data; + ret = polling_with_timeout(addr, data, mask, + PLL_LOCK_TIMEOUT, + REG_32BIT); + if (ret) + ERROR("Failed to lock PCIE PLL\n"); + } + } + + debug_exit(); + + return ret; +} + +static int mvebu_cp110_comphy_rxaui_power_on(uint64_t comphy_base, + uint8_t comphy_index, uint32_t comphy_mode) +{ + uintptr_t hpipe_addr, sd_ip_addr, comphy_addr, addr; + uint32_t mask, data; + int ret = 0; + + debug_enter(); + + hpipe_addr = HPIPE_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + comphy_addr = COMPHY_ADDR(comphy_base, comphy_index); + sd_ip_addr = SD_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + + /* configure phy selector for RXAUI */ + mvebu_cp110_comphy_set_phy_selector(comphy_base, comphy_index, + comphy_mode); + + /* RFU configurations - hard reset comphy */ + mask = COMMON_PHY_CFG1_PWR_UP_MASK; + data = 0x1 << COMMON_PHY_CFG1_PWR_UP_OFFSET; + mask |= COMMON_PHY_CFG1_PIPE_SELECT_MASK; + data |= 0x0 << COMMON_PHY_CFG1_PIPE_SELECT_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG1_REG, data, mask); + + if (comphy_index == 2) { + reg_set(comphy_base + COMMON_PHY_SD_CTRL1, + 0x1 << COMMON_PHY_SD_CTRL1_RXAUI0_OFFSET, + COMMON_PHY_SD_CTRL1_RXAUI0_MASK); + } + if (comphy_index == 4) { + reg_set(comphy_base + COMMON_PHY_SD_CTRL1, + 0x1 << COMMON_PHY_SD_CTRL1_RXAUI1_OFFSET, + COMMON_PHY_SD_CTRL1_RXAUI1_MASK); + } + + /* Select Baud Rate of Comphy And PD_PLL/Tx/Rx */ + mask = SD_EXTERNAL_CONFIG0_SD_PU_PLL_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG0_SD_PU_PLL_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_MASK; + data |= 0xB << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_RX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_MASK; + data |= 0xB << SD_EXTERNAL_CONFIG0_SD_PHY_GEN_TX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_RX_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG0_SD_PU_RX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_TX_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG0_SD_PU_TX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_HALF_BUS_MODE_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG0_HALF_BUS_MODE_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_MEDIA_MODE_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG0_MEDIA_MODE_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG0_REG, data, mask); + + /* release from hard reset */ + mask = SD_EXTERNAL_CONFIG1_RESET_IN_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RESET_CORE_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RF_RESET_IN_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + mask = SD_EXTERNAL_CONFIG1_RESET_IN_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RESET_CORE_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + /* Wait 1ms - until band gap and ref clock ready */ + mdelay(1); + + /* Start comphy Configuration */ + debug("stage: Comphy configuration\n"); + /* set reference clock */ + reg_set(hpipe_addr + HPIPE_MISC_REG, + 0x0 << HPIPE_MISC_REFCLK_SEL_OFFSET, + HPIPE_MISC_REFCLK_SEL_MASK); + /* Power and PLL Control */ + mask = HPIPE_PWR_PLL_REF_FREQ_MASK; + data = 0x1 << HPIPE_PWR_PLL_REF_FREQ_OFFSET; + mask |= HPIPE_PWR_PLL_PHY_MODE_MASK; + data |= 0x4 << HPIPE_PWR_PLL_PHY_MODE_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_PLL_REG, data, mask); + /* Loopback register */ + reg_set(hpipe_addr + HPIPE_LOOPBACK_REG, + 0x1 << HPIPE_LOOPBACK_SEL_OFFSET, HPIPE_LOOPBACK_SEL_MASK); + /* rx control 1 */ + mask = HPIPE_RX_CONTROL_1_RXCLK2X_SEL_MASK; + data = 0x1 << HPIPE_RX_CONTROL_1_RXCLK2X_SEL_OFFSET; + mask |= HPIPE_RX_CONTROL_1_CLK8T_EN_MASK; + data |= 0x1 << HPIPE_RX_CONTROL_1_CLK8T_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_RX_CONTROL_1_REG, data, mask); + /* DTL Control */ + reg_set(hpipe_addr + HPIPE_PWR_CTR_DTL_REG, + 0x0 << HPIPE_PWR_CTR_DTL_FLOOP_EN_OFFSET, + HPIPE_PWR_CTR_DTL_FLOOP_EN_MASK); + + /* Set analog parameters from ETP(HW) */ + debug("stage: Analog parameters from ETP(HW)\n"); + /* SERDES External Configuration 2 */ + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG2_REG, + 0x1 << SD_EXTERNAL_CONFIG2_PIN_DFE_EN_OFFSET, + SD_EXTERNAL_CONFIG2_PIN_DFE_EN_MASK); + /* 0x7-DFE Resolution control */ + reg_set(hpipe_addr + HPIPE_DFE_REG0, 0x1 << HPIPE_DFE_RES_FORCE_OFFSET, + HPIPE_DFE_RES_FORCE_MASK); + /* 0xd-G1_Setting_0 */ + reg_set(hpipe_addr + HPIPE_G1_SET_0_REG, + 0xd << HPIPE_G1_SET_0_G1_TX_EMPH1_OFFSET, + HPIPE_G1_SET_0_G1_TX_EMPH1_MASK); + /* 0xE-G1_Setting_1 */ + mask = HPIPE_G1_SET_1_G1_RX_SELMUPI_MASK; + data = 0x1 << HPIPE_G1_SET_1_G1_RX_SELMUPI_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_SELMUPP_MASK; + data |= 0x1 << HPIPE_G1_SET_1_G1_RX_SELMUPP_OFFSET; + mask |= HPIPE_G1_SET_1_G1_RX_DFE_EN_MASK; + data |= 0x1 << HPIPE_G1_SET_1_G1_RX_DFE_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SET_1_REG, data, mask); + /* 0xA-DFE_Reg3 */ + mask = HPIPE_DFE_F3_F5_DFE_EN_MASK; + data = 0x0 << HPIPE_DFE_F3_F5_DFE_EN_OFFSET; + mask |= HPIPE_DFE_F3_F5_DFE_CTRL_MASK; + data |= 0x0 << HPIPE_DFE_F3_F5_DFE_CTRL_OFFSET; + reg_set(hpipe_addr + HPIPE_DFE_F3_F5_REG, data, mask); + + /* 0x111-G1_Setting_4 */ + mask = HPIPE_G1_SETTINGS_4_G1_DFE_RES_MASK; + data = 0x1 << HPIPE_G1_SETTINGS_4_G1_DFE_RES_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SETTINGS_4_REG, data, mask); + + debug("stage: RFU configurations- Power Up PLL,Tx,Rx\n"); + /* SERDES External Configuration */ + mask = SD_EXTERNAL_CONFIG0_SD_PU_PLL_MASK; + data = 0x1 << SD_EXTERNAL_CONFIG0_SD_PU_PLL_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_RX_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG0_SD_PU_RX_OFFSET; + mask |= SD_EXTERNAL_CONFIG0_SD_PU_TX_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG0_SD_PU_TX_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG0_REG, data, mask); + + + /* check PLL rx & tx ready */ + addr = sd_ip_addr + SD_EXTERNAL_STATUS0_REG; + data = SD_EXTERNAL_STATUS0_PLL_RX_MASK | + SD_EXTERNAL_STATUS0_PLL_TX_MASK; + mask = data; + data = polling_with_timeout(addr, data, mask, 15000, REG_32BIT); + if (data != 0) { + debug("Read from reg = %lx - value = 0x%x\n", + sd_ip_addr + SD_EXTERNAL_STATUS0_REG, data); + ERROR("SD_EXTERNAL_STATUS0_PLL_RX is %d, -\"-_PLL_TX is %d\n", + (data & SD_EXTERNAL_STATUS0_PLL_RX_MASK), + (data & SD_EXTERNAL_STATUS0_PLL_TX_MASK)); + ret = -ETIMEDOUT; + } + + /* RX init */ + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, + 0x1 << SD_EXTERNAL_CONFIG1_RX_INIT_OFFSET, + SD_EXTERNAL_CONFIG1_RX_INIT_MASK); + + /* check that RX init done */ + addr = sd_ip_addr + SD_EXTERNAL_STATUS0_REG; + data = SD_EXTERNAL_STATUS0_RX_INIT_MASK; + mask = data; + data = polling_with_timeout(addr, data, mask, 100, REG_32BIT); + if (data != 0) { + debug("Read from reg = %lx - value = 0x%x\n", + sd_ip_addr + SD_EXTERNAL_STATUS0_REG, data); + ERROR("SD_EXTERNAL_STATUS0_RX_INIT is 0\n"); + ret = -ETIMEDOUT; + } + + debug("stage: RF Reset\n"); + /* RF Reset */ + mask = SD_EXTERNAL_CONFIG1_RX_INIT_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG1_RX_INIT_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RF_RESET_IN_MASK; + data |= 0x1 << SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + debug_exit(); + + return ret; +} + +static int mvebu_cp110_comphy_usb3_power_on(uint64_t comphy_base, + uint8_t comphy_index, uint32_t comphy_mode) +{ + uintptr_t hpipe_addr, comphy_addr, addr; + uint32_t mask, data; + int ret = 0; + + debug_enter(); + + /* Configure PIPE selector for USB3 */ + mvebu_cp110_comphy_set_pipe_selector(comphy_base, comphy_index, + comphy_mode); + + hpipe_addr = HPIPE_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + comphy_addr = COMPHY_ADDR(comphy_base, comphy_index); + + debug("stage: RFU configurations - hard reset comphy\n"); + /* RFU configurations - hard reset comphy */ + mask = COMMON_PHY_CFG1_PWR_UP_MASK; + data = 0x1 << COMMON_PHY_CFG1_PWR_UP_OFFSET; + mask |= COMMON_PHY_CFG1_PIPE_SELECT_MASK; + data |= 0x1 << COMMON_PHY_CFG1_PIPE_SELECT_OFFSET; + mask |= COMMON_PHY_CFG1_PWR_ON_RESET_MASK; + data |= 0x0 << COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET; + mask |= COMMON_PHY_CFG1_CORE_RSTN_MASK; + data |= 0x0 << COMMON_PHY_CFG1_CORE_RSTN_OFFSET; + mask |= COMMON_PHY_PHY_MODE_MASK; + data |= 0x1 << COMMON_PHY_PHY_MODE_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG1_REG, data, mask); + + /* release from hard reset */ + mask = COMMON_PHY_CFG1_PWR_ON_RESET_MASK; + data = 0x1 << COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET; + mask |= COMMON_PHY_CFG1_CORE_RSTN_MASK; + data |= 0x1 << COMMON_PHY_CFG1_CORE_RSTN_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG1_REG, data, mask); + + /* Wait 1ms - until band gap and ref clock ready */ + mdelay(1); + + /* Start comphy Configuration */ + debug("stage: Comphy configuration\n"); + /* Set PIPE soft reset */ + mask = HPIPE_RST_CLK_CTRL_PIPE_RST_MASK; + data = 0x1 << HPIPE_RST_CLK_CTRL_PIPE_RST_OFFSET; + /* Set PHY datapath width mode for V0 */ + mask |= HPIPE_RST_CLK_CTRL_FIXED_PCLK_MASK; + data |= 0x0 << HPIPE_RST_CLK_CTRL_FIXED_PCLK_OFFSET; + /* Set Data bus width USB mode for V0 */ + mask |= HPIPE_RST_CLK_CTRL_PIPE_WIDTH_MASK; + data |= 0x0 << HPIPE_RST_CLK_CTRL_PIPE_WIDTH_OFFSET; + /* Set CORE_CLK output frequency for 250Mhz */ + mask |= HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_MASK; + data |= 0x0 << HPIPE_RST_CLK_CTRL_CORE_FREQ_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_RST_CLK_CTRL_REG, data, mask); + /* Set PLL ready delay for 0x2 */ + reg_set(hpipe_addr + HPIPE_CLK_SRC_LO_REG, + 0x2 << HPIPE_CLK_SRC_LO_PLL_RDY_DL_OFFSET, + HPIPE_CLK_SRC_LO_PLL_RDY_DL_MASK); + /* Set reference clock to come from group 1 - 25Mhz */ + reg_set(hpipe_addr + HPIPE_MISC_REG, + 0x0 << HPIPE_MISC_REFCLK_SEL_OFFSET, + HPIPE_MISC_REFCLK_SEL_MASK); + /* Set reference frequcency select - 0x2 */ + mask = HPIPE_PWR_PLL_REF_FREQ_MASK; + data = 0x2 << HPIPE_PWR_PLL_REF_FREQ_OFFSET; + /* Set PHY mode to USB - 0x5 */ + mask |= HPIPE_PWR_PLL_PHY_MODE_MASK; + data |= 0x5 << HPIPE_PWR_PLL_PHY_MODE_OFFSET; + reg_set(hpipe_addr + HPIPE_PWR_PLL_REG, data, mask); + /* Set the amount of time spent in the LoZ state - set for 0x7 */ + reg_set(hpipe_addr + HPIPE_GLOBAL_PM_CTRL, + 0x7 << HPIPE_GLOBAL_PM_RXDLOZ_WAIT_OFFSET, + HPIPE_GLOBAL_PM_RXDLOZ_WAIT_MASK); + /* Set max PHY generation setting - 5Gbps */ + reg_set(hpipe_addr + HPIPE_INTERFACE_REG, + 0x1 << HPIPE_INTERFACE_GEN_MAX_OFFSET, + HPIPE_INTERFACE_GEN_MAX_MASK); + /* Set select data width 20Bit (SEL_BITS[2:0]) */ + reg_set(hpipe_addr + HPIPE_LOOPBACK_REG, + 0x1 << HPIPE_LOOPBACK_SEL_OFFSET, + HPIPE_LOOPBACK_SEL_MASK); + /* select de-emphasize 3.5db */ + reg_set(hpipe_addr + HPIPE_LANE_CONFIG0_REG, + 0x1 << HPIPE_LANE_CONFIG0_TXDEEMPH0_OFFSET, + HPIPE_LANE_CONFIG0_TXDEEMPH0_MASK); + /* override tx margining from the MAC */ + reg_set(hpipe_addr + HPIPE_TST_MODE_CTRL_REG, + 0x1 << HPIPE_TST_MODE_CTRL_MODE_MARGIN_OFFSET, + HPIPE_TST_MODE_CTRL_MODE_MARGIN_MASK); + + /* Start analog parameters from ETP(HW) */ + debug("stage: Analog parameters from ETP(HW)\n"); + /* Set Pin DFE_PAT_DIS -> Bit[1]: PIN_DFE_PAT_DIS = 0x0 */ + mask = HPIPE_LANE_CFG4_DFE_CTRL_MASK; + data = 0x1 << HPIPE_LANE_CFG4_DFE_CTRL_OFFSET; + /* Set Override PHY DFE control pins for 0x1 */ + mask |= HPIPE_LANE_CFG4_DFE_OVER_MASK; + data |= 0x1 << HPIPE_LANE_CFG4_DFE_OVER_OFFSET; + /* Set Spread Spectrum Clock Enable fot 0x1 */ + mask |= HPIPE_LANE_CFG4_SSC_CTRL_MASK; + data |= 0x1 << HPIPE_LANE_CFG4_SSC_CTRL_OFFSET; + reg_set(hpipe_addr + HPIPE_LANE_CFG4_REG, data, mask); + /* Confifure SSC amplitude */ + mask = HPIPE_G2_TX_SSC_AMP_MASK; + data = 0x1f << HPIPE_G2_TX_SSC_AMP_OFFSET; + reg_set(hpipe_addr + HPIPE_G2_SET_2_REG, data, mask); + /* End of analog parameters */ + + debug("stage: Comphy power up\n"); + /* Release from PIPE soft reset */ + reg_set(hpipe_addr + HPIPE_RST_CLK_CTRL_REG, + 0x0 << HPIPE_RST_CLK_CTRL_PIPE_RST_OFFSET, + HPIPE_RST_CLK_CTRL_PIPE_RST_MASK); + + /* wait 15ms - for comphy calibration done */ + debug("stage: Check PLL\n"); + /* Read lane status */ + addr = hpipe_addr + HPIPE_LANE_STATUS1_REG; + data = HPIPE_LANE_STATUS1_PCLK_EN_MASK; + mask = data; + data = polling_with_timeout(addr, data, mask, 15000, REG_32BIT); + if (data != 0) { + debug("Read from reg = %lx - value = 0x%x\n", + hpipe_addr + HPIPE_LANE_STATUS1_REG, data); + ERROR("HPIPE_LANE_STATUS1_PCLK_EN_MASK is 0\n"); + ret = -ETIMEDOUT; + } + + debug_exit(); + + return ret; +} + +/* This function performs RX training for one Feed Forward Equalization (FFE) + * value. + * The RX traiing result is stored in 'Saved DFE values Register' (SAV_F0D). + * + * Return '0' on success, error code in a case of failure. + */ +static int mvebu_cp110_comphy_test_single_ffe(uint64_t comphy_base, + uint8_t comphy_index, + uint32_t ffe, uint32_t *result) +{ + uint32_t mask, data, timeout; + uintptr_t hpipe_addr, sd_ip_addr; + + hpipe_addr = HPIPE_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + + sd_ip_addr = SD_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + + /* Configure PRBS counters */ + mask = HPIPE_PHY_TEST_PATTERN_SEL_MASK; + data = 0xe << HPIPE_PHY_TEST_PATTERN_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_PHY_TEST_CONTROL_REG, data, mask); + + mask = HPIPE_PHY_TEST_DATA_MASK; + data = 0x64 << HPIPE_PHY_TEST_DATA_OFFSET; + reg_set(hpipe_addr + HPIPE_PHY_TEST_DATA_REG, data, mask); + + mask = HPIPE_PHY_TEST_EN_MASK; + data = 0x1 << HPIPE_PHY_TEST_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_PHY_TEST_CONTROL_REG, data, mask); + + mdelay(50); + + /* Set the FFE value */ + mask = HPIPE_G1_SETTINGS_3_G1_FFE_RES_SEL_MASK; + data = ffe << HPIPE_G1_SETTINGS_3_G1_FFE_RES_SEL_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SETTINGS_3_REG, data, mask); + + /* Start RX training */ + mask = SD_EXTERNAL_STATUS_START_RX_TRAINING_MASK; + data = 1 << SD_EXTERNAL_STATUS_START_RX_TRAINING_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_STATUS_REG, data, mask); + + /* Check the result of RX training */ + timeout = RX_TRAINING_TIMEOUT; + while (timeout) { + data = mmio_read_32(sd_ip_addr + SD_EXTERNAL_STATAUS1_REG); + if (data & SD_EXTERNAL_STATAUS1_REG_RX_TRAIN_COMP_MASK) + break; + mdelay(1); + timeout--; + } + + if (timeout == 0) + return -ETIMEDOUT; + + if (data & SD_EXTERNAL_STATAUS1_REG_RX_TRAIN_FAILED_MASK) + return -EINVAL; + + /* Stop RX training */ + mask = SD_EXTERNAL_STATUS_START_RX_TRAINING_MASK; + data = 0 << SD_EXTERNAL_STATUS_START_RX_TRAINING_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_STATUS_REG, data, mask); + + /* Read the result */ + data = mmio_read_32(hpipe_addr + HPIPE_SAVED_DFE_VALUES_REG); + data &= HPIPE_SAVED_DFE_VALUES_SAV_F0D_MASK; + data >>= HPIPE_SAVED_DFE_VALUES_SAV_F0D_OFFSET; + *result = data; + + mask = HPIPE_PHY_TEST_RESET_MASK; + data = 0x1 << HPIPE_PHY_TEST_RESET_OFFSET; + mask |= HPIPE_PHY_TEST_EN_MASK; + data |= 0x0 << HPIPE_PHY_TEST_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_PHY_TEST_CONTROL_REG, data, mask); + + mask = HPIPE_PHY_TEST_RESET_MASK; + data = 0x0 << HPIPE_PHY_TEST_RESET_OFFSET; + reg_set(hpipe_addr + HPIPE_PHY_TEST_CONTROL_REG, data, mask); + + return 0; +} + +/* This function runs complete RX training sequence: + * - Run RX training for all possible Feed Forward Equalization values + * - Choose the FFE which gives the best result. + * - Run RX training again with the best result. + * + * Return '0' on success, error code in a case of failure. + */ +int mvebu_cp110_comphy_xfi_rx_training(uint64_t comphy_base, + uint8_t comphy_index) +{ + uint32_t mask, data, max_rx_train = 0, max_rx_train_index = 0; + uintptr_t hpipe_addr; + uint32_t rx_train_result; + int ret, i; + + hpipe_addr = HPIPE_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + + debug_enter(); + + /* Configure SQ threshold and CDR lock */ + mask = HPIPE_SQUELCH_THRESH_IN_MASK; + data = 0xc << HPIPE_SQUELCH_THRESH_IN_OFFSET; + reg_set(hpipe_addr + HPIPE_SQUELCH_FFE_SETTING_REG, data, mask); + + mask = HPIPE_SQ_DEGLITCH_WIDTH_P_MASK; + data = 0xf << HPIPE_SQ_DEGLITCH_WIDTH_P_OFFSET; + mask |= HPIPE_SQ_DEGLITCH_WIDTH_N_MASK; + data |= 0xf << HPIPE_SQ_DEGLITCH_WIDTH_N_OFFSET; + mask |= HPIPE_SQ_DEGLITCH_EN_MASK; + data |= 0x1 << HPIPE_SQ_DEGLITCH_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_SQ_GLITCH_FILTER_CTRL, data, mask); + + mask = HPIPE_CDR_LOCK_DET_EN_MASK; + data = 0x1 << HPIPE_CDR_LOCK_DET_EN_OFFSET; + reg_set(hpipe_addr + HPIPE_LOOPBACK_REG, data, mask); + + udelay(100); + + /* Determine if we have a cable attached to this comphy, if not, + * we can't perform RX training. + */ + data = mmio_read_32(hpipe_addr + HPIPE_SQUELCH_FFE_SETTING_REG); + if (data & HPIPE_SQUELCH_DETECTED_MASK) { + ERROR("Squelsh is not detected, can't perform RX training\n"); + return -EINVAL; + } + + data = mmio_read_32(hpipe_addr + HPIPE_LOOPBACK_REG); + if (!(data & HPIPE_CDR_LOCK_MASK)) { + ERROR("CDR is not locked, can't perform RX training\n"); + return -EINVAL; + } + + /* Do preparations for RX training */ + mask = HPIPE_DFE_RES_FORCE_MASK; + data = 0x0 << HPIPE_DFE_RES_FORCE_OFFSET; + reg_set(hpipe_addr + HPIPE_DFE_REG0, data, mask); + + mask = HPIPE_G1_SETTINGS_3_G1_FFE_CAP_SEL_MASK; + data = 0xf << HPIPE_G1_SETTINGS_3_G1_FFE_CAP_SEL_OFFSET; + mask |= HPIPE_G1_SETTINGS_3_G1_FFE_SETTING_FORCE_MASK; + data |= 1 << HPIPE_G1_SETTINGS_3_G1_FFE_SETTING_FORCE_OFFSET; + reg_set(hpipe_addr + HPIPE_G1_SETTINGS_3_REG, data, mask); + + /* Perform RX training for all possible FFE (Feed Forward + * Equalization, possible values are 0-7). + * We update the best value reached and the FFE which gave this value. + */ + for (i = 0; i < MAX_NUM_OF_FFE; i++) { + rx_train_result = 0; + ret = mvebu_cp110_comphy_test_single_ffe(comphy_base, + comphy_index, i, + &rx_train_result); + + if ((!ret) && (rx_train_result > max_rx_train)) { + max_rx_train = rx_train_result; + max_rx_train_index = i; + } + } + + /* If we were able to determine which FFE gives the best value, + * now we need to set it and run RX training again (only for this + * FFE). + */ + if (max_rx_train) { + ret = mvebu_cp110_comphy_test_single_ffe(comphy_base, + comphy_index, + max_rx_train_index, + &rx_train_result); + + if (ret == 0) + debug("RX Training passed (FFE = %d, result = 0x%x)\n", + max_rx_train_index, rx_train_result); + } else { + ERROR("RX Training failed for comphy%d\n", comphy_index); + ret = -EINVAL; + } + + debug_exit(); + + return ret; +} + +/* During AP the proper mode is auto-negotiated and the mac, pcs and serdes + * configuration are done by the firmware loaded to the MG's CM3 for appropriate + * negotiated mode. Therefore there is no need to configure the mac, pcs and + * serdes from u-boot. The only thing that need to be setup is powering up + * the comphy, which is done through Common PHY<n> Configuration 1 Register + * (CP0: 0xF2441000, CP1: 0xF4441000). This step can't be done by MG's CM3, + * since it doesn't have an access to this register-set (but it has access to + * the network registers like: MG, AP, MAC, PCS, Serdes etc.) + */ +static int mvebu_cp110_comphy_ap_power_on(uint64_t comphy_base, + uint8_t comphy_index) +{ + uint32_t mask, data; + uintptr_t comphy_addr = comphy_addr = + COMPHY_ADDR(comphy_base, comphy_index); + + debug_enter(); + debug("stage: RFU configurations - hard reset comphy\n"); + /* RFU configurations - hard reset comphy */ + mask = COMMON_PHY_CFG1_PWR_UP_MASK; + data = 0x1 << COMMON_PHY_CFG1_PWR_UP_OFFSET; + mask |= COMMON_PHY_CFG1_PIPE_SELECT_MASK; + data |= 0x0 << COMMON_PHY_CFG1_PIPE_SELECT_OFFSET; + reg_set(comphy_addr + COMMON_PHY_CFG1_REG, data, mask); + debug_exit(); + + return 0; +} + +/* + * This function allows to reset the digital synchronizers between + * the MAC and the PHY, it is required when the MAC changes its state. + */ +int mvebu_cp110_comphy_digital_reset(uint64_t comphy_base, + uint8_t comphy_index, + uint32_t comphy_mode, uint32_t command) +{ + int mode = COMPHY_GET_MODE(comphy_mode); + uintptr_t sd_ip_addr; + uint32_t mask, data; + + sd_ip_addr = SD_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + + switch (mode) { + case (COMPHY_SGMII_MODE): + case (COMPHY_HS_SGMII_MODE): + case (COMPHY_XFI_MODE): + case (COMPHY_SFI_MODE): + case (COMPHY_RXAUI_MODE): + mask = SD_EXTERNAL_CONFIG1_RF_RESET_IN_MASK; + data = ((command == COMPHY_COMMAND_DIGITAL_PWR_OFF) ? + 0x0 : 0x1) << SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + break; + default: + ERROR("comphy%d: Digital PWR ON/OFF is not supported\n", + comphy_index); + return -EINVAL; + } + + return 0; +} + +int mvebu_cp110_comphy_power_on(uint64_t comphy_base, uint64_t comphy_index, + uint64_t comphy_mode) +{ + int mode = COMPHY_GET_MODE(comphy_mode); + int err = 0; + + debug_enter(); + + switch (mode) { + case(COMPHY_SATA_MODE): + err = mvebu_cp110_comphy_sata_power_on(comphy_base, + comphy_index, + comphy_mode); + break; + case(COMPHY_SGMII_MODE): + case(COMPHY_HS_SGMII_MODE): + err = mvebu_cp110_comphy_sgmii_power_on(comphy_base, + comphy_index, + comphy_mode); + break; + /* From comphy perspective, XFI and SFI are the same */ + case (COMPHY_XFI_MODE): + case (COMPHY_SFI_MODE): + err = mvebu_cp110_comphy_xfi_power_on(comphy_base, + comphy_index, + comphy_mode); + break; + case (COMPHY_PCIE_MODE): + err = mvebu_cp110_comphy_pcie_power_on(comphy_base, + comphy_index, + comphy_mode); + break; + case (COMPHY_RXAUI_MODE): + err = mvebu_cp110_comphy_rxaui_power_on(comphy_base, + comphy_index, + comphy_mode); + case (COMPHY_USB3H_MODE): + case (COMPHY_USB3D_MODE): + err = mvebu_cp110_comphy_usb3_power_on(comphy_base, + comphy_index, + comphy_mode); + break; + case (COMPHY_AP_MODE): + err = mvebu_cp110_comphy_ap_power_on(comphy_base, comphy_index); + break; + default: + ERROR("comphy%lld: unsupported comphy mode\n", comphy_index); + err = -EINVAL; + break; + } + + debug_exit(); + + return err; +} + +int mvebu_cp110_comphy_power_off(uint64_t comphy_base, uint64_t comphy_index) +{ + uintptr_t sd_ip_addr, comphy_ip_addr; + uint32_t mask, data; + + debug_enter(); + + sd_ip_addr = SD_ADDR(COMPHY_PIPE_FROM_COMPHY_ADDR(comphy_base), + comphy_index); + comphy_ip_addr = COMPHY_ADDR(comphy_base, comphy_index); + + /* Hard reset the comphy, for Ethernet modes and Sata */ + mask = SD_EXTERNAL_CONFIG1_RESET_IN_MASK; + data = 0x0 << SD_EXTERNAL_CONFIG1_RESET_IN_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RESET_CORE_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG1_RESET_CORE_OFFSET; + mask |= SD_EXTERNAL_CONFIG1_RF_RESET_IN_MASK; + data |= 0x0 << SD_EXTERNAL_CONFIG1_RF_RESET_IN_OFFSET; + reg_set(sd_ip_addr + SD_EXTERNAL_CONFIG1_REG, data, mask); + + /* PCIe reset */ + spin_lock(&cp110_mac_reset_lock); + + /* The mvebu_cp110_comphy_power_off will be called only from Linux (to + * override settings done by bootloader) and it will be relevant only + * to PCIe (called before check if to skip pcie power off or not). + */ + data = mmio_read_32(SYS_CTRL_FROM_COMPHY_ADDR(comphy_base) + + SYS_CTRL_UINIT_SOFT_RESET_REG); + switch (comphy_index) { + case COMPHY_LANE0: + data &= ~PCIE_MAC_RESET_MASK_PORT0; + break; + case COMPHY_LANE4: + data &= ~PCIE_MAC_RESET_MASK_PORT1; + break; + case COMPHY_LANE5: + data &= ~PCIE_MAC_RESET_MASK_PORT2; + break; + } + + mmio_write_32(SYS_CTRL_FROM_COMPHY_ADDR(comphy_base) + + SYS_CTRL_UINIT_SOFT_RESET_REG, data); + spin_unlock(&cp110_mac_reset_lock); + + /* Hard reset the comphy, for PCIe and usb3 */ + mask = COMMON_PHY_CFG1_PWR_ON_RESET_MASK; + data = 0x0 << COMMON_PHY_CFG1_PWR_ON_RESET_OFFSET; + mask |= COMMON_PHY_CFG1_CORE_RSTN_MASK; + data |= 0x0 << COMMON_PHY_CFG1_CORE_RSTN_OFFSET; + reg_set(comphy_ip_addr + COMMON_PHY_CFG1_REG, data, mask); + + /* Clear comphy PHY and PIPE selector, can't rely on previous config. */ + mvebu_cp110_comphy_clr_phy_selector(comphy_base, comphy_index); + mvebu_cp110_comphy_clr_pipe_selector(comphy_base, comphy_index); + + debug_exit(); + + return 0; +} diff --git a/drivers/marvell/comphy/phy-comphy-cp110.h b/drivers/marvell/comphy/phy-comphy-cp110.h new file mode 100644 index 00000000..ada6aeca --- /dev/null +++ b/drivers/marvell/comphy/phy-comphy-cp110.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* Marvell CP110 SoC COMPHY unit driver */ + +int mvebu_cp110_comphy_is_pll_locked(uint64_t comphy_base, + uint64_t comphy_index); +int mvebu_cp110_comphy_power_off(uint64_t comphy_base, + uint64_t comphy_index); +int mvebu_cp110_comphy_power_on(uint64_t comphy_base, + uint64_t comphy_index, uint64_t comphy_mode); +int mvebu_cp110_comphy_xfi_rx_training(uint64_t comphy_base, + uint8_t comphy_index); +int mvebu_cp110_comphy_digital_reset(uint64_t comphy_base, uint8_t comphy_index, + uint32_t comphy_mode, uint32_t command); diff --git a/drivers/marvell/gwin.c b/drivers/marvell/gwin.c new file mode 100644 index 00000000..2b17f35c --- /dev/null +++ b/drivers/marvell/gwin.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* GWIN unit device driver for Marvell AP810 SoC */ + +#include <a8k_common.h> +#include <debug.h> +#include <gwin.h> +#include <mmio.h> +#include <mvebu.h> +#include <mvebu_def.h> + +#if LOG_LEVEL >= LOG_LEVEL_INFO +#define DEBUG_ADDR_MAP +#endif + +/* common defines */ +#define WIN_ENABLE_BIT (0x1) +#define WIN_TARGET_MASK (0xF) +#define WIN_TARGET_SHIFT (0x8) +#define WIN_TARGET(tgt) (((tgt) & WIN_TARGET_MASK) \ + << WIN_TARGET_SHIFT) + +/* Bits[43:26] of the physical address are the window base, + * which is aligned to 64MB + */ +#define ADDRESS_RSHIFT (26) +#define ADDRESS_LSHIFT (10) +#define GWIN_ALIGNMENT_64M (0x4000000) + +/* AP registers */ +#define GWIN_CR_OFFSET(ap, win) (MVEBU_GWIN_BASE(ap) + 0x0 + \ + (0x10 * (win))) +#define GWIN_ALR_OFFSET(ap, win) (MVEBU_GWIN_BASE(ap) + 0x8 + \ + (0x10 * (win))) +#define GWIN_AHR_OFFSET(ap, win) (MVEBU_GWIN_BASE(ap) + 0xc + \ + (0x10 * (win))) + +#define CCU_GRU_CR_OFFSET(ap) (MVEBU_CCU_GRU_BASE(ap)) +#define CCR_GRU_CR_GWIN_MBYPASS (1 << 1) + +static void gwin_check(struct addr_map_win *win) +{ + /* The base is always 64M aligned */ + if (IS_NOT_ALIGN(win->base_addr, GWIN_ALIGNMENT_64M)) { + win->base_addr &= ~(GWIN_ALIGNMENT_64M - 1); + NOTICE("%s: Align the base address to 0x%llx\n", + __func__, win->base_addr); + } + + /* size parameter validity check */ + if (IS_NOT_ALIGN(win->win_size, GWIN_ALIGNMENT_64M)) { + win->win_size = ALIGN_UP(win->win_size, GWIN_ALIGNMENT_64M); + NOTICE("%s: Aligning window size to 0x%llx\n", + __func__, win->win_size); + } +} + +static void gwin_enable_window(int ap_index, struct addr_map_win *win, + uint32_t win_num) +{ + uint32_t alr, ahr; + uint64_t end_addr; + + if ((win->target_id & WIN_TARGET_MASK) != win->target_id) { + ERROR("target ID = %d, is invalid\n", win->target_id); + return; + } + + /* calculate 64bit end-address */ + end_addr = (win->base_addr + win->win_size - 1); + + alr = (uint32_t)((win->base_addr >> ADDRESS_RSHIFT) << ADDRESS_LSHIFT); + ahr = (uint32_t)((end_addr >> ADDRESS_RSHIFT) << ADDRESS_LSHIFT); + + /* write start address and end address for GWIN */ + mmio_write_32(GWIN_ALR_OFFSET(ap_index, win_num), alr); + mmio_write_32(GWIN_AHR_OFFSET(ap_index, win_num), ahr); + + /* write the target ID and enable the window */ + mmio_write_32(GWIN_CR_OFFSET(ap_index, win_num), + WIN_TARGET(win->target_id) | WIN_ENABLE_BIT); +} + +static void gwin_disable_window(int ap_index, uint32_t win_num) +{ + uint32_t win_reg; + + win_reg = mmio_read_32(GWIN_CR_OFFSET(ap_index, win_num)); + win_reg &= ~WIN_ENABLE_BIT; + mmio_write_32(GWIN_CR_OFFSET(ap_index, win_num), win_reg); +} + +/* Insert/Remove temporary window for using the out-of reset default + * CPx base address to access the CP configuration space prior to + * the further base address update in accordance with address mapping + * design. + * + * NOTE: Use the same window array for insertion and removal of + * temporary windows. + */ +void gwin_temp_win_insert(int ap_index, struct addr_map_win *win, int size) +{ + uint32_t win_id; + + for (int i = 0; i < size; i++) { + win_id = MVEBU_GWIN_MAX_WINS - i - 1; + gwin_check(win); + gwin_enable_window(ap_index, win, win_id); + win++; + } +} + +/* + * NOTE: Use the same window array for insertion and removal of + * temporary windows. + */ +void gwin_temp_win_remove(int ap_index, struct addr_map_win *win, int size) +{ + uint32_t win_id; + + for (int i = 0; i < size; i++) { + uint64_t base; + uint32_t target; + + win_id = MVEBU_GWIN_MAX_WINS - i - 1; + + target = mmio_read_32(GWIN_CR_OFFSET(ap_index, win_id)); + target >>= WIN_TARGET_SHIFT; + target &= WIN_TARGET_MASK; + + base = mmio_read_32(GWIN_ALR_OFFSET(ap_index, win_id)); + base >>= ADDRESS_LSHIFT; + base <<= ADDRESS_RSHIFT; + + if (win->target_id != target) { + ERROR("%s: Trying to remove bad window-%d!\n", + __func__, win_id); + continue; + } + gwin_disable_window(ap_index, win_id); + win++; + } +} + +#ifdef DEBUG_ADDR_MAP +static void dump_gwin(int ap_index) +{ + uint32_t win_num; + + /* Dump all GWIN windows */ + tf_printf("\tbank target start end\n"); + tf_printf("\t----------------------------------------------------\n"); + for (win_num = 0; win_num < MVEBU_GWIN_MAX_WINS; win_num++) { + uint32_t cr; + uint64_t alr, ahr; + + cr = mmio_read_32(GWIN_CR_OFFSET(ap_index, win_num)); + /* Window enabled */ + if (cr & WIN_ENABLE_BIT) { + alr = mmio_read_32(GWIN_ALR_OFFSET(ap_index, win_num)); + alr = (alr >> ADDRESS_LSHIFT) << ADDRESS_RSHIFT; + ahr = mmio_read_32(GWIN_AHR_OFFSET(ap_index, win_num)); + ahr = (ahr >> ADDRESS_LSHIFT) << ADDRESS_RSHIFT; + tf_printf("\tgwin %d 0x%016llx 0x%016llx\n", + (cr >> 8) & 0xF, alr, ahr); + } + } +} +#endif + +int init_gwin(int ap_index) +{ + struct addr_map_win *win; + uint32_t win_id; + uint32_t win_count; + uint32_t win_reg; + + INFO("Initializing GWIN Address decoding\n"); + + /* Get the array of the windows and its size */ + marvell_get_gwin_memory_map(ap_index, &win, &win_count); + if (win_count <= 0) { + INFO("no windows configurations found\n"); + return 0; + } + + if (win_count > MVEBU_GWIN_MAX_WINS) { + ERROR("number of windows is bigger than %d\n", + MVEBU_GWIN_MAX_WINS); + return 0; + } + + /* disable all windows */ + for (win_id = 0; win_id < MVEBU_GWIN_MAX_WINS; win_id++) + gwin_disable_window(ap_index, win_id); + + /* enable relevant windows */ + for (win_id = 0; win_id < win_count; win_id++, win++) { + gwin_check(win); + gwin_enable_window(ap_index, win, win_id); + } + + /* GWIN Miss feature has not verified, therefore any access towards + * remote AP should be accompanied with proper configuration to + * GWIN registers group and therefore the GWIN Miss feature + * should be set into Bypass mode, need to make sure all GWIN regions + * are defined correctly that will assure no GWIN miss occurrance + * JIRA-AURORA2-1630 + */ + INFO("Update GWIN miss bypass\n"); + win_reg = mmio_read_32(CCU_GRU_CR_OFFSET(ap_index)); + win_reg |= CCR_GRU_CR_GWIN_MBYPASS; + mmio_write_32(CCU_GRU_CR_OFFSET(ap_index), win_reg); + +#ifdef DEBUG_ADDR_MAP + dump_gwin(ap_index); +#endif + + INFO("Done GWIN Address decoding Initializing\n"); + + return 0; +} diff --git a/drivers/marvell/i2c/a8k_i2c.c b/drivers/marvell/i2c/a8k_i2c.c new file mode 100644 index 00000000..737dd0a7 --- /dev/null +++ b/drivers/marvell/i2c/a8k_i2c.c @@ -0,0 +1,613 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* This driver provides I2C support for Marvell A8K and compatible SoCs */ + +#include <a8k_i2c.h> +#include <debug.h> +#include <delay_timer.h> +#include <errno.h> +#include <mmio.h> +#include <mvebu_def.h> + +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE +#define DEBUG_I2C +#endif + +#define CONFIG_SYS_TCLK 250000000 +#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C_SLAVE 0x0 +#define I2C_TIMEOUT_VALUE 0x500 +#define I2C_MAX_RETRY_CNT 1000 +#define I2C_CMD_WRITE 0x0 +#define I2C_CMD_READ 0x1 + +#define I2C_DATA_ADDR_7BIT_OFFS 0x1 +#define I2C_DATA_ADDR_7BIT_MASK (0xFF << I2C_DATA_ADDR_7BIT_OFFS) + +#define I2C_CONTROL_ACK 0x00000004 +#define I2C_CONTROL_IFLG 0x00000008 +#define I2C_CONTROL_STOP 0x00000010 +#define I2C_CONTROL_START 0x00000020 +#define I2C_CONTROL_TWSIEN 0x00000040 +#define I2C_CONTROL_INTEN 0x00000080 + +#define I2C_STATUS_START 0x08 +#define I2C_STATUS_REPEATED_START 0x10 +#define I2C_STATUS_ADDR_W_ACK 0x18 +#define I2C_STATUS_DATA_W_ACK 0x28 +#define I2C_STATUS_LOST_ARB_DATA_ADDR_TRANSFER 0x38 +#define I2C_STATUS_ADDR_R_ACK 0x40 +#define I2C_STATUS_DATA_R_ACK 0x50 +#define I2C_STATUS_DATA_R_NAK 0x58 +#define I2C_STATUS_LOST_ARB_GENERAL_CALL 0x78 +#define I2C_STATUS_IDLE 0xF8 + +#define I2C_UNSTUCK_TRIGGER 0x1 +#define I2C_UNSTUCK_ONGOING 0x2 +#define I2C_UNSTUCK_ERROR 0x4 +struct marvell_i2c_regs { + uint32_t slave_address; + uint32_t data; + uint32_t control; + union { + uint32_t status; /* when reading */ + uint32_t baudrate; /* when writing */ + } u; + uint32_t xtnd_slave_addr; + uint32_t reserved[2]; + uint32_t soft_reset; + uint8_t reserved2[0xa0 - 0x20]; + uint32_t unstuck; +}; + +static struct marvell_i2c_regs *base; + +static int marvell_i2c_lost_arbitration(uint32_t *status) +{ + *status = mmio_read_32((uintptr_t)&base->u.status); + if ((*status == I2C_STATUS_LOST_ARB_DATA_ADDR_TRANSFER) || + (*status == I2C_STATUS_LOST_ARB_GENERAL_CALL)) + return -EAGAIN; + + return 0; +} + +static void marvell_i2c_interrupt_clear(void) +{ + uint32_t reg; + + reg = mmio_read_32((uintptr_t)&base->control); + reg &= ~(I2C_CONTROL_IFLG); + mmio_write_32((uintptr_t)&base->control, reg); + /* Wait for 1 us for the clear to take effect */ + udelay(1); +} + +static int marvell_i2c_interrupt_get(void) +{ + uint32_t reg; + + /* get the interrupt flag bit */ + reg = mmio_read_32((uintptr_t)&base->control); + reg &= I2C_CONTROL_IFLG; + return reg && I2C_CONTROL_IFLG; +} + +static int marvell_i2c_wait_interrupt(void) +{ + uint32_t timeout = 0; + + while (!marvell_i2c_interrupt_get() && (timeout++ < I2C_TIMEOUT_VALUE)) + ; + if (timeout >= I2C_TIMEOUT_VALUE) + return -ETIMEDOUT; + + return 0; +} + +static int marvell_i2c_start_bit_set(void) +{ + int is_int_flag = 0; + uint32_t status; + + if (marvell_i2c_interrupt_get()) + is_int_flag = 1; + + /* set start bit */ + mmio_write_32((uintptr_t)&base->control, + mmio_read_32((uintptr_t)&base->control) | + I2C_CONTROL_START); + + /* in case that the int flag was set before i.e. repeated start bit */ + if (is_int_flag) { + VERBOSE("%s: repeated start Bit\n", __func__); + marvell_i2c_interrupt_clear(); + } + + if (marvell_i2c_wait_interrupt()) { + ERROR("Start clear bit timeout\n"); + return -ETIMEDOUT; + } + + /* check that start bit went down */ + if ((mmio_read_32((uintptr_t)&base->control) & + I2C_CONTROL_START) != 0) { + ERROR("Start bit didn't went down\n"); + return -EPERM; + } + + /* check the status */ + if (marvell_i2c_lost_arbitration(&status)) { + ERROR("%s - %d: Lost arbitration, got status %x\n", + __func__, __LINE__, status); + return -EAGAIN; + } + if ((status != I2C_STATUS_START) && + (status != I2C_STATUS_REPEATED_START)) { + ERROR("Got status %x after enable start bit.\n", status); + return -EPERM; + } + + return 0; +} + +static int marvell_i2c_stop_bit_set(void) +{ + int timeout; + uint32_t status; + + /* Generate stop bit */ + mmio_write_32((uintptr_t)&base->control, + mmio_read_32((uintptr_t)&base->control) | + I2C_CONTROL_STOP); + marvell_i2c_interrupt_clear(); + + timeout = 0; + /* Read control register, check the control stop bit */ + while ((mmio_read_32((uintptr_t)&base->control) & I2C_CONTROL_STOP) && + (timeout++ < I2C_TIMEOUT_VALUE)) + ; + if (timeout >= I2C_TIMEOUT_VALUE) { + ERROR("Stop bit didn't went down\n"); + return -ETIMEDOUT; + } + + /* check that stop bit went down */ + if ((mmio_read_32((uintptr_t)&base->control) & I2C_CONTROL_STOP) != 0) { + ERROR("Stop bit didn't went down\n"); + return -EPERM; + } + + /* check the status */ + if (marvell_i2c_lost_arbitration(&status)) { + ERROR("%s - %d: Lost arbitration, got status %x\n", + __func__, __LINE__, status); + return -EAGAIN; + } + if (status != I2C_STATUS_IDLE) { + ERROR("Got status %x after enable stop bit.\n", status); + return -EPERM; + } + + return 0; +} + +static int marvell_i2c_address_set(uint8_t chain, int command) +{ + uint32_t reg, status; + + reg = (chain << I2C_DATA_ADDR_7BIT_OFFS) & I2C_DATA_ADDR_7BIT_MASK; + reg |= command; + mmio_write_32((uintptr_t)&base->data, reg); + udelay(1); + + marvell_i2c_interrupt_clear(); + + if (marvell_i2c_wait_interrupt()) { + ERROR("Start clear bit timeout\n"); + return -ETIMEDOUT; + } + + /* check the status */ + if (marvell_i2c_lost_arbitration(&status)) { + ERROR("%s - %d: Lost arbitration, got status %x\n", + __func__, __LINE__, status); + return -EAGAIN; + } + if (((status != I2C_STATUS_ADDR_R_ACK) && (command == I2C_CMD_READ)) || + ((status != I2C_STATUS_ADDR_W_ACK) && (command == I2C_CMD_WRITE))) { + /* only in debug, since in boot we try to read the SPD + * of both DRAM, and we don't want error messages in cas + * DIMM doesn't exist. + */ + INFO("%s: ERROR - status %x addr in %s mode.\n", __func__, + status, (command == I2C_CMD_WRITE) ? "Write" : "Read"); + return -EPERM; + } + + return 0; +} + +/* + * The I2C module contains a clock divider to generate the SCL clock. + * This function calculates and sets the <N> and <M> fields in the I2C Baud + * Rate Register (t=01) to obtain given 'requested_speed'. + * The requested_speed will be equal to: + * CONFIG_SYS_TCLK / (10 * (M + 1) * (2 << N)) + * Where M is the value represented by bits[6:3] and N is the value represented + * by bits[2:0] of "I2C Baud Rate Register". + * Therefore max M which can be set is 16 (2^4) and max N is 8 (2^3). So the + * lowest possible baudrate is: + * CONFIG_SYS_TCLK/(10 * (16 +1) * (2 << 8), which equals to: + * CONFIG_SYS_TCLK/87040. Assuming that CONFIG_SYS_TCLK=250MHz, the lowest + * possible frequency is ~2,872KHz. + */ +static unsigned int marvell_i2c_bus_speed_set(unsigned int requested_speed) +{ + unsigned int n, m, freq, margin, min_margin = 0xffffffff; + unsigned int actual_n = 0, actual_m = 0; + int val; + + /* Calculate N and M for the TWSI clock baud rate */ + for (n = 0; n < 8; n++) { + for (m = 0; m < 16; m++) { + freq = CONFIG_SYS_TCLK / (10 * (m + 1) * (2 << n)); + val = requested_speed - freq; + margin = (val > 0) ? val : -val; + + if ((freq <= requested_speed) && + (margin < min_margin)) { + min_margin = margin; + actual_n = n; + actual_m = m; + } + } + } + VERBOSE("%s: actual_n = %u, actual_m = %u\n", + __func__, actual_n, actual_m); + /* Set the baud rate */ + mmio_write_32((uintptr_t)&base->u.baudrate, (actual_m << 3) | actual_n); + + return 0; +} + +#ifdef DEBUG_I2C +static int marvell_i2c_probe(uint8_t chip) +{ + int ret = 0; + + ret = marvell_i2c_start_bit_set(); + if (ret != 0) { + marvell_i2c_stop_bit_set(); + ERROR("%s - %d: %s", __func__, __LINE__, + "marvell_i2c_start_bit_set failed\n"); + return -EPERM; + } + + ret = marvell_i2c_address_set(chip, I2C_CMD_WRITE); + if (ret != 0) { + marvell_i2c_stop_bit_set(); + ERROR("%s - %d: %s", __func__, __LINE__, + "marvell_i2c_address_set failed\n"); + return -EPERM; + } + + marvell_i2c_stop_bit_set(); + + VERBOSE("%s: successful I2C probe\n", __func__); + + return ret; +} +#endif + +/* regular i2c transaction */ +static int marvell_i2c_data_receive(uint8_t *p_block, uint32_t block_size) +{ + uint32_t reg, status, block_size_read = block_size; + + /* Wait for cause interrupt */ + if (marvell_i2c_wait_interrupt()) { + ERROR("Start clear bit timeout\n"); + return -ETIMEDOUT; + } + while (block_size_read) { + if (block_size_read == 1) { + reg = mmio_read_32((uintptr_t)&base->control); + reg &= ~(I2C_CONTROL_ACK); + mmio_write_32((uintptr_t)&base->control, reg); + } + marvell_i2c_interrupt_clear(); + + if (marvell_i2c_wait_interrupt()) { + ERROR("Start clear bit timeout\n"); + return -ETIMEDOUT; + } + /* check the status */ + if (marvell_i2c_lost_arbitration(&status)) { + ERROR("%s - %d: Lost arbitration, got status %x\n", + __func__, __LINE__, status); + return -EAGAIN; + } + if ((status != I2C_STATUS_DATA_R_ACK) && + (block_size_read != 1)) { + ERROR("Status %x in read transaction\n", status); + return -EPERM; + } + if ((status != I2C_STATUS_DATA_R_NAK) && + (block_size_read == 1)) { + ERROR("Status %x in Rd Terminate\n", status); + return -EPERM; + } + + /* read the data */ + *p_block = (uint8_t) mmio_read_32((uintptr_t)&base->data); + VERBOSE("%s: place %d read %x\n", __func__, + block_size - block_size_read, *p_block); + p_block++; + block_size_read--; + } + + return 0; +} + +static int marvell_i2c_data_transmit(uint8_t *p_block, uint32_t block_size) +{ + uint32_t status, block_size_write = block_size; + + if (marvell_i2c_wait_interrupt()) { + ERROR("Start clear bit timeout\n"); + return -ETIMEDOUT; + } + + while (block_size_write) { + /* write the data */ + mmio_write_32((uintptr_t)&base->data, (uint32_t) *p_block); + VERBOSE("%s: index = %d, data = %x\n", __func__, + block_size - block_size_write, *p_block); + p_block++; + block_size_write--; + + marvell_i2c_interrupt_clear(); + + if (marvell_i2c_wait_interrupt()) { + ERROR("Start clear bit timeout\n"); + return -ETIMEDOUT; + } + + /* check the status */ + if (marvell_i2c_lost_arbitration(&status)) { + ERROR("%s - %d: Lost arbitration, got status %x\n", + __func__, __LINE__, status); + return -EAGAIN; + } + if (status != I2C_STATUS_DATA_W_ACK) { + ERROR("Status %x in write transaction\n", status); + return -EPERM; + } + } + + return 0; +} + +static int marvell_i2c_target_offset_set(uint8_t chip, uint32_t addr, int alen) +{ + uint8_t off_block[2]; + uint32_t off_size; + + if (alen == 2) { /* 2-byte addresses support */ + off_block[0] = (addr >> 8) & 0xff; + off_block[1] = addr & 0xff; + off_size = 2; + } else { /* 1-byte addresses support */ + off_block[0] = addr & 0xff; + off_size = 1; + } + VERBOSE("%s: off_size = %x addr1 = %x addr2 = %x\n", __func__, + off_size, off_block[0], off_block[1]); + return marvell_i2c_data_transmit(off_block, off_size); +} + +static int marvell_i2c_unstuck(int ret) +{ + uint32_t v; + + if (ret != -ETIMEDOUT) + return ret; + VERBOSE("Trying to \"unstuck i2c\"... "); + i2c_init(base); + mmio_write_32((uintptr_t)&base->unstuck, I2C_UNSTUCK_TRIGGER); + do { + v = mmio_read_32((uintptr_t)&base->unstuck); + } while (v & I2C_UNSTUCK_ONGOING); + + if (v & I2C_UNSTUCK_ERROR) { + VERBOSE("failed - soft reset i2c\n"); + ret = -EPERM; + } else { + VERBOSE("ok\n"); + i2c_init(base); + ret = -EAGAIN; + } + return ret; +} + +/* + * API Functions + */ +void i2c_init(void *i2c_base) +{ + /* For I2C speed and slave address, now we do not set them since + * we just provide the working speed and slave address in plat_def.h + * for i2c_init + */ + base = (struct marvell_i2c_regs *)i2c_base; + + /* Reset the I2C logic */ + mmio_write_32((uintptr_t)&base->soft_reset, 0); + + udelay(200); + + marvell_i2c_bus_speed_set(CONFIG_SYS_I2C_SPEED); + + /* Enable the I2C and slave */ + mmio_write_32((uintptr_t)&base->control, + I2C_CONTROL_TWSIEN | I2C_CONTROL_ACK); + + /* set the I2C slave address */ + mmio_write_32((uintptr_t)&base->xtnd_slave_addr, 0); + mmio_write_32((uintptr_t)&base->slave_address, CONFIG_SYS_I2C_SLAVE); + + /* unmask I2C interrupt */ + mmio_write_32((uintptr_t)&base->control, + mmio_read_32((uintptr_t)&base->control) | + I2C_CONTROL_INTEN); + + udelay(10); +} + +/* + * i2c_read: - Read multiple bytes from an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be read + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to write the data + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_read(uint8_t chip, uint32_t addr, int alen, uint8_t *buffer, int len) +{ + int ret = 0; + uint32_t counter = 0; + +#ifdef DEBUG_I2C + marvell_i2c_probe(chip); +#endif + + do { + if (ret != -EAGAIN && ret) { + ERROR("i2c transaction failed, after %d retries\n", + counter); + marvell_i2c_stop_bit_set(); + return ret; + } + + /* wait for 1 us for the interrupt clear to take effect */ + if (counter > 0) + udelay(1); + counter++; + + ret = marvell_i2c_start_bit_set(); + if (ret) { + ret = marvell_i2c_unstuck(ret); + continue; + } + + /* if EEPROM device */ + if (alen != 0) { + ret = marvell_i2c_address_set(chip, I2C_CMD_WRITE); + if (ret) + continue; + + ret = marvell_i2c_target_offset_set(chip, addr, alen); + if (ret) + continue; + ret = marvell_i2c_start_bit_set(); + if (ret) + continue; + } + + ret = marvell_i2c_address_set(chip, I2C_CMD_READ); + if (ret) + continue; + + ret = marvell_i2c_data_receive(buffer, len); + if (ret) + continue; + + ret = marvell_i2c_stop_bit_set(); + } while ((ret == -EAGAIN) && (counter < I2C_MAX_RETRY_CNT)); + + if (counter == I2C_MAX_RETRY_CNT) { + ERROR("I2C transactions failed, got EAGAIN %d times\n", + I2C_MAX_RETRY_CNT); + ret = -EPERM; + } + mmio_write_32((uintptr_t)&base->control, + mmio_read_32((uintptr_t)&base->control) | + I2C_CONTROL_ACK); + + udelay(1); + return ret; +} + +/* + * i2c_write: - Write multiple bytes to an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be written + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to find the data to be written + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_write(uint8_t chip, uint32_t addr, int alen, uint8_t *buffer, int len) +{ + int ret = 0; + uint32_t counter = 0; + + do { + if (ret != -EAGAIN && ret) { + ERROR("i2c transaction failed\n"); + marvell_i2c_stop_bit_set(); + return ret; + } + /* wait for 1 us for the interrupt clear to take effect */ + if (counter > 0) + udelay(1); + counter++; + + ret = marvell_i2c_start_bit_set(); + if (ret) { + ret = marvell_i2c_unstuck(ret); + continue; + } + + ret = marvell_i2c_address_set(chip, I2C_CMD_WRITE); + if (ret) + continue; + + /* if EEPROM device */ + if (alen != 0) { + ret = marvell_i2c_target_offset_set(chip, addr, alen); + if (ret) + continue; + } + + ret = marvell_i2c_data_transmit(buffer, len); + if (ret) + continue; + + ret = marvell_i2c_stop_bit_set(); + } while ((ret == -EAGAIN) && (counter < I2C_MAX_RETRY_CNT)); + + if (counter == I2C_MAX_RETRY_CNT) { + ERROR("I2C transactions failed, got EAGAIN %d times\n", + I2C_MAX_RETRY_CNT); + ret = -EPERM; + } + + udelay(1); + return ret; +} diff --git a/drivers/marvell/io_win.c b/drivers/marvell/io_win.c new file mode 100644 index 00000000..701dbb81 --- /dev/null +++ b/drivers/marvell/io_win.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* IO Window unit device driver for Marvell AP807, AP807 and AP810 SoCs */ + +#include <a8k_common.h> +#include <debug.h> +#include <io_win.h> +#include <mmio.h> +#include <mvebu.h> +#include <mvebu_def.h> + +#if LOG_LEVEL >= LOG_LEVEL_INFO +#define DEBUG_ADDR_MAP +#endif + +/* common defines */ +#define WIN_ENABLE_BIT (0x1) +/* Physical address of the base of the window = {Addr[19:0],20`h0} */ +#define ADDRESS_SHIFT (20 - 4) +#define ADDRESS_MASK (0xFFFFFFF0) +#define IO_WIN_ALIGNMENT_1M (0x100000) +#define IO_WIN_ALIGNMENT_64K (0x10000) + +/* AP registers */ +#define IO_WIN_ALR_OFFSET(ap, win) (MVEBU_IO_WIN_BASE(ap) + 0x0 + \ + (0x10 * win)) +#define IO_WIN_AHR_OFFSET(ap, win) (MVEBU_IO_WIN_BASE(ap) + 0x8 + \ + (0x10 * win)) +#define IO_WIN_CR_OFFSET(ap, win) (MVEBU_IO_WIN_BASE(ap) + 0xC + \ + (0x10 * win)) + +/* For storage of CR, ALR, AHR abd GCR */ +static uint32_t io_win_regs_save[MVEBU_IO_WIN_MAX_WINS * 3 + 1]; + +static void io_win_check(struct addr_map_win *win) +{ + /* for IO The base is always 1M aligned */ + /* check if address is aligned to 1M */ + if (IS_NOT_ALIGN(win->base_addr, IO_WIN_ALIGNMENT_1M)) { + win->base_addr = ALIGN_UP(win->base_addr, IO_WIN_ALIGNMENT_1M); + NOTICE("%s: Align up the base address to 0x%llx\n", + __func__, win->base_addr); + } + + /* size parameter validity check */ + if (IS_NOT_ALIGN(win->win_size, IO_WIN_ALIGNMENT_1M)) { + win->win_size = ALIGN_UP(win->win_size, IO_WIN_ALIGNMENT_1M); + NOTICE("%s: Aligning size to 0x%llx\n", + __func__, win->win_size); + } +} + +static void io_win_enable_window(int ap_index, struct addr_map_win *win, + uint32_t win_num) +{ + uint32_t alr, ahr; + uint64_t end_addr; + + if (win->target_id < 0 || win->target_id >= MVEBU_IO_WIN_MAX_WINS) { + ERROR("target ID = %d, is invalid\n", win->target_id); + return; + } + + if ((win_num == 0) || (win_num > MVEBU_IO_WIN_MAX_WINS)) { + ERROR("Enabling wrong IOW window %d!\n", win_num); + return; + } + + /* calculate the end-address */ + end_addr = (win->base_addr + win->win_size - 1); + + alr = (uint32_t)((win->base_addr >> ADDRESS_SHIFT) & ADDRESS_MASK); + alr |= WIN_ENABLE_BIT; + ahr = (uint32_t)((end_addr >> ADDRESS_SHIFT) & ADDRESS_MASK); + + /* write start address and end address for IO window */ + mmio_write_32(IO_WIN_ALR_OFFSET(ap_index, win_num), alr); + mmio_write_32(IO_WIN_AHR_OFFSET(ap_index, win_num), ahr); + + /* write window target */ + mmio_write_32(IO_WIN_CR_OFFSET(ap_index, win_num), win->target_id); +} + +static void io_win_disable_window(int ap_index, uint32_t win_num) +{ + uint32_t win_reg; + + if ((win_num == 0) || (win_num > MVEBU_IO_WIN_MAX_WINS)) { + ERROR("Disabling wrong IOW window %d!\n", win_num); + return; + } + + win_reg = mmio_read_32(IO_WIN_ALR_OFFSET(ap_index, win_num)); + win_reg &= ~WIN_ENABLE_BIT; + mmio_write_32(IO_WIN_ALR_OFFSET(ap_index, win_num), win_reg); +} + +/* Insert/Remove temporary window for using the out-of reset default + * CPx base address to access the CP configuration space prior to + * the further base address update in accordance with address mapping + * design. + * + * NOTE: Use the same window array for insertion and removal of + * temporary windows. + */ +void iow_temp_win_insert(int ap_index, struct addr_map_win *win, int size) +{ + uint32_t win_id; + + for (int i = 0; i < size; i++) { + win_id = MVEBU_IO_WIN_MAX_WINS - i - 1; + io_win_check(win); + io_win_enable_window(ap_index, win, win_id); + win++; + } +} + +/* + * NOTE: Use the same window array for insertion and removal of + * temporary windows. + */ +void iow_temp_win_remove(int ap_index, struct addr_map_win *win, int size) +{ + uint32_t win_id; + + /* Start from the last window and do not touch Win0 */ + for (int i = 0; i < size; i++) { + uint64_t base; + uint32_t target; + + win_id = MVEBU_IO_WIN_MAX_WINS - i - 1; + + target = mmio_read_32(IO_WIN_CR_OFFSET(ap_index, win_id)); + base = mmio_read_32(IO_WIN_ALR_OFFSET(ap_index, win_id)); + base &= ~WIN_ENABLE_BIT; + base <<= ADDRESS_SHIFT; + + if ((win->target_id != target) || (win->base_addr != base)) { + ERROR("%s: Trying to remove bad window-%d!\n", + __func__, win_id); + continue; + } + io_win_disable_window(ap_index, win_id); + win++; + } +} + +#ifdef DEBUG_ADDR_MAP +static void dump_io_win(int ap_index) +{ + uint32_t trgt_id, win_id; + uint32_t alr, ahr; + uint64_t start, end; + + /* Dump all IO windows */ + tf_printf("\tbank target start end\n"); + tf_printf("\t----------------------------------------------------\n"); + for (win_id = 0; win_id < MVEBU_IO_WIN_MAX_WINS; win_id++) { + alr = mmio_read_32(IO_WIN_ALR_OFFSET(ap_index, win_id)); + if (alr & WIN_ENABLE_BIT) { + alr &= ~WIN_ENABLE_BIT; + ahr = mmio_read_32(IO_WIN_AHR_OFFSET(ap_index, win_id)); + trgt_id = mmio_read_32(IO_WIN_CR_OFFSET(ap_index, + win_id)); + start = ((uint64_t)alr << ADDRESS_SHIFT); + end = (((uint64_t)ahr + 0x10) << ADDRESS_SHIFT); + tf_printf("\tio-win %d 0x%016llx 0x%016llx\n", + trgt_id, start, end); + } + } + tf_printf("\tio-win gcr is %x\n", + mmio_read_32(MVEBU_IO_WIN_BASE(ap_index) + + MVEBU_IO_WIN_GCR_OFFSET)); +} +#endif + +static void iow_save_win_range(int ap_id, int win_first, int win_last, + uint32_t *buffer) +{ + int win_id, idx; + + /* Save IOW */ + for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) { + buffer[idx++] = mmio_read_32(IO_WIN_CR_OFFSET(ap_id, win_id)); + buffer[idx++] = mmio_read_32(IO_WIN_ALR_OFFSET(ap_id, win_id)); + buffer[idx++] = mmio_read_32(IO_WIN_AHR_OFFSET(ap_id, win_id)); + } + buffer[idx] = mmio_read_32(MVEBU_IO_WIN_BASE(ap_id) + + MVEBU_IO_WIN_GCR_OFFSET); +} + +static void iow_restore_win_range(int ap_id, int win_first, int win_last, + uint32_t *buffer) +{ + int win_id, idx; + + /* Restore IOW */ + for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) { + mmio_write_32(IO_WIN_CR_OFFSET(ap_id, win_id), buffer[idx++]); + mmio_write_32(IO_WIN_ALR_OFFSET(ap_id, win_id), buffer[idx++]); + mmio_write_32(IO_WIN_AHR_OFFSET(ap_id, win_id), buffer[idx++]); + } + mmio_write_32(MVEBU_IO_WIN_BASE(ap_id) + MVEBU_IO_WIN_GCR_OFFSET, + buffer[idx++]); +} + +void iow_save_win_all(int ap_id) +{ + iow_save_win_range(ap_id, 0, MVEBU_IO_WIN_MAX_WINS - 1, + io_win_regs_save); +} + +void iow_restore_win_all(int ap_id) +{ + iow_restore_win_range(ap_id, 0, MVEBU_IO_WIN_MAX_WINS - 1, + io_win_regs_save); +} + +int init_io_win(int ap_index) +{ + struct addr_map_win *win; + uint32_t win_id, win_reg; + uint32_t win_count; + + INFO("Initializing IO WIN Address decoding\n"); + + /* Get the array of the windows and its size */ + marvell_get_io_win_memory_map(ap_index, &win, &win_count); + if (win_count <= 0) + INFO("no windows configurations found\n"); + + if (win_count > MVEBU_IO_WIN_MAX_WINS) { + INFO("number of windows is bigger than %d\n", + MVEBU_IO_WIN_MAX_WINS); + return 0; + } + + /* Get the default target id to set the GCR */ + win_reg = marvell_get_io_win_gcr_target(ap_index); + mmio_write_32(MVEBU_IO_WIN_BASE(ap_index) + MVEBU_IO_WIN_GCR_OFFSET, + win_reg); + + /* disable all IO windows */ + for (win_id = 1; win_id < MVEBU_IO_WIN_MAX_WINS; win_id++) + io_win_disable_window(ap_index, win_id); + + /* enable relevant windows, starting from win_id = 1 because + * index 0 dedicated for BootROM + */ + for (win_id = 1; win_id <= win_count; win_id++, win++) { + io_win_check(win); + io_win_enable_window(ap_index, win, win_id); + } + +#ifdef DEBUG_ADDR_MAP + dump_io_win(ap_index); +#endif + + INFO("Done IO WIN Address decoding Initializing\n"); + + return 0; +} diff --git a/drivers/marvell/iob.c b/drivers/marvell/iob.c new file mode 100644 index 00000000..9f9d0479 --- /dev/null +++ b/drivers/marvell/iob.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2016 - 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* IOW unit device driver for Marvell CP110 and CP115 SoCs */ + +#include <a8k_common.h> +#include <arch_helpers.h> +#include <debug.h> +#include <iob.h> +#include <mmio.h> +#include <mvebu.h> +#include <mvebu_def.h> + +#if LOG_LEVEL >= LOG_LEVEL_INFO +#define DEBUG_ADDR_MAP +#endif + +#define MVEBU_IOB_OFFSET (0x190000) +#define MVEBU_IOB_MAX_WINS 16 + +/* common defines */ +#define WIN_ENABLE_BIT (0x1) +/* Physical address of the base of the window = {AddrLow[19:0],20`h0} */ +#define ADDRESS_SHIFT (20 - 4) +#define ADDRESS_MASK (0xFFFFFFF0) +#define IOB_WIN_ALIGNMENT (0x100000) + +/* IOB registers */ +#define IOB_WIN_CR_OFFSET(win) (iob_base + 0x0 + (0x20 * win)) +#define IOB_TARGET_ID_OFFSET (8) +#define IOB_TARGET_ID_MASK (0xF) + +#define IOB_WIN_SCR_OFFSET(win) (iob_base + 0x4 + (0x20 * win)) +#define IOB_WIN_ENA_CTRL_WRITE_SECURE (0x1) +#define IOB_WIN_ENA_CTRL_READ_SECURE (0x2) +#define IOB_WIN_ENA_WRITE_SECURE (0x4) +#define IOB_WIN_ENA_READ_SECURE (0x8) + +#define IOB_WIN_ALR_OFFSET(win) (iob_base + 0x8 + (0x20 * win)) +#define IOB_WIN_AHR_OFFSET(win) (iob_base + 0xC + (0x20 * win)) + +uintptr_t iob_base; + +static void iob_win_check(struct addr_map_win *win, uint32_t win_num) +{ + /* check if address is aligned to the size */ + if (IS_NOT_ALIGN(win->base_addr, IOB_WIN_ALIGNMENT)) { + win->base_addr = ALIGN_UP(win->base_addr, IOB_WIN_ALIGNMENT); + ERROR("Window %d: base address unaligned to 0x%x\n", + win_num, IOB_WIN_ALIGNMENT); + tf_printf("Align up the base address to 0x%llx\n", + win->base_addr); + } + + /* size parameter validity check */ + if (IS_NOT_ALIGN(win->win_size, IOB_WIN_ALIGNMENT)) { + win->win_size = ALIGN_UP(win->win_size, IOB_WIN_ALIGNMENT); + ERROR("Window %d: window size unaligned to 0x%x\n", win_num, + IOB_WIN_ALIGNMENT); + tf_printf("Aligning size to 0x%llx\n", win->win_size); + } +} + +static void iob_enable_win(struct addr_map_win *win, uint32_t win_id) +{ + uint32_t iob_win_reg; + uint32_t alr, ahr; + uint64_t end_addr; + + end_addr = (win->base_addr + win->win_size - 1); + alr = (uint32_t)((win->base_addr >> ADDRESS_SHIFT) & ADDRESS_MASK); + ahr = (uint32_t)((end_addr >> ADDRESS_SHIFT) & ADDRESS_MASK); + + mmio_write_32(IOB_WIN_ALR_OFFSET(win_id), alr); + mmio_write_32(IOB_WIN_AHR_OFFSET(win_id), ahr); + + iob_win_reg = WIN_ENABLE_BIT; + iob_win_reg |= (win->target_id & IOB_TARGET_ID_MASK) + << IOB_TARGET_ID_OFFSET; + mmio_write_32(IOB_WIN_CR_OFFSET(win_id), iob_win_reg); + +} + +#ifdef DEBUG_ADDR_MAP +static void dump_iob(void) +{ + uint32_t win_id, win_cr, alr, ahr; + uint8_t target_id; + uint64_t start, end; + char *iob_target_name[IOB_MAX_TID] = { + "CFG ", "MCI0 ", "PEX1 ", "PEX2 ", + "PEX0 ", "NAND ", "RUNIT", "MCI1 " }; + + /* Dump all IOB windows */ + tf_printf("bank id target start end\n"); + tf_printf("----------------------------------------------------\n"); + for (win_id = 0; win_id < MVEBU_IOB_MAX_WINS; win_id++) { + win_cr = mmio_read_32(IOB_WIN_CR_OFFSET(win_id)); + if (win_cr & WIN_ENABLE_BIT) { + target_id = (win_cr >> IOB_TARGET_ID_OFFSET) & + IOB_TARGET_ID_MASK; + alr = mmio_read_32(IOB_WIN_ALR_OFFSET(win_id)); + start = ((uint64_t)alr << ADDRESS_SHIFT); + if (win_id != 0) { + ahr = mmio_read_32(IOB_WIN_AHR_OFFSET(win_id)); + end = (((uint64_t)ahr + 0x10) << ADDRESS_SHIFT); + } else { + /* Window #0 size is hardcoded to 16MB, as it's + * reserved for CP configuration space. + */ + end = start + (16 << 20); + } + tf_printf("iob %02d %s 0x%016llx 0x%016llx\n", + win_id, iob_target_name[target_id], + start, end); + } + } +} +#endif + +void iob_cfg_space_update(int ap_idx, int cp_idx, uintptr_t base, + uintptr_t new_base) +{ + debug_enter(); + + iob_base = base + MVEBU_IOB_OFFSET; + + NOTICE("Change the base address of AP%d-CP%d to %lx\n", + ap_idx, cp_idx, new_base); + mmio_write_32(IOB_WIN_ALR_OFFSET(0), new_base >> ADDRESS_SHIFT); + + iob_base = new_base + MVEBU_IOB_OFFSET; + + /* Make sure the address was configured by the CPU before + * any possible access to the CP. + */ + dsb(); + + debug_exit(); +} + +int init_iob(uintptr_t base) +{ + struct addr_map_win *win; + uint32_t win_id, win_reg; + uint32_t win_count; + + INFO("Initializing IOB Address decoding\n"); + + /* Get the base address of the address decoding MBUS */ + iob_base = base + MVEBU_IOB_OFFSET; + + /* Get the array of the windows and fill the map data */ + marvell_get_iob_memory_map(&win, &win_count, base); + if (win_count <= 0) { + INFO("no windows configurations found\n"); + return 0; + } else if (win_count > (MVEBU_IOB_MAX_WINS - 1)) { + ERROR("IOB mem map array > than max available windows (%d)\n", + MVEBU_IOB_MAX_WINS); + win_count = MVEBU_IOB_MAX_WINS; + } + + /* disable all IOB windows, start from win_id = 1 + * because can't disable internal register window + */ + for (win_id = 1; win_id < MVEBU_IOB_MAX_WINS; win_id++) { + win_reg = mmio_read_32(IOB_WIN_CR_OFFSET(win_id)); + win_reg &= ~WIN_ENABLE_BIT; + mmio_write_32(IOB_WIN_CR_OFFSET(win_id), win_reg); + + win_reg = ~IOB_WIN_ENA_CTRL_WRITE_SECURE; + win_reg &= ~IOB_WIN_ENA_CTRL_READ_SECURE; + win_reg &= ~IOB_WIN_ENA_WRITE_SECURE; + win_reg &= ~IOB_WIN_ENA_READ_SECURE; + mmio_write_32(IOB_WIN_SCR_OFFSET(win_id), win_reg); + } + + for (win_id = 1; win_id < win_count + 1; win_id++, win++) { + iob_win_check(win, win_id); + iob_enable_win(win, win_id); + } + +#ifdef DEBUG_ADDR_MAP + dump_iob(); +#endif + + INFO("Done IOB Address decoding Initializing\n"); + + return 0; +} diff --git a/drivers/marvell/mci.c b/drivers/marvell/mci.c new file mode 100644 index 00000000..721504e0 --- /dev/null +++ b/drivers/marvell/mci.c @@ -0,0 +1,832 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* MCI bus driver for Marvell ARMADA 8K and 8K+ SoCs */ + +#include <debug.h> +#include <delay_timer.h> +#include <mmio.h> +#include <mci.h> +#include <mvebu.h> +#include <mvebu_def.h> +#include <plat_marvell.h> + +/* /HB /Units /Direct_regs /Direct regs + * /Configuration Register Write/Read Data Register + */ +#define MCI_WRITE_READ_DATA_REG(mci_index) \ + MVEBU_MCI_REG_BASE_REMAP(mci_index) +/* /HB /Units /Direct_regs /Direct regs + * /Configuration Register Access Command Register + */ +#define MCI_ACCESS_CMD_REG(mci_index) \ + (MVEBU_MCI_REG_BASE_REMAP(mci_index) + 0x4) + +/* Access Command fields : + * bit[3:0] - Sub command: 1 => Peripheral Config Register Read, + * 0 => Peripheral Config Register Write, + * 2 => Peripheral Assign ID request, + * 3 => Circular Config Write + * bit[5] - 1 => Local (same chip access) 0 => Remote + * bit[15:8] - Destination hop ID. Put Global ID (GID) here (see scheme below). + * bit[23:22] - 0x3 IHB PHY REG address space, 0x0 IHB Controller space + * bit[21:16] - Low 6 bits of offset. Hight 2 bits are taken from bit[28:27] + * of IHB_PHY_CTRL + * (must be set before any PHY register access occurs): + * /IHB_REG /IHB_REGInterchip Hopping Bus Registers + * /IHB Version Control Register + * + * ixi_ihb_top IHB PHY + * AXI ----------------------------- ------------- + * <--| axi_hb_top | ihb_pipe_top |-->| | + * -->| GID=1 | GID=0 |<--| | + * ----------------------------- ------------- + */ +#define MCI_INDIRECT_CTRL_READ_CMD 0x1 +#define MCI_INDIRECT_CTRL_ASSIGN_CMD 0x2 +#define MCI_INDIRECT_CTRL_CIRCULAR_CMD 0x3 +#define MCI_INDIRECT_CTRL_LOCAL_PKT (1 << 5) +#define MCI_INDIRECT_CTRL_CMD_DONE_OFFSET 6 +#define MCI_INDIRECT_CTRL_CMD_DONE \ + (1 << MCI_INDIRECT_CTRL_CMD_DONE_OFFSET) +#define MCI_INDIRECT_CTRL_DATA_READY_OFFSET 7 +#define MCI_INDIRECT_CTRL_DATA_READY \ + (1 << MCI_INDIRECT_CTRL_DATA_READY_OFFSET) +#define MCI_INDIRECT_CTRL_HOPID_OFFSET 8 +#define MCI_INDIRECT_CTRL_HOPID(id) \ + (((id) & 0xFF) << MCI_INDIRECT_CTRL_HOPID_OFFSET) +#define MCI_INDIRECT_CTRL_REG_CHIPID_OFFSET 16 +#define MCI_INDIRECT_REG_CTRL_ADDR(reg_num) \ + (reg_num << MCI_INDIRECT_CTRL_REG_CHIPID_OFFSET) + +/* Hop ID values */ +#define GID_IHB_PIPE 0 +#define GID_AXI_HB 1 +#define GID_IHB_EXT 2 + +#define MCI_DID_GLOBAL_ASSIGNMENT_REQUEST_REG 0x2 +/* Target MCi Local ID (LID, which is = self DID) */ +#define MCI_DID_GLOBAL_ASSIGN_REQ_MCI_LOCAL_ID(val) (((val) & 0xFF) << 16) +/* Bits [15:8]: Number of MCis on chip of target MCi */ +#define MCI_DID_GLOBAL_ASSIGN_REQ_MCI_COUNT(val) (((val) & 0xFF) << 8) +/* Bits [7:0]: Number of hops on chip of target MCi */ +#define MCI_DID_GLOBAL_ASSIGN_REQ_HOPS_NUM(val) (((val) & 0xFF) << 0) + +/* IHB_REG domain registers */ +/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers/ + * Rx Memory Configuration Register (RX_MEM_CFG) + */ +#define MCI_CTRL_RX_MEM_CFG_REG_NUM 0x0 +#define MCI_CTRL_RX_TX_MEM_CFG_RQ_THRESH(val) (((val) & 0xFF) << 24) +#define MCI_CTRL_RX_TX_MEM_CFG_PQ_THRESH(val) (((val) & 0xFF) << 16) +#define MCI_CTRL_RX_TX_MEM_CFG_NQ_THRESH(val) (((val) & 0xFF) << 8) +#define MCI_CTRL_RX_TX_MEM_CFG_DELTA_THRESH(val) (((val) & 0xF) << 4) +#define MCI_CTRL_RX_TX_MEM_CFG_RTC(val) (((val) & 0x3) << 2) +#define MCI_CTRL_RX_TX_MEM_CFG_WTC(val) (((val) & 0x3) << 0) +#define MCI_CTRL_RX_MEM_CFG_REG_DEF_CP_VAL \ + (MCI_CTRL_RX_TX_MEM_CFG_RQ_THRESH(0x07) | \ + MCI_CTRL_RX_TX_MEM_CFG_PQ_THRESH(0x3f) | \ + MCI_CTRL_RX_TX_MEM_CFG_NQ_THRESH(0x3f) | \ + MCI_CTRL_RX_TX_MEM_CFG_DELTA_THRESH(0xf) | \ + MCI_CTRL_RX_TX_MEM_CFG_RTC(1) | \ + MCI_CTRL_RX_TX_MEM_CFG_WTC(1)) + +#define MCI_CTRL_RX_MEM_CFG_REG_DEF_AP_VAL \ + (MCI_CTRL_RX_TX_MEM_CFG_RQ_THRESH(0x3f) | \ + MCI_CTRL_RX_TX_MEM_CFG_PQ_THRESH(0x03) | \ + MCI_CTRL_RX_TX_MEM_CFG_NQ_THRESH(0x3f) | \ + MCI_CTRL_RX_TX_MEM_CFG_DELTA_THRESH(0xf) | \ + MCI_CTRL_RX_TX_MEM_CFG_RTC(1) | \ + MCI_CTRL_RX_TX_MEM_CFG_WTC(1)) + + +/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers/ + * Tx Memory Configuration Register (TX_MEM_CFG) + */ +#define MCI_CTRL_TX_MEM_CFG_REG_NUM 0x1 +/* field mapping for TX mem config register + * are the same as for RX register - see register above + */ +#define MCI_CTRL_TX_MEM_CFG_REG_DEF_VAL \ + (MCI_CTRL_RX_TX_MEM_CFG_RQ_THRESH(0x20) | \ + MCI_CTRL_RX_TX_MEM_CFG_PQ_THRESH(0x20) | \ + MCI_CTRL_RX_TX_MEM_CFG_NQ_THRESH(0x20) | \ + MCI_CTRL_RX_TX_MEM_CFG_DELTA_THRESH(2) | \ + MCI_CTRL_RX_TX_MEM_CFG_RTC(1) | \ + MCI_CTRL_RX_TX_MEM_CFG_WTC(1)) + +/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers + * /IHB Link CRC Control + */ +/* MCi Link CRC Control Register (MCi_CRC_CTRL) */ +#define MCI_LINK_CRC_CTRL_REG_NUM 0x4 + +/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers + * /IHB Status Register + */ +/* MCi Status Register (MCi_STS) */ +#define MCI_CTRL_STATUS_REG_NUM 0x5 +#define MCI_CTRL_STATUS_REG_PHY_READY (1 << 12) +#define MCI_CTRL_STATUS_REG_LINK_PRESENT (1 << 15) +#define MCI_CTRL_STATUS_REG_PHY_CID_VIO_OFFSET 24 +#define MCI_CTRL_STATUS_REG_PHY_CID_VIO_MASK \ + (0xF << MCI_CTRL_STATUS_REG_PHY_CID_VIO_OFFSET) +/* Expected successful Link result, including reserved bit */ +#define MCI_CTRL_PHY_READY (MCI_CTRL_STATUS_REG_PHY_READY | \ + MCI_CTRL_STATUS_REG_LINK_PRESENT | \ + MCI_CTRL_STATUS_REG_PHY_CID_VIO_MASK) + +/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers/ + * MCi PHY Speed Settings Register (MCi_PHY_SETTING) + */ +#define MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM 0x8 +#define MCI_CTRL_MCI_PHY_SET_DLO_FIFO_FULL_TRESH(val) (((val) & 0xF) << 28) +#define MCI_CTRL_MCI_PHY_SET_PHY_MAX_SPEED(val) (((val) & 0xF) << 12) +#define MCI_CTRL_MCI_PHY_SET_PHYCLK_SEL(val) (((val) & 0xF) << 8) +#define MCI_CTRL_MCI_PHY_SET_REFCLK_FREQ_SEL(val) (((val) & 0xF) << 4) +#define MCI_CTRL_MCI_PHY_SET_AUTO_LINK_EN(val) (((val) & 0x1) << 1) +#define MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL \ + (MCI_CTRL_MCI_PHY_SET_DLO_FIFO_FULL_TRESH(0x3) | \ + MCI_CTRL_MCI_PHY_SET_PHY_MAX_SPEED(0x3) | \ + MCI_CTRL_MCI_PHY_SET_PHYCLK_SEL(0x2) | \ + MCI_CTRL_MCI_PHY_SET_REFCLK_FREQ_SEL(0x1)) +#define MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL2 \ + (MCI_CTRL_MCI_PHY_SET_DLO_FIFO_FULL_TRESH(0x3) | \ + MCI_CTRL_MCI_PHY_SET_PHY_MAX_SPEED(0x3) | \ + MCI_CTRL_MCI_PHY_SET_PHYCLK_SEL(0x5) | \ + MCI_CTRL_MCI_PHY_SET_REFCLK_FREQ_SEL(0x1)) + +/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers + * /IHB Mode Config + */ +#define MCI_CTRL_IHB_MODE_CFG_REG_NUM 0x25 +#define MCI_CTRL_IHB_MODE_HBCLK_DIV(val) ((val) & 0xFF) +#define MCI_CTRL_IHB_MODE_CHUNK_MOD_OFFSET 8 +#define MCI_CTRL_IHB_MODE_CHUNK_MOD \ + (1 << MCI_CTRL_IHB_MODE_CHUNK_MOD_OFFSET) +#define MCI_CTRL_IHB_MODE_FWD_MOD_OFFSET 9 +#define MCI_CTRL_IHB_MODE_FWD_MOD \ + (1 << MCI_CTRL_IHB_MODE_FWD_MOD_OFFSET) +#define MCI_CTRL_IHB_MODE_SEQFF_FINE_MOD(val) (((val) & 0xF) << 12) +#define MCI_CTRL_IHB_MODE_RX_COMB_THRESH(val) (((val) & 0xFF) << 16) +#define MCI_CTRL_IHB_MODE_TX_COMB_THRESH(val) (((val) & 0xFF) << 24) + +#define MCI_CTRL_IHB_MODE_CFG_REG_DEF_VAL \ + (MCI_CTRL_IHB_MODE_HBCLK_DIV(6) | \ + MCI_CTRL_IHB_MODE_FWD_MOD | \ + MCI_CTRL_IHB_MODE_SEQFF_FINE_MOD(0xF) | \ + MCI_CTRL_IHB_MODE_RX_COMB_THRESH(0x3f) | \ + MCI_CTRL_IHB_MODE_TX_COMB_THRESH(0x40)) +/* AXI_HB registers */ +#define MCI_AXI_ACCESS_DATA_REG_NUM 0x0 +#define MCI_AXI_ACCESS_PCIE_MODE 1 +#define MCI_AXI_ACCESS_CACHE_CHECK_OFFSET 5 +#define MCI_AXI_ACCESS_CACHE_CHECK \ + (1 << MCI_AXI_ACCESS_CACHE_CHECK_OFFSET) +#define MCI_AXI_ACCESS_FORCE_POST_WR_OFFSET 6 +#define MCI_AXI_ACCESS_FORCE_POST_WR \ + (1 << MCI_AXI_ACCESS_FORCE_POST_WR_OFFSET) +#define MCI_AXI_ACCESS_DISABLE_CLK_GATING_OFFSET 9 +#define MCI_AXI_ACCESS_DISABLE_CLK_GATING \ + (1 << MCI_AXI_ACCESS_DISABLE_CLK_GATING_OFFSET) + +/* /HB /Units /HB_REG /HB_REGHopping Bus Registers + * /Window 0 Address Mask Register + */ +#define MCI_HB_CTRL_WIN0_ADDRESS_MASK_REG_NUM 0x2 + +/* /HB /Units /HB_REG /HB_REGHopping Bus Registers + * /Window 0 Destination Register + */ +#define MCI_HB_CTRL_WIN0_DESTINATION_REG_NUM 0x3 +#define MCI_HB_CTRL_WIN0_DEST_VALID_FLAG(val) (((val) & 0x1) << 16) +#define MCI_HB_CTRL_WIN0_DEST_ID(val) (((val) & 0xFF) << 0) + +/* /HB /Units /HB_REG /HB_REGHopping Bus Registers /Tx Control Register */ +#define MCI_HB_CTRL_TX_CTRL_REG_NUM 0xD +#define MCI_HB_CTRL_TX_CTRL_PCIE_MODE_OFFSET 24 +#define MCI_HB_CTRL_TX_CTRL_PCIE_MODE \ + (1 << MCI_HB_CTRL_TX_CTRL_PCIE_MODE_OFFSET) +#define MCI_HB_CTRL_TX_CTRL_PRI_TH_QOS(val) (((val) & 0xF) << 12) +#define MCI_HB_CTRL_TX_CTRL_MAX_RD_CNT(val) (((val) & 0x1F) << 6) +#define MCI_HB_CTRL_TX_CTRL_MAX_WR_CNT(val) (((val) & 0x1F) << 0) + +/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers + * /IHB Version Control Register + */ +#define MCI_PHY_CTRL_REG_NUM 0x7 +#define MCI_PHY_CTRL_MCI_MINOR 0x8 /* BITS [3:0] */ +#define MCI_PHY_CTRL_MCI_MAJOR_OFFSET 4 +#define MCI_PHY_CTRL_MCI_MAJOR \ + (1 << MCI_PHY_CTRL_MCI_MAJOR_OFFSET) +#define MCI_PHY_CTRL_MCI_SLEEP_REQ_OFFSET 11 +#define MCI_PHY_CTRL_MCI_SLEEP_REQ \ + (1 << MCI_PHY_CTRL_MCI_SLEEP_REQ_OFFSET) +/* Host=1 / Device=0 PHY mode */ +#define MCI_PHY_CTRL_MCI_PHY_MODE_OFFSET 24 +#define MCI_PHY_CTRL_MCI_PHY_MODE_HOST \ + (1 << MCI_PHY_CTRL_MCI_PHY_MODE_OFFSET) +/* Register=1 / PWM=0 interface */ +#define MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE_OFFSET 25 +#define MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE \ + (1 << MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE_OFFSET) + /* PHY code InReset=1 */ +#define MCI_PHY_CTRL_MCI_PHY_RESET_CORE_OFFSET 26 +#define MCI_PHY_CTRL_MCI_PHY_RESET_CORE \ + (1 << MCI_PHY_CTRL_MCI_PHY_RESET_CORE_OFFSET) +#define MCI_PHY_CTRL_PHY_ADDR_MSB_OFFSET 27 +#define MCI_PHY_CTRL_PHY_ADDR_MSB(addr) \ + (((addr) & 0x3) << \ + MCI_PHY_CTRL_PHY_ADDR_MSB_OFFSET) +#define MCI_PHY_CTRL_PIDI_MODE_OFFSET 31 +#define MCI_PHY_CTRL_PIDI_MODE \ + (1 << MCI_PHY_CTRL_PIDI_MODE_OFFSET) + +/* Number of times to wait for the MCI link ready after MCI configurations + * Normally takes 34-35 successive reads + */ +#define LINK_READY_TIMEOUT 100 + +enum mci_register_type { + MCI_REG_TYPE_PHY = 0, + MCI_REG_TYPE_CTRL, +}; + +enum { + MCI_CMD_WRITE, + MCI_CMD_READ +}; + +/* Write wrapper callback for debug: + * will print written data in case LOG_LEVEL >= 40 + */ +static void mci_mmio_write_32(uintptr_t addr, uint32_t value) +{ + VERBOSE("Write:\t0x%x = 0x%x\n", (uint32_t)addr, value); + mmio_write_32(addr, value); +} +/* Read wrapper callback for debug: + * will print read data in case LOG_LEVEL >= 40 + */ +static uint32_t mci_mmio_read_32(uintptr_t addr) +{ + uint32_t value; + + value = mmio_read_32(addr); + VERBOSE("Read:\t0x%x = 0x%x\n", (uint32_t)addr, value); + return value; +} + +/* MCI indirect access command completion polling: + * Each write/read command done via MCI indirect registers must be polled + * for command completions status. + * + * Returns 1 in case of error + * Returns 0 in case of command completed successfully. + */ +static int mci_poll_command_completion(int mci_index, int command_type) +{ + uint32_t mci_cmd_value = 0, retry_count = 100, ret = 0; + uint32_t completion_flags = MCI_INDIRECT_CTRL_CMD_DONE; + + debug_enter(); + /* Read commands require validating that requested data is ready */ + if (command_type == MCI_CMD_READ) + completion_flags |= MCI_INDIRECT_CTRL_DATA_READY; + + do { + /* wait 1 ms before each polling */ + mdelay(1); + mci_cmd_value = mci_mmio_read_32(MCI_ACCESS_CMD_REG(mci_index)); + } while (((mci_cmd_value & completion_flags) != completion_flags) && + (retry_count-- > 0)); + + if (retry_count == 0) { + ERROR("%s: MCI command timeout (command status = 0x%x)\n", + __func__, mci_cmd_value); + ret = 1; + } + + debug_exit(); + return ret; +} + +int mci_read(int mci_idx, uint32_t cmd, uint32_t *value) +{ + int rval; + + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_idx), cmd); + + rval = mci_poll_command_completion(mci_idx, MCI_CMD_READ); + + *value = mci_mmio_read_32(MCI_WRITE_READ_DATA_REG(mci_idx)); + + return rval; +} + +int mci_write(int mci_idx, uint32_t cmd, uint32_t data) +{ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_idx), data); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_idx), cmd); + + return mci_poll_command_completion(mci_idx, MCI_CMD_WRITE); +} + +/* Perform 3 configurations in one command: PCI mode, + * queues separation and cache bit + */ +static int mci_axi_set_pcie_mode(int mci_index) +{ + uint32_t reg_data, ret = 1; + + debug_enter(); + /* This configuration makes MCI IP behave consistently with AXI protocol + * It should be configured at one side only (for example locally at AP). + * The IP takes care of performing the same configurations at MCI on + * another side (for example remotely at CP). + */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_AXI_ACCESS_PCIE_MODE | + MCI_AXI_ACCESS_CACHE_CHECK | + MCI_AXI_ACCESS_FORCE_POST_WR | + MCI_AXI_ACCESS_DISABLE_CLK_GATING); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_AXI_ACCESS_DATA_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) | + MCI_INDIRECT_CTRL_LOCAL_PKT | + MCI_INDIRECT_CTRL_CIRCULAR_CMD); + + /* if Write command was successful, verify PCIe mode */ + if (mci_poll_command_completion(mci_index, MCI_CMD_WRITE) == 0) { + /* Verify the PCIe mode selected */ + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_HB_CTRL_TX_CTRL_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) | + MCI_INDIRECT_CTRL_LOCAL_PKT | + MCI_INDIRECT_CTRL_READ_CMD); + /* if read was completed, verify PCIe mode */ + if (mci_poll_command_completion(mci_index, MCI_CMD_READ) == 0) { + reg_data = mci_mmio_read_32( + MCI_WRITE_READ_DATA_REG(mci_index)); + if (reg_data & MCI_HB_CTRL_TX_CTRL_PCIE_MODE) + ret = 0; + } + } + + debug_exit(); + return ret; +} + +/* Reduce sequence FIFO timer expiration threshold */ +static int mci_axi_set_fifo_thresh(int mci_index) +{ + uint32_t reg_data, ret = 0; + + debug_enter(); + /* This configuration reduces sequence FIFO timer expiration threshold + * (to 0x7 instead of 0xA). + * In MCI 1.6 version this configuration prevents possible functional + * issues. + * In version 1.82 the configuration prevents performance degradation + */ + + /* Configure local AP side */ + reg_data = MCI_PHY_CTRL_PIDI_MODE | + MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE | + MCI_PHY_CTRL_MCI_PHY_MODE_HOST | + MCI_PHY_CTRL_MCI_MAJOR | + MCI_PHY_CTRL_MCI_MINOR; + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), reg_data); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* Reduce the threshold */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_CTRL_IHB_MODE_CFG_REG_DEF_VAL); + + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_CTRL_IHB_MODE_CFG_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* Exit PIDI mode */ + reg_data = MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE | + MCI_PHY_CTRL_MCI_PHY_MODE_HOST | + MCI_PHY_CTRL_MCI_MAJOR | + MCI_PHY_CTRL_MCI_MINOR; + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), reg_data); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* Configure remote CP side */ + reg_data = MCI_PHY_CTRL_PIDI_MODE | + MCI_PHY_CTRL_MCI_MAJOR | + MCI_PHY_CTRL_MCI_MINOR | + MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE; + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), reg_data); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) | + MCI_CTRL_IHB_MODE_FWD_MOD); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* Reduce the threshold */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_CTRL_IHB_MODE_CFG_REG_DEF_VAL); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_CTRL_IHB_MODE_CFG_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT)); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* Exit PIDI mode */ + reg_data = MCI_PHY_CTRL_MCI_MAJOR | + MCI_PHY_CTRL_MCI_MINOR | + MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE; + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), reg_data); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) | + MCI_CTRL_IHB_MODE_FWD_MOD); + + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + debug_exit(); + return ret; +} + +/* Configure: + * 1. AP & CP TX thresholds and delta configurations + * 2. DLO & DLI FIFO full threshold + * 3. RX thresholds and delta configurations + * 4. CP AR and AW outstanding + * 5. AP AR and AW outstanding + */ +static int mci_axi_set_fifo_rx_tx_thresh(int mci_index) +{ + uint32_t ret = 0; + + debug_enter(); + /* AP TX thresholds and delta configurations (IHB_reg 0x1) */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_CTRL_TX_MEM_CFG_REG_DEF_VAL); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_CTRL_TX_MEM_CFG_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* CP TX thresholds and delta configurations (IHB_reg 0x1) */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_CTRL_TX_MEM_CFG_REG_DEF_VAL); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_CTRL_TX_MEM_CFG_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT)); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* AP DLO & DLI FIFO full threshold & Auto-Link enable (IHB_reg 0x8) */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL | + MCI_CTRL_MCI_PHY_SET_AUTO_LINK_EN(1)); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* CP DLO & DLI FIFO full threshold (IHB_reg 0x8) */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT)); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* AP RX thresholds and delta configurations (IHB_reg 0x0) */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_CTRL_RX_MEM_CFG_REG_DEF_AP_VAL); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_CTRL_RX_MEM_CFG_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* CP RX thresholds and delta configurations (IHB_reg 0x0) */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_CTRL_RX_MEM_CFG_REG_DEF_CP_VAL); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_CTRL_RX_MEM_CFG_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT)); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* AP AR & AW maximum AXI outstanding request cfg (HB_reg 0xd) */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_HB_CTRL_TX_CTRL_PRI_TH_QOS(8) | + MCI_HB_CTRL_TX_CTRL_MAX_RD_CNT(3) | + MCI_HB_CTRL_TX_CTRL_MAX_WR_CNT(3)); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_HB_CTRL_TX_CTRL_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* CP AR & AW maximum AXI outstanding request cfg (HB_reg 0xd) */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), + MCI_HB_CTRL_TX_CTRL_PRI_TH_QOS(8) | + MCI_HB_CTRL_TX_CTRL_MAX_RD_CNT(0xB) | + MCI_HB_CTRL_TX_CTRL_MAX_WR_CNT(0x11)); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_HB_CTRL_TX_CTRL_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT) | + MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB)); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + debug_exit(); + return ret; +} + +/* configure MCI to allow read & write transactions to arrive at the same time. + * Without the below configuration, MCI won't sent response to CPU for + * transactions which arrived simultaneously and will lead to CPU hang. + * The below will configure MCI to be able to pass transactions from/to CP/AP. + */ +static int mci_enable_simultaneous_transactions(int mci_index) +{ + uint32_t ret = 0; + + debug_enter(); + /* ID assignment (assigning global ID offset to CP) */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0), + MCI_DID_GLOBAL_ASSIGN_REQ_MCI_LOCAL_ID(2) | + MCI_DID_GLOBAL_ASSIGN_REQ_MCI_COUNT(2) | + MCI_DID_GLOBAL_ASSIGN_REQ_HOPS_NUM(2)); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(0), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_DID_GLOBAL_ASSIGNMENT_REQUEST_REG) | + MCI_INDIRECT_CTRL_ASSIGN_CMD); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* Assigning dest. ID=3 to all transactions entering from AXI at AP */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0), + MCI_HB_CTRL_WIN0_DEST_VALID_FLAG(1) | + MCI_HB_CTRL_WIN0_DEST_ID(3)); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(0), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_HB_CTRL_WIN0_DESTINATION_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* Assigning dest. ID=1 to all transactions entering from AXI at CP */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0), + MCI_HB_CTRL_WIN0_DEST_VALID_FLAG(1) | + MCI_HB_CTRL_WIN0_DEST_ID(1)); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(0), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_HB_CTRL_WIN0_DESTINATION_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT) | + MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB)); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* End address to all transactions entering from AXI at AP. + * This will lead to get match for any AXI address + * and receive destination ID=3 + */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0), 0xffffffff); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(0), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_HB_CTRL_WIN0_ADDRESS_MASK_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + /* End address to all transactions entering from AXI at CP. + * This will lead to get match for any AXI address + * and receive destination ID=1 + */ + mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0), 0xffffffff); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(0), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_HB_CTRL_WIN0_ADDRESS_MASK_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT) | + MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB)); + ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE); + + debug_exit(); + return ret; +} + +/* Check if MCI simultaneous transaction was already enabled. + * Currently bootrom does this mci configuration only when the boot source is + * SAR_MCIX4, in other cases it should be done at this stage. + * It is worth noticing that in case of booting from uart, the bootrom + * flow is different and this mci initialization is skipped even if boot + * source is SAR_MCIX4. Therefore new verification bases on appropriate mci's + * register content: if the appropriate reg contains 0x0 it means that the + * bootrom didn't perform required mci configuration. + * + * Returns: + * 0 - configuration already done + * 1 - configuration missing + */ +static _Bool mci_simulatenous_trans_missing(int mci_index) +{ + uint32_t reg, ret; + + /* read 'Window 0 Destination ID assignment' from HB register 0x3 + * (TX_CFG_W0_DST_ID) to check whether ID assignment was already + * performed by BootROM. + */ + debug_enter(); + mci_mmio_write_32(MCI_ACCESS_CMD_REG(0), + MCI_INDIRECT_REG_CTRL_ADDR( + MCI_HB_CTRL_WIN0_DESTINATION_REG_NUM) | + MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) | + MCI_INDIRECT_CTRL_LOCAL_PKT | + MCI_INDIRECT_CTRL_READ_CMD); + ret = mci_poll_command_completion(mci_index, MCI_CMD_READ); + + reg = mci_mmio_read_32(MCI_WRITE_READ_DATA_REG(mci_index)); + + if (ret) + ERROR("Failed to verify MCI simultaneous read/write status\n"); + + debug_exit(); + /* default ID assignment is 0, so if register doesn't contain zeros + * it means that bootrom already performed required configuration. + */ + if (reg != 0) + return 0; + + return 1; +} + +/* For A1 revision, configure the MCI link for performance improvement: + * - set MCI to support read/write transactions to arrive at the same time + * - Switch AXI to PCIe mode + * - Reduce sequence FIFO threshold + * - Configure RX/TX FIFO thresholds + * + * Note: + * We don't exit on error code from any sub routine, to try (best effort) to + * complete the MCI configuration. + * (If we exit - Bootloader will surely fail to boot) + */ +int mci_configure(int mci_index) +{ + int rval; + + debug_enter(); + /* According to design guidelines the MCI simultaneous transaction + * shouldn't be enabled more then once - therefore make sure that it + * wasn't already enabled in bootrom. + */ + if (mci_simulatenous_trans_missing(mci_index)) { + VERBOSE("Enabling MCI simultaneous transaction\n"); + /* set MCI to support read/write transactions + * to arrive at the same time + */ + rval = mci_enable_simultaneous_transactions(mci_index); + if (rval) + ERROR("Failed to set MCI simultaneous read/write\n"); + } else + VERBOSE("Skip MCI ID assignment - already done by bootrom\n"); + + /* Configure MCI for more consistent behavior with AXI protocol */ + rval = mci_axi_set_pcie_mode(mci_index); + if (rval) + ERROR("Failed to set MCI to AXI PCIe mode\n"); + + /* reduce FIFO global threshold */ + rval = mci_axi_set_fifo_thresh(mci_index); + if (rval) + ERROR("Failed to set MCI FIFO global threshold\n"); + + /* configure RX/TX FIFO thresholds */ + rval = mci_axi_set_fifo_rx_tx_thresh(mci_index); + if (rval) + ERROR("Failed to set MCI RX/TX FIFO threshold\n"); + + debug_exit(); + return 1; +} + +int mci_get_link_status(void) +{ + uint32_t cmd, data; + + cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_CTRL_STATUS_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT | MCI_INDIRECT_CTRL_READ_CMD); + if (mci_read(0, cmd, &data)) { + ERROR("Failed to read status register\n"); + return -1; + } + + /* Check if the link is ready */ + if (data != MCI_CTRL_PHY_READY) { + ERROR("Bad link status %x\n", data); + return -1; + } + + return 0; +} + +void mci_turn_link_down(void) +{ + uint32_t cmd, data; + int rval = 0; + + debug_enter(); + + /* Turn off auto-link */ + cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + data = (MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL2 | + MCI_CTRL_MCI_PHY_SET_AUTO_LINK_EN(0)); + rval = mci_write(0, cmd, data); + if (rval) + ERROR("Failed to turn off auto-link\n"); + + /* Reset AP PHY */ + cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + data = (MCI_PHY_CTRL_MCI_MINOR | + MCI_PHY_CTRL_MCI_MAJOR | + MCI_PHY_CTRL_MCI_PHY_MODE_HOST | + MCI_PHY_CTRL_MCI_PHY_RESET_CORE); + rval = mci_write(0, cmd, data); + if (rval) + ERROR("Failed to reset AP PHY\n"); + + /* Clear all status & CRC values */ + cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_LINK_CRC_CTRL_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + data = 0x0; + mci_write(0, cmd, data); + cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_CTRL_STATUS_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + data = 0x0; + rval = mci_write(0, cmd, data); + if (rval) + ERROR("Failed to reset AP PHY\n"); + + /* Wait 5ms before un-reset the PHY */ + mdelay(5); + + /* Un-reset AP PHY */ + cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + data = (MCI_PHY_CTRL_MCI_MINOR | MCI_PHY_CTRL_MCI_MAJOR | + MCI_PHY_CTRL_MCI_PHY_MODE_HOST); + rval = mci_write(0, cmd, data); + if (rval) + ERROR("Failed to un-reset AP PHY\n"); + + debug_exit(); +} + +void mci_turn_link_on(void) +{ + uint32_t cmd, data; + int rval = 0; + + debug_enter(); + /* Turn on auto-link */ + cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM) | + MCI_INDIRECT_CTRL_LOCAL_PKT); + data = (MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL2 | + MCI_CTRL_MCI_PHY_SET_AUTO_LINK_EN(1)); + rval = mci_write(0, cmd, data); + if (rval) + ERROR("Failed to turn on auto-link\n"); + + debug_exit(); +} + +/* Initialize MCI for performance improvements */ +int mci_initialize(int mci_index) +{ + int ret; + + debug_enter(); + INFO("MCI%d initialization:\n", mci_index); + + ret = mci_configure(mci_index); + + debug_exit(); + return ret; +} diff --git a/drivers/marvell/mochi/ap807_setup.c b/drivers/marvell/mochi/ap807_setup.c new file mode 100644 index 00000000..075ca31f --- /dev/null +++ b/drivers/marvell/mochi/ap807_setup.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* AP807 Marvell SoC driver */ + +#include <ap_setup.h> +#include <cache_llc.h> +#include <ccu.h> +#include <debug.h> +#include <io_win.h> +#include <mci.h> +#include <mmio.h> +#include <mvebu_def.h> + +#define SMMU_sACR (MVEBU_SMMU_BASE + 0x10) +#define SMMU_sACR_PG_64K (1 << 16) + +#define CCU_GSPMU_CR (MVEBU_CCU_BASE(MVEBU_AP0) \ + + 0x3F0) +#define GSPMU_CPU_CONTROL (0x1 << 0) + +#define CCU_HTC_CR (MVEBU_CCU_BASE(MVEBU_AP0) \ + + 0x200) +#define CCU_SET_POC_OFFSET 5 + +#define DSS_CR0 (MVEBU_RFU_BASE + 0x100) +#define DVM_48BIT_VA_ENABLE (1 << 21) + +/* Secure MoChi incoming access */ +#define SEC_MOCHI_IN_ACC_REG (MVEBU_RFU_BASE + 0x4738) +#define SEC_MOCHI_IN_ACC_IHB0_EN (1) +#define SEC_MOCHI_IN_ACC_IHB1_EN (1 << 3) +#define SEC_MOCHI_IN_ACC_IHB2_EN (1 << 6) +#define SEC_MOCHI_IN_ACC_PIDI_EN (1 << 9) +#define SEC_IN_ACCESS_ENA_ALL_MASTERS (SEC_MOCHI_IN_ACC_IHB0_EN | \ + SEC_MOCHI_IN_ACC_IHB1_EN | \ + SEC_MOCHI_IN_ACC_IHB2_EN | \ + SEC_MOCHI_IN_ACC_PIDI_EN) + +/* SYSRST_OUTn Config definitions */ +#define MVEBU_SYSRST_OUT_CONFIG_REG (MVEBU_MISC_SOC_BASE + 0x4) +#define WD_MASK_SYS_RST_OUT (1 << 2) + +/* DSS PHY for DRAM */ +#define DSS_SCR_REG (MVEBU_RFU_BASE + 0x208) +#define DSS_PPROT_OFFS 4 +#define DSS_PPROT_MASK 0x7 +#define DSS_PPROT_PRIV_SECURE_DATA 0x1 + +/* Used for Units of AP-807 (e.g. SDIO and etc) */ +#define MVEBU_AXI_ATTR_BASE (MVEBU_REGS_BASE + 0x6F4580) +#define MVEBU_AXI_ATTR_REG(index) (MVEBU_AXI_ATTR_BASE + \ + 0x4 * index) + +enum axi_attr { + AXI_SDIO_ATTR = 0, + AXI_DFX_ATTR, + AXI_MAX_ATTR, +}; + +static void ap_sec_masters_access_en(uint32_t enable) +{ + uint32_t reg; + + /* Open/Close incoming access for all masters. + * The access is disabled in trusted boot mode + * Could only be done in EL3 + */ + reg = mmio_read_32(SEC_MOCHI_IN_ACC_REG); + if (enable) + mmio_write_32(SEC_MOCHI_IN_ACC_REG, reg | + SEC_IN_ACCESS_ENA_ALL_MASTERS); + else + mmio_write_32(SEC_MOCHI_IN_ACC_REG, + reg & ~SEC_IN_ACCESS_ENA_ALL_MASTERS); +} + +static void setup_smmu(void) +{ + uint32_t reg; + + /* Set the SMMU page size to 64 KB */ + reg = mmio_read_32(SMMU_sACR); + reg |= SMMU_sACR_PG_64K; + mmio_write_32(SMMU_sACR, reg); +} + +static void init_aurora2(void) +{ + uint32_t reg; + + /* Enable GSPMU control by CPU */ + reg = mmio_read_32(CCU_GSPMU_CR); + reg |= GSPMU_CPU_CONTROL; + mmio_write_32(CCU_GSPMU_CR, reg); + +#if LLC_ENABLE + /* Enable LLC for AP807 in exclusive mode */ + llc_enable(0, 1); + + /* Set point of coherency to DDR. + * This is required by units which have + * SW cache coherency + */ + reg = mmio_read_32(CCU_HTC_CR); + reg |= (0x1 << CCU_SET_POC_OFFSET); + mmio_write_32(CCU_HTC_CR, reg); +#endif /* LLC_ENABLE */ +} + + +/* MCIx indirect access register are based by default at 0xf4000000/0xf6000000 + * to avoid conflict of internal registers of units connected via MCIx, which + * can be based on the same address (i.e CP1 base is also 0xf4000000), + * the following routines remaps the MCIx indirect bases to another domain + */ +static void mci_remap_indirect_access_base(void) +{ + uint32_t mci; + + for (mci = 0; mci < MCI_MAX_UNIT_ID; mci++) + mmio_write_32(MCIX4_REG_START_ADDRESS_REG(mci), + MVEBU_MCI_REG_BASE_REMAP(mci) >> + MCI_REMAP_OFF_SHIFT); +} + +static void ap807_axi_attr_init(void) +{ + uint32_t index, data; + + /* Initialize AXI attributes for AP807 */ + /* Go over the AXI attributes and set Ax-Cache and Ax-Domain */ + for (index = 0; index < AXI_MAX_ATTR; index++) { + switch (index) { + /* DFX works with no coherent only - + * there's no option to configure the Ax-Cache and Ax-Domain + */ + case AXI_DFX_ATTR: + continue; + default: + /* Set Ax-Cache as cacheable, no allocate, modifiable, + * bufferable. + * The values are different because Read & Write + * definition is different in Ax-Cache + */ + data = mmio_read_32(MVEBU_AXI_ATTR_REG(index)); + data &= ~MVEBU_AXI_ATTR_ARCACHE_MASK; + data |= (CACHE_ATTR_WRITE_ALLOC | + CACHE_ATTR_CACHEABLE | + CACHE_ATTR_BUFFERABLE) << + MVEBU_AXI_ATTR_ARCACHE_OFFSET; + data &= ~MVEBU_AXI_ATTR_AWCACHE_MASK; + data |= (CACHE_ATTR_READ_ALLOC | + CACHE_ATTR_CACHEABLE | + CACHE_ATTR_BUFFERABLE) << + MVEBU_AXI_ATTR_AWCACHE_OFFSET; + /* Set Ax-Domain as Outer domain */ + data &= ~MVEBU_AXI_ATTR_ARDOMAIN_MASK; + data |= DOMAIN_OUTER_SHAREABLE << + MVEBU_AXI_ATTR_ARDOMAIN_OFFSET; + data &= ~MVEBU_AXI_ATTR_AWDOMAIN_MASK; + data |= DOMAIN_OUTER_SHAREABLE << + MVEBU_AXI_ATTR_AWDOMAIN_OFFSET; + mmio_write_32(MVEBU_AXI_ATTR_REG(index), data); + } + } +} + +static void misc_soc_configurations(void) +{ + uint32_t reg; + + /* Enable 48-bit VA */ + mmio_setbits_32(DSS_CR0, DVM_48BIT_VA_ENABLE); + + /* Un-mask Watchdog reset from influencing the SYSRST_OUTn. + * Otherwise, upon WD timeout, the WD reset signal won't trigger reset + */ + reg = mmio_read_32(MVEBU_SYSRST_OUT_CONFIG_REG); + reg &= ~(WD_MASK_SYS_RST_OUT); + mmio_write_32(MVEBU_SYSRST_OUT_CONFIG_REG, reg); +} + +void ap_init(void) +{ + /* Setup Aurora2. */ + init_aurora2(); + + /* configure MCI mapping */ + mci_remap_indirect_access_base(); + + /* configure IO_WIN windows */ + init_io_win(MVEBU_AP0); + + /* configure CCU windows */ + init_ccu(MVEBU_AP0); + + /* configure the SMMU */ + setup_smmu(); + + /* Open AP incoming access for all masters */ + ap_sec_masters_access_en(1); + + /* configure axi for AP */ + ap807_axi_attr_init(); + + /* misc configuration of the SoC */ + misc_soc_configurations(); +} + +static void ap807_dram_phy_access_config(void) +{ + uint32_t reg_val; + /* Update DSS port access permission to DSS_PHY */ + reg_val = mmio_read_32(DSS_SCR_REG); + reg_val &= ~(DSS_PPROT_MASK << DSS_PPROT_OFFS); + reg_val |= ((DSS_PPROT_PRIV_SECURE_DATA & DSS_PPROT_MASK) << + DSS_PPROT_OFFS); + mmio_write_32(DSS_SCR_REG, reg_val); +} + +void ap_ble_init(void) +{ + /* Enable DSS port */ + ap807_dram_phy_access_config(); +} + +int ap_get_count(void) +{ + return 1; +} + + diff --git a/drivers/marvell/mochi/apn806_setup.c b/drivers/marvell/mochi/apn806_setup.c new file mode 100644 index 00000000..1d33be93 --- /dev/null +++ b/drivers/marvell/mochi/apn806_setup.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* AP806 Marvell SoC driver */ + +#include <ap_setup.h> +#include <ccu.h> +#include <cache_llc.h> +#include <debug.h> +#include <io_win.h> +#include <mci.h> +#include <mmio.h> +#include <mvebu_def.h> + +#define SMMU_sACR (MVEBU_SMMU_BASE + 0x10) +#define SMMU_sACR_PG_64K (1 << 16) + +#define CCU_GSPMU_CR (MVEBU_CCU_BASE(MVEBU_AP0) + \ + 0x3F0) +#define GSPMU_CPU_CONTROL (0x1 << 0) + +#define CCU_HTC_CR (MVEBU_CCU_BASE(MVEBU_AP0) + \ + 0x200) +#define CCU_SET_POC_OFFSET 5 + +#define CCU_RGF(win) (MVEBU_CCU_BASE(MVEBU_AP0) + \ + 0x90 + 4 * (win)) + +#define DSS_CR0 (MVEBU_RFU_BASE + 0x100) +#define DVM_48BIT_VA_ENABLE (1 << 21) + +/* Secure MoChi incoming access */ +#define SEC_MOCHI_IN_ACC_REG (MVEBU_RFU_BASE + 0x4738) +#define SEC_MOCHI_IN_ACC_IHB0_EN (1) +#define SEC_MOCHI_IN_ACC_IHB1_EN (1 << 3) +#define SEC_MOCHI_IN_ACC_IHB2_EN (1 << 6) +#define SEC_MOCHI_IN_ACC_PIDI_EN (1 << 9) +#define SEC_IN_ACCESS_ENA_ALL_MASTERS (SEC_MOCHI_IN_ACC_IHB0_EN | \ + SEC_MOCHI_IN_ACC_IHB1_EN | \ + SEC_MOCHI_IN_ACC_IHB2_EN | \ + SEC_MOCHI_IN_ACC_PIDI_EN) + +/* SYSRST_OUTn Config definitions */ +#define MVEBU_SYSRST_OUT_CONFIG_REG (MVEBU_MISC_SOC_BASE + 0x4) +#define WD_MASK_SYS_RST_OUT (1 << 2) + +/* Generic Timer System Controller */ +#define MVEBU_MSS_GTCR_REG (MVEBU_REGS_BASE + 0x581000) +#define MVEBU_MSS_GTCR_ENABLE_BIT 0x1 + +/* + * AXI Configuration. + */ + +/* Used for Units of AP-806 (e.g. SDIO and etc) */ +#define MVEBU_AXI_ATTR_BASE (MVEBU_REGS_BASE + 0x6F4580) +#define MVEBU_AXI_ATTR_REG(index) (MVEBU_AXI_ATTR_BASE + \ + 0x4 * index) + +enum axi_attr { + AXI_SDIO_ATTR = 0, + AXI_DFX_ATTR, + AXI_MAX_ATTR, +}; + +static void apn_sec_masters_access_en(uint32_t enable) +{ + uint32_t reg; + + /* Open/Close incoming access for all masters. + * The access is disabled in trusted boot mode + * Could only be done in EL3 + */ + reg = mmio_read_32(SEC_MOCHI_IN_ACC_REG); + if (enable) + mmio_write_32(SEC_MOCHI_IN_ACC_REG, reg | + SEC_IN_ACCESS_ENA_ALL_MASTERS); + else + mmio_write_32(SEC_MOCHI_IN_ACC_REG, reg & + ~SEC_IN_ACCESS_ENA_ALL_MASTERS); +} + +static void setup_smmu(void) +{ + uint32_t reg; + + /* Set the SMMU page size to 64 KB */ + reg = mmio_read_32(SMMU_sACR); + reg |= SMMU_sACR_PG_64K; + mmio_write_32(SMMU_sACR, reg); +} + +static void apn806_errata_wa_init(void) +{ + /* + * ERRATA ID: RES-3033912 - Internal Address Space Init state causes + * a hang upon accesses to [0xf070_0000, 0xf07f_ffff] + * Workaround: Boot Firmware (ATF) should configure CCU_RGF_WIN(4) to + * split [0x6e_0000, 0xff_ffff] to values [0x6e_0000, 0x6f_ffff] and + * [0x80_0000, 0xff_ffff] that cause accesses to the + * segment of [0xf070_0000, 0xf07f_ffff] to act as RAZWI. + */ + mmio_write_32(CCU_RGF(4), 0x37f9b809); + mmio_write_32(CCU_RGF(5), 0x7ffa0009); +} + +static void init_aurora2(void) +{ + uint32_t reg; + + /* Enable GSPMU control by CPU */ + reg = mmio_read_32(CCU_GSPMU_CR); + reg |= GSPMU_CPU_CONTROL; + mmio_write_32(CCU_GSPMU_CR, reg); + +#if LLC_ENABLE + /* Enable LLC for AP806 in exclusive mode */ + llc_enable(0, 1); + + /* Set point of coherency to DDR. + * This is required by units which have + * SW cache coherency + */ + reg = mmio_read_32(CCU_HTC_CR); + reg |= (0x1 << CCU_SET_POC_OFFSET); + mmio_write_32(CCU_HTC_CR, reg); +#endif /* LLC_ENABLE */ + + apn806_errata_wa_init(); +} + + +/* MCIx indirect access register are based by default at 0xf4000000/0xf6000000 + * to avoid conflict of internal registers of units connected via MCIx, which + * can be based on the same address (i.e CP1 base is also 0xf4000000), + * the following routines remaps the MCIx indirect bases to another domain + */ +static void mci_remap_indirect_access_base(void) +{ + uint32_t mci; + + for (mci = 0; mci < MCI_MAX_UNIT_ID; mci++) + mmio_write_32(MCIX4_REG_START_ADDRESS_REG(mci), + MVEBU_MCI_REG_BASE_REMAP(mci) >> + MCI_REMAP_OFF_SHIFT); +} + +static void apn806_axi_attr_init(void) +{ + uint32_t index, data; + + /* Initialize AXI attributes for APN806 */ + + /* Go over the AXI attributes and set Ax-Cache and Ax-Domain */ + for (index = 0; index < AXI_MAX_ATTR; index++) { + switch (index) { + /* DFX works with no coherent only - + * there's no option to configure the Ax-Cache and Ax-Domain + */ + case AXI_DFX_ATTR: + continue; + default: + /* Set Ax-Cache as cacheable, no allocate, modifiable, + * bufferable + * The values are different because Read & Write + * definition is different in Ax-Cache + */ + data = mmio_read_32(MVEBU_AXI_ATTR_REG(index)); + data &= ~MVEBU_AXI_ATTR_ARCACHE_MASK; + data |= (CACHE_ATTR_WRITE_ALLOC | + CACHE_ATTR_CACHEABLE | + CACHE_ATTR_BUFFERABLE) << + MVEBU_AXI_ATTR_ARCACHE_OFFSET; + data &= ~MVEBU_AXI_ATTR_AWCACHE_MASK; + data |= (CACHE_ATTR_READ_ALLOC | + CACHE_ATTR_CACHEABLE | + CACHE_ATTR_BUFFERABLE) << + MVEBU_AXI_ATTR_AWCACHE_OFFSET; + /* Set Ax-Domain as Outer domain */ + data &= ~MVEBU_AXI_ATTR_ARDOMAIN_MASK; + data |= DOMAIN_OUTER_SHAREABLE << + MVEBU_AXI_ATTR_ARDOMAIN_OFFSET; + data &= ~MVEBU_AXI_ATTR_AWDOMAIN_MASK; + data |= DOMAIN_OUTER_SHAREABLE << + MVEBU_AXI_ATTR_AWDOMAIN_OFFSET; + mmio_write_32(MVEBU_AXI_ATTR_REG(index), data); + } + } +} + +static void dss_setup(void) +{ + /* Enable 48-bit VA */ + mmio_setbits_32(DSS_CR0, DVM_48BIT_VA_ENABLE); +} + +void misc_soc_configurations(void) +{ + uint32_t reg; + + /* Un-mask Watchdog reset from influencing the SYSRST_OUTn. + * Otherwise, upon WD timeout, the WD reset signal won't trigger reset + */ + reg = mmio_read_32(MVEBU_SYSRST_OUT_CONFIG_REG); + reg &= ~(WD_MASK_SYS_RST_OUT); + mmio_write_32(MVEBU_SYSRST_OUT_CONFIG_REG, reg); +} + +void ap_init(void) +{ + /* Setup Aurora2. */ + init_aurora2(); + + /* configure MCI mapping */ + mci_remap_indirect_access_base(); + + /* configure IO_WIN windows */ + init_io_win(MVEBU_AP0); + + /* configure CCU windows */ + init_ccu(MVEBU_AP0); + + /* configure DSS */ + dss_setup(); + + /* configure the SMMU */ + setup_smmu(); + + /* Open APN incoming access for all masters */ + apn_sec_masters_access_en(1); + + /* configure axi for APN*/ + apn806_axi_attr_init(); + + /* misc configuration of the SoC */ + misc_soc_configurations(); +} + +void ap_ble_init(void) +{ +} + +int ap_get_count(void) +{ + return 1; +} + diff --git a/drivers/marvell/mochi/cp110_setup.c b/drivers/marvell/mochi/cp110_setup.c new file mode 100644 index 00000000..c4cb307f --- /dev/null +++ b/drivers/marvell/mochi/cp110_setup.c @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* CP110 Marvell SoC driver */ + +#include <amb_adec.h> +#include <cp110_setup.h> +#include <debug.h> +#include <delay_timer.h> +#include <iob.h> +#include <plat_marvell.h> + +/* + * AXI Configuration. + */ + + /* Used for Units of CP-110 (e.g. USB device, USB Host, and etc) */ +#define MVEBU_AXI_ATTR_OFFSET (0x441300) +#define MVEBU_AXI_ATTR_REG(index) (MVEBU_AXI_ATTR_OFFSET + \ + 0x4 * index) + +/* AXI Protection bits */ +#define MVEBU_AXI_PROT_OFFSET (0x441200) + +/* AXI Protection regs */ +#define MVEBU_AXI_PROT_REG(index) ((index <= 4) ? \ + (MVEBU_AXI_PROT_OFFSET + \ + 0x4 * index) : \ + (MVEBU_AXI_PROT_OFFSET + 0x18)) +#define MVEBU_AXI_PROT_REGS_NUM (6) + +#define MVEBU_SOC_CFGS_OFFSET (0x441900) +#define MVEBU_SOC_CFG_REG(index) (MVEBU_SOC_CFGS_OFFSET + \ + 0x4 * index) +#define MVEBU_SOC_CFG_REG_NUM (0) +#define MVEBU_SOC_CFG_GLOG_SECURE_EN_MASK (0xE) + +/* SATA3 MBUS to AXI regs */ +#define MVEBU_BRIDGE_WIN_DIS_REG (MVEBU_SOC_CFGS_OFFSET + 0x10) +#define MVEBU_BRIDGE_WIN_DIS_OFF (0x0) + +/* SATA3 MBUS to AXI regs */ +#define MVEBU_SATA_M2A_AXI_PORT_CTRL_REG (0x54ff04) + +/* AXI to MBUS bridge registers */ +#define MVEBU_AMB_IP_OFFSET (0x13ff00) +#define MVEBU_AMB_IP_BRIDGE_WIN_REG(win) (MVEBU_AMB_IP_OFFSET + \ + (win * 0x8)) +#define MVEBU_AMB_IP_BRIDGE_WIN_EN_OFFSET 0 +#define MVEBU_AMB_IP_BRIDGE_WIN_EN_MASK \ + (0x1 << MVEBU_AMB_IP_BRIDGE_WIN_EN_OFFSET) +#define MVEBU_AMB_IP_BRIDGE_WIN_SIZE_OFFSET 16 +#define MVEBU_AMB_IP_BRIDGE_WIN_SIZE_MASK \ + (0xffff << MVEBU_AMB_IP_BRIDGE_WIN_SIZE_OFFSET) + +#define MVEBU_SAMPLE_AT_RESET_REG (0x440600) +#define SAR_PCIE1_CLK_CFG_OFFSET 31 +#define SAR_PCIE1_CLK_CFG_MASK (0x1 << SAR_PCIE1_CLK_CFG_OFFSET) +#define SAR_PCIE0_CLK_CFG_OFFSET 30 +#define SAR_PCIE0_CLK_CFG_MASK (0x1 << SAR_PCIE0_CLK_CFG_OFFSET) +#define SAR_I2C_INIT_EN_OFFSET 24 +#define SAR_I2C_INIT_EN_MASK (1 << SAR_I2C_INIT_EN_OFFSET) + +/******************************************************************************* + * PCIE clock buffer control + ******************************************************************************/ +#define MVEBU_PCIE_REF_CLK_BUF_CTRL (0x4404F0) +#define PCIE1_REFCLK_BUFF_SOURCE 0x800 +#define PCIE0_REFCLK_BUFF_SOURCE 0x400 + +/******************************************************************************* + * MSS Device Push Set Register + ******************************************************************************/ +#define MVEBU_CP_MSS_DPSHSR_REG (0x280040) +#define MSS_DPSHSR_REG_PCIE_CLK_SEL 0x8 + +/******************************************************************************* + * RTC Configuration + ******************************************************************************/ +#define MVEBU_RTC_BASE (0x284000) +#define MVEBU_RTC_STATUS_REG (MVEBU_RTC_BASE + 0x0) +#define MVEBU_RTC_STATUS_ALARM1_MASK 0x1 +#define MVEBU_RTC_STATUS_ALARM2_MASK 0x2 +#define MVEBU_RTC_IRQ_1_CONFIG_REG (MVEBU_RTC_BASE + 0x4) +#define MVEBU_RTC_IRQ_2_CONFIG_REG (MVEBU_RTC_BASE + 0x8) +#define MVEBU_RTC_TIME_REG (MVEBU_RTC_BASE + 0xC) +#define MVEBU_RTC_ALARM_1_REG (MVEBU_RTC_BASE + 0x10) +#define MVEBU_RTC_ALARM_2_REG (MVEBU_RTC_BASE + 0x14) +#define MVEBU_RTC_CCR_REG (MVEBU_RTC_BASE + 0x18) +#define MVEBU_RTC_NOMINAL_TIMING 0x2000 +#define MVEBU_RTC_NOMINAL_TIMING_MASK 0x7FFF +#define MVEBU_RTC_TEST_CONFIG_REG (MVEBU_RTC_BASE + 0x1C) +#define MVEBU_RTC_BRIDGE_TIMING_CTRL0_REG (MVEBU_RTC_BASE + 0x80) +#define MVEBU_RTC_WRCLK_PERIOD_MASK 0xFFFF +#define MVEBU_RTC_WRCLK_PERIOD_DEFAULT 0x3FF +#define MVEBU_RTC_WRCLK_SETUP_OFFS 16 +#define MVEBU_RTC_WRCLK_SETUP_MASK 0xFFFF0000 +#define MVEBU_RTC_WRCLK_SETUP_DEFAULT 0x29 +#define MVEBU_RTC_BRIDGE_TIMING_CTRL1_REG (MVEBU_RTC_BASE + 0x84) +#define MVEBU_RTC_READ_OUTPUT_DELAY_MASK 0xFFFF +#define MVEBU_RTC_READ_OUTPUT_DELAY_DEFAULT 0x1F + +enum axi_attr { + AXI_ADUNIT_ATTR = 0, + AXI_COMUNIT_ATTR, + AXI_EIP197_ATTR, + AXI_USB3D_ATTR, + AXI_USB3H0_ATTR, + AXI_USB3H1_ATTR, + AXI_SATA0_ATTR, + AXI_SATA1_ATTR, + AXI_DAP_ATTR, + AXI_DFX_ATTR, + AXI_DBG_TRC_ATTR = 12, + AXI_SDIO_ATTR, + AXI_MSS_ATTR, + AXI_MAX_ATTR, +}; + +/* Most stream IDS are configured centrally in the CP-110 RFU + * but some are configured inside the unit registers + */ +#define RFU_STREAM_ID_BASE (0x450000) +#define USB3H_0_STREAM_ID_REG (RFU_STREAM_ID_BASE + 0xC) +#define USB3H_1_STREAM_ID_REG (RFU_STREAM_ID_BASE + 0x10) +#define SATA_0_STREAM_ID_REG (RFU_STREAM_ID_BASE + 0x14) +#define SATA_1_STREAM_ID_REG (RFU_STREAM_ID_BASE + 0x18) + +#define CP_DMA_0_STREAM_ID_REG (0x6B0010) +#define CP_DMA_1_STREAM_ID_REG (0x6D0010) + +/* We allocate IDs 128-255 for PCIe */ +#define MAX_STREAM_ID (0x80) + +uintptr_t stream_id_reg[] = { + USB3H_0_STREAM_ID_REG, + USB3H_1_STREAM_ID_REG, + CP_DMA_0_STREAM_ID_REG, + CP_DMA_1_STREAM_ID_REG, + SATA_0_STREAM_ID_REG, + SATA_1_STREAM_ID_REG, + 0 +}; + +static void cp110_errata_wa_init(uintptr_t base) +{ + uint32_t data; + + /* ERRATA GL-4076863: + * Reset value for global_secure_enable inputs must be changed + * from '1' to '0'. + * When asserted, only "secured" transactions can enter IHB + * configuration space. + * However, blocking AXI transactions is performed by IOB. + * Performing it also at IHB/HB complicates programming model. + * + * Enable non-secure access in SOC configuration register + */ + data = mmio_read_32(base + MVEBU_SOC_CFG_REG(MVEBU_SOC_CFG_REG_NUM)); + data &= ~MVEBU_SOC_CFG_GLOG_SECURE_EN_MASK; + mmio_write_32(base + MVEBU_SOC_CFG_REG(MVEBU_SOC_CFG_REG_NUM), data); +} + +static void cp110_pcie_clk_cfg(uintptr_t base) +{ + uint32_t pcie0_clk, pcie1_clk, reg; + + /* + * Determine the pcie0/1 clock direction (input/output) from the + * sample at reset. + */ + reg = mmio_read_32(base + MVEBU_SAMPLE_AT_RESET_REG); + pcie0_clk = (reg & SAR_PCIE0_CLK_CFG_MASK) >> SAR_PCIE0_CLK_CFG_OFFSET; + pcie1_clk = (reg & SAR_PCIE1_CLK_CFG_MASK) >> SAR_PCIE1_CLK_CFG_OFFSET; + + /* CP110 revision A2 */ + if (cp110_rev_id_get(base) == MVEBU_CP110_REF_ID_A2) { + /* + * PCIe Reference Clock Buffer Control register must be + * set according to the clock direction (input/output) + */ + reg = mmio_read_32(base + MVEBU_PCIE_REF_CLK_BUF_CTRL); + reg &= ~(PCIE0_REFCLK_BUFF_SOURCE | PCIE1_REFCLK_BUFF_SOURCE); + if (!pcie0_clk) + reg |= PCIE0_REFCLK_BUFF_SOURCE; + if (!pcie1_clk) + reg |= PCIE1_REFCLK_BUFF_SOURCE; + + mmio_write_32(base + MVEBU_PCIE_REF_CLK_BUF_CTRL, reg); + } + + /* CP110 revision A1 */ + if (cp110_rev_id_get(base) == MVEBU_CP110_REF_ID_A1) { + if (!pcie0_clk || !pcie1_clk) { + /* + * if one of the pcie clocks is set to input, + * we need to set mss_push[131] field, otherwise, + * the pcie clock might not work. + */ + reg = mmio_read_32(base + MVEBU_CP_MSS_DPSHSR_REG); + reg |= MSS_DPSHSR_REG_PCIE_CLK_SEL; + mmio_write_32(base + MVEBU_CP_MSS_DPSHSR_REG, reg); + } + } +} + +/* Set a unique stream id for all DMA capable devices */ +static void cp110_stream_id_init(uintptr_t base, uint32_t stream_id) +{ + int i = 0; + + while (stream_id_reg[i]) { + if (i > MAX_STREAM_ID_PER_CP) { + NOTICE("Only first %d (maximum) Stream IDs allocated\n", + MAX_STREAM_ID_PER_CP); + return; + } + + if ((stream_id_reg[i] == CP_DMA_0_STREAM_ID_REG) || + (stream_id_reg[i] == CP_DMA_1_STREAM_ID_REG)) + mmio_write_32(base + stream_id_reg[i], + stream_id << 16 | stream_id); + else + mmio_write_32(base + stream_id_reg[i], stream_id); + + /* SATA port 0/1 are in the same SATA unit, and they should use + * the same STREAM ID number + */ + if (stream_id_reg[i] != SATA_0_STREAM_ID_REG) + stream_id++; + + i++; + } +} + +static void cp110_axi_attr_init(uintptr_t base) +{ + uint32_t index, data; + + /* Initialize AXI attributes for Armada-7K/8K SoC */ + + /* Go over the AXI attributes and set Ax-Cache and Ax-Domain */ + for (index = 0; index < AXI_MAX_ATTR; index++) { + switch (index) { + /* DFX and MSS unit works with no coherent only - + * there's no option to configure the Ax-Cache and Ax-Domain + */ + case AXI_DFX_ATTR: + case AXI_MSS_ATTR: + continue; + default: + /* Set Ax-Cache as cacheable, no allocate, modifiable, + * bufferable + * The values are different because Read & Write + * definition is different in Ax-Cache + */ + data = mmio_read_32(base + MVEBU_AXI_ATTR_REG(index)); + data &= ~MVEBU_AXI_ATTR_ARCACHE_MASK; + data |= (CACHE_ATTR_WRITE_ALLOC | + CACHE_ATTR_CACHEABLE | + CACHE_ATTR_BUFFERABLE) << + MVEBU_AXI_ATTR_ARCACHE_OFFSET; + data &= ~MVEBU_AXI_ATTR_AWCACHE_MASK; + data |= (CACHE_ATTR_READ_ALLOC | + CACHE_ATTR_CACHEABLE | + CACHE_ATTR_BUFFERABLE) << + MVEBU_AXI_ATTR_AWCACHE_OFFSET; + /* Set Ax-Domain as Outer domain */ + data &= ~MVEBU_AXI_ATTR_ARDOMAIN_MASK; + data |= DOMAIN_OUTER_SHAREABLE << + MVEBU_AXI_ATTR_ARDOMAIN_OFFSET; + data &= ~MVEBU_AXI_ATTR_AWDOMAIN_MASK; + data |= DOMAIN_OUTER_SHAREABLE << + MVEBU_AXI_ATTR_AWDOMAIN_OFFSET; + mmio_write_32(base + MVEBU_AXI_ATTR_REG(index), data); + } + } + + /* SATA IOCC supported, cache attributes + * for SATA MBUS to AXI configuration. + */ + data = mmio_read_32(base + MVEBU_SATA_M2A_AXI_PORT_CTRL_REG); + data &= ~MVEBU_SATA_M2A_AXI_AWCACHE_MASK; + data |= (CACHE_ATTR_WRITE_ALLOC | + CACHE_ATTR_CACHEABLE | + CACHE_ATTR_BUFFERABLE) << + MVEBU_SATA_M2A_AXI_AWCACHE_OFFSET; + data &= ~MVEBU_SATA_M2A_AXI_ARCACHE_MASK; + data |= (CACHE_ATTR_READ_ALLOC | + CACHE_ATTR_CACHEABLE | + CACHE_ATTR_BUFFERABLE) << + MVEBU_SATA_M2A_AXI_ARCACHE_OFFSET; + mmio_write_32(base + MVEBU_SATA_M2A_AXI_PORT_CTRL_REG, data); + + /* Set all IO's AXI attribute to non-secure access. */ + for (index = 0; index < MVEBU_AXI_PROT_REGS_NUM; index++) + mmio_write_32(base + MVEBU_AXI_PROT_REG(index), + DOMAIN_SYSTEM_SHAREABLE); +} + +static void amb_bridge_init(uintptr_t base) +{ + uint32_t reg; + + /* Open AMB bridge Window to Access COMPHY/MDIO registers */ + reg = mmio_read_32(base + MVEBU_AMB_IP_BRIDGE_WIN_REG(0)); + reg &= ~(MVEBU_AMB_IP_BRIDGE_WIN_SIZE_MASK | + MVEBU_AMB_IP_BRIDGE_WIN_EN_MASK); + reg |= (0x7ff << MVEBU_AMB_IP_BRIDGE_WIN_SIZE_OFFSET) | + (0x1 << MVEBU_AMB_IP_BRIDGE_WIN_EN_OFFSET); + mmio_write_32(base + MVEBU_AMB_IP_BRIDGE_WIN_REG(0), reg); +} + +static void cp110_rtc_init(uintptr_t base) +{ + /* Update MBus timing parameters before accessing RTC registers */ + mmio_clrsetbits_32(base + MVEBU_RTC_BRIDGE_TIMING_CTRL0_REG, + MVEBU_RTC_WRCLK_PERIOD_MASK, + MVEBU_RTC_WRCLK_PERIOD_DEFAULT); + + mmio_clrsetbits_32(base + MVEBU_RTC_BRIDGE_TIMING_CTRL0_REG, + MVEBU_RTC_WRCLK_SETUP_MASK, + MVEBU_RTC_WRCLK_SETUP_DEFAULT << + MVEBU_RTC_WRCLK_SETUP_OFFS); + + mmio_clrsetbits_32(base + MVEBU_RTC_BRIDGE_TIMING_CTRL1_REG, + MVEBU_RTC_READ_OUTPUT_DELAY_MASK, + MVEBU_RTC_READ_OUTPUT_DELAY_DEFAULT); + + /* + * Issue reset to the RTC if Clock Correction register + * contents did not sustain the reboot/power-on. + */ + if ((mmio_read_32(base + MVEBU_RTC_CCR_REG) & + MVEBU_RTC_NOMINAL_TIMING_MASK) != MVEBU_RTC_NOMINAL_TIMING) { + /* Reset Test register */ + mmio_write_32(base + MVEBU_RTC_TEST_CONFIG_REG, 0); + mdelay(500); + + /* Reset Time register */ + mmio_write_32(base + MVEBU_RTC_TIME_REG, 0); + udelay(62); + + /* Reset Status register */ + mmio_write_32(base + MVEBU_RTC_STATUS_REG, + (MVEBU_RTC_STATUS_ALARM1_MASK | + MVEBU_RTC_STATUS_ALARM2_MASK)); + udelay(62); + + /* Turn off Int1 and Int2 sources & clear the Alarm count */ + mmio_write_32(base + MVEBU_RTC_IRQ_1_CONFIG_REG, 0); + mmio_write_32(base + MVEBU_RTC_IRQ_2_CONFIG_REG, 0); + mmio_write_32(base + MVEBU_RTC_ALARM_1_REG, 0); + mmio_write_32(base + MVEBU_RTC_ALARM_2_REG, 0); + + /* Setup nominal register access timing */ + mmio_write_32(base + MVEBU_RTC_CCR_REG, + MVEBU_RTC_NOMINAL_TIMING); + + /* Reset Time register */ + mmio_write_32(base + MVEBU_RTC_TIME_REG, 0); + udelay(10); + + /* Reset Status register */ + mmio_write_32(base + MVEBU_RTC_STATUS_REG, + (MVEBU_RTC_STATUS_ALARM1_MASK | + MVEBU_RTC_STATUS_ALARM2_MASK)); + udelay(50); + } +} + +static void cp110_amb_adec_init(uintptr_t base) +{ + /* enable AXI-MBUS by clearing "Bridge Windows Disable" */ + mmio_clrbits_32(base + MVEBU_BRIDGE_WIN_DIS_REG, + (1 << MVEBU_BRIDGE_WIN_DIS_OFF)); + + /* configure AXI-MBUS windows for CP */ + init_amb_adec(base); +} + +void cp110_init(uintptr_t cp110_base, uint32_t stream_id) +{ + INFO("%s: Initialize CPx - base = %lx\n", __func__, cp110_base); + + /* configure IOB windows for CP0*/ + init_iob(cp110_base); + + /* configure AXI-MBUS windows for CP0*/ + cp110_amb_adec_init(cp110_base); + + /* configure axi for CP0*/ + cp110_axi_attr_init(cp110_base); + + /* Execute SW WA for erratas */ + cp110_errata_wa_init(cp110_base); + + /* Confiure pcie clock according to clock direction */ + cp110_pcie_clk_cfg(cp110_base); + + /* configure stream id for CP0 */ + cp110_stream_id_init(cp110_base, stream_id); + + /* Open AMB bridge for comphy for CP0 & CP1*/ + amb_bridge_init(cp110_base); + + /* Reset RTC if needed */ + cp110_rtc_init(cp110_base); +} + +/* Do the minimal setup required to configure the CP in BLE */ +void cp110_ble_init(uintptr_t cp110_base) +{ +#if PCI_EP_SUPPORT + INFO("%s: Initialize CPx - base = %lx\n", __func__, cp110_base); + + amb_bridge_init(cp110_base); + + /* Configure PCIe clock */ + cp110_pcie_clk_cfg(cp110_base); + + /* Configure PCIe endpoint */ + ble_plat_pcie_ep_setup(); +#endif +} diff --git a/drivers/marvell/thermal.c b/drivers/marvell/thermal.c new file mode 100644 index 00000000..c7ceb929 --- /dev/null +++ b/drivers/marvell/thermal.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* Driver for thermal unit located in Marvell ARMADA 8K and compatible SoCs */ + +#include <debug.h> +#include <thermal.h> + +int marvell_thermal_init(struct tsen_config *tsen_cfg) +{ + if (tsen_cfg->tsen_ready == 1) { + INFO("thermal sensor is already initialized\n"); + return 0; + } + + if (tsen_cfg->ptr_tsen_probe == NULL) { + ERROR("initial thermal sensor configuration is missing\n"); + return -1; + } + + if (tsen_cfg->ptr_tsen_probe(tsen_cfg)) { + ERROR("thermal sensor initialization failed\n"); + return -1; + } + + VERBOSE("thermal sensor was initialized\n"); + + return 0; +} + +int marvell_thermal_read(struct tsen_config *tsen_cfg, int *temp) +{ + if (temp == NULL) { + ERROR("NULL pointer for temperature read\n"); + return -1; + } + + if (tsen_cfg->ptr_tsen_read == NULL || + tsen_cfg->tsen_ready == 0) { + ERROR("thermal sensor was not initialized\n"); + return -1; + } + + if (tsen_cfg->ptr_tsen_read(tsen_cfg, temp)) { + ERROR("temperature read failed\n"); + return -1; + } + + return 0; +} diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c new file mode 100644 index 00000000..8fe3239e --- /dev/null +++ b/drivers/mmc/mmc.c @@ -0,0 +1,714 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Define a simple and generic interface to access eMMC and SD-card devices. */ + +#include <arch_helpers.h> +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <mmc.h> +#include <stdbool.h> +#include <string.h> +#include <utils.h> + +#define MMC_DEFAULT_MAX_RETRIES 5 +#define SEND_OP_COND_MAX_RETRIES 100 + +#define MULT_BY_512K_SHIFT 19 + +static const struct mmc_ops *ops; +static unsigned int mmc_ocr_value; +static struct mmc_csd_emmc mmc_csd; +static unsigned char mmc_ext_csd[512] __aligned(4); +static unsigned int mmc_flags; +static struct mmc_device_info *mmc_dev_info; +static unsigned int rca; + +static const unsigned char tran_speed_base[16] = { + 0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80 +}; + +static const unsigned char sd_tran_speed_base[16] = { + 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 +}; + +static bool is_cmd23_enabled(void) +{ + return ((mmc_flags & MMC_FLAG_CMD23) != 0U); +} + +static int mmc_send_cmd(unsigned int idx, unsigned int arg, + unsigned int r_type, unsigned int *r_data) +{ + struct mmc_cmd cmd; + int ret; + + zeromem(&cmd, sizeof(struct mmc_cmd)); + + cmd.cmd_idx = idx; + cmd.cmd_arg = arg; + cmd.resp_type = r_type; + + ret = ops->send_cmd(&cmd); + + if ((ret == 0) && (r_data != NULL)) { + int i; + + for (i = 0; i < 4; i++) { + *r_data = cmd.resp_data[i]; + r_data++; + } + } + + if (ret != 0) { + VERBOSE("Send command %u error: %d\n", idx, ret); + } + + return ret; +} + +static int mmc_device_state(void) +{ + int retries = MMC_DEFAULT_MAX_RETRIES; + unsigned int resp_data[4]; + + do { + int ret; + + if (retries == 0) { + ERROR("CMD13 failed after %d retries\n", + MMC_DEFAULT_MAX_RETRIES); + return -EIO; + } + + ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET, + MMC_RESPONSE_R(1), &resp_data[0]); + if (ret != 0) { + return ret; + } + + if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) { + return -EIO; + } + + retries--; + } while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U); + + return MMC_GET_STATE(resp_data[0]); +} + +static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value) +{ + int ret; + + ret = mmc_send_cmd(MMC_CMD(6), + EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) | + EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL, + 0, NULL); + if (ret != 0) { + return ret; + } + + do { + ret = mmc_device_state(); + if (ret < 0) { + return ret; + } + } while (ret == MMC_STATE_PRG); + + return 0; +} + +static int mmc_sd_switch(unsigned int bus_width) +{ + int ret; + int retries = MMC_DEFAULT_MAX_RETRIES; + unsigned int scr[2] = { 0 }; + unsigned int bus_width_arg = 0; + + ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr)); + if (ret != 0) { + return ret; + } + + /* CMD55: Application Specific Command */ + ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET, + MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return ret; + } + + /* ACMD51: SEND_SCR */ + do { + ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R(1), NULL); + if ((ret != 0) && (retries == 0)) { + ERROR("ACMD51 failed after %d retries (ret=%d)\n", + MMC_DEFAULT_MAX_RETRIES, ret); + return ret; + } + + retries--; + } while (ret != 0); + + ret = ops->read(0, (uintptr_t)&scr, sizeof(scr)); + if (ret != 0) { + return ret; + } + + if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) && + (bus_width == MMC_BUS_WIDTH_4)) { + bus_width_arg = 2; + } + + /* CMD55: Application Specific Command */ + ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET, + MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return ret; + } + + /* ACMD6: SET_BUS_WIDTH */ + ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return ret; + } + + do { + ret = mmc_device_state(); + if (ret < 0) { + return ret; + } + } while (ret == MMC_STATE_PRG); + + return 0; +} + +static int mmc_set_ios(unsigned int clk, unsigned int bus_width) +{ + int ret; + unsigned int width = bus_width; + + if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) { + if (width == MMC_BUS_WIDTH_8) { + WARN("Wrong bus config for SD-card, force to 4\n"); + width = MMC_BUS_WIDTH_4; + } + ret = mmc_sd_switch(width); + if (ret != 0) { + return ret; + } + } else if (mmc_csd.spec_vers == 4U) { + ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH, + (unsigned int)width); + if (ret != 0) { + return ret; + } + } else { + VERBOSE("Wrong MMC type or spec version\n"); + } + + return ops->set_ios(clk, width); +} + +static int mmc_fill_device_info(void) +{ + unsigned long long c_size; + unsigned int speed_idx; + unsigned int nb_blocks; + unsigned int freq_unit; + int ret; + struct mmc_csd_sd_v2 *csd_sd_v2; + + switch (mmc_dev_info->mmc_dev_type) { + case MMC_IS_EMMC: + mmc_dev_info->block_size = MMC_BLOCK_SIZE; + + ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd, + sizeof(mmc_ext_csd)); + if (ret != 0) { + return ret; + } + + /* MMC CMD8: SEND_EXT_CSD */ + ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return ret; + } + + ret = ops->read(0, (uintptr_t)&mmc_ext_csd, + sizeof(mmc_ext_csd)); + if (ret != 0) { + return ret; + } + + nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) | + (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) | + (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) | + (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24); + + mmc_dev_info->device_size = (unsigned long long)nb_blocks * + mmc_dev_info->block_size; + + break; + + case MMC_IS_SD: + /* + * Use the same mmc_csd struct, as required fields here + * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC. + */ + mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len); + + c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) | + (unsigned long long)mmc_csd.c_size_low; + assert(c_size != 0xFFFU); + + mmc_dev_info->device_size = (c_size + 1U) * + BIT_64(mmc_csd.c_size_mult + 2U) * + mmc_dev_info->block_size; + + break; + + case MMC_IS_SD_HC: + assert(mmc_csd.csd_structure == 1U); + + mmc_dev_info->block_size = MMC_BLOCK_SIZE; + + /* Need to use mmc_csd_sd_v2 struct */ + csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd; + c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) | + (unsigned long long)csd_sd_v2->c_size_low; + + mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT; + + break; + + default: + ret = -EINVAL; + break; + } + + if (ret != 0) { + return ret; + } + + speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >> + CSD_TRAN_SPEED_MULT_SHIFT; + + assert(speed_idx > 0U); + + if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { + mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx]; + } else { + mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx]; + } + + freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK; + while (freq_unit != 0U) { + mmc_dev_info->max_bus_freq *= 10U; + --freq_unit; + } + + mmc_dev_info->max_bus_freq *= 10000U; + + return 0; +} + +static int sd_send_op_cond(void) +{ + int retries = SEND_OP_COND_MAX_RETRIES; + unsigned int resp_data[4]; + + do { + int ret; + + if (retries == 0) { + ERROR("ACMD41 failed after %d retries\n", + SEND_OP_COND_MAX_RETRIES); + return -EIO; + } + + /* CMD55: Application Specific Command */ + ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return ret; + } + + /* ACMD41: SD_SEND_OP_COND */ + ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS, MMC_RESPONSE_R(3), + &resp_data[0]); + if (ret != 0) { + return ret; + } + + retries--; + } while ((resp_data[0] & OCR_POWERUP) == 0U); + + mmc_ocr_value = resp_data[0]; + + if ((mmc_ocr_value & OCR_HCS) != 0U) { + mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC; + } else { + mmc_dev_info->mmc_dev_type = MMC_IS_SD; + } + + return 0; +} + +static int mmc_send_op_cond(void) +{ + int ret; + int retries = SEND_OP_COND_MAX_RETRIES; + unsigned int resp_data[4]; + + /* CMD0: reset to IDLE */ + ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL); + if (ret != 0) { + return ret; + } + + do { + if (retries == 0) { + ERROR("CMD1 failed after %d retries\n", + SEND_OP_COND_MAX_RETRIES); + return -EIO; + } + + /* CMD1: get OCR register (SEND_OP_COND) */ + ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE | + OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7, + MMC_RESPONSE_R(3), &resp_data[0]); + if (ret != 0) { + return ret; + } + + retries--; + } while ((resp_data[0] & OCR_POWERUP) == 0U); + + mmc_ocr_value = resp_data[0]; + + return 0; +} + +static int mmc_enumerate(unsigned int clk, unsigned int bus_width) +{ + int ret; + unsigned int resp_data[4]; + + ops->init(); + + /* CMD0: reset to IDLE */ + ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL); + if (ret != 0) { + return ret; + } + + /* CMD8: Send Interface Condition Command */ + ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN, + MMC_RESPONSE_R(7), &resp_data[0]); + + if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) { + ret = sd_send_op_cond(); + } else { + ret = mmc_send_op_cond(); + } + if (ret != 0) { + return ret; + } + + /* CMD2: Card Identification */ + ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R(2), NULL); + if (ret != 0) { + return ret; + } + + /* CMD3: Set Relative Address */ + if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) { + rca = MMC_FIX_RCA; + ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET, + MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return ret; + } + } else { + ret = mmc_send_cmd(MMC_CMD(3), 0, + MMC_RESPONSE_R(6), &resp_data[0]); + if (ret != 0) { + return ret; + } + + rca = (resp_data[0] & 0xFFFF0000U) >> 16; + } + + /* CMD9: CSD Register */ + ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET, + MMC_RESPONSE_R(2), &resp_data[0]); + if (ret != 0) { + return ret; + } + + memcpy(&mmc_csd, &resp_data, sizeof(resp_data)); + + /* CMD7: Select Card */ + ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET, + MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return ret; + } + + do { + ret = mmc_device_state(); + if (ret < 0) { + return ret; + } + } while (ret != MMC_STATE_TRAN); + + ret = mmc_fill_device_info(); + if (ret != 0) { + return ret; + } + + return mmc_set_ios(clk, bus_width); +} + +size_t mmc_read_blocks(unsigned int lba, uintptr_t buf, size_t size) +{ + int ret; + unsigned int cmd_idx, cmd_arg; + + assert((ops != NULL) && + (ops->read != NULL) && + (size != 0U) && + ((size & MMC_BLOCK_MASK) == 0U)); + + ret = ops->prepare(lba, buf, size); + if (ret != 0) { + return 0; + } + + if (is_cmd23_enabled()) { + /* Set block count */ + ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE, + MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return 0; + } + + cmd_idx = MMC_CMD(18); + } else { + if (size > MMC_BLOCK_SIZE) { + cmd_idx = MMC_CMD(18); + } else { + cmd_idx = MMC_CMD(17); + } + } + + if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) && + (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) { + cmd_arg = lba * MMC_BLOCK_SIZE; + } else { + cmd_arg = lba; + } + + ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return 0; + } + + ret = ops->read(lba, buf, size); + if (ret != 0) { + return 0; + } + + /* Wait buffer empty */ + do { + ret = mmc_device_state(); + if (ret < 0) { + return 0; + } + } while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA)); + + if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) { + ret = mmc_send_cmd(MMC_CMD(12), 0, 0, NULL); + if (ret != 0) { + return 0; + } + } + + return size; +} + +size_t mmc_write_blocks(unsigned int lba, const uintptr_t buf, size_t size) +{ + int ret; + unsigned int cmd_idx, cmd_arg; + + assert((ops != NULL) && + (ops->write != NULL) && + (size != 0U) && + ((buf & MMC_BLOCK_MASK) == 0U) && + ((size & MMC_BLOCK_MASK) == 0U)); + + ret = ops->prepare(lba, buf, size); + if (ret != 0) { + return 0; + } + + if (is_cmd23_enabled()) { + /* Set block count */ + ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE, + MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return 0; + } + + cmd_idx = MMC_CMD(25); + } else { + if (size > MMC_BLOCK_SIZE) { + cmd_idx = MMC_CMD(25); + } else { + cmd_idx = MMC_CMD(24); + } + } + + if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) { + cmd_arg = lba * MMC_BLOCK_SIZE; + } else { + cmd_arg = lba; + } + + ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return 0; + } + + ret = ops->write(lba, buf, size); + if (ret != 0) { + return 0; + } + + /* Wait buffer empty */ + do { + ret = mmc_device_state(); + if (ret < 0) { + return 0; + } + } while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV)); + + if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) { + ret = mmc_send_cmd(MMC_CMD(12), 0, 0, NULL); + if (ret != 0) { + return 0; + } + } + + return size; +} + +size_t mmc_erase_blocks(unsigned int lba, size_t size) +{ + int ret; + + assert(ops != NULL); + assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U)); + + ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return 0; + } + + ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U, + MMC_RESPONSE_R(1), NULL); + if (ret != 0) { + return 0; + } + + ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R(0x1B), NULL); + if (ret != 0) { + return 0; + } + + do { + ret = mmc_device_state(); + if (ret < 0) { + return 0; + } + } while (ret != MMC_STATE_TRAN); + + return size; +} + +static inline void mmc_rpmb_enable(void) +{ + mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG, + PART_CFG_BOOT_PARTITION1_ENABLE | + PART_CFG_PARTITION1_ACCESS); +} + +static inline void mmc_rpmb_disable(void) +{ + mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG, + PART_CFG_BOOT_PARTITION1_ENABLE); +} + +size_t mmc_rpmb_read_blocks(unsigned int lba, uintptr_t buf, size_t size) +{ + size_t size_read; + + mmc_rpmb_enable(); + size_read = mmc_read_blocks(lba, buf, size); + mmc_rpmb_disable(); + + return size_read; +} + +size_t mmc_rpmb_write_blocks(unsigned int lba, const uintptr_t buf, size_t size) +{ + size_t size_written; + + mmc_rpmb_enable(); + size_written = mmc_write_blocks(lba, buf, size); + mmc_rpmb_disable(); + + return size_written; +} + +size_t mmc_rpmb_erase_blocks(unsigned int lba, size_t size) +{ + size_t size_erased; + + mmc_rpmb_enable(); + size_erased = mmc_erase_blocks(lba, size); + mmc_rpmb_disable(); + + return size_erased; +} + +int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk, + unsigned int width, unsigned int flags, + struct mmc_device_info *device_info) +{ + assert((ops_ptr != NULL) && + (ops_ptr->init != NULL) && + (ops_ptr->send_cmd != NULL) && + (ops_ptr->set_ios != NULL) && + (ops_ptr->prepare != NULL) && + (ops_ptr->read != NULL) && + (ops_ptr->write != NULL) && + (device_info != NULL) && + (clk != 0) && + ((width == MMC_BUS_WIDTH_1) || + (width == MMC_BUS_WIDTH_4) || + (width == MMC_BUS_WIDTH_8) || + (width == MMC_BUS_WIDTH_DDR_4) || + (width == MMC_BUS_WIDTH_DDR_8))); + + ops = ops_ptr; + mmc_flags = flags; + mmc_dev_info = device_info; + + return mmc_enumerate(clk, width); +} |