/****************************************************************************** Copyright (c) 2011 Lantiq Deutschland GmbH For licensing information, see the file 'LICENSE' in the root folder of this software module. ******************************************************************************/ #include "drv_onu_api.h" #include "drv_onu_register.h" #include "drv_onu_ll_sys.h" #include "drv_onu_ll_octrlg.h" STATIC int octrlg_basic_init(void); STATIC void octrlg_tcont_map_init(void); STATIC void octrlg_tcont_table_init(void); STATIC void octrlg_gpix_table_init(void); int octrlg_init(void) { uint32_t i; /* Enable module clock, release reset. This is mandatory before accessing module's registers. */ sys_gpe_hw_activate_or_reboot(SYS_GPE_ACT_GPONE_SET); if (octrlg_basic_init() != 0) return -1; /* OCTRLG.TCMAP[0..1023] = 0x0000 0000 OCTRLG.TCTABLE[0..15] = 0xFFFF FFFF OCTRLG.GPIXTABLE[0..127] = 0x0000 0000 */ octrlg_tcont_map_init(); octrlg_tcont_table_init(); octrlg_gpix_table_init(); /* Initialization of counter memories */ for (i = 0; i < GPIXTABLE_LEN; i++) { octrlg_w32(0, txpcnt[i]); octrlg_w32(0, txbcntl[i]); } return 0; } /* nGemBlockLength: configure in OCTRLG, OCTRLG.CFG0.IBS = 2^15/gem_block_len gem_payload_sz_max: configure in OCTRLG, OCTRLG.CFG1.GEMPLSIZE */ int octrlg_config_set(const uint32_t gem_block_len, const uint32_t gem_payload_sz_max) { if (gem_block_len == 0) return -1; octrlg_w32_mask(OCTRLG_CFG0_IBS_MASK, (((1 << 15) / gem_block_len) & OCTRLG_CFG0_IBS_MASK), cfg0); octrlg_w32_mask(OCTRLG_CFG1_GEMPLSIZE_MASK, ((gem_payload_sz_max << OCTRLG_CFG1_GEMPLSIZE_OFFSET) & OCTRLG_CFG1_GEMPLSIZE_MASK), cfg1); return 0; } int octrlg_config_get(uint32_t *gem_block_len, uint32_t *gem_payload_sz_max) { if (octrlg_r32(cfg0) & OCTRLG_CFG0_IBS_MASK) *gem_block_len = (1 << 15) / (octrlg_r32(cfg0) & OCTRLG_CFG0_IBS_MASK); *gem_payload_sz_max = (octrlg_r32(cfg1) & OCTRLG_CFG1_GEMPLSIZE_MASK) >> OCTRLG_CFG1_GEMPLSIZE_OFFSET; return 0; } void octrlg_dbru_mode_dbg_set(const uint32_t act) { octrlg_w32_mask(OCTRLG_CFG1_DBRUDBG_EN, act ? OCTRLG_CFG1_DBRUDBG_EN : 0, cfg1); } void octrlg_dbru_mode_dbg_get(uint32_t *act) { *act = (octrlg_r32(cfg1) & OCTRLG_CFG1_DBRUDBG_EN) ? true : false; } void octrlg_dbru_debug_set(const uint32_t mode2y, const uint32_t mode2g, const uint32_t mode1) { octrlg_w32_mask(OCTRLG_DBRUDBG_DBRU2Y_MASK | OCTRLG_DBRUDBG_DBRU2G_MASK | OCTRLG_DBRUDBG_DBRU1_MASK, (mode2y << OCTRLG_DBRUDBG_DBRU2Y_OFFSET) | (mode2g << OCTRLG_DBRUDBG_DBRU2G_OFFSET) | (mode1 << OCTRLG_DBRUDBG_DBRU1_OFFSET), dbrudbg); } #ifdef INCLUDE_UNUSED_LL_FUNCTIONS void octrlg_dbru_debug_get(uint32_t *mode2y, uint32_t *mode2g, uint32_t *mode1) { uint32_t reg; reg = octrlg_r32(dbrudbg); *mode2y = (reg & OCTRLG_DBRUDBG_DBRU2Y_MASK) >> OCTRLG_DBRUDBG_DBRU2Y_OFFSET; *mode2g = (reg & OCTRLG_DBRUDBG_DBRU2G_MASK) >> OCTRLG_DBRUDBG_DBRU2G_OFFSET; *mode1 = (reg & OCTRLG_DBRUDBG_DBRU1_MASK) >> OCTRLG_DBRUDBG_DBRU1_OFFSET; } #endif /* #ifdef INCLUDE_UNUSED_LL_FUNCTIONS*/ void octrlg_dbru_mode_get(uint32_t *mode) { *mode = (octrlg_r32(tcreq) & OCTRLG_TCREQ_DBRU_MASK) >> OCTRLG_TCREQ_DBRU_OFFSET; } /** OCTRLG.TCTABLE[n].REPN[n] = egress_port_idx, with n = tcont_idx */ int octrlg_epn_set(const uint32_t tcont_idx, const uint32_t egress_port_idx, const uint32_t prempted_epn_idx) { if (tcont_idx >= ONU_GPE_MAX_TCONT) return -1; octrlg_w32_mask(OCTRLG_TCTABLE0_REPN0_MASK, ((egress_port_idx << OCTRLG_TCTABLE0_REPN0_OFFSET) & OCTRLG_TCTABLE0_REPN0_MASK), tctable[tcont_idx]); octrlg_w32_mask(OCTRLG_TCTABLE0_PEPN0_MASK, ((prempted_epn_idx << OCTRLG_TCTABLE0_PEPN0_OFFSET) & OCTRLG_TCTABLE0_PEPN0_MASK), tctable[tcont_idx]); return 0; } int octrlg_epn_get(const uint32_t tcont_idx, uint32_t *egress_port_idx, uint32_t *prempted_epn_idx) { if (tcont_idx >= ONU_GPE_MAX_TCONT) { *egress_port_idx = 0; *prempted_epn_idx = 0; return -1; } *egress_port_idx = (octrlg_r32(tctable[tcont_idx]) & OCTRLG_TCTABLE0_REPN0_MASK) >> OCTRLG_TCTABLE0_REPN0_OFFSET; *prempted_epn_idx = (octrlg_r32(tctable[tcont_idx]) & OCTRLG_TCTABLE0_PEPN0_MASK) >> OCTRLG_TCTABLE0_PEPN0_OFFSET; return 0; } /** Hardware Programming Details The following hardware functions must be configured: - tcont_idx: selects the table entry - alloc_id : Allocation ID of the T-CONT - valid : true indicates that the Alloc Id has been assigned and is valid. The T-CONT Table is addressed by the 12-bit alloc_id value, the tcont_idx is written as the data value to the selected table location, together with the valid indication being set (0b1). OCTRLG(OCTRLG), TCMAP[n].TCIX[n]: write a free T-CONT index value (tcont_idx) into the location that is defined by n = alloc_id. OCTRLG(OCTRLG), TCMAP[n].V[n] : enable T-CONT index value at the location that is defined by n = alloc_id, by setting the related valid bit. */ int octrlg_tcont_set(const uint32_t tcont_idx, const uint32_t alloc_id) { octrlg_w32((tcont_idx & OCTRLG_TCMAP0_TCIX0_MASK) | OCTRLG_TCMAP0_V0, tcmap[alloc_id]); return 0; } /** Hardware Programming Details The following hardware functions must be handled: The T-CONT mapping table is read at the location given by the Allocation ID. If the valid flag is set, the index value is returned with valid = true. If the valid is not set, the index value is returned with valid = false. The hardware table is located in OCTRLG (TCMAP), TCMAP[n].TCIX[n], n = Allocation ID TCMAP[n].V[n], n = Allocation ID */ int octrlg_tcont_get(const uint32_t tcont_idx, uint32_t *alloc_id) { uint32_t i, val; for (i = 0; i < TCMAP_LEN; i++) { val = octrlg_r32(tcmap[i]); if ((val & OCTRLG_TCMAP0_V0) == 0) continue; if (tcont_idx == (val & OCTRLG_TCMAP0_TCIX0_MASK)) { *alloc_id = i; return 0; } } *alloc_id = 0; return -1; } /** Hardware Programming Details The following hardware functions must be handled: The T-CONT mapping table is searched for an entry that matches the tcont_idx with the valid bit being set. If the given tcont_idx value is out of range (>= ONU_GPE_MAX_TCONT), an error code is returned (GPE_STATUS_VALUE_RANGE_ERR). If found, the table entry is set to "invalid". If no match is found within the table (the table address range (Allocation ID value range) is from ONU_GPE_MIN_ALLOCATION_ID to ONU_GPE_MAX_ALLOCATION_ID), the return status is set to GPE_STATUS_NOT_AVAILABLE. The hardware table to be searched and modified is located in OCTRLG, TCMAP[n].V[n] = 0b0 (n = Allocation ID). */ int octrlg_tcont_delete(const uint32_t tcont_idx) { uint32_t i, val; for (i = 0; i < TCMAP_LEN; i++) { val = octrlg_r32(tcmap[i]); if ((val & OCTRLG_TCMAP0_V0) == 0) continue; if (tcont_idx == (val & OCTRLG_TCMAP0_TCIX0_MASK)) { octrlg_w32(0, tcmap[i]); return 0; } } return -1; } /** Direct deletion of an entry in TCMAP table by TCONT ID - alloc_id: TCONT ID is an index of an entry to be deleted */ int octrlg_tcont_alloc_id_delete(const uint32_t alloc_id) { if (alloc_id >= TCMAP_LEN) return -1; octrlg_w32(0, tcmap[alloc_id]); return 0; } int octrlg_tcont_alloc_id_get(const uint32_t alloc_id, uint32_t *tcont_idx) { uint32_t val; val = octrlg_r32(tcmap[alloc_id]); if ((val & OCTRLG_TCMAP0_V0) == 0) return -1; *tcont_idx = val & OCTRLG_TCMAP0_TCIX0_MASK; return 0; } /** Read hardware counter. - gpix: selects the GEM port index (0..ONU_GPE_MAX_GPIX - 1) - tx_frames: OCTRLG.TXPCNT(gem_port_index) - tx_bytes: OCTRLG.TXBCNTH(gem_port_index)*2^32 + OCTRLG.TXBCNTL(gem_port_index) Hardware Programming Details These are the counters that are provided by the OCTRLG blocks of the OCTRLG module. The counters wrap around an need to be checked regularly. */ int octrlg_gem_counter_get(const uint32_t gpix, struct gpe_cnt_octrlg_gem_val *counter) { uint32_t l, h; if (gpix >= ONU_GPE_MAX_GPIX) return -1; counter->tx_frames = (uint64_t)octrlg_r32(txpcnt[gpix]); /* First the Low and then the High Part has to be read. No overflow checks are needed since the HW stores the high part on the low part read access*/ l = octrlg_r32(txbcntl[gpix]); h = octrlg_r32(txbcnth[gpix]); counter->tx_bytes = ((uint64_t)h << 32) | l; return 0; } /** Read hardware counters from OCTRLG: - tx_gem_idle_frames_total: OCTRLG.TXTICNT - tx_gem_frames_total: OCTRLG.TXTPCNT - tx_gem_bytes_total: OCTRLG.TXCNT - tx_tcont_total: OCTRLG.TXTTCNT Hardware Programming Details These are the counters that are provided by the OCTRLG block of the OCTRLG module. The counters wrap around an need to be checked regularly. */ int octrlg_counter_get(struct gpe_cnt_octrlg_val *counter) { uint32_t l, h; if (octrlg_is_enabled()) { counter->tx_gem_idle_frames_total = octrlg_r32(txticnt); counter->tx_gem_frames_total = octrlg_r32(txtpcnt); counter->tx_gem_bytes_total = octrlg_r32(txtcnt); counter->tx_tcont_total = octrlg_r32(txttcnt); /* First the Low and then the High Part has to be read. No overflow checks are needed since the HW stores the high part on the low part read access*/ l = octrlg_r32(txtbcntl); h = octrlg_r32(txtbcnth); counter->tx_gem_pdu_bytes_total = ((uint64_t)h << 32) | l; } else { memset(counter, 0x00, sizeof(struct gpe_cnt_octrlg_val)); } return 0; } int octrlg_gem_port_set(const uint32_t gem_port_id, const uint32_t gem_port_index, const enum gpe_direction data_direction) { if ((uint32_t)data_direction & (uint32_t)GPE_DIRECTION_UPSTREAM) octrlg_w32(gem_port_id, gpixtable[gem_port_index]); return 0; } int octrlg_gpix_get(const uint32_t gem_port_id, uint32_t *gem_port_index) { uint32_t i; for (i = 0; i < GPIXTABLE_LEN; i++) { if (octrlg_r32(gpixtable[i]) == gem_port_id) { *gem_port_index = i; return 0; } } return -1; } int octrlg_gem_port_delete(const uint32_t gem_port_index) { octrlg_w32(0x0, gpixtable[gem_port_index]); return 0; } /* OCTRLG.CFG0.IDLELEN = 4 OCTRLG.CFG1.GEMPLSIZE = 0x0FFF OCTRLG.IDLEFRAME0.IFB0 = 0xB6 OCTRLG.IDLEFRAME0.IFB1 = 0xAB OCTRLG.IDLEFRAME0.IFB2 = 0x31 OCTRLG.IDLEFRAME0.IFB3 = 0xE0 OCTRLG.IDLEFRAME1.IFB4 = 0x55 */ STATIC int octrlg_basic_init(void) { uint32_t cfg; octrlg_w32(0, ctrl); cfg = 0x0; /* set FIFO watermarks * Set value of 4 - this results in the ability to serve single T-Cont Requests down to 16 bytes. */ set_val(cfg, 4, OCTRLG_CFG0_GTCFIFOTHRES_MASK, OCTRLG_CFG0_GTCFIFOTHRES_OFFSET); set_val(cfg, 4, OCTRLG_CFG0_IDLELEN_MASK, OCTRLG_CFG0_IDLELEN_OFFSET); octrlg_w32(cfg, cfg0); if (octrlg_config_set(48, 4095) != 0) return -1; octrlg_w32(OCTRLG_DCTRL_FQ_Q1, dctrl); /* Registers IDLEFRAME0...15 are left in reset state, which is the standardized idle frame header followed by all zeroes. */ /* Make OCTRLG counter count per packet, not per GEM frame */ octrlg_w32(OCTRLG_CFG1_GEMPLSIZE_MASK, cfg1); /* reset register-based counters */ octrlg_w32(0, txtpcnt); octrlg_w32(0, txtbcnth); octrlg_w32(0, txtbcntl); octrlg_w32(0, txticnt); octrlg_w32(0, txtcnt); octrlg_w32(0, txttcnt); return 0; } /* tcmap[alloc_id] = tcont_index | valid bit */ STATIC void octrlg_tcont_map_init(void) { /* All T-Cont IDs invalid */ uint32_t i; for (i = 0; i < TCMAP_LEN; i++) octrlg_w32(0x0, tcmap[i]); } /* tctable[tcont_index] = epn */ STATIC void octrlg_tcont_table_init(void) { uint32_t i; for (i = 0; i < ONU_GPE_MAX_TCONT; i++) octrlg_w32(0xFFFFFFFF, tctable[i]); octrlg_epn_set(OMCI_TCIX, 127, ONU_GPE_OMCI_EGRESS_PORT); } /* gpixtable[gpix] = gpid */ STATIC void octrlg_gpix_table_init(void) { /* all GPIXs translate into GPID=0 */ uint32_t i; for (i = 0; i < GPIXTABLE_LEN; i++) octrlg_w32(0x0, gpixtable[i]); } void octrlg_enable(const uint32_t act) { /* enable/disable the module */ octrlg_w32_mask(OCTRLG_CTRL_ACT_EN, act ? OCTRLG_CTRL_ACT_EN : 0, ctrl); } uint32_t octrlg_is_enabled(void) { return (octrlg_r32(ctrl) & OCTRLG_CTRL_ACT_EN) ? true : false; } #ifdef INCLUDE_UNUSED_LL_FUNCTIONS void octrlg_gtc_fifo_threshold_set(const uint32_t value) { octrlg_w32_mask(value, OCTRLG_CFG0_GTCFIFOTHRES_MASK, cfg0); } #endif /* #ifdef INCLUDE_UNUSED_LL_FUNCTIONS*/ #ifdef INCLUDE_UNUSED_LL_FUNCTIONS void octrlg_gtc_fifo_threshold_get(uint32_t *value) { *value = (octrlg_r32(cfg0) & OCTRLG_CFG0_GTCFIFOTHRES_MASK) >> OCTRLG_CFG0_GTCFIFOTHRES_OFFSET; } #endif /* #ifdef INCLUDE_UNUSED_LL_FUNCTIONS*/ #ifdef INCLUDE_UNUSED_LL_FUNCTIONS int octrlg_idle_len_set(const uint32_t value) { uint32_t reg = 0, val; if (value == 0) return -1; val = (uint32_t)(value - 1); set_val(reg, val, OCTRLG_CFG0_IDLELEN_MASK, OCTRLG_CFG0_IDLELEN_OFFSET); octrlg_w32_mask(value, OCTRLG_CFG0_IDLELEN_MASK, cfg0); return 0; } #endif /* #ifdef INCLUDE_UNUSED_LL_FUNCTIONS*/ uint32_t octrlg_idle_len_get(void) { uint32_t val; val = (octrlg_r32(cfg0) & OCTRLG_CFG0_IDLELEN_MASK) >> OCTRLG_CFG0_IDLELEN_OFFSET; return (uint32_t)(val + 1); } #ifdef ONU_LIBRARY /** Reads total transmitted bytes counter and recalculate laser life time. \remarks This function is used by optic library and is called at least each 27 seconds */ void octrlg_laser_ageupdate ( uint32_t *seconds ) { uint32_t reg, diff; static uint32_t reg_old = 0; static uint32_t last = 0; reg = octrlg_r32 (txtcnt); if (reg > reg_old) diff = reg - reg_old; else diff = 0xFFFFFFFF - reg_old + reg + 1; /* not clear on read */ reg_old = reg; /* counter = 19440 in 125 us */ *seconds = diff / (0x9450C00); last += (diff % (0x9450C00)); if (last > 0x9450C00) { (*seconds) ++; last -= 0x9450C00; } } #endif #ifdef INCLUDE_UNUSED_LL_FUNCTIONS void octrlg_interrupt_mask_set(const uint32_t value) { octrlg_w32(value, irnen); } #endif /* #ifdef INCLUDE_UNUSED_LL_FUNCTIONS*/ #ifdef INCLUDE_UNUSED_LL_FUNCTIONS void octrlg_interrupt_set(const uint32_t value) { octrlg_w32(value, irnicr); } #endif /* #ifdef INCLUDE_UNUSED_LL_FUNCTIONS*/ #ifdef INCLUDE_UNUSED_LL_FUNCTIONS void octrlg_interrupt_get(uint32_t *value) { *value = octrlg_r32(irncr); } #endif /* #ifdef INCLUDE_UNUSED_LL_FUNCTIONS*/ #if defined(INCLUDE_DUMP) void octrlg_dump(struct seq_file *s) { if (sys_gpe_hw_is_activated(SYS_GPE_ACT_GPONE_SET) == 0) { seq_printf(s, "octrlg not activated\n"); return; } #define dump_reg(reg) \ seq_printf(s, "%-14s = 0x%08x\n", # reg, octrlg_r32(reg)) dump_reg(ctrl); dump_reg(cfg0); dump_reg(cfg1); dump_reg(dctrl); dump_reg(txtpcnt); dump_reg(txttcnt); dump_reg(txticnt); dump_reg(txtcnt); dump_reg(txtbcntl); dump_reg(txtbcnth); dump_reg(txbcntl); dump_reg(tcreq); dump_reg(tcstate); dump_reg(irnicr); #undef dump_reg } void octrlg_table_dump(struct seq_file *s) { uint32_t i, k; if (sys_gpe_hw_is_activated(SYS_GPE_ACT_GPONE_SET) == 0) { seq_printf(s, "octrlg not activated\n"); return; } seq_printf(s, "tcmap table\n"); for (i = 0; i < TCMAP_LEN;) { seq_printf(s, "%08x: ", octrlg_adr_table(tcmap, i)); for (k = 0; k < 16 && i < TCMAP_LEN; k++, i++) seq_printf(s, "%08x ", octrlg_r32(tcmap[i])); seq_printf(s, "\n"); } seq_printf(s, "tcont table\n"); for (i = 0; i < ONU_GPE_MAX_TCONT;) { seq_printf(s, "%08x: ", octrlg_adr_table(tctable, i)); for (k = 0; k < 16 && i < ONU_GPE_MAX_TCONT; k++, i++) seq_printf(s, "%08x ", octrlg_r32(tctable[i])); seq_printf(s, "\n"); } seq_printf(s, "gpix table\n"); for (i = 0; i < GPIXTABLE_LEN;) { seq_printf(s, "%08x: ", octrlg_adr_table(gpixtable, i)); for (k = 0; k < 16 && i < GPIXTABLE_LEN; k++, i++) seq_printf(s, "%08x ", octrlg_r32(gpixtable[i])); seq_printf(s, "\n"); } seq_printf(s, "txpcnt table\n"); for (i = 0; i < GPIXTABLE_LEN;) { seq_printf(s, "%08x: ", octrlg_adr_table(txpcnt, i)); for (k = 0; k < 16 && i < GPIXTABLE_LEN; k++, i++) seq_printf(s, "%08x ", octrlg_r32(txpcnt[i])); seq_printf(s, "\n"); } seq_printf(s, "dptr table\n"); for (i = 0; i < DPTRTABLE_LEN;) { seq_printf(s, "%08x: ", octrlg_adr_table(dptr, i)); for (k = 0; k < 16 && i < GPIXTABLE_LEN; k++, i++) seq_printf(s, "%08x ", octrlg_r32(dptr[i])); seq_printf(s, "\n"); } seq_printf(s, "dcontext table\n"); for (i = 0; i < DPTRTABLE_LEN;) { seq_printf(s, "%08x: ", octrlg_adr_table(dcontext, i)); for (k = 0; k < 16 && i < GPIXTABLE_LEN; k++, i++) seq_printf(s, "%08x ", octrlg_r32(dcontext[i])); seq_printf(s, "\n"); } } #endif