From fb2c10f9dcb4b4062ece23f5b903881db7a9f3a6 Mon Sep 17 00:00:00 2001 From: acoul Date: Thu, 24 Jun 2010 15:07:36 +0000 Subject: mpc83xx: add kernel 2.6.35 preliminary support git-svn-id: svn://svn.openwrt.org/openwrt/trunk@21896 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../linux/mpc83xx/patches-2.6.35/030-ucc_tdm.patch | 1307 ++++++++++++++++++++ 1 file changed, 1307 insertions(+) create mode 100644 target/linux/mpc83xx/patches-2.6.35/030-ucc_tdm.patch (limited to 'target/linux/mpc83xx/patches-2.6.35/030-ucc_tdm.patch') diff --git a/target/linux/mpc83xx/patches-2.6.35/030-ucc_tdm.patch b/target/linux/mpc83xx/patches-2.6.35/030-ucc_tdm.patch new file mode 100644 index 000000000..16689212d --- /dev/null +++ b/target/linux/mpc83xx/patches-2.6.35/030-ucc_tdm.patch @@ -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 ++#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 +@@ -8,6 +8,7 @@ obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot + 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_ICS932S401) += ics932s401.o + obj-$(CONFIG_LKDTM) += lkdtm.o + obj-$(CONFIG_TIFM_CORE) += tifm_core.o +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -164,6 +164,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 -- cgit v1.2.3