/* * SBE 2T3E3 synchronous serial card driver for Linux * * Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl> * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. * * This code is based on a driver written by SBE Inc. */ #include <linux/delay.h> #include "2t3e3.h" #include "ctrl.h" #define bootrom_set_bit(sc, reg, bit) \ bootrom_write((sc), (reg), \ bootrom_read((sc), (reg)) | (bit)) #define bootrom_clear_bit(sc, reg, bit) \ bootrom_write((sc), (reg), \ bootrom_read((sc), (reg)) & ~(bit)) static inline void cpld_set_bit(struct channel *channel, unsigned reg, u32 bit) { unsigned long flags; spin_lock_irqsave(&channel->card->bootrom_lock, flags); bootrom_set_bit(channel, CPLD_MAP_REG(reg, channel), bit); spin_unlock_irqrestore(&channel->card->bootrom_lock, flags); } static inline void cpld_clear_bit(struct channel *channel, unsigned reg, u32 bit) { unsigned long flags; spin_lock_irqsave(&channel->card->bootrom_lock, flags); bootrom_clear_bit(channel, CPLD_MAP_REG(reg, channel), bit); spin_unlock_irqrestore(&channel->card->bootrom_lock, flags); } void cpld_init(struct channel *sc) { u32 val; /* PCRA */ val = SBE_2T3E3_CPLD_VAL_CRC32 | cpld_val_map[SBE_2T3E3_CPLD_VAL_LOOP_TIMING_SOURCE][sc->h.slot]; cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRA, val); /* PCRB */ val = 0; cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRB, val); /* PCRC */ val = 0; cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC, val); /* PBWF */ val = 0; cpld_write(sc, SBE_2T3E3_CPLD_REG_PBWF, val); /* PBWL */ val = 0; cpld_write(sc, SBE_2T3E3_CPLD_REG_PBWL, val); /* PLTR */ val = SBE_2T3E3_CPLD_VAL_LCV_COUNTER; cpld_write(sc, SBE_2T3E3_CPLD_REG_PLTR, val); udelay(1000); /* PLCR */ val = 0; cpld_write(sc, SBE_2T3E3_CPLD_REG_PLCR, val); udelay(1000); /* PPFR */ val = 0x55; cpld_write(sc, SBE_2T3E3_CPLD_REG_PPFR, val); /* TODO: this doesn't work!!! */ /* SERIAL_CHIP_SELECT */ val = 0; cpld_write(sc, SBE_2T3E3_CPLD_REG_SERIAL_CHIP_SELECT, val); /* PICSR */ val = SBE_2T3E3_CPLD_VAL_DMO_SIGNAL_DETECTED | SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_LOCK_DETECTED | SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_SIGNAL_DETECTED; cpld_write(sc, SBE_2T3E3_CPLD_REG_PICSR, val); cpld_start_intr(sc); udelay(1000); } void cpld_start_intr(struct channel *sc) { u32 val; /* PIER */ val = SBE_2T3E3_CPLD_VAL_INTERRUPT_FROM_ETHERNET_ENABLE | SBE_2T3E3_CPLD_VAL_INTERRUPT_FROM_FRAMER_ENABLE; cpld_write(sc, SBE_2T3E3_CPLD_REG_PIER, val); } void cpld_stop_intr(struct channel *sc) { u32 val; /* PIER */ val = 0; cpld_write(sc, SBE_2T3E3_CPLD_REG_PIER, val); } void cpld_set_frame_mode(struct channel *sc, u32 mode) { if (sc->p.frame_mode == mode) return; switch (mode) { case SBE_2T3E3_FRAME_MODE_HDLC: cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_TRANSPARENT_MODE | SBE_2T3E3_CPLD_VAL_RAW_MODE); exar7250_unipolar_onoff(sc, SBE_2T3E3_OFF); exar7300_unipolar_onoff(sc, SBE_2T3E3_OFF); break; case SBE_2T3E3_FRAME_MODE_TRANSPARENT: cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_RAW_MODE); cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_TRANSPARENT_MODE); exar7250_unipolar_onoff(sc, SBE_2T3E3_OFF); exar7300_unipolar_onoff(sc, SBE_2T3E3_OFF); break; case SBE_2T3E3_FRAME_MODE_RAW: cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_RAW_MODE); exar7250_unipolar_onoff(sc, SBE_2T3E3_ON); exar7300_unipolar_onoff(sc, SBE_2T3E3_ON); break; default: return; } sc->p.frame_mode = mode; } /* set rate of the local clock */ void cpld_set_frame_type(struct channel *sc, u32 type) { switch (type) { case SBE_2T3E3_FRAME_TYPE_E3_G751: case SBE_2T3E3_FRAME_TYPE_E3_G832: cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_LOCAL_CLOCK_E3); break; case SBE_2T3E3_FRAME_TYPE_T3_CBIT: case SBE_2T3E3_FRAME_TYPE_T3_M13: cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_LOCAL_CLOCK_E3); break; default: return; } } void cpld_set_scrambler(struct channel *sc, u32 mode) { if (sc->p.scrambler == mode) return; switch (mode) { case SBE_2T3E3_SCRAMBLER_OFF: cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRB, SBE_2T3E3_CPLD_VAL_SCRAMBLER_ENABLE); break; case SBE_2T3E3_SCRAMBLER_LARSCOM: cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRB, SBE_2T3E3_CPLD_VAL_SCRAMBLER_TYPE); cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRB, SBE_2T3E3_CPLD_VAL_SCRAMBLER_ENABLE); break; case SBE_2T3E3_SCRAMBLER_ADC_KENTROX_DIGITAL: cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRB, SBE_2T3E3_CPLD_VAL_SCRAMBLER_TYPE); cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRB, SBE_2T3E3_CPLD_VAL_SCRAMBLER_ENABLE); break; default: return; } sc->p.scrambler = mode; } void cpld_set_crc(struct channel *sc, u32 crc) { if (sc->p.crc == crc) return; switch (crc) { case SBE_2T3E3_CRC_16: cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_CRC32); break; case SBE_2T3E3_CRC_32: cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_CRC32); break; default: return; } sc->p.crc = crc; } void cpld_select_panel(struct channel *sc, u32 panel) { if (sc->p.panel == panel) return; switch (panel) { case SBE_2T3E3_PANEL_FRONT: cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_REAR_PANEL); break; case SBE_2T3E3_PANEL_REAR: cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_REAR_PANEL); break; default: return; } udelay(100); sc->p.panel = panel; } extern void cpld_set_clock(struct channel *sc, u32 mode) { if (sc->p.clock_source == mode) return; switch (mode) { case SBE_2T3E3_TIMING_LOCAL: cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_ALT); break; case SBE_2T3E3_TIMING_LOOP: cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA, SBE_2T3E3_CPLD_VAL_ALT); break; default: return; } sc->p.clock_source = mode; } void cpld_set_pad_count(struct channel *sc, u32 count) { u32 val; if (sc->p.pad_count == count) return; switch (count) { case SBE_2T3E3_PAD_COUNT_1: val = SBE_2T3E3_CPLD_VAL_PAD_COUNT_1; break; case SBE_2T3E3_PAD_COUNT_2: val = SBE_2T3E3_CPLD_VAL_PAD_COUNT_2; break; case SBE_2T3E3_PAD_COUNT_3: val = SBE_2T3E3_CPLD_VAL_PAD_COUNT_3; break; case SBE_2T3E3_PAD_COUNT_4: val = SBE_2T3E3_CPLD_VAL_PAD_COUNT_4; break; default: return; } cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRB, SBE_2T3E3_CPLD_VAL_PAD_COUNT); cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRB, val); sc->p.pad_count = count; } void cpld_LOS_update(struct channel *sc) { u_int8_t los; cpld_write(sc, SBE_2T3E3_CPLD_REG_PICSR, SBE_2T3E3_CPLD_VAL_DMO_SIGNAL_DETECTED | SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_LOCK_DETECTED | SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_SIGNAL_DETECTED); los = cpld_read(sc, SBE_2T3E3_CPLD_REG_PICSR) & SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_SIGNAL_DETECTED; if (los != sc->s.LOS) dev_info(&sc->pdev->dev, "SBE 2T3E3: LOS status: %s\n", los ? "Loss of signal" : "Signal OK"); sc->s.LOS = los; } void cpld_set_fractional_mode(struct channel *sc, u32 mode, u32 start, u32 stop) { if (mode == SBE_2T3E3_FRACTIONAL_MODE_NONE) { start = 0; stop = 0; } if (sc->p.fractional_mode == mode && sc->p.bandwidth_start == start && sc->p.bandwidth_stop == stop) return; switch (mode) { case SBE_2T3E3_FRACTIONAL_MODE_NONE: cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC, SBE_2T3E3_CPLD_VAL_FRACTIONAL_MODE_NONE); break; case SBE_2T3E3_FRACTIONAL_MODE_0: cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC, SBE_2T3E3_CPLD_VAL_FRACTIONAL_MODE_0); break; case SBE_2T3E3_FRACTIONAL_MODE_1: cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC, SBE_2T3E3_CPLD_VAL_FRACTIONAL_MODE_1); break; case SBE_2T3E3_FRACTIONAL_MODE_2: cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC, SBE_2T3E3_CPLD_VAL_FRACTIONAL_MODE_2); break; default: netdev_err(sc->dev, "wrong mode in set_fractional_mode\n"); return; } cpld_write(sc, SBE_2T3E3_CPLD_REG_PBWF, start); cpld_write(sc, SBE_2T3E3_CPLD_REG_PBWL, stop); sc->p.fractional_mode = mode; sc->p.bandwidth_start = start; sc->p.bandwidth_stop = stop; }