|
|
@@ -0,0 +1,1307 @@
|
|
|
+--- /dev/null
|
|
|
++++ b/drivers/misc/ucc_tdm.h
|
|
|
+@@ -0,0 +1,221 @@
|
|
|
++/*
|
|
|
++ * drivers/misc/ucc_tdm.h
|
|
|
++ *
|
|
|
++ * UCC Based Linux TDM Driver
|
|
|
++ * This driver is designed to support UCC based TDM for PowerPC processors.
|
|
|
++ * This driver can interface with SLIC device to run VOIP kind of
|
|
|
++ * applications.
|
|
|
++ *
|
|
|
++ * Author: Ashish Kalra & Poonam Aggrwal
|
|
|
++ *
|
|
|
++ * Copyright (c) 2007 Freescale Semiconductor, Inc.
|
|
|
++ *
|
|
|
++ * This program is free software; you can redistribute it and/or modify it
|
|
|
++ * under the terms of the GNU General Public License as published by the
|
|
|
++ * Free Software Foundation; either version 2 of the License, or (at your
|
|
|
++ * option) any later version.
|
|
|
++ */
|
|
|
++
|
|
|
++#ifndef TDM_H
|
|
|
++#define TDM_H
|
|
|
++
|
|
|
++#define NUM_TS 8
|
|
|
++#define ACTIVE_CH 8
|
|
|
++
|
|
|
++/* SAMPLE_DEPTH is the sample depth is the number of frames before
|
|
|
++ * an interrupt. Must be a multiple of 4
|
|
|
++ */
|
|
|
++#define SAMPLE_DEPTH 80
|
|
|
++
|
|
|
++/* define the number of Rx interrupts to go by for initial stuttering */
|
|
|
++#define STUTTER_INT_CNT 1
|
|
|
++
|
|
|
++/* BMRx Field Descriptions to specify tstate and rstate in UCC parameter RAM*/
|
|
|
++#define EN_BUS_SNOOPING 0x20
|
|
|
++#define BE_BO 0x10
|
|
|
++
|
|
|
++/* UPSMR Register for Transparent UCC controller Bit definitions*/
|
|
|
++#define NBO 0x00000000 /* Normal Mode 1 bit of data per clock */
|
|
|
++
|
|
|
++/* SI Mode register bit definitions */
|
|
|
++#define NORMAL_OPERATION 0x0000
|
|
|
++#define AUTO_ECHO 0x0400
|
|
|
++#define INTERNAL_LB 0x0800
|
|
|
++#define CONTROL_LB 0x0c00
|
|
|
++#define SIMODE_CRT (0x8000 >> 9)
|
|
|
++#define SIMODE_SL (0x8000 >> 10)
|
|
|
++#define SIMODE_CE (0x8000 >> 11)
|
|
|
++#define SIMODE_FE (0x8000 >> 12)
|
|
|
++#define SIMODE_GM (0x8000 >> 13)
|
|
|
++#define SIMODE_TFSD(val) (val)
|
|
|
++#define SIMODE_RFSD(val) ((val) << 8)
|
|
|
++
|
|
|
++#define SI_TDM_MODE_REGISTER_OFFSET 0
|
|
|
++
|
|
|
++#define R_CM 0x02000000
|
|
|
++#define T_CM 0x02000000
|
|
|
++
|
|
|
++#define SET_RX_SI_RAM(n, val) \
|
|
|
++ out_be16((u16 *)&qe_immr->sir.rx[(n)*2], (u16)(val))
|
|
|
++
|
|
|
++#define SET_TX_SI_RAM(n, val) \
|
|
|
++ out_be16((u16 *)&qe_immr->sir.tx[(n)*2], (u16)(val))
|
|
|
++
|
|
|
++/* SI RAM entries */
|
|
|
++#define SIR_LAST 0x0001
|
|
|
++#define SIR_CNT(n) ((n) << 2)
|
|
|
++#define SIR_BYTE 0x0002
|
|
|
++#define SIR_BIT 0x0000
|
|
|
++#define SIR_IDLE 0
|
|
|
++#define SIR_UCC(uccx) (((uccx+9)) << 5)
|
|
|
++
|
|
|
++/* BRGC Register Bit definitions */
|
|
|
++#define BRGC_RESET (0x1<<17)
|
|
|
++#define BRGC_EN (0x1<<16)
|
|
|
++#define BRGC_EXTC_QE (0x00<<14)
|
|
|
++#define BRGC_EXTC_CLK3 (0x01<<14)
|
|
|
++#define BRGC_EXTC_CLK5 (0x01<<15)
|
|
|
++#define BRGC_EXTC_CLK9 (0x01<<14)
|
|
|
++#define BRGC_EXTC_CLK11 (0x01<<14)
|
|
|
++#define BRGC_EXTC_CLK13 (0x01<<14)
|
|
|
++#define BRGC_EXTC_CLK15 (0x01<<15)
|
|
|
++#define BRGC_ATB (0x1<<13)
|
|
|
++#define BRGC_DIV16 (0x1)
|
|
|
++
|
|
|
++/* structure representing UCC transparent parameter RAM */
|
|
|
++struct ucc_transparent_pram {
|
|
|
++ __be16 riptr;
|
|
|
++ __be16 tiptr;
|
|
|
++ __be16 res0;
|
|
|
++ __be16 mrblr;
|
|
|
++ __be32 rstate;
|
|
|
++ __be32 rbase;
|
|
|
++ __be16 rbdstat;
|
|
|
++ __be16 rbdlen;
|
|
|
++ __be32 rdptr;
|
|
|
++ __be32 tstate;
|
|
|
++ __be32 tbase;
|
|
|
++ __be16 tbdstat;
|
|
|
++ __be16 tbdlen;
|
|
|
++ __be32 tdptr;
|
|
|
++ __be32 rbptr;
|
|
|
++ __be32 tbptr;
|
|
|
++ __be32 rcrc;
|
|
|
++ __be32 res1;
|
|
|
++ __be32 tcrc;
|
|
|
++ __be32 res2;
|
|
|
++ __be32 res3;
|
|
|
++ __be32 c_mask;
|
|
|
++ __be32 c_pres;
|
|
|
++ __be16 disfc;
|
|
|
++ __be16 crcec;
|
|
|
++ __be32 res4[4];
|
|
|
++ __be16 ts_tmp;
|
|
|
++ __be16 tmp_mb;
|
|
|
++};
|
|
|
++
|
|
|
++#define UCC_TRANSPARENT_PRAM_SIZE 0x100
|
|
|
++
|
|
|
++struct tdm_cfg {
|
|
|
++ u8 com_pin; /* Common receive and transmit pins
|
|
|
++ * 0 = separate pins
|
|
|
++ * 1 = common pins
|
|
|
++ */
|
|
|
++
|
|
|
++ u8 fr_sync_level; /* SLx bit Frame Sync Polarity
|
|
|
++ * 0 = L1R/TSYNC active logic "1"
|
|
|
++ * 1 = L1R/TSYNC active logic "0"
|
|
|
++ */
|
|
|
++
|
|
|
++ u8 clk_edge; /* CEx bit Tx Rx Clock Edge
|
|
|
++ * 0 = TX data on rising edge of clock
|
|
|
++ * RX data on falling edge
|
|
|
++ * 1 = TX data on falling edge of clock
|
|
|
++ * RX data on rising edge
|
|
|
++ */
|
|
|
++
|
|
|
++ u8 fr_sync_edge; /* FEx bit Frame sync edge
|
|
|
++ * Determine when the sync pulses are sampled
|
|
|
++ * 0 = Falling edge
|
|
|
++ * 1 = Rising edge
|
|
|
++ */
|
|
|
++
|
|
|
++ u8 rx_fr_sync_delay; /* TFSDx/RFSDx bits Frame Sync Delay
|
|
|
++ * 00 = no bit delay
|
|
|
++ * 01 = 1 bit delay
|
|
|
++ * 10 = 2 bit delay
|
|
|
++ * 11 = 3 bit delay
|
|
|
++ */
|
|
|
++
|
|
|
++ u8 tx_fr_sync_delay; /* TFSDx/RFSDx bits Frame Sync Delay
|
|
|
++ * 00 = no bit delay
|
|
|
++ * 01 = 1 bit delay
|
|
|
++ * 10 = 2 bit delay
|
|
|
++ * 11 = 3 bit delay
|
|
|
++ */
|
|
|
++
|
|
|
++ u8 active_num_ts; /* Number of active time slots in TDM
|
|
|
++ * assume same active Rx/Tx time slots
|
|
|
++ */
|
|
|
++};
|
|
|
++
|
|
|
++struct ucc_tdm_info {
|
|
|
++ struct ucc_fast_info uf_info;
|
|
|
++ u32 ucc_busy;
|
|
|
++};
|
|
|
++
|
|
|
++struct tdm_ctrl {
|
|
|
++ u32 device_busy;
|
|
|
++ struct device *device;
|
|
|
++ struct ucc_fast_private *uf_private;
|
|
|
++ struct ucc_tdm_info *ut_info;
|
|
|
++ u32 tdm_port; /* port for this tdm:TDMA,TDMB,TDMC,TDMD */
|
|
|
++ u32 si; /* serial interface: 0 or 1 */
|
|
|
++ struct ucc_fast __iomem *uf_regs; /* UCC Fast registers */
|
|
|
++ u16 rx_mask[8]; /* Active Receive channels LSB is ch0 */
|
|
|
++ u16 tx_mask[8]; /* Active Transmit channels LSB is ch0 */
|
|
|
++ /* Only channels less than the number of FRAME_SIZE are implemented */
|
|
|
++ struct tdm_cfg cfg_ctrl; /* Signaling controls configuration */
|
|
|
++ u8 *tdm_input_data; /* buffer used for Rx by the tdm */
|
|
|
++ u8 *tdm_output_data; /* buffer used for Tx by the tdm */
|
|
|
++
|
|
|
++ dma_addr_t dma_input_addr; /* dma mapped buffer for TDM Rx */
|
|
|
++ dma_addr_t dma_output_addr; /* dma mapped buffer for TDM Tx */
|
|
|
++ u16 physical_num_ts; /* physical number of timeslots in the tdm
|
|
|
++ frame */
|
|
|
++ u32 phase_rx; /* cycles through 0, 1, 2 */
|
|
|
++ u32 phase_tx; /* cycles through 0, 1, 2 */
|
|
|
++ /*
|
|
|
++ * the following two variables are for dealing with "stutter" problem
|
|
|
++ * "stutter" period is about 20 frames or so, varies depending active
|
|
|
++ * channel num depending on the sample depth, the code should let a
|
|
|
++ * few Rx interrupts go by
|
|
|
++ */
|
|
|
++ u32 tdm_icnt;
|
|
|
++ u32 tdm_flag;
|
|
|
++ struct ucc_transparent_pram __iomem *ucc_pram;
|
|
|
++ struct qe_bd __iomem *tx_bd;
|
|
|
++ struct qe_bd __iomem *rx_bd;
|
|
|
++ u32 ucc_pram_offset;
|
|
|
++ u32 tx_bd_offset;
|
|
|
++ u32 rx_bd_offset;
|
|
|
++ u32 rx_ucode_buf_offset;
|
|
|
++ u32 tx_ucode_buf_offset;
|
|
|
++ bool leg_slic;
|
|
|
++ wait_queue_head_t wakeup_event;
|
|
|
++};
|
|
|
++
|
|
|
++struct tdm_client {
|
|
|
++ u32 client_id;
|
|
|
++ void (*tdm_read)(u32 client_id, short chn_id,
|
|
|
++ short *pcm_buffer, short len);
|
|
|
++ void (*tdm_write)(u32 client_id, short chn_id,
|
|
|
++ short *pcm_buffer, short len);
|
|
|
++ wait_queue_head_t *wakeup_event;
|
|
|
++ };
|
|
|
++
|
|
|
++#define MAX_PHASE 1
|
|
|
++#define NR_BUFS 2
|
|
|
++#define EFF_ACTIVE_CH ACTIVE_CH / 2
|
|
|
++
|
|
|
++#endif
|
|
|
+--- /dev/null
|
|
|
++++ b/drivers/misc/ucc_tdm.c
|
|
|
+@@ -0,0 +1,1017 @@
|
|
|
++/*
|
|
|
++ * drivers/misc/ucc_tdm.c
|
|
|
++ *
|
|
|
++ * UCC Based Linux TDM Driver
|
|
|
++ * This driver is designed to support UCC based TDM for PowerPC processors.
|
|
|
++ * This driver can interface with SLIC device to run VOIP kind of
|
|
|
++ * applications.
|
|
|
++ *
|
|
|
++ * Author: Ashish Kalra & Poonam Aggrwal
|
|
|
++ *
|
|
|
++ * Copyright (c) 2007 Freescale Semiconductor, Inc.
|
|
|
++ *
|
|
|
++ * This program is free software; you can redistribute it and/or modify it
|
|
|
++ * under the terms of the GNU General Public License as published by the
|
|
|
++ * Free Software Foundation; either version 2 of the License, or (at your
|
|
|
++ * option) any later version.
|
|
|
++ */
|
|
|
++
|
|
|
++#include <generated/autoconf.h>
|
|
|
++#include <linux/module.h>
|
|
|
++#include <linux/sched.h>
|
|
|
++#include <linux/kernel.h>
|
|
|
++#include <linux/slab.h>
|
|
|
++#include <linux/errno.h>
|
|
|
++#include <linux/types.h>
|
|
|
++#include <linux/interrupt.h>
|
|
|
++#include <linux/time.h>
|
|
|
++#include <linux/skbuff.h>
|
|
|
++#include <linux/proc_fs.h>
|
|
|
++#include <linux/delay.h>
|
|
|
++#include <linux/dma-mapping.h>
|
|
|
++#include <linux/string.h>
|
|
|
++#include <linux/irq.h>
|
|
|
++#include <linux/of_platform.h>
|
|
|
++#include <linux/io.h>
|
|
|
++#include <linux/wait.h>
|
|
|
++#include <linux/timer.h>
|
|
|
++
|
|
|
++#include <asm/immap_qe.h>
|
|
|
++#include <asm/qe.h>
|
|
|
++#include <asm/ucc.h>
|
|
|
++#include <asm/ucc_fast.h>
|
|
|
++#include <asm/ucc_slow.h>
|
|
|
++
|
|
|
++#include "ucc_tdm.h"
|
|
|
++#define DRV_DESC "Freescale QE UCC TDM Driver"
|
|
|
++#define DRV_NAME "ucc_tdm"
|
|
|
++
|
|
|
++
|
|
|
++/*
|
|
|
++ * define the following #define if snooping or hardware-based cache coherency
|
|
|
++ * is disabled on the UCC transparent controller.This flag enables
|
|
|
++ * software-based cache-coherency support by explicitly flushing data cache
|
|
|
++ * contents after setting up the TDM output buffer(s) and invalidating the
|
|
|
++ * data cache contents before the TDM input buffer(s) are read.
|
|
|
++ */
|
|
|
++#undef UCC_CACHE_SNOOPING_DISABLED
|
|
|
++
|
|
|
++#define MAX_NUM_TDM_DEVICES 8
|
|
|
++
|
|
|
++static struct tdm_ctrl *tdm_ctrl[MAX_NUM_TDM_DEVICES];
|
|
|
++
|
|
|
++static int num_tdm_devices;
|
|
|
++static int num_tdm_clients;
|
|
|
++
|
|
|
++static struct ucc_tdm_info utdm_primary_info = {
|
|
|
++ .uf_info = {
|
|
|
++ .tsa = 1,
|
|
|
++ .cdp = 1,
|
|
|
++ .cds = 1,
|
|
|
++ .ctsp = 1,
|
|
|
++ .ctss = 1,
|
|
|
++ .revd = 1,
|
|
|
++ .urfs = 0x128,
|
|
|
++ .utfs = 0x128,
|
|
|
++ .utfet = 0,
|
|
|
++ .utftt = 0x128,
|
|
|
++ .ufpt = 256,
|
|
|
++ .ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_TRANSPARENT,
|
|
|
++ .tenc = UCC_FAST_TX_ENCODING_NRZ,
|
|
|
++ .renc = UCC_FAST_RX_ENCODING_NRZ,
|
|
|
++ .tcrc = UCC_FAST_16_BIT_CRC,
|
|
|
++ .synl = UCC_FAST_SYNC_LEN_NOT_USED,
|
|
|
++ },
|
|
|
++ .ucc_busy = 0,
|
|
|
++};
|
|
|
++
|
|
|
++static struct ucc_tdm_info utdm_info[8];
|
|
|
++
|
|
|
++static void dump_siram(struct tdm_ctrl *tdm_c)
|
|
|
++{
|
|
|
++#ifdef DEBUG
|
|
|
++ int i;
|
|
|
++ u16 phy_num_ts;
|
|
|
++
|
|
|
++ phy_num_ts = tdm_c->physical_num_ts;
|
|
|
++
|
|
|
++ pr_debug("SI TxRAM dump\n");
|
|
|
++ /* each slot entry in SI RAM is of 2 bytes */
|
|
|
++ for (i = 0; i < phy_num_ts * 2; i++)
|
|
|
++ pr_debug("%x ", in_8(&qe_immr->sir.tx[i]));
|
|
|
++ pr_debug("\nSI RxRAM dump\n");
|
|
|
++ for (i = 0; i < phy_num_ts * 2; i++)
|
|
|
++ pr_debug("%x ", in_8(&qe_immr->sir.rx[i]));
|
|
|
++ pr_debug("\n");
|
|
|
++#endif
|
|
|
++}
|
|
|
++
|
|
|
++static void dump_ucc(struct tdm_ctrl *tdm_c)
|
|
|
++{
|
|
|
++#ifdef DEBUG
|
|
|
++ struct ucc_transparent_pram *ucc_pram;
|
|
|
++
|
|
|
++ ucc_pram = tdm_c->ucc_pram;
|
|
|
++
|
|
|
++ pr_debug("%s Dumping UCC Registers\n", __FUNCTION__);
|
|
|
++ ucc_fast_dump_regs(tdm_c->uf_private);
|
|
|
++ pr_debug("%s Dumping UCC Parameter RAM\n", __FUNCTION__);
|
|
|
++ pr_debug("rbase = 0x%x\n", in_be32(&ucc_pram->rbase));
|
|
|
++ pr_debug("rbptr = 0x%x\n", in_be32(&ucc_pram->rbptr));
|
|
|
++ pr_debug("mrblr = 0x%x\n", in_be16(&ucc_pram->mrblr));
|
|
|
++ pr_debug("rbdlen = 0x%x\n", in_be16(&ucc_pram->rbdlen));
|
|
|
++ pr_debug("rbdstat = 0x%x\n", in_be16(&ucc_pram->rbdstat));
|
|
|
++ pr_debug("rstate = 0x%x\n", in_be32(&ucc_pram->rstate));
|
|
|
++ pr_debug("rdptr = 0x%x\n", in_be32(&ucc_pram->rdptr));
|
|
|
++ pr_debug("tbase = 0x%x\n", in_be32(&ucc_pram->tbase));
|
|
|
++ pr_debug("tbptr = 0x%x\n", in_be32(&ucc_pram->tbptr));
|
|
|
++ pr_debug("tbdlen = 0x%x\n", in_be16(&ucc_pram->tbdlen));
|
|
|
++ pr_debug("tbdstat = 0x%x\n", in_be16(&ucc_pram->tbdstat));
|
|
|
++ pr_debug("tstate = 0x%x\n", in_be32(&ucc_pram->tstate));
|
|
|
++ pr_debug("tdptr = 0x%x\n", in_be32(&ucc_pram->tdptr));
|
|
|
++#endif
|
|
|
++}
|
|
|
++
|
|
|
++/*
|
|
|
++ * For use when a framing bit is not present
|
|
|
++ * Program current-route SI ram
|
|
|
++ * Set SIxRAM TDMx
|
|
|
++ * Entries must be in units of 8.
|
|
|
++ * SIR_UCC -> Channel Select
|
|
|
++ * SIR_CNT -> Number of bits or bytes
|
|
|
++ * SIR_BYTE -> Byte or Bit resolution
|
|
|
++ * SIR_LAST -> Indicates last entry in SIxRAM
|
|
|
++ * SIR_IDLE -> The Tx data pin is Tri-stated and the Rx data pin is
|
|
|
++ * ignored
|
|
|
++ */
|
|
|
++static void set_siram(struct tdm_ctrl *tdm_c, enum comm_dir dir)
|
|
|
++{
|
|
|
++ const u16 *mask;
|
|
|
++ u16 temp_mask = 1;
|
|
|
++ u16 siram_code = 0;
|
|
|
++ u32 i, j, k;
|
|
|
++ u32 ucc;
|
|
|
++ u32 phy_num_ts;
|
|
|
++
|
|
|
++ phy_num_ts = tdm_c->physical_num_ts;
|
|
|
++ ucc = tdm_c->ut_info->uf_info.ucc_num;
|
|
|
++
|
|
|
++ if (dir == COMM_DIR_RX)
|
|
|
++ mask = tdm_c->rx_mask;
|
|
|
++ else
|
|
|
++ mask = tdm_c->tx_mask;
|
|
|
++ k = 0;
|
|
|
++ j = 0;
|
|
|
++ for (i = 0; i < phy_num_ts; i++) {
|
|
|
++ if ((mask[k] & temp_mask) == temp_mask)
|
|
|
++ siram_code = SIR_UCC(ucc) | SIR_CNT(0) | SIR_BYTE;
|
|
|
++ else
|
|
|
++ siram_code = SIR_IDLE | SIR_CNT(0) | SIR_BYTE;
|
|
|
++ if (dir == COMM_DIR_RX)
|
|
|
++ out_be16((u16 *)&qe_immr->sir.rx[i * 2], siram_code);
|
|
|
++ else
|
|
|
++ out_be16((u16 *)&qe_immr->sir.tx[i * 2], siram_code);
|
|
|
++ temp_mask = temp_mask << 1;
|
|
|
++ j++;
|
|
|
++ if (j >= 16) {
|
|
|
++ j = 0;
|
|
|
++ temp_mask = 0x0001;
|
|
|
++ k++;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ siram_code = siram_code | SIR_LAST;
|
|
|
++
|
|
|
++ if (dir == COMM_DIR_RX)
|
|
|
++ out_be16((u16 *)&qe_immr->sir.rx[(phy_num_ts - 1) * 2],
|
|
|
++ siram_code);
|
|
|
++ else
|
|
|
++ out_be16((u16 *)&qe_immr->sir.tx[(phy_num_ts - 1) * 2],
|
|
|
++ siram_code);
|
|
|
++}
|
|
|
++
|
|
|
++static void config_si(struct tdm_ctrl *tdm_c)
|
|
|
++{
|
|
|
++ u8 rxsyncdelay, txsyncdelay, tdm_port;
|
|
|
++ u16 sixmr_val = 0;
|
|
|
++ u32 tdma_mode_off;
|
|
|
++ u16 *si1_tdm_mode_reg;
|
|
|
++
|
|
|
++ tdm_port = tdm_c->tdm_port;
|
|
|
++
|
|
|
++ set_siram(tdm_c, COMM_DIR_RX);
|
|
|
++
|
|
|
++ set_siram(tdm_c, COMM_DIR_TX);
|
|
|
++
|
|
|
++ rxsyncdelay = tdm_c->cfg_ctrl.rx_fr_sync_delay;
|
|
|
++ txsyncdelay = tdm_c->cfg_ctrl.tx_fr_sync_delay;
|
|
|
++ if (tdm_c->cfg_ctrl.com_pin)
|
|
|
++ sixmr_val |= SIMODE_CRT;
|
|
|
++ if (tdm_c->cfg_ctrl.fr_sync_level == 1)
|
|
|
++ sixmr_val |= SIMODE_SL;
|
|
|
++ if (tdm_c->cfg_ctrl.clk_edge == 1)
|
|
|
++ sixmr_val |= SIMODE_CE;
|
|
|
++ if (tdm_c->cfg_ctrl.fr_sync_edge == 1)
|
|
|
++ sixmr_val |= SIMODE_FE;
|
|
|
++ sixmr_val |= (SIMODE_TFSD(txsyncdelay) | SIMODE_RFSD(rxsyncdelay));
|
|
|
++
|
|
|
++ tdma_mode_off = SI_TDM_MODE_REGISTER_OFFSET * tdm_c->tdm_port;
|
|
|
++
|
|
|
++ si1_tdm_mode_reg = (u8 *)&qe_immr->si1 + tdma_mode_off;
|
|
|
++ out_be16(si1_tdm_mode_reg, sixmr_val);
|
|
|
++
|
|
|
++ dump_siram(tdm_c);
|
|
|
++}
|
|
|
++
|
|
|
++static int tdm_init(struct tdm_ctrl *tdm_c)
|
|
|
++{
|
|
|
++ u32 tdm_port, ucc, act_num_ts;
|
|
|
++ int ret, i, err;
|
|
|
++ u32 cecr_subblock;
|
|
|
++ u32 pram_offset;
|
|
|
++ u32 rxbdt_offset;
|
|
|
++ u32 txbdt_offset;
|
|
|
++ u32 rx_ucode_buf_offset, tx_ucode_buf_offset;
|
|
|
++ u16 bd_status, bd_len;
|
|
|
++ enum qe_clock clock;
|
|
|
++ struct qe_bd __iomem *rx_bd, *tx_bd;
|
|
|
++
|
|
|
++ tdm_port = tdm_c->tdm_port;
|
|
|
++ ucc = tdm_c->ut_info->uf_info.ucc_num;
|
|
|
++ act_num_ts = tdm_c->cfg_ctrl.active_num_ts;
|
|
|
++
|
|
|
++ /*
|
|
|
++ * TDM Tx and Rx CLKs = 2048 KHz.
|
|
|
++ */
|
|
|
++ if (strstr(tdm_c->ut_info->uf_info.tdm_tx_clk, "BRG")) {
|
|
|
++ clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_tx_clk);
|
|
|
++ err = qe_setbrg(clock, 2048000, 1);
|
|
|
++ if (err < 0) {
|
|
|
++ printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
|
|
|
++ tdm_c->ut_info->uf_info.tdm_tx_clk);
|
|
|
++ return err;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (strstr(tdm_c->ut_info->uf_info.tdm_rx_clk, "BRG")) {
|
|
|
++ clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_rx_clk);
|
|
|
++ err = qe_setbrg(clock, 2048000, 1);
|
|
|
++ if (err < 0) {
|
|
|
++ printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
|
|
|
++ tdm_c->ut_info->uf_info.tdm_rx_clk);
|
|
|
++ return err;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ /*
|
|
|
++ * TDM FSyncs = 4 KHz.
|
|
|
++ */
|
|
|
++ if (strstr(tdm_c->ut_info->uf_info.tdm_tx_sync, "BRG")) {
|
|
|
++ clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_tx_sync);
|
|
|
++ err = qe_setbrg(clock, 4000, 1);
|
|
|
++ if (err < 0) {
|
|
|
++ printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
|
|
|
++ tdm_c->ut_info->uf_info.tdm_tx_sync);
|
|
|
++ return err;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (strstr(tdm_c->ut_info->uf_info.tdm_rx_sync, "BRG")) {
|
|
|
++ clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_rx_sync);
|
|
|
++ err = qe_setbrg(clock, 4000, 1);
|
|
|
++ if (err < 0) {
|
|
|
++ printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
|
|
|
++ tdm_c->ut_info->uf_info.tdm_rx_sync);
|
|
|
++ return err;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ tdm_c->ut_info->uf_info.uccm_mask = (u32)
|
|
|
++ ((UCC_TRANS_UCCE_RXB | UCC_TRANS_UCCE_BSY) << 16);
|
|
|
++
|
|
|
++ if (ucc_fast_init(&(tdm_c->ut_info->uf_info), &tdm_c->uf_private)) {
|
|
|
++ printk(KERN_ERR "%s: Failed to init uccf\n", __FUNCTION__);
|
|
|
++ return -ENOMEM;
|
|
|
++ }
|
|
|
++
|
|
|
++ ucc_fast_disable(tdm_c->uf_private, COMM_DIR_RX | COMM_DIR_TX);
|
|
|
++
|
|
|
++ /* Write to QE CECR, UCCx channel to Stop Transmission */
|
|
|
++ cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
|
|
|
++ qe_issue_cmd(QE_STOP_TX, cecr_subblock,
|
|
|
++ (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
|
|
|
++
|
|
|
++ pram_offset = qe_muram_alloc(UCC_TRANSPARENT_PRAM_SIZE,
|
|
|
++ ALIGNMENT_OF_UCC_SLOW_PRAM);
|
|
|
++ if (IS_ERR_VALUE(pram_offset)) {
|
|
|
++ printk(KERN_ERR "%s: Cannot allocate MURAM memory for"
|
|
|
++ " transparent UCC\n", __FUNCTION__);
|
|
|
++ ret = -ENOMEM;
|
|
|
++ goto pram_alloc_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
|
|
|
++ qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock,
|
|
|
++ QE_CR_PROTOCOL_UNSPECIFIED, pram_offset);
|
|
|
++
|
|
|
++ tdm_c->ucc_pram = qe_muram_addr(pram_offset);
|
|
|
++ tdm_c->ucc_pram_offset = pram_offset;
|
|
|
++
|
|
|
++ /*
|
|
|
++ * zero-out pram, this will also ensure RSTATE, TSTATE are cleared, also
|
|
|
++ * DISFC & CRCEC counters will be initialized.
|
|
|
++ */
|
|
|
++ memset(tdm_c->ucc_pram, 0, sizeof(struct ucc_transparent_pram));
|
|
|
++
|
|
|
++ /* rbase, tbase alignment is 8. */
|
|
|
++ rxbdt_offset = qe_muram_alloc(NR_BUFS * sizeof(struct qe_bd),
|
|
|
++ QE_ALIGNMENT_OF_BD);
|
|
|
++ if (IS_ERR_VALUE(rxbdt_offset)) {
|
|
|
++ printk(KERN_ERR "%s: Cannot allocate MURAM memory for RxBDs\n",
|
|
|
++ __FUNCTION__);
|
|
|
++ ret = -ENOMEM;
|
|
|
++ goto rxbd_alloc_error;
|
|
|
++ }
|
|
|
++ txbdt_offset = qe_muram_alloc(NR_BUFS * sizeof(struct qe_bd),
|
|
|
++ QE_ALIGNMENT_OF_BD);
|
|
|
++ if (IS_ERR_VALUE(txbdt_offset)) {
|
|
|
++ printk(KERN_ERR "%s: Cannot allocate MURAM memory for TxBDs\n",
|
|
|
++ __FUNCTION__);
|
|
|
++ ret = -ENOMEM;
|
|
|
++ goto txbd_alloc_error;
|
|
|
++ }
|
|
|
++ tdm_c->tx_bd = qe_muram_addr(txbdt_offset);
|
|
|
++ tdm_c->rx_bd = qe_muram_addr(rxbdt_offset);
|
|
|
++
|
|
|
++ tdm_c->tx_bd_offset = txbdt_offset;
|
|
|
++ tdm_c->rx_bd_offset = rxbdt_offset;
|
|
|
++
|
|
|
++ rx_bd = tdm_c->rx_bd;
|
|
|
++ tx_bd = tdm_c->tx_bd;
|
|
|
++
|
|
|
++ out_be32(&tdm_c->ucc_pram->rbase, (u32) immrbar_virt_to_phys(rx_bd));
|
|
|
++ out_be32(&tdm_c->ucc_pram->tbase, (u32) immrbar_virt_to_phys(tx_bd));
|
|
|
++
|
|
|
++ for (i = 0; i < NR_BUFS - 1; i++) {
|
|
|
++ bd_status = (u16) ((R_E | R_CM | R_I) >> 16);
|
|
|
++ bd_len = 0;
|
|
|
++ out_be16(&rx_bd->length, bd_len);
|
|
|
++ out_be16(&rx_bd->status, bd_status);
|
|
|
++ out_be32(&rx_bd->buf,
|
|
|
++ tdm_c->dma_input_addr + i * SAMPLE_DEPTH * act_num_ts);
|
|
|
++ rx_bd += 1;
|
|
|
++
|
|
|
++ bd_status = (u16) ((T_R | T_CM) >> 16);
|
|
|
++ bd_len = SAMPLE_DEPTH * act_num_ts;
|
|
|
++ out_be16(&tx_bd->length, bd_len);
|
|
|
++ out_be16(&tx_bd->status, bd_status);
|
|
|
++ out_be32(&tx_bd->buf,
|
|
|
++ tdm_c->dma_output_addr + i * SAMPLE_DEPTH * act_num_ts);
|
|
|
++ tx_bd += 1;
|
|
|
++ }
|
|
|
++
|
|
|
++ bd_status = (u16) ((R_E | R_CM | R_I | R_W) >> 16);
|
|
|
++ bd_len = 0;
|
|
|
++ out_be16(&rx_bd->length, bd_len);
|
|
|
++ out_be16(&rx_bd->status, bd_status);
|
|
|
++ out_be32(&rx_bd->buf,
|
|
|
++ tdm_c->dma_input_addr + i * SAMPLE_DEPTH * act_num_ts);
|
|
|
++
|
|
|
++ bd_status = (u16) ((T_R | T_CM | T_W) >> 16);
|
|
|
++ bd_len = SAMPLE_DEPTH * act_num_ts;
|
|
|
++ out_be16(&tx_bd->length, bd_len);
|
|
|
++ out_be16(&tx_bd->status, bd_status);
|
|
|
++ out_be32(&tx_bd->buf,
|
|
|
++ tdm_c->dma_output_addr + i * SAMPLE_DEPTH * act_num_ts);
|
|
|
++
|
|
|
++ config_si(tdm_c);
|
|
|
++
|
|
|
++ setbits32(&qe_immr->ic.qimr, (0x80000000UL >> ucc));
|
|
|
++
|
|
|
++ rx_ucode_buf_offset = qe_muram_alloc(32, 32);
|
|
|
++ if (IS_ERR_VALUE(rx_ucode_buf_offset)) {
|
|
|
++ printk(KERN_ERR "%s: Cannot allocate MURAM mem for Rx"
|
|
|
++ " ucode buf\n", __FUNCTION__);
|
|
|
++ ret = -ENOMEM;
|
|
|
++ goto rxucode_buf_alloc_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ tx_ucode_buf_offset = qe_muram_alloc(32, 32);
|
|
|
++ if (IS_ERR_VALUE(tx_ucode_buf_offset)) {
|
|
|
++ printk(KERN_ERR "%s: Cannot allocate MURAM mem for Tx"
|
|
|
++ " ucode buf\n", __FUNCTION__);
|
|
|
++ ret = -ENOMEM;
|
|
|
++ goto txucode_buf_alloc_error;
|
|
|
++ }
|
|
|
++ out_be16(&tdm_c->ucc_pram->riptr, (u16) rx_ucode_buf_offset);
|
|
|
++ out_be16(&tdm_c->ucc_pram->tiptr, (u16) tx_ucode_buf_offset);
|
|
|
++
|
|
|
++ tdm_c->rx_ucode_buf_offset = rx_ucode_buf_offset;
|
|
|
++ tdm_c->tx_ucode_buf_offset = tx_ucode_buf_offset;
|
|
|
++
|
|
|
++ /*
|
|
|
++ * set the receive buffer descriptor maximum size to be
|
|
|
++ * SAMPLE_DEPTH * number of active RX channels
|
|
|
++ */
|
|
|
++ out_be16(&tdm_c->ucc_pram->mrblr, (u16) SAMPLE_DEPTH * act_num_ts);
|
|
|
++
|
|
|
++ /*
|
|
|
++ * enable snooping and BE byte ordering on the UCC pram's
|
|
|
++ * tstate & rstate registers.
|
|
|
++ */
|
|
|
++ out_be32(&tdm_c->ucc_pram->tstate, 0x30000000UL);
|
|
|
++ out_be32(&tdm_c->ucc_pram->rstate, 0x30000000UL);
|
|
|
++
|
|
|
++ /*Put UCC transparent controller into serial interface mode. */
|
|
|
++ out_be32(&tdm_c->uf_regs->upsmr, 0);
|
|
|
++
|
|
|
++ /* Reset TX and RX for UCCx */
|
|
|
++ cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
|
|
|
++ qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock,
|
|
|
++ (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
|
|
|
++
|
|
|
++ return 0;
|
|
|
++
|
|
|
++txucode_buf_alloc_error:
|
|
|
++ qe_muram_free(rx_ucode_buf_offset);
|
|
|
++rxucode_buf_alloc_error:
|
|
|
++ qe_muram_free(txbdt_offset);
|
|
|
++txbd_alloc_error:
|
|
|
++ qe_muram_free(rxbdt_offset);
|
|
|
++rxbd_alloc_error:
|
|
|
++ qe_muram_free(pram_offset);
|
|
|
++pram_alloc_error:
|
|
|
++ ucc_fast_free(tdm_c->uf_private);
|
|
|
++ return ret;
|
|
|
++}
|
|
|
++
|
|
|
++static void tdm_deinit(struct tdm_ctrl *tdm_c)
|
|
|
++{
|
|
|
++ qe_muram_free(tdm_c->rx_ucode_buf_offset);
|
|
|
++ qe_muram_free(tdm_c->tx_ucode_buf_offset);
|
|
|
++
|
|
|
++ if (tdm_c->rx_bd_offset) {
|
|
|
++ qe_muram_free(tdm_c->rx_bd_offset);
|
|
|
++ tdm_c->rx_bd = NULL;
|
|
|
++ tdm_c->rx_bd_offset = 0;
|
|
|
++ }
|
|
|
++ if (tdm_c->tx_bd_offset) {
|
|
|
++ qe_muram_free(tdm_c->tx_bd_offset);
|
|
|
++ tdm_c->tx_bd = NULL;
|
|
|
++ tdm_c->tx_bd_offset = 0;
|
|
|
++ }
|
|
|
++ if (tdm_c->ucc_pram_offset) {
|
|
|
++ qe_muram_free(tdm_c->ucc_pram_offset);
|
|
|
++ tdm_c->ucc_pram = NULL;
|
|
|
++ tdm_c->ucc_pram_offset = 0;
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++static irqreturn_t tdm_isr(int irq, void *dev_id)
|
|
|
++{
|
|
|
++ u8 *input_tdm_buffer, *output_tdm_buffer;
|
|
|
++ u32 txb, rxb;
|
|
|
++ u32 ucc;
|
|
|
++ register u32 ucce = 0;
|
|
|
++ struct tdm_ctrl *tdm_c;
|
|
|
++ tdm_c = (struct tdm_ctrl *)dev_id;
|
|
|
++
|
|
|
++ tdm_c->tdm_icnt++;
|
|
|
++ ucc = tdm_c->ut_info->uf_info.ucc_num;
|
|
|
++ input_tdm_buffer = tdm_c->tdm_input_data;
|
|
|
++ output_tdm_buffer = tdm_c->tdm_output_data;
|
|
|
++
|
|
|
++ if (in_be32(tdm_c->uf_private->p_ucce) &
|
|
|
++ (UCC_TRANS_UCCE_BSY << 16)) {
|
|
|
++ out_be32(tdm_c->uf_private->p_ucce,
|
|
|
++ (UCC_TRANS_UCCE_BSY << 16));
|
|
|
++ pr_info("%s: From tdm isr busy interrupt\n",
|
|
|
++ __FUNCTION__);
|
|
|
++ dump_ucc(tdm_c);
|
|
|
++
|
|
|
++ return IRQ_HANDLED;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (tdm_c->tdm_flag == 1) {
|
|
|
++ /* track phases for Rx/Tx */
|
|
|
++ tdm_c->phase_rx += 1;
|
|
|
++ if (tdm_c->phase_rx == MAX_PHASE)
|
|
|
++ tdm_c->phase_rx = 0;
|
|
|
++
|
|
|
++ tdm_c->phase_tx += 1;
|
|
|
++ if (tdm_c->phase_tx == MAX_PHASE)
|
|
|
++ tdm_c->phase_tx = 0;
|
|
|
++
|
|
|
++#ifdef CONFIG_TDM_HW_LB_TSA_SLIC
|
|
|
++ {
|
|
|
++ u32 temp_rx, temp_tx, phase_tx, phase_rx;
|
|
|
++ int i;
|
|
|
++ phase_rx = tdm_c->phase_rx;
|
|
|
++ phase_tx = tdm_c->phase_tx;
|
|
|
++ if (phase_rx == 0)
|
|
|
++ phase_rx = MAX_PHASE;
|
|
|
++ else
|
|
|
++ phase_rx -= 1;
|
|
|
++ if (phase_tx == 0)
|
|
|
++ phase_tx = MAX_PHASE;
|
|
|
++ else
|
|
|
++ phase_tx -= 1;
|
|
|
++ temp_rx = phase_rx * SAMPLE_DEPTH * ACTIVE_CH;
|
|
|
++ temp_tx = phase_tx * SAMPLE_DEPTH * ACTIVE_CH;
|
|
|
++
|
|
|
++ /*check if loopback received data on TS0 is correct. */
|
|
|
++ pr_debug("%s: check if loopback received data on TS0"
|
|
|
++ " is correct\n", __FUNCTION__);
|
|
|
++ pr_debug("%d,%d ", phase_rx, phase_tx);
|
|
|
++ for (i = 0; i < 8; i++)
|
|
|
++ pr_debug("%1d,%1d ",
|
|
|
++ input_tdm_buffer[temp_rx + i],
|
|
|
++ output_tdm_buffer[temp_tx + i]);
|
|
|
++ pr_debug("\n");
|
|
|
++ }
|
|
|
++#endif
|
|
|
++
|
|
|
++ /* schedule BH */
|
|
|
++ wake_up_interruptible(&tdm_c->wakeup_event);
|
|
|
++ } else {
|
|
|
++ if (tdm_c->tdm_icnt == STUTTER_INT_CNT) {
|
|
|
++ txb = in_be32(&tdm_c->ucc_pram->tbptr) -
|
|
|
++ in_be32(&tdm_c->ucc_pram->tbase);
|
|
|
++ rxb = in_be32(&tdm_c->ucc_pram->rbptr) -
|
|
|
++ in_be32(&tdm_c->ucc_pram->rbase);
|
|
|
++ tdm_c->phase_tx = txb / sizeof(struct qe_bd);
|
|
|
++ tdm_c->phase_rx = rxb / sizeof(struct qe_bd);
|
|
|
++
|
|
|
++#ifdef CONFIG_TDM_HW_LB_TSA_SLIC
|
|
|
++ tdm_c->phase_tx = tdm_c->phase_rx;
|
|
|
++#endif
|
|
|
++
|
|
|
++ /* signal "stuttering" period is over */
|
|
|
++ tdm_c->tdm_flag = 1;
|
|
|
++
|
|
|
++ pr_debug("%s: stuttering period is over\n",
|
|
|
++ __FUNCTION__);
|
|
|
++
|
|
|
++ if (in_be32(tdm_c->uf_private->p_ucce) &
|
|
|
++ (UCC_TRANS_UCCE_TXE << 16)) {
|
|
|
++ u32 cecr_subblock;
|
|
|
++ out_be32(tdm_c->uf_private->p_ucce,
|
|
|
++ (UCC_TRANS_UCCE_TXE << 16));
|
|
|
++ pr_debug("%s: From tdm isr txe interrupt\n",
|
|
|
++ __FUNCTION__);
|
|
|
++
|
|
|
++ cecr_subblock =
|
|
|
++ ucc_fast_get_qe_cr_subblock(ucc);
|
|
|
++ qe_issue_cmd(QE_RESTART_TX, cecr_subblock,
|
|
|
++ (u8) QE_CR_PROTOCOL_UNSPECIFIED,
|
|
|
++ 0);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ ucce = (in_be32(tdm_c->uf_private->p_ucce)
|
|
|
++ & in_be32(tdm_c->uf_private->p_uccm));
|
|
|
++
|
|
|
++ out_be32(tdm_c->uf_private->p_ucce, ucce);
|
|
|
++
|
|
|
++ return IRQ_HANDLED;
|
|
|
++}
|
|
|
++
|
|
|
++static int tdm_start(struct tdm_ctrl *tdm_c)
|
|
|
++{
|
|
|
++ if (request_irq(tdm_c->ut_info->uf_info.irq, tdm_isr,
|
|
|
++ 0, "tdm", tdm_c)) {
|
|
|
++ printk(KERN_ERR "%s: request_irq for tdm_isr failed\n",
|
|
|
++ __FUNCTION__);
|
|
|
++ return -ENODEV;
|
|
|
++ }
|
|
|
++
|
|
|
++ ucc_fast_enable(tdm_c->uf_private, COMM_DIR_RX | COMM_DIR_TX);
|
|
|
++
|
|
|
++ pr_info("%s 16-bit linear pcm mode active with"
|
|
|
++ " slots 0 & 2\n", __FUNCTION__);
|
|
|
++
|
|
|
++ dump_siram(tdm_c);
|
|
|
++ dump_ucc(tdm_c);
|
|
|
++
|
|
|
++ setbits8(&(qe_immr->si1.siglmr1_h), (0x1 << tdm_c->tdm_port));
|
|
|
++ pr_info("%s UCC based TDM enabled\n", __FUNCTION__);
|
|
|
++
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
++static void tdm_stop(struct tdm_ctrl *tdm_c)
|
|
|
++{
|
|
|
++ u32 port, si;
|
|
|
++ u32 ucc;
|
|
|
++ u32 cecr_subblock;
|
|
|
++
|
|
|
++ port = tdm_c->tdm_port;
|
|
|
++ si = tdm_c->si;
|
|
|
++ ucc = tdm_c->ut_info->uf_info.ucc_num;
|
|
|
++ cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
|
|
|
++
|
|
|
++ qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock,
|
|
|
++ (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
|
|
|
++ qe_issue_cmd(QE_CLOSE_RX_BD, cecr_subblock,
|
|
|
++ (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
|
|
|
++
|
|
|
++ clrbits8(&qe_immr->si1.siglmr1_h, (0x1 << port));
|
|
|
++ ucc_fast_disable(tdm_c->uf_private, COMM_DIR_RX);
|
|
|
++ ucc_fast_disable(tdm_c->uf_private, COMM_DIR_TX);
|
|
|
++ free_irq(tdm_c->ut_info->uf_info.irq, tdm_c);
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++static void config_tdm(struct tdm_ctrl *tdm_c)
|
|
|
++{
|
|
|
++ u32 i, j, k;
|
|
|
++
|
|
|
++ j = 0;
|
|
|
++ k = 0;
|
|
|
++
|
|
|
++ /* Set Mask Bits */
|
|
|
++ for (i = 0; i < ACTIVE_CH; i++) {
|
|
|
++ tdm_c->tx_mask[k] |= (1 << j);
|
|
|
++ tdm_c->rx_mask[k] |= (1 << j);
|
|
|
++ j++;
|
|
|
++ if (j >= 16) {
|
|
|
++ j = 0;
|
|
|
++ k++;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ /* physical number of slots in a frame */
|
|
|
++ tdm_c->physical_num_ts = NUM_TS;
|
|
|
++
|
|
|
++ /* common receive and transmit pins */
|
|
|
++ tdm_c->cfg_ctrl.com_pin = 1;
|
|
|
++
|
|
|
++ /* L1R/TSYNC active logic "1" */
|
|
|
++ tdm_c->cfg_ctrl.fr_sync_level = 0;
|
|
|
++
|
|
|
++ /*
|
|
|
++ * TX data on rising edge of clock
|
|
|
++ * RX data on falling edge
|
|
|
++ */
|
|
|
++ tdm_c->cfg_ctrl.clk_edge = 0;
|
|
|
++
|
|
|
++ /* Frame sync sampled on falling edge */
|
|
|
++ tdm_c->cfg_ctrl.fr_sync_edge = 0;
|
|
|
++
|
|
|
++ /* no bit delay */
|
|
|
++ tdm_c->cfg_ctrl.rx_fr_sync_delay = 0;
|
|
|
++
|
|
|
++ /* no bit delay */
|
|
|
++ tdm_c->cfg_ctrl.tx_fr_sync_delay = 0;
|
|
|
++
|
|
|
++#ifndef CONFIG_TDM_HW_LB_TSA_SLIC
|
|
|
++ if (tdm_c->leg_slic) {
|
|
|
++ /* Need 1 bit delay for Legrity SLIC */
|
|
|
++ tdm_c->cfg_ctrl.rx_fr_sync_delay = 1;
|
|
|
++ tdm_c->cfg_ctrl.tx_fr_sync_delay = 1;
|
|
|
++ pr_info("%s Delay for Legerity!\n", __FUNCTION__);
|
|
|
++ }
|
|
|
++#endif
|
|
|
++
|
|
|
++ tdm_c->cfg_ctrl.active_num_ts = ACTIVE_CH;
|
|
|
++}
|
|
|
++
|
|
|
++static void tdm_read(u32 client_id, short chn_id, short *pcm_buffer,
|
|
|
++ short len)
|
|
|
++{
|
|
|
++ int i;
|
|
|
++ u32 phase_rx;
|
|
|
++ /* point to where to start for the current phase data processing */
|
|
|
++ u32 temp_rx;
|
|
|
++
|
|
|
++ struct tdm_ctrl *tdm_c = tdm_ctrl[client_id];
|
|
|
++
|
|
|
++ u16 *input_tdm_buffer =
|
|
|
++ (u16 *)tdm_c->tdm_input_data;
|
|
|
++
|
|
|
++ phase_rx = tdm_c->phase_rx;
|
|
|
++ if (phase_rx == 0)
|
|
|
++ phase_rx = MAX_PHASE;
|
|
|
++ else
|
|
|
++ phase_rx -= 1;
|
|
|
++
|
|
|
++ temp_rx = phase_rx * SAMPLE_DEPTH * EFF_ACTIVE_CH;
|
|
|
++
|
|
|
++#ifdef UCC_CACHE_SNOOPING_DISABLED
|
|
|
++ flush_dcache_range((size_t) &input_tdm_buffer[temp_rx],
|
|
|
++ (size_t) &input_tdm_buffer[temp_rx +
|
|
|
++ SAMPLE_DEPTH * ACTIVE_CH]);
|
|
|
++#endif
|
|
|
++ for (i = 0; i < len; i++)
|
|
|
++ pcm_buffer[i] =
|
|
|
++ input_tdm_buffer[i * EFF_ACTIVE_CH + temp_rx + chn_id];
|
|
|
++
|
|
|
++}
|
|
|
++
|
|
|
++static void tdm_write(u32 client_id, short chn_id, short *pcm_buffer,
|
|
|
++ short len)
|
|
|
++{
|
|
|
++ int i;
|
|
|
++ int phase_tx;
|
|
|
++ u32 txb;
|
|
|
++ /* point to where to start for the current phase data processing */
|
|
|
++ int temp_tx;
|
|
|
++ struct tdm_ctrl *tdm_c = tdm_ctrl[client_id];
|
|
|
++
|
|
|
++ u16 *output_tdm_buffer;
|
|
|
++ output_tdm_buffer = (u16 *)tdm_c->tdm_output_data;
|
|
|
++ txb = in_be32(&tdm_c->ucc_pram->tbptr) -
|
|
|
++ in_be32(&tdm_c->ucc_pram->tbase);
|
|
|
++ phase_tx = txb / sizeof(struct qe_bd);
|
|
|
++
|
|
|
++ if (phase_tx == 0)
|
|
|
++ phase_tx = MAX_PHASE;
|
|
|
++ else
|
|
|
++ phase_tx -= 1;
|
|
|
++
|
|
|
++ temp_tx = phase_tx * SAMPLE_DEPTH * EFF_ACTIVE_CH;
|
|
|
++
|
|
|
++ for (i = 0; i < len; i++)
|
|
|
++ output_tdm_buffer[i * EFF_ACTIVE_CH + temp_tx + chn_id] =
|
|
|
++ pcm_buffer[i];
|
|
|
++
|
|
|
++#ifdef UCC_CACHE_SNOOPING_DISABLED
|
|
|
++ flush_dcache_range((size_t) &output_tdm_buffer[temp_tx],
|
|
|
++ (size_t) &output_tdm_buffer[temp_tx + SAMPLE_DEPTH *
|
|
|
++ ACTIVE_CH]);
|
|
|
++#endif
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++static int tdm_register_client(struct tdm_client *tdm_client)
|
|
|
++{
|
|
|
++ u32 i;
|
|
|
++ if (num_tdm_clients == num_tdm_devices) {
|
|
|
++ printk(KERN_ERR "all TDM devices busy\n");
|
|
|
++ return -EBUSY;
|
|
|
++ }
|
|
|
++
|
|
|
++ for (i = 0; i < num_tdm_devices; i++) {
|
|
|
++ if (!tdm_ctrl[i]->device_busy) {
|
|
|
++ tdm_ctrl[i]->device_busy = 1;
|
|
|
++ break;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ num_tdm_clients++;
|
|
|
++ tdm_client->client_id = i;
|
|
|
++ tdm_client->tdm_read = tdm_read;
|
|
|
++ tdm_client->tdm_write = tdm_write;
|
|
|
++ tdm_client->wakeup_event =
|
|
|
++ &(tdm_ctrl[i]->wakeup_event);
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++EXPORT_SYMBOL_GPL(tdm_register_client);
|
|
|
++
|
|
|
++static int tdm_deregister_client(struct tdm_client *tdm_client)
|
|
|
++{
|
|
|
++ num_tdm_clients--;
|
|
|
++ tdm_ctrl[tdm_client->client_id]->device_busy = 0;
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++EXPORT_SYMBOL_GPL(tdm_deregister_client);
|
|
|
++
|
|
|
++static int ucc_tdm_probe(struct of_device *ofdev,
|
|
|
++ const struct of_device_id *match)
|
|
|
++{
|
|
|
++ struct device_node *np = ofdev->node;
|
|
|
++ struct resource res;
|
|
|
++ const unsigned int *prop;
|
|
|
++ u32 ucc_num, device_num, err, ret = 0;
|
|
|
++ struct device_node *np_tmp;
|
|
|
++ dma_addr_t physaddr;
|
|
|
++ void *tdm_buff;
|
|
|
++ struct ucc_tdm_info *ut_info;
|
|
|
++
|
|
|
++ prop = of_get_property(np, "device-id", NULL);
|
|
|
++ if (prop == NULL) {
|
|
|
++ printk(KERN_ERR "ucc_tdm: device-id missing\n");
|
|
|
++ return -ENODEV;
|
|
|
++ }
|
|
|
++
|
|
|
++ ucc_num = *prop - 1;
|
|
|
++ if ((ucc_num < 0) || (ucc_num > 7))
|
|
|
++ return -ENODEV;
|
|
|
++
|
|
|
++ ut_info = &utdm_info[ucc_num];
|
|
|
++ if (ut_info->ucc_busy) {
|
|
|
++ printk(KERN_ERR "ucc_tdm: UCC in use by another TDM driver"
|
|
|
++ "instance\n");
|
|
|
++ return -EBUSY;
|
|
|
++ }
|
|
|
++ if (num_tdm_devices == MAX_NUM_TDM_DEVICES) {
|
|
|
++ printk(KERN_ERR "ucc_tdm: All TDM devices already"
|
|
|
++ " initialized\n");
|
|
|
++ return -ENODEV;
|
|
|
++ }
|
|
|
++
|
|
|
++ ut_info->ucc_busy = 1;
|
|
|
++ tdm_ctrl[num_tdm_devices++] =
|
|
|
++ kzalloc(sizeof(struct tdm_ctrl), GFP_KERNEL);
|
|
|
++ if (!tdm_ctrl[num_tdm_devices - 1]) {
|
|
|
++ printk(KERN_ERR "ucc_tdm: no memory to allocate for"
|
|
|
++ " tdm control structure\n");
|
|
|
++ num_tdm_devices--;
|
|
|
++ return -ENOMEM;
|
|
|
++ }
|
|
|
++ device_num = num_tdm_devices - 1;
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->device = &ofdev->dev;
|
|
|
++ tdm_ctrl[device_num]->ut_info = ut_info;
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->ut_info->uf_info.ucc_num = ucc_num;
|
|
|
++
|
|
|
++ prop = of_get_property(np, "fsl,tdm-num", NULL);
|
|
|
++ if (prop == NULL) {
|
|
|
++ ret = -EINVAL;
|
|
|
++ goto get_property_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->tdm_port = *prop - 1;
|
|
|
++
|
|
|
++ if (tdm_ctrl[device_num]->tdm_port > 3) {
|
|
|
++ ret = -EINVAL;
|
|
|
++ goto get_property_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ prop = of_get_property(np, "fsl,si-num", NULL);
|
|
|
++ if (prop == NULL) {
|
|
|
++ ret = -EINVAL;
|
|
|
++ goto get_property_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->si = *prop - 1;
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_clk =
|
|
|
++ of_get_property(np, "fsl,tdm-tx-clk", NULL);
|
|
|
++ if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_clk == NULL) {
|
|
|
++ ret = -EINVAL;
|
|
|
++ goto get_property_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_clk =
|
|
|
++ of_get_property(np, "fsl,tdm-rx-clk", NULL);
|
|
|
++ if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_clk == NULL) {
|
|
|
++ ret = -EINVAL;
|
|
|
++ goto get_property_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_sync =
|
|
|
++ of_get_property(np, "fsl,tdm-tx-sync", NULL);
|
|
|
++ if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_sync == NULL) {
|
|
|
++ ret = -EINVAL;
|
|
|
++ goto get_property_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_sync =
|
|
|
++ of_get_property(np, "fsl,tdm-rx-sync", NULL);
|
|
|
++ if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_sync == NULL) {
|
|
|
++ ret = -EINVAL;
|
|
|
++ goto get_property_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->ut_info->uf_info.irq =
|
|
|
++ irq_of_parse_and_map(np, 0);
|
|
|
++ err = of_address_to_resource(np, 0, &res);
|
|
|
++ if (err) {
|
|
|
++ ret = -EINVAL;
|
|
|
++ goto get_property_error;
|
|
|
++ }
|
|
|
++ tdm_ctrl[device_num]->ut_info->uf_info.regs = res.start;
|
|
|
++ tdm_ctrl[device_num]->uf_regs = of_iomap(np, 0);
|
|
|
++
|
|
|
++ np_tmp = NULL;
|
|
|
++ np_tmp = of_find_compatible_node(np_tmp, "slic", "legerity-slic");
|
|
|
++ if (np_tmp != NULL) {
|
|
|
++ tdm_ctrl[device_num]->leg_slic = 1;
|
|
|
++ of_node_put(np_tmp);
|
|
|
++ } else
|
|
|
++ tdm_ctrl[device_num]->leg_slic = 0;
|
|
|
++
|
|
|
++ config_tdm(tdm_ctrl[device_num]);
|
|
|
++
|
|
|
++ tdm_buff = dma_alloc_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
|
|
|
++ tdm_ctrl[device_num]->cfg_ctrl.active_num_ts,
|
|
|
++ &physaddr, GFP_KERNEL);
|
|
|
++ if (!tdm_buff) {
|
|
|
++ printk(KERN_ERR "ucc-tdm: could not allocate buffer"
|
|
|
++ "descriptors\n");
|
|
|
++ ret = -ENOMEM;
|
|
|
++ goto alloc_error;
|
|
|
++ }
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->tdm_input_data = tdm_buff;
|
|
|
++ tdm_ctrl[device_num]->dma_input_addr = physaddr;
|
|
|
++
|
|
|
++ tdm_ctrl[device_num]->tdm_output_data = tdm_buff + NR_BUFS *
|
|
|
++ SAMPLE_DEPTH * tdm_ctrl[device_num]->cfg_ctrl.active_num_ts;
|
|
|
++ tdm_ctrl[device_num]->dma_output_addr = physaddr + NR_BUFS *
|
|
|
++ SAMPLE_DEPTH * tdm_ctrl[device_num]->cfg_ctrl.active_num_ts;
|
|
|
++
|
|
|
++ init_waitqueue_head(&(tdm_ctrl[device_num]->wakeup_event));
|
|
|
++
|
|
|
++ ret = tdm_init(tdm_ctrl[device_num]);
|
|
|
++ if (ret != 0)
|
|
|
++ goto tdm_init_error;
|
|
|
++
|
|
|
++ ret = tdm_start(tdm_ctrl[device_num]);
|
|
|
++ if (ret != 0)
|
|
|
++ goto tdm_start_error;
|
|
|
++
|
|
|
++ dev_set_drvdata(&(ofdev->dev), tdm_ctrl[device_num]);
|
|
|
++
|
|
|
++ pr_info("%s UCC based tdm module installed\n", __FUNCTION__);
|
|
|
++ return 0;
|
|
|
++
|
|
|
++tdm_start_error:
|
|
|
++ tdm_deinit(tdm_ctrl[device_num]);
|
|
|
++tdm_init_error:
|
|
|
++ dma_free_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
|
|
|
++ tdm_ctrl[device_num]->cfg_ctrl.active_num_ts,
|
|
|
++ tdm_ctrl[device_num]->tdm_input_data,
|
|
|
++ tdm_ctrl[device_num]->dma_input_addr);
|
|
|
++
|
|
|
++alloc_error:
|
|
|
++ irq_dispose_mapping(tdm_ctrl[device_num]->ut_info->uf_info.irq);
|
|
|
++ iounmap(tdm_ctrl[device_num]->uf_regs);
|
|
|
++
|
|
|
++get_property_error:
|
|
|
++ num_tdm_devices--;
|
|
|
++ kfree(tdm_ctrl[device_num]);
|
|
|
++ ut_info->ucc_busy = 0;
|
|
|
++ return ret;
|
|
|
++}
|
|
|
++
|
|
|
++static int ucc_tdm_remove(struct of_device *ofdev)
|
|
|
++{
|
|
|
++ struct tdm_ctrl *tdm_c;
|
|
|
++ struct ucc_tdm_info *ut_info;
|
|
|
++ u32 ucc_num;
|
|
|
++
|
|
|
++ tdm_c = dev_get_drvdata(&(ofdev->dev));
|
|
|
++ dev_set_drvdata(&(ofdev->dev), NULL);
|
|
|
++ ucc_num = tdm_c->ut_info->uf_info.ucc_num;
|
|
|
++ ut_info = &utdm_info[ucc_num];
|
|
|
++ tdm_stop(tdm_c);
|
|
|
++ tdm_deinit(tdm_c);
|
|
|
++
|
|
|
++ ucc_fast_free(tdm_c->uf_private);
|
|
|
++
|
|
|
++ dma_free_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
|
|
|
++ tdm_c->cfg_ctrl.active_num_ts,
|
|
|
++ tdm_c->tdm_input_data,
|
|
|
++ tdm_c->dma_input_addr);
|
|
|
++
|
|
|
++ irq_dispose_mapping(tdm_c->ut_info->uf_info.irq);
|
|
|
++ iounmap(tdm_c->uf_regs);
|
|
|
++
|
|
|
++ num_tdm_devices--;
|
|
|
++ kfree(tdm_c);
|
|
|
++
|
|
|
++ ut_info->ucc_busy = 0;
|
|
|
++
|
|
|
++ pr_info("%s UCC based tdm module uninstalled\n", __FUNCTION__);
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
++const struct of_device_id ucc_tdm_match[] = {
|
|
|
++ { .type = "tdm", .compatible = "fsl,ucc-tdm", },
|
|
|
++ {},
|
|
|
++};
|
|
|
++
|
|
|
++MODULE_DEVICE_TABLE(of, ucc_tdm_match);
|
|
|
++
|
|
|
++static struct of_platform_driver ucc_tdm_driver = {
|
|
|
++ .name = DRV_NAME,
|
|
|
++ .match_table = ucc_tdm_match,
|
|
|
++ .probe = ucc_tdm_probe,
|
|
|
++ .remove = ucc_tdm_remove,
|
|
|
++ .driver = {
|
|
|
++ .name = DRV_NAME,
|
|
|
++ .owner = THIS_MODULE,
|
|
|
++ },
|
|
|
++};
|
|
|
++
|
|
|
++static int __init ucc_tdm_init(void)
|
|
|
++{
|
|
|
++ u32 i;
|
|
|
++
|
|
|
++ pr_info("ucc_tdm: " DRV_DESC "\n");
|
|
|
++ for (i = 0; i < 8; i++)
|
|
|
++ memcpy(&(utdm_info[i]), &utdm_primary_info,
|
|
|
++ sizeof(utdm_primary_info));
|
|
|
++
|
|
|
++ return of_register_platform_driver(&ucc_tdm_driver);
|
|
|
++}
|
|
|
++
|
|
|
++static void __exit ucc_tdm_exit(void)
|
|
|
++{
|
|
|
++ of_unregister_platform_driver(&ucc_tdm_driver);
|
|
|
++}
|
|
|
++
|
|
|
++module_init(ucc_tdm_init);
|
|
|
++module_exit(ucc_tdm_exit);
|
|
|
++MODULE_AUTHOR("Freescale Semiconductor, Inc");
|
|
|
++MODULE_DESCRIPTION(DRV_DESC);
|
|
|
++MODULE_LICENSE("GPL");
|
|
|
+--- a/drivers/misc/Makefile
|
|
|
++++ b/drivers/misc/Makefile
|
|
|
+@@ -10,6 +10,7 @@ obj-$(CONFIG_INTEL_MID_PTI) += pti.o
|
|
|
+ obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
|
|
|
+ obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
|
|
|
+ obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
|
|
|
++obj-$(CONFIG_UCC_TDM) += ucc_tdm.o
|
|
|
+ obj-$(CONFIG_BMP085) += bmp085.o
|
|
|
+ obj-$(CONFIG_BMP085_I2C) += bmp085-i2c.o
|
|
|
+ obj-$(CONFIG_BMP085_SPI) += bmp085-spi.o
|
|
|
+--- a/drivers/misc/Kconfig
|
|
|
++++ b/drivers/misc/Kconfig
|
|
|
+@@ -210,6 +210,20 @@ config ATMEL_SSC
|
|
|
+
|
|
|
+ If unsure, say N.
|
|
|
+
|
|
|
++config UCC_TDM
|
|
|
++ tristate "Freescale UCC TDM Driver"
|
|
|
++ depends on QUICC_ENGINE && UCC_FAST
|
|
|
++ default n
|
|
|
++ help
|
|
|
++ The TDM driver is for UCC based TDM devices for example, TDM device on
|
|
|
++ MPC832x RDB. Select it to run PowerVoIP on MPC832x RDB board.
|
|
|
++ The TDM driver can interface with SLIC kind of devices to transmit
|
|
|
++ and receive TDM samples. The TDM driver receives Time Division
|
|
|
++ multiplexed samples(for different channels) from the SLIC device,
|
|
|
++ demutiplexes them and sends them to the upper layers. At the transmit
|
|
|
++ end the TDM drivers receives samples for different channels, it
|
|
|
++ multiplexes them and sends them to the SLIC device.
|
|
|
++
|
|
|
+ config ENCLOSURE_SERVICES
|
|
|
+ tristate "Enclosure Services"
|
|
|
+ default n
|
|
|
+--- a/arch/powerpc/include/asm/ucc_fast.h
|
|
|
++++ b/arch/powerpc/include/asm/ucc_fast.h
|
|
|
+@@ -150,6 +150,10 @@ struct ucc_fast_info {
|
|
|
+ enum ucc_fast_rx_decoding_method renc;
|
|
|
+ enum ucc_fast_transparent_tcrc tcrc;
|
|
|
+ enum ucc_fast_sync_len synl;
|
|
|
++ char *tdm_rx_clk;
|
|
|
++ char *tdm_tx_clk;
|
|
|
++ char *tdm_rx_sync;
|
|
|
++ char *tdm_tx_sync;
|
|
|
+ };
|
|
|
+
|
|
|
+ struct ucc_fast_private {
|
|
|
+--- a/arch/powerpc/include/asm/qe.h
|
|
|
++++ b/arch/powerpc/include/asm/qe.h
|
|
|
+@@ -670,6 +670,14 @@ struct ucc_slow_pram {
|
|
|
+ #define UCC_GETH_UCCE_RXF1 0x00000002
|
|
|
+ #define UCC_GETH_UCCE_RXF0 0x00000001
|
|
|
+
|
|
|
++/* Transparent UCC Event Register (UCCE) */
|
|
|
++#define UCC_TRANS_UCCE_GRA 0x0080
|
|
|
++#define UCC_TRANS_UCCE_TXE 0x0010
|
|
|
++#define UCC_TRANS_UCCE_RXF 0x0008
|
|
|
++#define UCC_TRANS_UCCE_BSY 0x0004
|
|
|
++#define UCC_TRANS_UCCE_TXB 0x0002
|
|
|
++#define UCC_TRANS_UCCE_RXB 0x0001
|
|
|
++
|
|
|
+ /* UCC Protocol Specific Mode Register (UPSMR), when used for UART */
|
|
|
+ #define UCC_UART_UPSMR_FLC 0x8000
|
|
|
+ #define UCC_UART_UPSMR_SL 0x4000
|