--- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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_ICS932S401) += ics932s401.o obj-$(CONFIG_LKDTM) += lkdtm.o --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -211,6 +211,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 @@ -669,6 +669,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