* [PATCH] UCC TDM driver for QE based MPC83xx platforms.
From: Poonam_Aggrwal-b10812 @ 2008-01-24 4:46 UTC (permalink / raw)
To: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev
Cc: michael.barkowski, rich.cutler, timur, ashish.kalra
From: Poonam Agarwal-b10812 <b10812@freescale.com>
The UCC TDM driver basically multiplexes and demultiplexes data from
different channels. It can interface with for example SLIC kind of devices
to receive TDM data demultiplex it and send to upper modules. At the
transmit end it receives data for different channels multiplexes it and
sends them on the TDM channel. It internally uses TSA( Time Slot Assigner)
which does multiplexing and demultiplexing, UCC to perform SDMA between
host buffers and the TSA, CMX to connect TSA to UCC.
It can be used by a kernel module which can call tdm_register_client to
get access to a TDM device.
The driver is right now a misc driver with no subsystem as such.
There can be a platform independent TDM layer which is planned to be
done after this. TDM bus sort of thing.
The dts file keeps a track of the TDM devices present on the board.
Depending on them the TDM driver initializes those many driver instances
while coming up.
The driver on the upper level can plug to more than one tdm clients
depending on the availablity of TDM devices. At every new request of a TDM
client to bind with a TDM device, a free driver instance is allocated to
the client.
The interface can be described as follows.
tdm_register_client(struct tdm_client *)
This API returns a pointer to the structure tdm_client which is of
type
struct tdm_client {
u32 client_id;
u32 (*tdm_read)(u32 client_id, short chn_id, short
*pcm_buffer, short len);
u32 (*tdm_write)(u32 client_id, short chn_id, short
*pcm_buffer, short len);
wait_queue_head_t *wakeup_event;
}
It consists of:
- client_id: It is basically to identify the particular TDM
device/driver instance.
- tdm_read: It is a function pointer returned by the TDM driver to be
used to read TDM data from a particular TDM channel.
- tdm_write: It is a function pointer returned by the TDM driver to be
used to write TDM data to a particular TDM channel.
- wakeup_event: It is address of a wait_queue event on which the client
keeps on sleeping, and the TDM driver wakes it up periodically. The driver
is configured to
wake up the client after every 10ms.
Once the TDM client gets registered to a TDM driver instance and a TDM
device, it interfaces with the driver using tdm_read, tdm_write and
wakeup_event.
This driver will run on MPC8323E-RDB platforms.
Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
drivers/misc/Kconfig | 14 +
drivers/misc/Makefile | 1 +
drivers/misc/ucc_tdm.c | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/misc/ucc_tdm.h | 221 +++++++++++
4 files changed, 1236 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/ucc_tdm.c
create mode 100644 drivers/misc/ucc_tdm.h
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b5e67c0..628b14b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -232,4 +232,18 @@ config ATMEL_SSC
If unsure, say N.
+config UCC_TDM
+ bool "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.
+
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 87f2685..6f0c49d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
+obj-$(CONFIG_UCC_TDM) += ucc_tdm.o
diff --git a/drivers/misc/ucc_tdm.c b/drivers/misc/ucc_tdm.c
new file mode 100644
index 0000000..98e7c72
--- /dev/null
+++ b/drivers/misc/ucc_tdm.c
@@ -0,0 +1,1000 @@
+/*
+ * 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 <linux/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 = NULL;
+ dma_addr_t physaddr;
+ void *tdm_buff;
+ struct ucc_tdm_info *ut_info;
+
+ prop = of_get_property(np, "device-id", NULL);
+ ucc_num = *prop - 1;
+ if ((ucc_num < 0) || (ucc_num > 7))
+ return -ENODEV;
+
+ ut_info = &utdm_info[ucc_num];
+ if (ut_info == NULL) {
+ printk(KERN_ERR "additional data missing\n");
+ return -ENODEV;
+ }
+ if (ut_info->ucc_busy) {
+ printk(KERN_ERR "UCC in use by another TDM driver instance\n");
+ return -EBUSY;
+ }
+ if (num_tdm_devices == MAX_NUM_TDM_DEVICES) {
+ printk(KERN_ERR "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 "%s: no memory to allocate for"
+ " tdm control structure\n", __FUNCTION__);
+ 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 =
+ (char *) 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 =
+ (char *) 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 =
+ (char *) 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 =
+ (char *) 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 = of_find_compatible_node(np_tmp, "slic", "legerity-slic");
+ if (np_tmp != NULL)
+ tdm_ctrl[device_num]->leg_slic = 1;
+ 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 get_property_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);
+
+get_property_error:
+ kfree(tdm_ctrl[device_num]);
+ 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));
+ 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);
+
+ num_tdm_devices--;
+ kfree(tdm_c);
+
+ ut_info->ucc_busy = 0;
+
+ pr_info("%s UCC based tdm module uninstalled\n", __FUNCTION__);
+ return 0;
+}
+
+static 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,
+};
+
+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");
diff --git a/drivers/misc/ucc_tdm.h b/drivers/misc/ucc_tdm.h
new file mode 100644
index 0000000..eaf2848
--- /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
--
1.5.2.4
^ permalink raw reply related
* [PATCH 2/3] Platform changes for UCC TDM driver for MPC8323ERDB.Also includes related QE changes and dts entries.
From: Poonam_Aggrwal-b10812 @ 2008-01-24 4:49 UTC (permalink / raw)
To: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev
Cc: michael.barkowski, rich.cutler, timur, ashish.kalra
From: Poonam Aggrwal <b10812@freescale.com>
This patch makes necessary changes in the QE and UCC framework to support
TDM. It also adds support to configure the BRG properly through device
tree entries. Includes the device tree changes for UCC TDM driver as well.
It also includes device tree entries for UCC TDM driver.
Tested on MPC8323ERDB platform.
Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
arch/powerpc/boot/dts/mpc832x_rdb.dts | 58 +++++++
arch/powerpc/sysdev/qe_lib/qe.c | 205 ++++++++++++++++++++++++--
arch/powerpc/sysdev/qe_lib/ucc.c | 265 +++++++++++++++++++++++++++++++++
arch/powerpc/sysdev/qe_lib/ucc_fast.c | 37 +++++
include/asm-powerpc/qe.h | 8 +
include/asm-powerpc/ucc.h | 4 +
include/asm-powerpc/ucc_fast.h | 4 +
7 files changed, 568 insertions(+), 13 deletions(-)
diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts
index 388c8a7..c0e6283 100644
--- a/arch/powerpc/boot/dts/mpc832x_rdb.dts
+++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts
@@ -105,6 +105,17 @@
device_type = "par_io";
num-ports = <7>;
+ ucc1pio:ucc_pin@01 {
+ pio-map = <
+ /* port pin dir open_drain assignment has_irq */
+ 0 e 2 0 1 0 /* CLK11 */
+ 3 16 1 0 2 0 /* BRG9 */
+ 3 1b 1 0 2 0 /* BRG3 */
+ 0 0 3 0 2 0 /* TDMATxD0 */
+ 0 4 3 0 2 0 /* TDMARxD0 */
+ 3 1b 2 0 1 0>; /* CLK1 */
+ };
+
ucc2pio:ucc_pin@02 {
pio-map = <
/* port pin dir open_drain assignment has_irq */
@@ -169,6 +180,36 @@
};
};
+ clocks {
+ compatible = "fsl,cpm-clocks";
+ /* clock freqs in Hz(for CLK1~CLK24).
+ * CLK11 is 1024KHz,
+ * all other clocks unused
+ * #clock-cells define number of cells
+ * used by the clock-frequency.
+ * right now only #clock cells=1 is
+ * implemented. Provision is there to
+ * handle frequencies >4Gig
+ */
+ #clock-cells = <1>;
+ clock-frequency = <0 0 0 0 0 0
+ 0 0 0 0 d#1024000 0
+ 0 0 0 0 0 0
+ 0 0 0 0 0 0>;
+ };
+
+ brg@640 {
+ compatible = "fsl,cpm-brg";
+ /* input clock sources for all the 16 BRGs.
+ * 1-24 for CLK1 to CLK24.
+ * BRG9 uses CLK11,BRG1 and BRG2-8 use
+ * the QE clock.
+ */
+ fsl,brg-sources = <0 0 0 0 0 0 0 0
+ b 0 0 0 0 0 0 0>;
+ reg = <640 7f>;
+ };
+
spi@4c0 {
device_type = "spi";
compatible = "fsl_spi";
@@ -187,6 +228,23 @@
mode = "cpu";
};
+ ucc@2000 {
+ device_type = "tdm";
+ compatible = "fsl,ucc-tdm";
+ model = "UCC";
+ device-id = <1>;
+ fsl,tdm-num = <1>;
+ fsl,si-num = <1>;
+ fsl,tdm-tx-clk = "CLK1";
+ fsl,tdm-rx-clk = "CLK1";
+ fsl,tdm-tx-sync = "BRG9";
+ fsl,tdm-rx-sync = "BRG9";
+ reg = <2000 200>;
+ interrupts = <20>;
+ interrupt-parent = <&qeic>;
+ pio-handle = <&ucc1pio>;
+ };
+
ucc@3000 {
device_type = "network";
compatible = "ucc_geth";
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index 1df3b4a..fddc3d8 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -149,20 +149,189 @@ EXPORT_SYMBOL(qe_issue_cmd);
*/
static unsigned int brg_clk = 0;
-unsigned int get_brg_clk(void)
+u32 get_brg_clk(enum qe_clock brgclk, enum qe_clock *brg_source)
{
- struct device_node *qe;
- if (brg_clk)
- return brg_clk;
+ struct device_node *qe, *brg, *clocks;
+ enum qe_clock brg_src;
+ u32 brg_input_freq = 0;
+ u32 brg_num;
+ int ret;
+ const unsigned int *prop;
- qe = of_find_node_by_type(NULL, "qe");
- if (qe) {
+ *brg_source = 0;
+
+ brg_num = brgclk - QE_BRG1;
+ brg = of_find_compatible_node(NULL, NULL, "fsl,cpm-brg");
+ if (brg) {
unsigned int size;
- const u32 *prop = of_get_property(qe, "brg-frequency", &size);
- brg_clk = *prop;
- of_node_put(qe);
- };
- return brg_clk;
+ prop = of_get_property(brg,
+ "fsl,brg-sources", &size);
+ of_node_put(brg);
+
+ if (prop)
+ brg_src = *(prop + brg_num);
+ else {
+ printk(KERN_ERR "%s: invalid fsl,brg-sources in device "
+ "tree\n", __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (brg_src == 0) {
+ *brg_source = 0;
+ if (brg_clk > 0)
+ return brg_clk;
+ qe = of_find_node_by_type(NULL, "qe");
+ if (qe) {
+ unsigned int size;
+ prop = of_get_property
+ (qe, "brg-frequency", &size);
+ if (!prop) {
+ printk(KERN_ERR "%s: QE brg-frequency"
+ "not present in device tree\n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ of_node_put(qe);
+ goto err;
+ }
+ if (*prop) {
+ of_node_put(qe);
+ brg_clk = *prop;
+ return *prop;
+ } else {
+ /*
+ * Older versions of U-Boot do not initialize
+ * the brg-frequency property, so in this case
+ * we assume the BRG frequency is half the QE
+ * bus frequency.
+ */
+ prop = of_get_property(qe,
+ "bus-frequency", NULL);
+ of_node_put(qe);
+ if (!prop) {
+ printk(KERN_ERR "%s: "
+ " QE bus-frequency not present"
+ " in device tree\n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (*prop) {
+ brg_clk = *prop / 2;
+ return brg_clk;
+ } else {
+ printk(KERN_ERR "%s: invalid"
+ " QE bus-frequency in device"
+ " tree\n", __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ } else {
+ printk(KERN_ERR "%s: no qe node in device tree"
+ "\n", __FUNCTION__);
+ ret = EINVAL;
+ goto err;
+ }
+ } else {
+ *brg_source = brg_src + QE_CLK1 - 1;
+ clocks = of_find_compatible_node(NULL, NULL,
+ "fsl,cpm-clocks");
+ if (!clocks) {
+ printk(KERN_ERR "%s: no clocks node in device"
+ " tree \n", __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ } else {
+ prop = of_get_property(clocks,
+ "#clock-cells", &size);
+ /*
+ * clock-cells = 1 only supported right now.
+ */
+ if (!prop || *prop != 1) {
+ printk(KERN_ERR "%s: invalid "
+ "#clock-cells value in device tree \n",
+ __FUNCTION__);
+ of_node_put(clocks);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ prop = of_get_property(clocks,
+ "clock-frequency", &size);
+ if (!prop) {
+ printk(KERN_ERR "%s: no"
+ " #clock-frequency prop in device"
+ " tree\n", __FUNCTION__);
+ of_node_put(clocks);
+ ret = -EINVAL;
+ goto err;
+ }
+ brg_input_freq = *(prop+(brg_src - 1));
+ of_node_put(clocks);
+ return brg_input_freq;
+ }
+ }
+ } else {
+ printk(KERN_ERR "%s: no brg node in device tree\n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+err: return ret;
+}
+
+u32 qe_brg_src(int brg_num, enum qe_clock brg_src)
+{
+ u32 clock_bits, shift;
+
+ clock_bits = 0;
+
+ switch (brg_num) {
+ case 1:
+ case 2:
+ case 5:
+ case 6:
+ switch (brg_src) {
+ case QE_CLK3: clock_bits = 1; break;
+ case QE_CLK5: clock_bits = 2; break;
+ default: break;
+ }
+ break;
+ case 3:
+ case 4:
+ case 7:
+ case 8:
+ switch (brg_src) {
+ case QE_CLK9: clock_bits = 1; break;
+ case QE_CLK15: clock_bits = 2; break;
+ default: break;
+ }
+ break;
+ case 9:
+ case 10:
+ switch (brg_src) {
+ case QE_CLK11: clock_bits = 1; break;
+ default: break;
+ }
+ break;
+ case 11:
+ case 15:
+ case 16:
+ switch (brg_src) {
+ case QE_CLK13: clock_bits = 1; break;
+ default: break;
+ }
+ break;
+ default: clock_bits = 0; break;
+ }
+ shift = 14;
+
+ if (!clock_bits)
+ return -ENOENT;
+
+ clock_bits <<= shift;
+
+ return clock_bits;
}
/* Program the BRG to the given sampling rate and multiplier
@@ -177,11 +346,18 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
{
u32 divisor, tempval;
u32 div16 = 0;
+ u32 brg_clock;
+ enum qe_clock brgsrc;
+ u32 src_bits = 0;
if ((brg < QE_BRG1) || (brg > QE_BRG16))
return -EINVAL;
- divisor = get_brg_clk() / (rate * multiplier);
+ brg_clock = get_brg_clk(brg, &brgsrc);
+ if (brg_clock < 0)
+ return -EINVAL;
+
+ divisor = brg_clock / (rate * multiplier);
if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
div16 = QE_BRGC_DIV16;
@@ -194,8 +370,11 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
if (!div16 && (divisor & 1))
divisor++;
+ if (brgsrc > 0)
+ src_bits = qe_brg_src(brg - QE_BRG1 + 1, brgsrc);
+
tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) |
- QE_BRGC_ENABLE | div16;
+ QE_BRGC_ENABLE | div16 | src_bits;
out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval);
diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c
index 0e348d9..f2de0ed 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc.c
@@ -213,3 +213,268 @@ int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
return 0;
}
+
+int ucc_set_tdm_rxtx_clk(int tdm_num, char *clk_src, enum comm_dir mode)
+{
+ enum qe_clock clock;
+ u32 clock_bits, shift;
+ struct qe_mux *qe_mux_reg = NULL;
+
+ clock_bits = 0;
+ qe_mux_reg = &qe_immr->qmx;
+
+ if ((tdm_num > 3 || tdm_num < 0))
+ return -EINVAL;
+
+ /* The communications direction must be RX or TX */
+ if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX)))
+ return -EINVAL;
+
+ clock = qe_clock_source(clk_src);
+ switch (mode) {
+ case COMM_DIR_RX:
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK3: clock_bits = 6; break;
+ case QE_CLK8: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 28;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK5: clock_bits = 6; break;
+ case QE_CLK10: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 24;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK7: clock_bits = 6; break;
+ case QE_CLK12: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 20;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK9: clock_bits = 6; break;
+ case QE_CLK14: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 16;
+ break;
+ default:
+ break;
+ }
+ break;
+ case COMM_DIR_TX:
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK4: clock_bits = 6; break;
+ case QE_CLK9: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 12;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK6: clock_bits = 6; break;
+ case QE_CLK11: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 8;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK8: clock_bits = 6; break;
+ case QE_CLK13: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 4;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK10: clock_bits = 6; break;
+ case QE_CLK15: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!clock_bits)
+ return -ENOENT;
+
+ clock_bits <<= shift;
+
+ qe_mux_reg->cmxsi1cr_l |= clock_bits;
+
+ return 0;
+}
+
+int ucc_set_tdm_rxtx_sync(int tdm_num, char *sync_src, enum comm_dir mode)
+{
+ enum qe_clock clock;
+ u32 shift, clock_bits;
+ struct qe_mux *qe_mux_reg = NULL;
+ int source;
+
+ source = -1;
+ qe_mux_reg = &qe_immr->qmx;
+
+ if ((tdm_num > 3 || tdm_num < 0))
+ return -EINVAL;
+
+ /* The communications direction must be RX or TX */
+ if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX)))
+ return -EINVAL;
+
+ switch (mode) {
+ case COMM_DIR_RX:
+ if (strcasecmp("RSYNC", sync_src) == 0) {
+ source = 0;
+ shift = 0;
+ break;
+ }
+ clock = qe_clock_source(sync_src);
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 30;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 28;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 26;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 24;
+ break;
+ default:
+ source = -1;
+ break;
+ }
+ break;
+ case COMM_DIR_TX:
+ if (strcasecmp("TSYNC", sync_src) == 0) {
+ source = 0;
+ shift = 0;
+ break;
+ }
+ clock = qe_clock_source(sync_src);
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 14;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 12;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 10;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 8;
+ break;
+ default:
+ source = -1;
+ break;
+ }
+ break;
+ default:
+ source = -1;
+ break;
+ }
+
+ if (source == -1)
+ return -ENOENT;
+
+ clock_bits = (u32) source;
+ clock_bits <<= shift;
+
+
+ qe_mux_reg->cmxsi1syr |= clock_bits;
+
+ return 0;
+}
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
index 3223acb..9c8559f 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
@@ -327,6 +327,43 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
ucc_fast_free(uccf);
return -EINVAL;
}
+ } else {
+ /* TDM Rx clock routing */
+ if ((uf_info->tdm_rx_clk != NULL) &&
+ ucc_set_tdm_rxtx_clk(uf_info->ucc_num,
+ uf_info->tdm_rx_clk, COMM_DIR_RX)) {
+ printk(KERN_ERR "%s: illegal value for TDM RX clock",
+ __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
+ /* TDM Tx clock routing */
+ if ((uf_info->tdm_tx_clk != NULL) &&
+ ucc_set_tdm_rxtx_clk(uf_info->ucc_num,
+ uf_info->tdm_tx_clk, COMM_DIR_TX)) {
+ printk(KERN_ERR "%s: illegal value for TDM TX clock",
+ __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
+ /* TDM Rx sync routing */
+ if ((uf_info->tdm_rx_sync != NULL) &&
+ ucc_set_tdm_rxtx_sync(uf_info->ucc_num,
+ uf_info->tdm_rx_sync, COMM_DIR_RX)) {
+ printk(KERN_ERR "%s: illegal value for TDM RX"
+ "Frame sync", __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
+ /* TDM Tx sync routing */
+ if ((uf_info->tdm_tx_sync != NULL) &&
+ ucc_set_tdm_rxtx_sync(uf_info->ucc_num,
+ uf_info->tdm_tx_sync, COMM_DIR_TX)) {
+ printk(KERN_ERR "%s: illegal value for TDM TX"
+ "Frame sync", __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
}
/* Set interrupt mask register at UCC level. */
diff --git a/include/asm-powerpc/qe.h b/include/asm-powerpc/qe.h
index bcf60be..51de236 100644
--- a/include/asm-powerpc/qe.h
+++ b/include/asm-powerpc/qe.h
@@ -497,6 +497,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
+
/* UPSMR, when used as a UART */
#define UCC_UART_UPSMR_FLC 0x8000
#define UCC_UART_UPSMR_SL 0x4000
diff --git a/include/asm-powerpc/ucc.h b/include/asm-powerpc/ucc.h
index 46b09ba..153db97 100644
--- a/include/asm-powerpc/ucc.h
+++ b/include/asm-powerpc/ucc.h
@@ -42,6 +42,10 @@ int ucc_set_qe_mux_mii_mng(unsigned int ucc_num);
int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
enum comm_dir mode);
+int ucc_set_tdm_rxtx_clk(int tdm_num, char *clk_src, enum comm_dir mode);
+
+int ucc_set_tdm_rxtx_sync(int tdm_num, char *clk_src, enum comm_dir mode);
+
int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask);
/* QE MUX clock routing for UCC
diff --git a/include/asm-powerpc/ucc_fast.h b/include/asm-powerpc/ucc_fast.h
index f529f70..d267983 100644
--- a/include/asm-powerpc/ucc_fast.h
+++ b/include/asm-powerpc/ucc_fast.h
@@ -152,6 +152,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 {
--
1.5.2.4
^ permalink raw reply related
* [PATCH UCC TDM 3/3 ] Modified Documentation to explain dts entries for TDM driver
From: Poonam_Aggrwal-b10812 @ 2008-01-24 4:54 UTC (permalink / raw)
To: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev
Cc: michael.barkowski, rich.cutler, timur, ashish.kalra
From: Poonam Aggrwal <b10812@freescale.com>
Modified Documentation to explain new properties introduced for UCC TDM
driver. Also two new nodes have been added "brg" and "clocks" to configure
a BRG from device tree.
Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
Documentation/powerpc/booting-without-of.txt | 96 +++++++++++++++++++++++++-
1 files changed, 94 insertions(+), 2 deletions(-)
diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index e9a3cb1..94a6b4b 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -1613,8 +1613,8 @@ platforms are moved over to use the flattened-device-tree model.
Required properties:
- device_type : should be "network", "hldc", "uart", "transparent"
- "bisync" or "atm".
- - compatible : could be "ucc_geth" or "fsl_atm" and so on.
+ "bisync", "atm" or "tdm".
+ - compatible : could be "ucc_geth", "fsl_atm" or "fsl,ucc_tdm" and so on.
- model : should be "UCC".
- device-id : the ucc number(1-8), corresponding to UCCx in UM.
- reg : Offset and length of the register set for the device
@@ -1666,7 +1666,44 @@ platforms are moved over to use the flattened-device-tree model.
pio-handle = <140001>;
};
+ Required properties for tdm device_type:
+ - instead of tx-clock and rx-clock following clock properties are
+ required:
+ - fsl,tdm-tx-clk : This property selects the TX clock source for TDM
+ from a bank of clocks.
+ - fsl,tdm-rx-clk : This property selects the RX clock source for TDM
+ from a bank of clocks.
+ - fsl,tdm-tx-sync : This property selects the TX Frame sync source
+ for TDM from a bank of clocks.
+ - fsl,tdm-rx-sync : This property selects the TX Frame sync source
+ for TDM from a bank of clocks.
+
+ All the above mentioned properties are string type with possible
+ values
+ "CLK1", "CLK2", "CLK3"..."CLK24" and so on
+ "BRG1", "BRG2", "BRG3"..."BRG16" and so on
+
+ - fsl,tdm-num : TDM to be used (1,2,3 or 4 for TDMA TDMB TDMC TDMD)
+ - fsl,si-num : Serial Interface to be used.
+ Example:
+ ucc@2000 {
+ device_type = "tdm";
+ compatible = "fsl,ucc-tdm";
+ model = "UCC";
+ device-id = <1>;
+ fsl,tdm-num = <1>;
+ fsl,si-num = <1>;
+ fsl,tdm-tx-clk = "CLK1";
+ fsl,tdm-rx-clk = "CLK1";
+ fsl,tdm-tx-sync = "BRG9";
+ fsl,tdm-rx-sync = "BRG9";
+ reg = <2000 200>;
+ interrupts = <20>;
+ interrupt-parent = <&qeic>;
+ pio-handle = <&ucc1pio>;
+ };
+
v) Parallel I/O Ports
This node configures Parallel I/O ports for CPUs with QE support.
@@ -1772,6 +1809,61 @@ platforms are moved over to use the flattened-device-tree model.
};
};
+ viii) Clocks (clocks)
+ This node specifies the frequency values for all the external clocks
+ viz CLK1 to CLK24 in Hz.
+
+ Required Properties:
+ - compatible : should be "fsl,cpm-clocks".
+ - #clock-cells : It specifies the number of cells occupied by clock-frequency
+ property. Currently #clock-cells = 1 is only supported and implemented.
+ This property is kept for future in case we need frequencies higher than
+ 4 GHz.
+ - clock-frequency : It is a list of u32 values to represent the frequency
+ of each external clock(CLK1 to CLK24) in Hz.Each entry occupies
+ number of cells specified by #clock-cells property(1 for now).
+
+ Example:
+
+ clocks {
+ compatible = "fsl,cpm-clocks";
+ #clock-cells = <1>;
+ /* clock freqs in Hz(for CLK1~CLK24).
+ * CLK11 is 1024KHz,
+ * all other clocks unused
+ */
+ clock-frequency = <0 0 0 0 0 0
+ 0 0 0 0 0 d#1024000 0
+ 0 0 0 0 0 0
+ 0 0 0 0 0 0>;
+ };
+
+ ix) Baud Rate Generator (BRG)
+
+ Required properties:
+ - compatible : shpuld be "fsl,cpm-brg"
+ - fsl,brg-sources : define the input clock for all 16 BRGs. The input
+ clock source could be 1 to 24 for CLK1 to CLK24. Zero means that the
+ particular BRG will be driven by QE clock(BRGCLK).
+ - reg : This property defines the address and size of the memory-mapped
+ registers of the BRG.
+
+ Example:
+
+ brg@640 {
+ compatible = "fsl,qe-brg";
+ /* input clock sources for all the 16 BRGs.
+ * 1-24 for CLK1 to CLK24.
+ * BRG9 uses CLK11 others use
+ * the QE clock.
+ */
+ fsl,brg-sources = <0 0 0 0 0 0 0 0
+ b 0 0 0 0 0 0 0>;
+ reg = <640 7f>;
+ };
+
+ In the above entry, for BRG9 the input clock is 0xb(decimal 11) ie QE_CLK11.
+
j) CFI or JEDEC memory-mapped NOR flash
Flash chips (Memory Technology Devices) are often used for solid state
--
1.5.2.4
^ permalink raw reply related
* [PATCH 6/6 v2] PS3: gelic: Add support for dual network interface
From: Masakazu Mokuno @ 2008-01-24 5:41 UTC (permalink / raw)
To: netdev; +Cc: linuxppc-dev
In-Reply-To: <20071213211332.BF8C.MOKUNO@sm.sony.co.jp>
Add support for dual network (net_device) interface so that ethernet
and wireless can own separate ethX interfaces.
V2
- Fix the bug that bringing down and up the interface keeps rx
disabled.
- Make 'gelic_net_poll_controller()' extern , as David Woodhouse
pointed out at the previous submission.
- Fix weird usage of member names for the rx descriptor chain
V1
- Export functions which are convenient for both interfaces
- Move irq allocation/release code to driver probe/remove handlers
because interfaces share interrupts.
- Allocate skbs by using dev_alloc_skb() instead of netdev_alloc_skb()
as the interfaces share the hardware rx queue.
- Add gelic_port struct in order to abstract dual interface handling
- Change handlers for hardware queues so that they can handle dual
{source,destination} interfaces.
- Use new NAPI functions
This is a prerequisite for the new PS3 wireless support.
Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
---
drivers/net/ps3_gelic_net.c | 765 +++++++++++++++++++++++++++-----------------
drivers/net/ps3_gelic_net.h | 108 +++++-
2 files changed, 564 insertions(+), 309 deletions(-)
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ps3_gelic_net.c
@@ -48,27 +48,22 @@
#include "ps3_gelic_net.h"
#define DRV_NAME "Gelic Network Driver"
-#define DRV_VERSION "1.0"
+#define DRV_VERSION "1.1"
MODULE_AUTHOR("SCE Inc.");
MODULE_DESCRIPTION("Gelic Network driver");
MODULE_LICENSE("GPL");
-static inline struct device *ctodev(struct gelic_card *card)
-{
- return &card->dev->core;
-}
-static inline u64 bus_id(struct gelic_card *card)
-{
- return card->dev->bus_id;
-}
-static inline u64 dev_id(struct gelic_card *card)
-{
- return card->dev->dev_id;
-}
+
+static inline void gelic_card_enable_rxdmac(struct gelic_card *card);
+static inline void gelic_card_disable_rxdmac(struct gelic_card *card);
+static inline void gelic_card_disable_txdmac(struct gelic_card *card);
+static inline void gelic_card_reset_chain(struct gelic_card *card,
+ struct gelic_descr_chain *chain,
+ struct gelic_descr *start_descr);
/* set irq_mask */
-static int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask)
+int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask)
{
int status;
@@ -76,20 +71,23 @@ static int gelic_card_set_irq_mask(struc
mask, 0);
if (status)
dev_info(ctodev(card),
- "lv1_net_set_interrupt_mask failed %d\n", status);
+ "%s failed %d\n", __func__, status);
return status;
}
+
static inline void gelic_card_rx_irq_on(struct gelic_card *card)
{
- gelic_card_set_irq_mask(card, card->ghiintmask | GELIC_CARD_RXINT);
+ card->irq_mask |= GELIC_CARD_RXINT;
+ gelic_card_set_irq_mask(card, card->irq_mask);
}
static inline void gelic_card_rx_irq_off(struct gelic_card *card)
{
- gelic_card_set_irq_mask(card, card->ghiintmask & ~GELIC_CARD_RXINT);
+ card->irq_mask &= ~GELIC_CARD_RXINT;
+ gelic_card_set_irq_mask(card, card->irq_mask);
}
-static void
-gelic_card_get_ether_port_status(struct gelic_card *card, int inform)
+static void gelic_card_get_ether_port_status(struct gelic_card *card,
+ int inform)
{
u64 v2;
struct net_device *ether_netdev;
@@ -100,7 +98,7 @@ gelic_card_get_ether_port_status(struct
&card->ether_port_status, &v2);
if (inform) {
- ether_netdev = card->netdev;
+ ether_netdev = card->netdev[GELIC_PORT_ETHERNET];
if (card->ether_port_status & GELIC_LV1_ETHER_LINK_UP)
netif_carrier_on(ether_netdev);
else
@@ -108,6 +106,48 @@ gelic_card_get_ether_port_status(struct
}
}
+void gelic_card_up(struct gelic_card *card)
+{
+ pr_debug("%s: called\n", __func__);
+ down(&card->updown_lock);
+ if (atomic_inc_return(&card->users) == 1) {
+ pr_debug("%s: real do\n", __func__);
+ /* enable irq */
+ gelic_card_set_irq_mask(card, card->irq_mask);
+ /* start rx */
+ gelic_card_enable_rxdmac(card);
+
+ napi_enable(&card->napi);
+ }
+ up(&card->updown_lock);
+ pr_debug("%s: done\n", __func__);
+}
+
+void gelic_card_down(struct gelic_card *card)
+{
+ u64 mask;
+ pr_debug("%s: called\n", __func__);
+ down(&card->updown_lock);
+ if (atomic_dec_if_positive(&card->users) == 0) {
+ pr_debug("%s: real do\n", __func__);
+ napi_disable(&card->napi);
+ /*
+ * Disable irq. Wireless interrupts will
+ * be disabled later if any
+ */
+ mask = card->irq_mask & (GELIC_CARD_WLAN_EVENT_RECEIVED |
+ GELIC_CARD_WLAN_COMMAND_COMPLETED);
+ gelic_card_set_irq_mask(card, mask);
+ /* stop rx */
+ gelic_card_disable_rxdmac(card);
+ gelic_card_reset_chain(card, &card->rx_chain,
+ card->descr + GELIC_NET_TX_DESCRIPTORS);
+ /* stop tx */
+ gelic_card_disable_txdmac(card);
+ }
+ up(&card->updown_lock);
+ pr_debug("%s: done\n", __func__);
+}
/**
* gelic_descr_get_status -- returns the status of a descriptor
@@ -133,8 +173,8 @@ static void gelic_descr_set_status(struc
enum gelic_descr_dma_status status)
{
descr->dmac_cmd_status = cpu_to_be32(status |
- (be32_to_cpu(descr->dmac_cmd_status) &
- ~GELIC_DESCR_DMA_STAT_MASK));
+ (be32_to_cpu(descr->dmac_cmd_status) &
+ ~GELIC_DESCR_DMA_STAT_MASK));
/*
* dma_cmd_status field is used to indicate whether the descriptor
* is valid or not.
@@ -225,6 +265,31 @@ iommu_error:
}
/**
+ * gelic_card_reset_chain - reset status of a descriptor chain
+ * @card: card structure
+ * @chain: address of chain
+ * @start_descr: address of descriptor array
+ *
+ * Reset the status of dma descriptors to ready state
+ * and re-initialize the hardware chain for later use
+ */
+static void gelic_card_reset_chain(struct gelic_card *card,
+ struct gelic_descr_chain *chain,
+ struct gelic_descr *start_descr)
+{
+ struct gelic_descr *descr;
+
+ for (descr = start_descr; start_descr != descr->next; descr++) {
+ gelic_descr_set_status(descr, GELIC_DESCR_DMA_CARDOWNED);
+ descr->next_descr_addr = cpu_to_be32(descr->next->bus_addr);
+ }
+
+ chain->head = start_descr;
+ chain->tail = (descr - 1);
+
+ (descr - 1)->next_descr_addr = 0;
+}
+/**
* gelic_descr_prepare_rx - reinitializes a rx descriptor
* @card: card structure
* @descr: descriptor to re-init
@@ -235,21 +300,19 @@ iommu_error:
* Activate the descriptor state-wise
*/
static int gelic_descr_prepare_rx(struct gelic_card *card,
- struct gelic_descr *descr)
+ struct gelic_descr *descr)
{
int offset;
unsigned int bufsize;
if (gelic_descr_get_status(descr) != GELIC_DESCR_DMA_NOT_IN_USE)
dev_info(ctodev(card), "%s: ERROR status \n", __func__);
-
/* we need to round up the buffer size to a multiple of 128 */
bufsize = ALIGN(GELIC_NET_MAX_MTU, GELIC_NET_RXBUF_ALIGN);
/* and we need to have it 128 byte aligned, therefore we allocate a
* bit more */
- descr->skb = netdev_alloc_skb(card->netdev,
- bufsize + GELIC_NET_RXBUF_ALIGN - 1);
+ descr->skb = dev_alloc_skb(bufsize + GELIC_NET_RXBUF_ALIGN - 1);
if (!descr->skb) {
descr->buf_addr = 0; /* tell DMAC don't touch memory */
dev_info(ctodev(card),
@@ -349,7 +412,7 @@ static int gelic_card_alloc_rx_skbs(stru
int ret;
chain = &card->rx_chain;
ret = gelic_card_fill_rx_chain(card);
- chain->head = card->rx_top->prev; /* point to the last */
+ chain->tail = card->rx_top->prev; /* point to the last */
return ret;
}
@@ -361,16 +424,14 @@ static int gelic_card_alloc_rx_skbs(stru
* releases a used tx descriptor (unmapping, freeing of skb)
*/
static void gelic_descr_release_tx(struct gelic_card *card,
- struct gelic_descr *descr)
+ struct gelic_descr *descr)
{
struct sk_buff *skb = descr->skb;
-#ifdef DEBUG
- BUG_ON(!(be32_to_cpu(descr->data_status) &
- (1 << GELIC_DESCR_TX_DMA_FRAME_TAIL)));
-#endif
- dma_unmap_single(ctodev(card),
- be32_to_cpu(descr->buf_addr), skb->len, DMA_TO_DEVICE);
+ BUG_ON(!(be32_to_cpu(descr->data_status) & GELIC_DESCR_TX_TAIL));
+
+ dma_unmap_single(ctodev(card), be32_to_cpu(descr->buf_addr), skb->len,
+ DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
descr->buf_addr = 0;
@@ -386,6 +447,20 @@ static void gelic_descr_release_tx(struc
gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE);
}
+static void gelic_card_stop_queues(struct gelic_card *card)
+{
+ netif_stop_queue(card->netdev[GELIC_PORT_ETHERNET]);
+
+ if (card->netdev[GELIC_PORT_WIRELESS])
+ netif_stop_queue(card->netdev[GELIC_PORT_WIRELESS]);
+}
+static void gelic_card_wake_queues(struct gelic_card *card)
+{
+ netif_wake_queue(card->netdev[GELIC_PORT_ETHERNET]);
+
+ if (card->netdev[GELIC_PORT_WIRELESS])
+ netif_wake_queue(card->netdev[GELIC_PORT_WIRELESS]);
+}
/**
* gelic_card_release_tx_chain - processes sent tx descriptors
* @card: adapter structure
@@ -397,12 +472,14 @@ static void gelic_card_release_tx_chain(
{
struct gelic_descr_chain *tx_chain;
enum gelic_descr_dma_status status;
+ struct net_device *netdev;
int release = 0;
for (tx_chain = &card->tx_chain;
tx_chain->head != tx_chain->tail && tx_chain->tail;
tx_chain->tail = tx_chain->tail->next) {
status = gelic_descr_get_status(tx_chain->tail);
+ netdev = tx_chain->tail->skb->dev;
switch (status) {
case GELIC_DESCR_DMA_RESPONSE_ERROR:
case GELIC_DESCR_DMA_PROTECTION_ERROR:
@@ -412,13 +489,13 @@ static void gelic_card_release_tx_chain(
"%s: forcing end of tx descriptor " \
"with status %x\n",
__func__, status);
- card->netdev->stats.tx_dropped++;
+ netdev->stats.tx_dropped++;
break;
case GELIC_DESCR_DMA_COMPLETE:
if (tx_chain->tail->skb) {
- card->netdev->stats.tx_packets++;
- card->netdev->stats.tx_bytes +=
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes +=
tx_chain->tail->skb->len;
}
break;
@@ -435,7 +512,7 @@ static void gelic_card_release_tx_chain(
}
out:
if (!stop && release)
- netif_wake_queue(card->netdev);
+ gelic_card_wake_queues(card);
}
/**
@@ -446,9 +523,9 @@ out:
* netdev interface. It also sets up multicast, allmulti and promisc
* flags appropriately
*/
-static void gelic_net_set_multi(struct net_device *netdev)
+void gelic_net_set_multi(struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
struct dev_mc_list *mc;
unsigned int i;
uint8_t *p;
@@ -470,8 +547,8 @@ static void gelic_net_set_multi(struct n
"lv1_net_add_multicast_address failed, %d\n",
status);
- if (netdev->flags & IFF_ALLMULTI
- || netdev->mc_count > GELIC_NET_MC_COUNT_MAX) { /* list max */
+ if ((netdev->flags & IFF_ALLMULTI) ||
+ (netdev->mc_count > GELIC_NET_MC_COUNT_MAX)) {
status = lv1_net_add_multicast_address(bus_id(card),
dev_id(card),
0, 1);
@@ -482,7 +559,7 @@ static void gelic_net_set_multi(struct n
return;
}
- /* set multicast address */
+ /* set multicast addresses */
for (mc = netdev->mc_list; mc; mc = mc->next) {
addr = 0;
p = mc->dmi_addr;
@@ -511,8 +588,19 @@ static inline void gelic_card_enable_rxd
{
int status;
+#ifdef DEBUG
+ if (gelic_descr_get_status(card->rx_chain.head) !=
+ GELIC_DESCR_DMA_CARDOWNED) {
+ printk(KERN_ERR "%s: status=%x\n", __func__,
+ be32_to_cpu(card->rx_chain.head->dmac_cmd_status));
+ printk(KERN_ERR "%s: nextphy=%x\n", __func__,
+ be32_to_cpu(card->rx_chain.head->next_descr_addr));
+ printk(KERN_ERR "%s: head=%p\n", __func__,
+ card->rx_chain.head);
+ }
+#endif
status = lv1_net_start_rx_dma(bus_id(card), dev_id(card),
- card->rx_chain.tail->bus_addr, 0);
+ card->rx_chain.head->bus_addr, 0);
if (status)
dev_info(ctodev(card),
"lv1_net_start_rx_dma failed, status=%d\n", status);
@@ -560,33 +648,19 @@ static inline void gelic_card_disable_tx
*
* always returns 0
*/
-static int gelic_net_stop(struct net_device *netdev)
+int gelic_net_stop(struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
-
- napi_disable(&card->napi);
- netif_stop_queue(netdev);
-
- /* turn off DMA, force end */
- gelic_card_disable_rxdmac(card);
- gelic_card_disable_txdmac(card);
-
- gelic_card_set_irq_mask(card, 0);
+ struct gelic_card *card;
- /* disconnect event port */
- free_irq(card->netdev->irq, card->netdev);
- ps3_sb_event_receive_port_destroy(card->dev, card->netdev->irq);
- card->netdev->irq = NO_IRQ;
+ pr_debug("%s: start\n", __func__);
+ netif_stop_queue(netdev);
netif_carrier_off(netdev);
- /* release chains */
- gelic_card_release_tx_chain(card, 1);
- gelic_card_release_rx_chain(card);
-
- gelic_card_free_chain(card, card->tx_top);
- gelic_card_free_chain(card, card->rx_top);
+ card = netdev_card(netdev);
+ gelic_card_down(card);
+ pr_debug("%s: done\n", __func__);
return 0;
}
@@ -612,7 +686,7 @@ gelic_card_get_next_tx_descr(struct geli
}
/**
- * gelic_descr_set_tx_cmdstat - sets the tx descriptor command field
+ * gelic_net_set_txdescr_cmdstat - sets the tx descriptor command field
* @descr: descriptor structure to fill out
* @skb: packet to consider
*
@@ -677,7 +751,7 @@ static inline struct sk_buff *gelic_put_
}
/**
- * gelic_descr_prepare_tx - get dma address of skb_data
+ * gelic_descr_prepare_tx - setup a descriptor for sending packets
* @card: card structure
* @descr: descriptor structure
* @skb: packet to use
@@ -691,10 +765,13 @@ static int gelic_descr_prepare_tx(struct
{
dma_addr_t buf;
- if (card->vlan_index != -1) {
+ if (card->vlan_required) {
struct sk_buff *skb_tmp;
+ enum gelic_port_type type;
+
+ type = netdev_port(skb->dev)->type;
skb_tmp = gelic_put_vlan_tag(skb,
- card->vlan_id[card->vlan_index]);
+ card->vlan[type].tx);
if (!skb_tmp)
return -ENOMEM;
skb = skb_tmp;
@@ -753,14 +830,14 @@ static int gelic_card_kick_txdma(struct
*
* returns 0 on success, <0 on failure
*/
-static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
+int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
struct gelic_descr *descr;
int result;
unsigned long flags;
- spin_lock_irqsave(&card->tx_dma_lock, flags);
+ spin_lock_irqsave(&card->tx_lock, flags);
gelic_card_release_tx_chain(card, 0);
@@ -769,8 +846,8 @@ static int gelic_net_xmit(struct sk_buff
/*
* no more descriptors free
*/
- netif_stop_queue(netdev);
- spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+ gelic_card_stop_queues(card);
+ spin_unlock_irqrestore(&card->tx_lock, flags);
return NETDEV_TX_BUSY;
}
@@ -780,9 +857,9 @@ static int gelic_net_xmit(struct sk_buff
* DMA map failed. As chanses are that failure
* would continue, just release skb and return
*/
- card->netdev->stats.tx_dropped++;
+ netdev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
- spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+ spin_unlock_irqrestore(&card->tx_lock, flags);
return NETDEV_TX_OK;
}
/*
@@ -800,7 +877,7 @@ static int gelic_net_xmit(struct sk_buff
* kick failed.
* release descriptors which were just prepared
*/
- card->netdev->stats.tx_dropped++;
+ netdev->stats.tx_dropped++;
gelic_descr_release_tx(card, descr);
gelic_descr_release_tx(card, descr->next);
card->tx_chain.tail = descr->next->next;
@@ -810,7 +887,7 @@ static int gelic_net_xmit(struct sk_buff
netdev->trans_start = jiffies;
}
- spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+ spin_unlock_irqrestore(&card->tx_lock, flags);
return NETDEV_TX_OK;
}
@@ -818,27 +895,27 @@ static int gelic_net_xmit(struct sk_buff
* gelic_net_pass_skb_up - takes an skb from a descriptor and passes it on
* @descr: descriptor to process
* @card: card structure
+ * @netdev: net_device structure to be passed packet
*
* iommu-unmaps the skb, fills out skb structure and passes the data to the
* stack. The descriptor state is not changed.
*/
static void gelic_net_pass_skb_up(struct gelic_descr *descr,
- struct gelic_card *card)
+ struct gelic_card *card,
+ struct net_device *netdev)
+
{
- struct sk_buff *skb;
- struct net_device *netdev;
+ struct sk_buff *skb = descr->skb;
u32 data_status, data_error;
data_status = be32_to_cpu(descr->data_status);
data_error = be32_to_cpu(descr->data_error);
- netdev = card->netdev;
/* unmap skb buffer */
- skb = descr->skb;
- dma_unmap_single(ctodev(card),
- be32_to_cpu(descr->buf_addr), GELIC_NET_MAX_MTU,
+ dma_unmap_single(ctodev(card), be32_to_cpu(descr->buf_addr),
+ GELIC_NET_MAX_MTU,
DMA_FROM_DEVICE);
- skb_put(skb, descr->valid_size ?
+ skb_put(skb, be32_to_cpu(descr->valid_size)?
be32_to_cpu(descr->valid_size) :
be32_to_cpu(descr->result_size));
if (!descr->valid_size)
@@ -866,8 +943,8 @@ static void gelic_net_pass_skb_up(struct
skb->ip_summed = CHECKSUM_NONE;
/* update netdevice statistics */
- card->netdev->stats.rx_packets++;
- card->netdev->stats.rx_bytes += skb->len;
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += skb->len;
/* pass skb up to stack */
netif_receive_skb(skb);
@@ -886,7 +963,8 @@ static int gelic_card_decode_one_descr(s
{
enum gelic_descr_dma_status status;
struct gelic_descr_chain *chain = &card->rx_chain;
- struct gelic_descr *descr = chain->tail;
+ struct gelic_descr *descr = chain->head;
+ struct net_device *netdev = NULL;
int dmac_chain_ended;
status = gelic_descr_get_status(descr);
@@ -903,12 +981,30 @@ static int gelic_card_decode_one_descr(s
return 0;
}
+ /* netdevice select */
+ if (card->vlan_required) {
+ unsigned int i;
+ u16 vid;
+ vid = *(u16 *)(descr->skb->data) & VLAN_VID_MASK;
+ for (i = 0; i < GELIC_PORT_MAX; i++) {
+ if (card->vlan[i].rx == vid) {
+ netdev = card->netdev[i];
+ break;
+ }
+ };
+ if (GELIC_PORT_MAX <= i) {
+ pr_info("%s: unknown packet vid=%x\n", __func__, vid);
+ goto refill;
+ }
+ } else
+ netdev = card->netdev[GELIC_PORT_ETHERNET];
+
if ((status == GELIC_DESCR_DMA_RESPONSE_ERROR) ||
(status == GELIC_DESCR_DMA_PROTECTION_ERROR) ||
(status == GELIC_DESCR_DMA_FORCE_END)) {
dev_info(ctodev(card), "dropping RX descriptor with state %x\n",
status);
- card->netdev->stats.rx_dropped++;
+ netdev->stats.rx_dropped++;
goto refill;
}
@@ -936,7 +1032,7 @@ static int gelic_card_decode_one_descr(s
}
/* ok, we've got a packet in descr */
- gelic_net_pass_skb_up(descr, card);
+ gelic_net_pass_skb_up(descr, card, netdev);
refill:
/*
* So that always DMAC can see the end
@@ -954,8 +1050,8 @@ refill:
*/
gelic_descr_prepare_rx(card, descr);
- chain->head = descr;
- chain->tail = descr->next;
+ chain->tail = descr;
+ chain->head = descr->next;
/*
* Set this descriptor the end of the chain.
@@ -976,17 +1072,15 @@ refill:
/**
* gelic_net_poll - NAPI poll function called by the stack to return packets
- * @netdev: interface device structure
+ * @napi: napi structure
* @budget: number of packets we can pass to the stack at most
*
- * returns 0 if no more packets available to the driver/stack. Returns 1,
- * if the quota is exceeded, but the driver has still packets.
+ * returns the number of the processed packets
*
*/
static int gelic_net_poll(struct napi_struct *napi, int budget)
{
struct gelic_card *card = container_of(napi, struct gelic_card, napi);
- struct net_device *netdev = card->netdev;
int packets_done = 0;
while (packets_done < budget) {
@@ -997,7 +1091,7 @@ static int gelic_net_poll(struct napi_st
}
if (packets_done < budget) {
- netif_rx_complete(netdev, napi);
+ napi_complete(napi);
gelic_card_rx_irq_on(card);
}
return packets_done;
@@ -1009,7 +1103,7 @@ static int gelic_net_poll(struct napi_st
*
* returns 0 on success, <0 on failure
*/
-static int gelic_net_change_mtu(struct net_device *netdev, int new_mtu)
+int gelic_net_change_mtu(struct net_device *netdev, int new_mtu)
{
/* no need to re-alloc skbs or so -- the max mtu is about 2.3k
* and mtu is outbound only anyway */
@@ -1027,8 +1121,7 @@ static int gelic_net_change_mtu(struct n
static irqreturn_t gelic_card_interrupt(int irq, void *ptr)
{
unsigned long flags;
- struct net_device *netdev = ptr;
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = ptr;
u64 status;
status = card->irq_status;
@@ -1036,6 +1129,8 @@ static irqreturn_t gelic_card_interrupt(
if (!status)
return IRQ_NONE;
+ status &= card->irq_mask;
+
if (card->rx_dma_restart_required) {
card->rx_dma_restart_required = 0;
gelic_card_enable_rxdmac(card);
@@ -1043,21 +1138,22 @@ static irqreturn_t gelic_card_interrupt(
if (status & GELIC_CARD_RXINT) {
gelic_card_rx_irq_off(card);
- netif_rx_schedule(netdev, &card->napi);
+ napi_schedule(&card->napi);
}
if (status & GELIC_CARD_TXINT) {
- spin_lock_irqsave(&card->tx_dma_lock, flags);
+ spin_lock_irqsave(&card->tx_lock, flags);
card->tx_dma_progress = 0;
gelic_card_release_tx_chain(card, 0);
/* kick outstanding tx descriptor if any */
gelic_card_kick_txdma(card, card->tx_chain.tail);
- spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+ spin_unlock_irqrestore(&card->tx_lock, flags);
}
/* ether port status changed */
if (status & GELIC_CARD_PORT_STATUS_CHANGED)
gelic_card_get_ether_port_status(card, 1);
+
return IRQ_HANDLED;
}
@@ -1068,55 +1164,17 @@ static irqreturn_t gelic_card_interrupt(
*
* see Documentation/networking/netconsole.txt
*/
-static void gelic_net_poll_controller(struct net_device *netdev)
+void gelic_net_poll_controller(struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
gelic_card_set_irq_mask(card, 0);
gelic_card_interrupt(netdev->irq, netdev);
- gelic_card_set_irq_mask(card, card->ghiintmask);
+ gelic_card_set_irq_mask(card, card->irq_mask);
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
/**
- * gelic_card_open - open device and map dma region
- * @card: card structure
- */
-static int gelic_card_open(struct gelic_card *card)
-{
- int result;
-
- result = ps3_sb_event_receive_port_setup(card->dev, PS3_BINDING_CPU_ANY,
- &card->netdev->irq);
-
- if (result) {
- dev_info(ctodev(card),
- "%s:%d: recieve_port_setup failed (%d)\n",
- __func__, __LINE__, result);
- result = -EPERM;
- goto fail_alloc_irq;
- }
-
- result = request_irq(card->netdev->irq, gelic_card_interrupt,
- IRQF_DISABLED, card->netdev->name, card->netdev);
-
- if (result) {
- dev_info(ctodev(card), "%s:%d: request_irq failed (%d)\n",
- __func__, __LINE__, result);
- goto fail_request_irq;
- }
-
- return 0;
-
-fail_request_irq:
- ps3_sb_event_receive_port_destroy(card->dev, card->netdev->irq);
- card->netdev->irq = NO_IRQ;
-fail_alloc_irq:
- return result;
-}
-
-
-/**
* gelic_net_open - called upon ifonfig up
* @netdev: interface device structure
*
@@ -1125,56 +1183,23 @@ fail_alloc_irq:
* gelic_net_open allocates all the descriptors and memory needed for
* operation, sets up multicast list and enables interrupts
*/
-static int gelic_net_open(struct net_device *netdev)
+int gelic_net_open(struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
-
- dev_dbg(ctodev(card), " -> %s:%d\n", __func__, __LINE__);
+ struct gelic_card *card = netdev_card(netdev);
- gelic_card_open(card);
+ dev_dbg(ctodev(card), " -> %s %p\n", __func__, netdev);
- if (gelic_card_init_chain(card, &card->tx_chain,
- card->descr, GELIC_NET_TX_DESCRIPTORS))
- goto alloc_tx_failed;
- if (gelic_card_init_chain(card, &card->rx_chain,
- card->descr + GELIC_NET_TX_DESCRIPTORS,
- GELIC_NET_RX_DESCRIPTORS))
- goto alloc_rx_failed;
-
- /* head of chain */
- card->tx_top = card->tx_chain.head;
- card->rx_top = card->rx_chain.head;
- dev_dbg(ctodev(card), "descr rx %p, tx %p, size %#lx, num %#x\n",
- card->rx_top, card->tx_top, sizeof(struct gelic_descr),
- GELIC_NET_RX_DESCRIPTORS);
- /* allocate rx skbs */
- if (gelic_card_alloc_rx_skbs(card))
- goto alloc_skbs_failed;
-
- napi_enable(&card->napi);
-
- card->tx_dma_progress = 0;
- card->ghiintmask = GELIC_CARD_RXINT | GELIC_CARD_TXINT |
- GELIC_CARD_PORT_STATUS_CHANGED;
-
- gelic_card_set_irq_mask(card, card->ghiintmask);
- gelic_card_enable_rxdmac(card);
+ gelic_card_up(card);
netif_start_queue(netdev);
gelic_card_get_ether_port_status(card, 1);
+ dev_dbg(ctodev(card), " <- %s\n", __func__);
return 0;
-
-alloc_skbs_failed:
- gelic_card_free_chain(card, card->rx_top);
-alloc_rx_failed:
- gelic_card_free_chain(card, card->tx_top);
-alloc_tx_failed:
- return -ENOMEM;
}
-static void gelic_net_get_drvinfo(struct net_device *netdev,
- struct ethtool_drvinfo *info)
+void gelic_net_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info)
{
strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1);
strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1);
@@ -1183,7 +1208,7 @@ static void gelic_net_get_drvinfo(struct
static int gelic_ether_get_settings(struct net_device *netdev,
struct ethtool_cmd *cmd)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
gelic_card_get_ether_port_status(card, 0);
@@ -1219,35 +1244,25 @@ static int gelic_ether_get_settings(stru
return 0;
}
-static int gelic_net_nway_reset(struct net_device *netdev)
+u32 gelic_net_get_rx_csum(struct net_device *netdev)
{
- if (netif_running(netdev)) {
- gelic_net_stop(netdev);
- gelic_net_open(netdev);
- }
- return 0;
-}
-
-static u32 gelic_net_get_rx_csum(struct net_device *netdev)
-{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
return card->rx_csum;
}
-static int gelic_net_set_rx_csum(struct net_device *netdev, u32 data)
+int gelic_net_set_rx_csum(struct net_device *netdev, u32 data)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
card->rx_csum = data;
return 0;
}
-static struct ethtool_ops gelic_net_ethtool_ops = {
+static struct ethtool_ops gelic_ether_ethtool_ops = {
.get_drvinfo = gelic_net_get_drvinfo,
.get_settings = gelic_ether_get_settings,
.get_link = ethtool_op_get_link,
- .nway_reset = gelic_net_nway_reset,
.get_tx_csum = ethtool_op_get_tx_csum,
.set_tx_csum = ethtool_op_set_tx_csum,
.get_rx_csum = gelic_net_get_rx_csum,
@@ -1265,7 +1280,7 @@ static void gelic_net_tx_timeout_task(st
{
struct gelic_card *card =
container_of(work, struct gelic_card, tx_timeout_task);
- struct net_device *netdev = card->netdev;
+ struct net_device *netdev = card->netdev[GELIC_PORT_ETHERNET];
dev_info(ctodev(card), "%s:Timed out. Restarting... \n", __func__);
@@ -1288,11 +1303,11 @@ out:
*
* called, if tx hangs. Schedules a task that resets the interface
*/
-static void gelic_net_tx_timeout(struct net_device *netdev)
+void gelic_net_tx_timeout(struct net_device *netdev)
{
struct gelic_card *card;
- card = netdev_priv(netdev);
+ card = netdev_card(netdev);
atomic_inc(&card->tx_timeout_task_counter);
if (netdev->flags & IFF_UP)
schedule_work(&card->tx_timeout_task);
@@ -1306,7 +1321,8 @@ static void gelic_net_tx_timeout(struct
*
* fills out function pointers in the net_device structure
*/
-static void gelic_ether_setup_netdev_ops(struct net_device *netdev)
+static void gelic_ether_setup_netdev_ops(struct net_device *netdev,
+ struct napi_struct *napi)
{
netdev->open = &gelic_net_open;
netdev->stop = &gelic_net_stop;
@@ -1316,86 +1332,63 @@ static void gelic_ether_setup_netdev_ops
/* tx watchdog */
netdev->tx_timeout = &gelic_net_tx_timeout;
netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT;
- netdev->ethtool_ops = &gelic_net_ethtool_ops;
+ /* NAPI */
+ netif_napi_add(netdev, napi,
+ gelic_net_poll, GELIC_NET_NAPI_WEIGHT);
+ netdev->ethtool_ops = &gelic_ether_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ netdev->poll_controller = gelic_net_poll_controller;
+#endif
}
/**
- * gelic_net_setup_netdev - initialization of net_device
+ * gelic_ether_setup_netdev - initialization of net_device
+ * @netdev: net_device structure
* @card: card structure
*
* Returns 0 on success or <0 on failure
*
- * gelic_net_setup_netdev initializes the net_device structure
+ * gelic_ether_setup_netdev initializes the net_device structure
+ * and register it.
**/
-static int gelic_net_setup_netdev(struct gelic_card *card)
+int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card)
{
- struct net_device *netdev = card->netdev;
- struct sockaddr addr;
- unsigned int i;
int status;
u64 v1, v2;
DECLARE_MAC_BUF(mac);
- SET_NETDEV_DEV(netdev, &card->dev->core);
- spin_lock_init(&card->tx_dma_lock);
-
- card->rx_csum = GELIC_NET_RX_CSUM_DEFAULT;
-
- gelic_ether_setup_netdev_ops(netdev);
-
- netif_napi_add(netdev, &card->napi,
- gelic_net_poll, GELIC_NET_NAPI_WEIGHT);
-
netdev->features = NETIF_F_IP_CSUM;
status = lv1_net_control(bus_id(card), dev_id(card),
GELIC_LV1_GET_MAC_ADDRESS,
0, 0, 0, &v1, &v2);
+ v1 <<= 16;
if (status || !is_valid_ether_addr((u8 *)&v1)) {
dev_info(ctodev(card),
"%s:lv1_net_control GET_MAC_ADDR failed %d\n",
__func__, status);
return -EINVAL;
}
- v1 <<= 16;
- memcpy(addr.sa_data, &v1, ETH_ALEN);
- memcpy(netdev->dev_addr, addr.sa_data, ETH_ALEN);
- dev_info(ctodev(card), "MAC addr %s\n",
- print_mac(mac, netdev->dev_addr));
-
- card->vlan_index = -1; /* no vlan */
- for (i = 0; i < GELIC_NET_VLAN_MAX; i++) {
- status = lv1_net_control(bus_id(card), dev_id(card),
- GELIC_LV1_GET_VLAN_ID,
- i + 1, /* index; one based */
- 0, 0, &v1, &v2);
- if (status == LV1_NO_ENTRY) {
- dev_dbg(ctodev(card),
- "GELIC_VLAN_ID no entry:%d, VLAN disabled\n",
- status);
- card->vlan_id[i] = 0;
- } else if (status) {
- dev_dbg(ctodev(card),
- "%s:get vlan id faild, status=%d\n",
- __func__, status);
- card->vlan_id[i] = 0;
- } else {
- card->vlan_id[i] = (u32)v1;
- dev_dbg(ctodev(card), "vlan_id:%d, %lx\n", i, v1);
- }
- }
+ memcpy(netdev->dev_addr, &v1, ETH_ALEN);
- if (card->vlan_id[GELIC_LV1_VLAN_TX_ETHERNET - 1]) {
- card->vlan_index = GELIC_LV1_VLAN_TX_ETHERNET - 1;
+ if (card->vlan_required) {
netdev->hard_header_len += VLAN_HLEN;
+ /*
+ * As vlan is internally used,
+ * we can not receive vlan packets
+ */
+ netdev->features |= NETIF_F_VLAN_CHALLENGED;
}
status = register_netdev(netdev);
if (status) {
- dev_err(ctodev(card), "%s:Couldn't register net_device: %d\n",
- __func__, status);
+ dev_err(ctodev(card), "%s:Couldn't register %s %d\n",
+ __func__, netdev->name, status);
return status;
}
+ dev_info(ctodev(card), "%s: MAC addr %s\n",
+ netdev->name,
+ print_mac(mac, netdev->dev_addr));
return 0;
}
@@ -1407,72 +1400,171 @@ static int gelic_net_setup_netdev(struct
*
* the card and net_device structures are linked to each other
*/
-static struct gelic_card *gelic_alloc_card_net(void)
+#define GELIC_ALIGN (32)
+static struct gelic_card *gelic_alloc_card_net(struct net_device **netdev)
{
- struct net_device *netdev;
struct gelic_card *card;
+ struct gelic_port *port;
+ void *p;
size_t alloc_size;
-
- alloc_size = sizeof(*card) +
- sizeof(struct gelic_descr) * GELIC_NET_RX_DESCRIPTORS +
- sizeof(struct gelic_descr) * GELIC_NET_TX_DESCRIPTORS;
/*
- * we assume private data is allocated 32 bytes (or more) aligned
- * so that gelic_descr should be 32 bytes aligned.
- * Current alloc_etherdev() does do it because NETDEV_ALIGN
- * is 32.
- * check this assumption here.
+ * gelic requires dma descriptor is 32 bytes aligned and
+ * the hypervisor requires irq_status is 8 bytes aligned.
*/
- BUILD_BUG_ON(NETDEV_ALIGN < 32);
BUILD_BUG_ON(offsetof(struct gelic_card, irq_status) % 8);
BUILD_BUG_ON(offsetof(struct gelic_card, descr) % 32);
+ alloc_size =
+ sizeof(struct gelic_card) +
+ sizeof(struct gelic_descr) * GELIC_NET_RX_DESCRIPTORS +
+ sizeof(struct gelic_descr) * GELIC_NET_TX_DESCRIPTORS +
+ GELIC_ALIGN - 1;
- netdev = alloc_etherdev(alloc_size);
- if (!netdev)
+ p = kzalloc(alloc_size, GFP_KERNEL);
+ if (!p)
return NULL;
+ card = PTR_ALIGN(p, GELIC_ALIGN);
+ card->unalign = p;
+
+ /*
+ * alloc netdev
+ */
+ *netdev = alloc_etherdev(sizeof(struct gelic_port));
+ if (!netdev) {
+ kfree(card->unalign);
+ return NULL;
+ }
+ port = netdev_priv(*netdev);
+
+ /* gelic_port */
+ port->netdev = *netdev;
+ port->card = card;
+ port->type = GELIC_PORT_ETHERNET;
+
+ /* gelic_card */
+ card->netdev[GELIC_PORT_ETHERNET] = *netdev;
- card = netdev_priv(netdev);
- card->netdev = netdev;
INIT_WORK(&card->tx_timeout_task, gelic_net_tx_timeout_task);
init_waitqueue_head(&card->waitq);
atomic_set(&card->tx_timeout_task_counter, 0);
+ init_MUTEX(&card->updown_lock);
+ atomic_set(&card->users, 0);
return card;
}
+static void gelic_card_get_vlan_info(struct gelic_card *card)
+{
+ u64 v1, v2;
+ int status;
+ unsigned int i;
+ struct {
+ int tx;
+ int rx;
+ } vlan_id_ix[2] = {
+ [GELIC_PORT_ETHERNET] = {
+ .tx = GELIC_LV1_VLAN_TX_ETHERNET,
+ .rx = GELIC_LV1_VLAN_RX_ETHERNET
+ },
+ [GELIC_PORT_WIRELESS] = {
+ .tx = GELIC_LV1_VLAN_TX_WIRELESS,
+ .rx = GELIC_LV1_VLAN_RX_WIRELESS
+ }
+ };
+
+ for (i = 0; i < ARRAY_SIZE(vlan_id_ix); i++) {
+ /* tx tag */
+ status = lv1_net_control(bus_id(card), dev_id(card),
+ GELIC_LV1_GET_VLAN_ID,
+ vlan_id_ix[i].tx,
+ 0, 0, &v1, &v2);
+ if (status || !v1) {
+ if (status != LV1_NO_ENTRY)
+ dev_dbg(ctodev(card),
+ "get vlan id for tx(%d) failed(%d)\n",
+ vlan_id_ix[i].tx, status);
+ card->vlan[i].tx = 0;
+ card->vlan[i].rx = 0;
+ continue;
+ }
+ card->vlan[i].tx = (u16)v1;
+
+ /* rx tag */
+ status = lv1_net_control(bus_id(card), dev_id(card),
+ GELIC_LV1_GET_VLAN_ID,
+ vlan_id_ix[i].rx,
+ 0, 0, &v1, &v2);
+ if (status || !v1) {
+ if (status != LV1_NO_ENTRY)
+ dev_info(ctodev(card),
+ "get vlan id for rx(%d) failed(%d)\n",
+ vlan_id_ix[i].rx, status);
+ card->vlan[i].tx = 0;
+ card->vlan[i].rx = 0;
+ continue;
+ }
+ card->vlan[i].rx = (u16)v1;
+
+ dev_dbg(ctodev(card), "vlan_id[%d] tx=%02x rx=%02x\n",
+ i, card->vlan[i].tx, card->vlan[i].rx);
+ }
+
+ if (card->vlan[GELIC_PORT_ETHERNET].tx) {
+ BUG_ON(!card->vlan[GELIC_PORT_WIRELESS].tx);
+ card->vlan_required = 1;
+ } else
+ card->vlan_required = 0;
+
+ /* check wirelss capable firmware */
+ if (ps3_compare_firmware_version(1, 6, 0) < 0) {
+ card->vlan[GELIC_PORT_WIRELESS].tx = 0;
+ card->vlan[GELIC_PORT_WIRELESS].rx = 0;
+ }
+
+ dev_info(ctodev(card), "internal vlan %s\n",
+ card->vlan_required? "enabled" : "disabled");
+}
/**
* ps3_gelic_driver_probe - add a device to the control of this driver
*/
static int ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
{
- struct gelic_card *card = gelic_alloc_card_net();
+ struct gelic_card *card;
+ struct net_device *netdev;
int result;
- if (!card) {
- dev_info(&dev->core, "gelic_net_alloc_card failed\n");
- result = -ENOMEM;
- goto fail_alloc_card;
- }
-
- ps3_system_bus_set_driver_data(dev, card);
- card->dev = dev;
-
+ pr_debug("%s: called\n", __func__);
result = ps3_open_hv_device(dev);
if (result) {
- dev_dbg(&dev->core, "ps3_open_hv_device failed\n");
+ dev_dbg(&dev->core, "%s:ps3_open_hv_device failed\n",
+ __func__);
goto fail_open;
}
result = ps3_dma_region_create(dev->d_region);
if (result) {
- dev_dbg(&dev->core, "ps3_dma_region_create failed(%d)\n",
- result);
+ dev_dbg(&dev->core, "%s:ps3_dma_region_create failed(%d)\n",
+ __func__, result);
BUG_ON("check region type");
goto fail_dma_region;
}
+ /* alloc card/netdevice */
+ card = gelic_alloc_card_net(&netdev);
+ if (!card) {
+ dev_info(&dev->core, "%s:gelic_net_alloc_card failed\n",
+ __func__);
+ result = -ENOMEM;
+ goto fail_alloc_card;
+ }
+ ps3_system_bus_set_driver_data(dev, card);
+ card->dev = dev;
+
+ /* get internal vlan info */
+ gelic_card_get_vlan_info(card);
+
+ /* setup interrupt */
result = lv1_net_set_interrupt_status_indicator(bus_id(card),
dev_id(card),
ps3_mm_phys_to_lpar(__pa(&card->irq_status)),
@@ -1480,34 +1572,95 @@ static int ps3_gelic_driver_probe(struct
if (result) {
dev_dbg(&dev->core,
- "lv1_net_set_interrupt_status_indicator failed: %s\n",
- ps3_result(result));
+ "%s:set_interrupt_status_indicator failed: %s\n",
+ __func__, ps3_result(result));
result = -EIO;
goto fail_status_indicator;
}
- result = gelic_net_setup_netdev(card);
+ result = ps3_sb_event_receive_port_setup(dev, PS3_BINDING_CPU_ANY,
+ &card->irq);
+
+ if (result) {
+ dev_info(ctodev(card),
+ "%s:gelic_net_open_device failed (%d)\n",
+ __func__, result);
+ result = -EPERM;
+ goto fail_alloc_irq;
+ }
+ result = request_irq(card->irq, gelic_card_interrupt,
+ IRQF_DISABLED, netdev->name, card);
+
+ if (result) {
+ dev_info(ctodev(card), "%s:request_irq failed (%d)\n",
+ __func__, result);
+ goto fail_request_irq;
+ }
+
+ /* setup card structure */
+ card->irq_mask = GELIC_CARD_RXINT | GELIC_CARD_TXINT |
+ GELIC_CARD_PORT_STATUS_CHANGED;
+ card->rx_csum = GELIC_CARD_RX_CSUM_DEFAULT;
+
+
+ if (gelic_card_init_chain(card, &card->tx_chain,
+ card->descr, GELIC_NET_TX_DESCRIPTORS))
+ goto fail_alloc_tx;
+ if (gelic_card_init_chain(card, &card->rx_chain,
+ card->descr + GELIC_NET_TX_DESCRIPTORS,
+ GELIC_NET_RX_DESCRIPTORS))
+ goto fail_alloc_rx;
+
+ /* head of chain */
+ card->tx_top = card->tx_chain.head;
+ card->rx_top = card->rx_chain.head;
+ dev_dbg(ctodev(card), "descr rx %p, tx %p, size %#lx, num %#x\n",
+ card->rx_top, card->tx_top, sizeof(struct gelic_descr),
+ GELIC_NET_RX_DESCRIPTORS);
+ /* allocate rx skbs */
+ if (gelic_card_alloc_rx_skbs(card))
+ goto fail_alloc_skbs;
+
+ spin_lock_init(&card->tx_lock);
+ card->tx_dma_progress = 0;
+ /* setup net_device structure */
+ netdev->irq = card->irq;
+ SET_NETDEV_DEV(netdev, &card->dev->core);
+ gelic_ether_setup_netdev_ops(netdev, &card->napi);
+ result = gelic_net_setup_netdev(netdev, card);
if (result) {
- dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
- "(%d)\n", __func__, __LINE__, result);
+ dev_dbg(&dev->core, "%s: setup_netdev failed %d",
+ __func__, result);
goto fail_setup_netdev;
}
+ pr_debug("%s: done\n", __func__);
return 0;
fail_setup_netdev:
+fail_alloc_skbs:
+ gelic_card_free_chain(card, card->rx_chain.head);
+fail_alloc_rx:
+ gelic_card_free_chain(card, card->tx_chain.head);
+fail_alloc_tx:
+ free_irq(card->irq, card);
+ netdev->irq = NO_IRQ;
+fail_request_irq:
+ ps3_sb_event_receive_port_destroy(dev, card->irq);
+fail_alloc_irq:
lv1_net_set_interrupt_status_indicator(bus_id(card),
- dev_id(card),
- 0 , 0);
+ bus_id(card),
+ 0, 0);
fail_status_indicator:
+ ps3_system_bus_set_driver_data(dev, NULL);
+ kfree(netdev_card(netdev)->unalign);
+ free_netdev(netdev);
+fail_alloc_card:
ps3_dma_region_free(dev->d_region);
fail_dma_region:
ps3_close_hv_device(dev);
fail_open:
- ps3_system_bus_set_driver_data(dev, NULL);
- free_netdev(card->netdev);
-fail_alloc_card:
return result;
}
@@ -1518,6 +1671,28 @@ fail_alloc_card:
static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
{
struct gelic_card *card = ps3_system_bus_get_driver_data(dev);
+ struct net_device *netdev0;
+ pr_debug("%s: called\n", __func__);
+
+ /* stop interrupt */
+ gelic_card_set_irq_mask(card, 0);
+
+ /* turn off DMA, force end */
+ gelic_card_disable_rxdmac(card);
+ gelic_card_disable_txdmac(card);
+
+ /* release chains */
+ gelic_card_release_tx_chain(card, 1);
+ gelic_card_release_rx_chain(card);
+
+ gelic_card_free_chain(card, card->tx_top);
+ gelic_card_free_chain(card, card->rx_top);
+
+ netdev0 = card->netdev[GELIC_PORT_ETHERNET];
+ /* disconnect event port */
+ free_irq(card->irq, card);
+ netdev0->irq = NO_IRQ;
+ ps3_sb_event_receive_port_destroy(card->dev, card->irq);
wait_event(card->waitq,
atomic_read(&card->tx_timeout_task_counter) == 0);
@@ -1525,8 +1700,9 @@ static int ps3_gelic_driver_remove(struc
lv1_net_set_interrupt_status_indicator(bus_id(card), dev_id(card),
0 , 0);
- unregister_netdev(card->netdev);
- free_netdev(card->netdev);
+ unregister_netdev(netdev0);
+ kfree(netdev_card(netdev0)->unalign);
+ free_netdev(netdev0);
ps3_system_bus_set_driver_data(dev, NULL);
@@ -1534,6 +1710,7 @@ static int ps3_gelic_driver_remove(struc
ps3_close_hv_device(dev);
+ pr_debug("%s: done\n", __func__);
return 0;
}
--- a/drivers/net/ps3_gelic_net.h
+++ b/drivers/net/ps3_gelic_net.h
@@ -35,12 +35,11 @@
#define GELIC_NET_MAX_MTU VLAN_ETH_FRAME_LEN
#define GELIC_NET_MIN_MTU VLAN_ETH_ZLEN
#define GELIC_NET_RXBUF_ALIGN 128
-#define GELIC_NET_RX_CSUM_DEFAULT 1 /* hw chksum */
+#define GELIC_CARD_RX_CSUM_DEFAULT 1 /* hw chksum */
#define GELIC_NET_WATCHDOG_TIMEOUT 5*HZ
#define GELIC_NET_NAPI_WEIGHT (GELIC_NET_RX_DESCRIPTORS)
#define GELIC_NET_BROADCAST_ADDR 0xffffffffffffL
-#define GELIC_NET_VLAN_POS (VLAN_ETH_ALEN * 2)
-#define GELIC_NET_VLAN_MAX 4
+
#define GELIC_NET_MC_COUNT_MAX 32 /* multicast address list */
/* virtual interrupt status register bits */
@@ -206,6 +205,13 @@ enum gelic_lv1_vlan_index {
/* size of hardware part of gelic descriptor */
#define GELIC_DESCR_SIZE (32)
+
+enum gelic_port_type {
+ GELIC_PORT_ETHERNET = 0,
+ GELIC_PORT_WIRELESS = 1,
+ GELIC_PORT_MAX
+};
+
struct gelic_descr {
/* as defined by the hardware */
__be32 buf_addr;
@@ -222,7 +228,6 @@ struct gelic_descr {
dma_addr_t bus_addr;
struct gelic_descr *next;
struct gelic_descr *prev;
- struct vlan_ethhdr vlan;
} __attribute__((aligned(32)));
struct gelic_descr_chain {
@@ -231,43 +236,116 @@ struct gelic_descr_chain {
struct gelic_descr *tail;
};
+struct gelic_vlan_id {
+ u16 tx;
+ u16 rx;
+};
+
struct gelic_card {
- struct net_device *netdev;
struct napi_struct napi;
+ struct net_device *netdev[GELIC_PORT_MAX];
/*
* hypervisor requires irq_status should be
* 8 bytes aligned, but u64 member is
* always disposed in that manner
*/
u64 irq_status;
- u64 ghiintmask;
+ u64 irq_mask;
struct ps3_system_bus_device *dev;
- u32 vlan_id[GELIC_NET_VLAN_MAX];
- int vlan_index;
+ struct gelic_vlan_id vlan[GELIC_PORT_MAX];
+ int vlan_required;
struct gelic_descr_chain tx_chain;
struct gelic_descr_chain rx_chain;
int rx_dma_restart_required;
- /* gurad dmac descriptor chain*/
- spinlock_t chain_lock;
-
int rx_csum;
- /* guard tx_dma_progress */
- spinlock_t tx_dma_lock;
+ /*
+ * tx_lock guards tx descriptor list and
+ * tx_dma_progress.
+ */
+ spinlock_t tx_lock;
int tx_dma_progress;
struct work_struct tx_timeout_task;
atomic_t tx_timeout_task_counter;
wait_queue_head_t waitq;
+ /* only first user should up the card */
+ struct semaphore updown_lock;
+ atomic_t users;
+
u64 ether_port_status;
+ /* original address returned by kzalloc */
+ void *unalign;
+ /*
+ * each netdevice has copy of irq
+ */
+ unsigned int irq;
struct gelic_descr *tx_top, *rx_top;
- struct gelic_descr descr[0];
+ struct gelic_descr descr[0]; /* must be the last */
};
+struct gelic_port {
+ struct gelic_card *card;
+ struct net_device *netdev;
+ enum gelic_port_type type;
+ long priv[0]; /* long for alignment */
+};
-extern unsigned long p_to_lp(long pa);
+static inline struct gelic_card *port_to_card(struct gelic_port *p)
+{
+ return p->card;
+}
+static inline struct net_device *port_to_netdev(struct gelic_port *p)
+{
+ return p->netdev;
+}
+static inline struct gelic_card *netdev_card(struct net_device *d)
+{
+ return ((struct gelic_port *)netdev_priv(d))->card;
+}
+static inline struct gelic_port *netdev_port(struct net_device *d)
+{
+ return (struct gelic_port *)netdev_priv(d);
+}
+static inline struct device *ctodev(struct gelic_card *card)
+{
+ return &card->dev->core;
+}
+static inline u64 bus_id(struct gelic_card *card)
+{
+ return card->dev->bus_id;
+}
+static inline u64 dev_id(struct gelic_card *card)
+{
+ return card->dev->dev_id;
+}
+
+static inline void *port_priv(struct gelic_port *port)
+{
+ return port->priv;
+}
+
+extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask);
+/* shared netdev ops */
+extern void gelic_card_up(struct gelic_card *card);
+extern void gelic_card_down(struct gelic_card *card);
+extern int gelic_net_open(struct net_device *netdev);
+extern int gelic_net_stop(struct net_device *netdev);
+extern int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev);
+extern void gelic_net_set_multi(struct net_device *netdev);
+extern void gelic_net_tx_timeout(struct net_device *netdev);
+extern int gelic_net_change_mtu(struct net_device *netdev, int new_mtu);
+extern int gelic_net_setup_netdev(struct net_device *netdev,
+ struct gelic_card *card);
+
+/* shared ethtool ops */
+extern void gelic_net_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info);
+extern u32 gelic_net_get_rx_csum(struct net_device *netdev);
+extern int gelic_net_set_rx_csum(struct net_device *netdev, u32 data);
+extern void gelic_net_poll_controller(struct net_device *netdev);
#endif /* _GELIC_NET_H */
^ permalink raw reply
* [PATCH] [POWERPC] Always build setup-bus.c on powerpc
From: Kumar Gala @ 2008-01-24 6:07 UTC (permalink / raw)
To: Greg KH; +Cc: linuxppc-dev, linux-pci, Paul Mackerras, linux-kernel
The common arch/powerpc code calls in to functions in setup-bus.c
so some builds of ppc32 would fail.
Note, ppc32 usage of setup-irq.c is limited to arch/ppc and should be
removed when arch/ppc goes away.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
Greg, recent changes that BenH has made to the arch/powerpc pci code make
this necessary. If you don't have an issue I'd like this patch to go via
the paulus's powerpc.git tree.
- k
drivers/pci/Makefile | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 945bf02..869d689 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -31,8 +31,8 @@ obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o
obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o
obj-$(CONFIG_PARISC) += setup-bus.o
obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o
-obj-$(CONFIG_PPC32) += setup-irq.o setup-bus.o
-obj-$(CONFIG_PPC64) += setup-bus.o
+obj-$(CONFIG_PPC) += setup-bus.o
+obj-$(CONFIG_PPC32) += setup-irq.o
obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
obj-$(CONFIG_X86_VISWS) += setup-irq.o
--
1.5.3.7
^ permalink raw reply related
* Re: [PATCH] UCC TDM driver for QE based MPC83xx platforms.
From: Stephen Rothwell @ 2008-01-24 6:19 UTC (permalink / raw)
To: Poonam_Aggrwal-b10812
Cc: michael.barkowski, netdev, kumar.gala, linux-kernel, rubini,
linuxppc-dev, ashish.kalra, rich.cutler, akpm, timur
In-Reply-To: <Pine.LNX.4.64.0801241014240.27491@linux121>
[-- Attachment #1: Type: text/plain, Size: 3620 bytes --]
On Thu, 24 Jan 2008 10:16:42 +0530 (IST) Poonam_Aggrwal-b10812 <b10812@freescale.com> wrote:
>
> +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 = NULL;
You don't need to initialise this.
> + dma_addr_t physaddr;
> + void *tdm_buff;
> + struct ucc_tdm_info *ut_info;
> +
> + prop = of_get_property(np, "device-id", NULL);
You should check for (prop == NULL).
> + ucc_num = *prop - 1;
> + if ((ucc_num < 0) || (ucc_num > 7))
> + return -ENODEV;
> +
> + ut_info = &utdm_info[ucc_num];
> + if (ut_info == NULL) {
This cannot be NULL as you have just taken the address of an array
element.
> + tdm_ctrl[device_num]->ut_info = ut_info;
> +
> + tdm_ctrl[device_num]->ut_info->uf_info.ucc_num = ucc_num;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is the same as "ut_info".
> + tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_clk =
> + (char *) of_get_property(np, "fsl,tdm-tx-clk", NULL);
^
We don't normall put spaces here.
> + tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_clk =
> + (char *) of_get_property(np, "fsl,tdm-rx-clk", NULL);
^
Ditto. And later as well.
> + 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;
This should be -EINVAL or err.
> + goto get_property_error;
You need to do something about unmapping the irq in the error path.
> + tdm_ctrl[device_num]->uf_regs = of_iomap(np, 0);
> +
> + np_tmp = of_find_compatible_node(np_tmp, "slic", "legerity-slic");
> + if (np_tmp != NULL)
> + tdm_ctrl[device_num]->leg_slic = 1;
> + else
> + tdm_ctrl[device_num]->leg_slic = 0;
of_node_ut(np_tmp);
> + 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 get_property_error;
You need to unmap the uf_regs in the error path.
> +get_property_error:
> + kfree(tdm_ctrl[device_num]);
Do you need to set "tdm_ctrl[device_num]" to NULL and decrement
num_tdm_devices?
> + 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(&of_dev->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);
> +
You need to unmap the uf_reg and the irq.
> +static struct of_device_id ucc_tdm_match[] = {
const, please.
> + {
> + .type = "tdm",
> + .compatible = "fsl,ucc-tdm",
> + }, {},
We euld normall format this like:
{ .type = "tdm", .compatible = "fsl,ucc-tdm", },
{},
> +static struct of_platform_driver ucc_tdm_driver = {
.driver = {
> + .name = DRV_NAME,
},
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [PATCH] [POWERPC] Always build setup-bus.c on powerpc
From: Greg KH @ 2008-01-24 6:29 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev, linux-pci, Paul Mackerras, linux-kernel
In-Reply-To: <Pine.LNX.4.64.0801240006380.21347@blarg.am.freescale.net>
On Thu, Jan 24, 2008 at 12:07:32AM -0600, Kumar Gala wrote:
> The common arch/powerpc code calls in to functions in setup-bus.c
> so some builds of ppc32 would fail.
>
> Note, ppc32 usage of setup-irq.c is limited to arch/ppc and should be
> removed when arch/ppc goes away.
>
> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
> ---
>
> Greg, recent changes that BenH has made to the arch/powerpc pci code make
> this necessary. If you don't have an issue I'd like this patch to go via
> the paulus's powerpc.git tree.
No objection at all. Feel free to add my:
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
to this patch and send it through Paul.
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH] [POWERPC] Always build setup-bus.c on powerpc
From: Benjamin Herrenschmidt @ 2008-01-24 6:28 UTC (permalink / raw)
To: Kumar Gala; +Cc: Greg KH, linux-pci, Paul Mackerras, linux-kernel, linuxppc-dev
In-Reply-To: <Pine.LNX.4.64.0801240006380.21347@blarg.am.freescale.net>
On Thu, 2008-01-24 at 00:07 -0600, Kumar Gala wrote:
> The common arch/powerpc code calls in to functions in setup-bus.c
> so some builds of ppc32 would fail.
>
> Note, ppc32 usage of setup-irq.c is limited to arch/ppc and should be
> removed when arch/ppc goes away.
I don't understand... the old code would build setup-bus.o for both
PPC32 and PPC64 cases, how did you make it fail ?
Ben.
> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
> ---
>
> Greg, recent changes that BenH has made to the arch/powerpc pci code make
> this necessary. If you don't have an issue I'd like this patch to go via
> the paulus's powerpc.git tree.
>
> - k
>
> drivers/pci/Makefile | 4 ++--
> 1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
> index 945bf02..869d689 100644
> --- a/drivers/pci/Makefile
> +++ b/drivers/pci/Makefile
> @@ -31,8 +31,8 @@ obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o
> obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o
> obj-$(CONFIG_PARISC) += setup-bus.o
> obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o
> -obj-$(CONFIG_PPC32) += setup-irq.o setup-bus.o
> -obj-$(CONFIG_PPC64) += setup-bus.o
> +obj-$(CONFIG_PPC) += setup-bus.o
> +obj-$(CONFIG_PPC32) += setup-irq.o
> obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
> obj-$(CONFIG_X86_VISWS) += setup-irq.o
>
^ permalink raw reply
* Re: [PATCH] [POWERPC] Always build setup-bus.c on powerpc
From: Kumar Gala @ 2008-01-24 6:33 UTC (permalink / raw)
To: benh; +Cc: Greg KH, linux-pci, Paul Mackerras, linux-kernel, linuxppc-dev
In-Reply-To: <1201156120.6815.48.camel@pasglop>
On Jan 24, 2008, at 12:28 AM, Benjamin Herrenschmidt wrote:
>
> On Thu, 2008-01-24 at 00:07 -0600, Kumar Gala wrote:
>> The common arch/powerpc code calls in to functions in setup-bus.c
>> so some builds of ppc32 would fail.
>>
>> Note, ppc32 usage of setup-irq.c is limited to arch/ppc and should be
>> removed when arch/ppc goes away.
>
> I don't understand... the old code would build setup-bus.o for both
> PPC32 and PPC64 cases, how did you make it fail ?
The patch is bogus. The old makefile rules looked like:
obj-$(CONFIG_PPC64) += setup-bus.o
obj-$(CONFIG_PPC32) += setup-irq.o
pmac most like builds because CONFIG_HOTPLUG pulls in setup-bus.o
(I'll fix my foobar'd patch and send this via Paul).
- k
^ permalink raw reply
* Re: [PATCH 2/3] Platform changes for UCC TDM driver for MPC8323ERDB.Also includes related QE changes and dts entries.
From: Stephen Rothwell @ 2008-01-24 6:34 UTC (permalink / raw)
To: Poonam_Aggrwal-b10812
Cc: michael.barkowski, netdev, kumar.gala, linux-kernel, rubini,
linuxppc-dev, ashish.kalra, rich.cutler, akpm, timur
In-Reply-To: <Pine.LNX.4.64.0801241016440.27491@linux121>
[-- Attachment #1: Type: text/plain, Size: 3163 bytes --]
This patch needs to come before the previous one ("UCC TDM driver for
QE based MPC83xx platforms") as that uses some of the fields defined here.
On Thu, 24 Jan 2008 10:19:44 +0530 (IST) Poonam_Aggrwal-b10812 <b10812@freescale.com> wrote:
>
> +u32 get_brg_clk(enum qe_clock brgclk, enum qe_clock *brg_source)
> {
> - struct device_node *qe;
> - if (brg_clk)
> - return brg_clk;
> + struct device_node *qe, *brg, *clocks;
> + enum qe_clock brg_src;
> + u32 brg_input_freq = 0;
> + u32 brg_num;
> + int ret;
> + const unsigned int *prop;
>
> - qe = of_find_node_by_type(NULL, "qe");
> - if (qe) {
> + *brg_source = 0;
> +
> + brg_num = brgclk - QE_BRG1;
> + brg = of_find_compatible_node(NULL, NULL, "fsl,cpm-brg");
> + if (brg) {
If you did
if (!brg) {
.
.
goto err;
}
Then you would save indenting all the rest of this function.
> + prop = of_get_property(brg,
> + "fsl,brg-sources", &size);
Join these lines.
> + of_node_put(brg);
> +
> + if (prop)
> + brg_src = *(prop + brg_num);
> + else {
> + printk(KERN_ERR "%s: invalid fsl,brg-sources in device "
> + "tree\n", __FUNCTION__);
> + ret = -EINVAL;
> + goto err;
> + }
> + if (brg_src == 0) {
> + *brg_source = 0;
> + if (brg_clk > 0)
> + return brg_clk;
> + qe = of_find_node_by_type(NULL, "qe");
> + if (qe) {
Again testing (!qe) and jumping to err would save another level if
indentation.
> + unsigned int size;
> + prop = of_get_property
> + (qe, "brg-frequency", &size);
And you wouldn't have to split things like this.
> + if (!prop) {
> + printk(KERN_ERR "%s: QE brg-frequency"
> + "not present in device tree\n",
> + __FUNCTION__);
> + ret = -EINVAL;
> + of_node_put(qe);
> + goto err;
> + }
> + if (*prop) {
> + of_node_put(qe);
> + brg_clk = *prop;
> + return *prop;
> + } else {
This else (and indentation) is unnecessary as you just returned above.
> + } else {
> + *brg_source = brg_src + QE_CLK1 - 1;
> + clocks = of_find_compatible_node(NULL, NULL,
> + "fsl,cpm-clocks");
> + if (!clocks) {
> + printk(KERN_ERR "%s: no clocks node in device"
> + " tree \n", __FUNCTION__);
> + ret = -EINVAL;
> + goto err;
> + } else {
Same here.
> + } else {
> + printk(KERN_ERR "%s: no brg node in device tree\n",
> + __FUNCTION__);
> + ret = -EINVAL;
> + goto err;
This goto is redundant.
> + }
> +err: return ret;
Put the label on a line by itself and indent it one space (that means that
"diff -p will reference the funstion anem instead of the label).
> @@ -152,6 +152,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;
If you make these "const char *" you won't have to cast the results of
of_get_property() that you assign to them.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* [PATCH v2][POWERPC] Always build setup-bus.c on powerpc
From: Kumar Gala @ 2008-01-24 6:38 UTC (permalink / raw)
To: benh; +Cc: Greg KH, linux-pci, Paul Mackerras, linux-kernel, linuxppc-dev
In-Reply-To: <8642742F-E8BF-43C7-9FE4-AAF54855ECF7@kernel.crashing.org>
The common arch/powerpc code calls in to functions in setup-bus.c
so some builds of ppc32 would fail.
Note, ppc32 usage of setup-irq.c is limited to arch/ppc and should be
removed when arch/ppc goes away.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
Here's the proper diff, will send this via paulus.
drivers/pci/Makefile | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 5550556..f697f3d 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -32,7 +32,7 @@ obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o
obj-$(CONFIG_PARISC) += setup-bus.o
obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o
obj-$(CONFIG_PPC32) += setup-irq.o
-obj-$(CONFIG_PPC64) += setup-bus.o
+obj-$(CONFIG_PPC) += setup-bus.o
obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
obj-$(CONFIG_X86_VISWS) += setup-irq.o
--
1.5.3.7
^ permalink raw reply related
* [PATCH] [POWERPC] 85xx: Port STX GP3 board over from arch/ppc
From: Kumar Gala @ 2008-01-24 6:43 UTC (permalink / raw)
To: linuxppc-dev
---
The defconfig has been removed from the patch to protect the innocent.
Let's see what sfr finds wrong with this patch :)
- k
arch/powerpc/boot/dts/stx_gp3_8560.dts | 216 ++++++
arch/powerpc/configs/stx_gp3_defconfig | 1183 ++++++++++++++++++++++++++++++++
arch/powerpc/platforms/85xx/Kconfig | 12 +-
arch/powerpc/platforms/85xx/Makefile | 1 +
arch/powerpc/platforms/85xx/stx_gp3.c | 256 +++++++
5 files changed, 1666 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/boot/dts/stx_gp3_8560.dts
create mode 100644 arch/powerpc/configs/stx_gp3_defconfig
create mode 100644 arch/powerpc/platforms/85xx/stx_gp3.c
diff --git a/arch/powerpc/boot/dts/stx_gp3_8560.dts b/arch/powerpc/boot/dts/stx_gp3_8560.dts
new file mode 100644
index 0000000..b32d3c5
--- /dev/null
+++ b/arch/powerpc/boot/dts/stx_gp3_8560.dts
@@ -0,0 +1,216 @@
+/*
+ * STX GP3 - 8560 ADS Device Tree Source
+ *
+ * Copyright 2008 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.
+ */
+
+/dts-v1/;
+
+/ {
+ model = "stx,gp3";
+ compatible = "stx,gp3-8560", "stx,gp3";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ aliases {
+ ethernet0 = &enet0;
+ ethernet1 = &enet1;
+ serial0 = &serial0;
+ pci0 = &pci0;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ PowerPC,8560@0 {
+ device_type = "cpu";
+ reg = <0>;
+ d-cache-line-size = <32>;
+ i-cache-line-size = <32>;
+ d-cache-size = <0x8000>; // L1, 32K
+ i-cache-size = <0x8000>; // L1, 32K
+ timebase-frequency = <0x4ead9a0>;
+ bus-frequency = <0x13ab6680>;
+ clock-frequency = <0x312c8040>;
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x10000000>;
+ };
+
+ soc8560@fdf00000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "soc";
+ ranges = <0 0xfdf00000 0x100000>;
+ reg = <0xfdf00000 0x1000>;
+ bus-frequency = <0x13ab6680>;
+
+ memory-controller@2000 {
+ compatible = "fsl,8540-memory-controller";
+ reg = <0x2000 0x1000>;
+ interrupt-parent = <&mpic>;
+ interrupts = <18 2>;
+ };
+
+ l2-cache-controller@20000 {
+ compatible = "fsl,8540-l2-cache-controller";
+ reg = <0x20000 0x1000>;
+ cache-line-size = <32>;
+ cache-size = <0x40000>; // L2, 256K
+ interrupt-parent = <&mpic>;
+ interrupts = <16 2>;
+ };
+
+ mdio@24520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-mdio";
+ reg = <0x24520 0x20>;
+
+ phy2: ethernet-phy@2 {
+ interrupt-parent = <&mpic>;
+ interrupts = <5 4>;
+ reg = <2>;
+ device_type = "ethernet-phy";
+ };
+ phy4: ethernet-phy@4 {
+ interrupt-parent = <&mpic>;
+ interrupts = <5 4>;
+ reg = <4>;
+ device_type = "ethernet-phy";
+ };
+ };
+
+ enet0: ethernet@24000 {
+ cell-index = <0>;
+ device_type = "network";
+ model = "TSEC";
+ compatible = "gianfar";
+ reg = <0x24000 0x1000>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ interrupts = <29 2 30 2 34 2>;
+ interrupt-parent = <&mpic>;
+ phy-handle = <&phy2>;
+ };
+
+ enet1: ethernet@25000 {
+ cell-index = <1>;
+ device_type = "network";
+ model = "TSEC";
+ compatible = "gianfar";
+ reg = <0x25000 0x1000>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ interrupts = <35 2 36 2 40 2>;
+ interrupt-parent = <&mpic>;
+ phy-handle = <&phy4>;
+ };
+
+ mpic: pic@40000 {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ reg = <0x40000 0x40000>;
+ device_type = "open-pic";
+ };
+
+ cpm@919c0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,mpc8560-cpm", "fsl,cpm2";
+ reg = <0x919c0 0x30>;
+ ranges;
+
+ muram@80000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x80000 0x10000>;
+
+ data@0 {
+ compatible = "fsl,cpm-muram-data";
+ reg = <0 0x4000 0x9000 0x2000>;
+ };
+ };
+
+ brg@919f0 {
+ compatible = "fsl,mpc8560-brg",
+ "fsl,cpm2-brg",
+ "fsl,cpm-brg";
+ reg = <0x919f0 0x10 0x915f0 0x10>;
+ clock-frequency = <133000000>;
+ };
+
+ cpmpic: pic@90c00 {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ interrupts = <46 2>;
+ interrupt-parent = <&mpic>;
+ reg = <0x90c00 0x80>;
+ compatible = "fsl,mpc8560-cpm-pic", "fsl,cpm2-pic";
+ };
+
+ serial0: serial@91a20 {
+ device_type = "serial";
+ compatible = "fsl,mpc8560-scc-uart",
+ "fsl,cpm2-scc-uart";
+ reg = <0x91a20 0x20 0x88100 0x100>;
+ fsl,cpm-brg = <2>;
+ fsl,cpm-command = <0x4a00000>;
+ interrupts = <41 8>;
+ interrupt-parent = <&cpmpic>;
+ };
+ };
+ };
+
+ pci0: pci@fdf08000 {
+ cell-index = <0>;
+ interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
+ interrupt-map = <
+
+ /* IDSEL 0x0c */
+ 0x6000 0 0 1 &mpic 1 1
+ 0x6000 0 0 2 &mpic 2 1
+ 0x6000 0 0 3 &mpic 3 1
+ 0x6000 0 0 4 &mpic 4 1
+
+ /* IDSEL 0x0d */
+ 0x6800 0 0 1 &mpic 4 1
+ 0x6800 0 0 2 &mpic 1 1
+ 0x6800 0 0 3 &mpic 2 1
+ 0x6800 0 0 4 &mpic 3 1
+
+ /* IDSEL 0x0e */
+ 0x7000 0 0 1 &mpic 3 1
+ 0x7000 0 0 2 &mpic 4 1
+ 0x7000 0 0 3 &mpic 1 1
+ 0x7000 0 0 4 &mpic 2 1
+
+ /* IDSEL 0x0f */
+ 0x7800 0 0 1 &mpic 2 1
+ 0x7800 0 0 2 &mpic 3 1
+ 0x7800 0 0 3 &mpic 4 1
+ 0x7800 0 0 4 &mpic 1 1>;
+
+ interrupt-parent = <&mpic>;
+ interrupts = <24 2>;
+ bus-range = <0 0>;
+ ranges = <0x02000000 0 0x80000000 0x80000000 0 0x20000000
+ 0x01000000 0 0x00000000 0xe2000000 0 0x00100000>;
+ clock-frequency = <0x3f940aa>;
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0xfdf08000 0x1000>;
+ compatible = "fsl,mpc8540-pcix", "fsl,mpc8540-pci";
+ device_type = "pci";
+ };
+};
diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig
index 7748a3a..26f9cd5 100644
--- a/arch/powerpc/platforms/85xx/Kconfig
+++ b/arch/powerpc/platforms/85xx/Kconfig
@@ -38,6 +38,14 @@ config MPC85xx_DS
help
This option enables support for the MPC85xx DS (MPC8544 DS) board
+config STX_GP3
+ bool "Silicon Turnkey Express GP3"
+ help
+ This option enables support for the Silicon Turnkey Express GP3
+ board.
+ select DEFAULT_UIMAGE
+ select PPC_CPM_NEW_BINDING
+
endchoice
config MPC8540
@@ -49,7 +57,7 @@ config MPC8540
config MPC8560
bool
select CPM2
- default y if MPC8560_ADS
+ default y if MPC8560_ADS || STX_GP3
config MPC85xx
bool
@@ -59,4 +67,4 @@ config MPC85xx
select FSL_PCI if PCI
select SERIAL_8250_SHARE_IRQ if SERIAL_8250
default y if MPC8540_ADS || MPC85xx_CDS || MPC8560_ADS \
- || MPC85xx_MDS || MPC85xx_DS
+ || MPC85xx_MDS || MPC85xx_DS || STX_GP3
diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index 5eca920..bf7d2e1 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o
obj-$(CONFIG_MPC85xx_CDS) += mpc85xx_cds.o
obj-$(CONFIG_MPC85xx_DS) += mpc85xx_ds.o
obj-$(CONFIG_MPC85xx_MDS) += mpc85xx_mds.o
+obj-$(CONFIG_STX_GP3) += stx_gp3.o
diff --git a/arch/powerpc/platforms/85xx/stx_gp3.c b/arch/powerpc/platforms/85xx/stx_gp3.c
new file mode 100644
index 0000000..a3aa9f1
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/stx_gp3.c
@@ -0,0 +1,256 @@
+/*
+ * Based on MPC8560 ADS and arch/ppc stx_gp3 ports
+ *
+ * Maintained by Kumar Gala (see MAINTAINERS for contact information)
+ *
+ * Copyright 2008 Freescale Semiconductor Inc.
+ *
+ * Dan Malek <dan@embeddededge.com>
+ * Copyright 2004 Embedded Edge, LLC
+ *
+ * Copied from mpc8560_ads.c
+ * Copyright 2002, 2003 Motorola Inc.
+ *
+ * Ported to 2.6, Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2004-2005 MontaVista Software, 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 <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/of_platform.h>
+
+#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/mpic.h>
+#include <mm/mmu_decl.h>
+#include <asm/udbg.h>
+
+#include <sysdev/fsl_soc.h>
+#include <sysdev/fsl_pci.h>
+
+#ifdef CONFIG_CPM2
+#include <asm/cpm2.h>
+#include <sysdev/cpm2_pic.h>
+#endif
+
+#ifdef CONFIG_CPM2
+static void cpm2_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ int cascade_irq;
+
+ while ((cascade_irq = cpm2_get_irq()) >= 0)
+ generic_handle_irq(cascade_irq);
+
+ desc->chip->eoi(irq);
+}
+
+#endif /* CONFIG_CPM2 */
+
+static void __init stx_gp3_pic_init(void)
+{
+ struct mpic *mpic;
+ struct resource r;
+ struct device_node *np = NULL;
+#ifdef CONFIG_CPM2
+ int irq;
+#endif
+
+ np = of_find_node_by_type(np, "open-pic");
+ if (!np) {
+ printk(KERN_ERR "Could not find open-pic node\n");
+ return;
+ }
+
+ if (of_address_to_resource(np, 0, &r)) {
+ printk(KERN_ERR "Could not map mpic register space\n");
+ of_node_put(np);
+ return;
+ }
+
+ mpic = mpic_alloc(np, r.start,
+ MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN,
+ 0, 256, " OpenPIC ");
+ BUG_ON(mpic == NULL);
+ of_node_put(np);
+
+ mpic_init(mpic);
+
+#ifdef CONFIG_CPM2
+ /* Setup CPM2 PIC */
+ np = of_find_compatible_node(NULL, NULL, "fsl,cpm2-pic");
+ if (np == NULL) {
+ printk(KERN_ERR "PIC init: can not find fsl,cpm2-pic node\n");
+ return;
+ }
+ irq = irq_of_parse_and_map(np, 0);
+
+ cpm2_pic_init(np);
+ of_node_put(np);
+ set_irq_chained_handler(irq, cpm2_cascade);
+#endif
+}
+
+/*
+ * Setup the architecture
+ */
+#ifdef CONFIG_CPM2
+struct cpm_pin {
+ int port, pin, flags;
+};
+
+static const struct cpm_pin mpc8560_ads_pins[] = {
+ /* SCC1 */
+ {3, 29, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {3, 30, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY},
+ {3, 31, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+
+ /* SCC2 */
+ {3, 26, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {3, 27, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {3, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+
+ /* FCC2 */
+ {1, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 20, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 21, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 22, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 25, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 26, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 27, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY},
+ {1, 30, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 31, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {2, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* CLK14 */
+ {2, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* CLK13 */
+
+ /* FCC3 */
+ {1, 4, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 5, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 6, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 7, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 8, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 9, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 10, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 11, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 12, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 13, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 14, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 15, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY},
+ {1, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {1, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY},
+ {2, 16, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, /* CLK16 */
+ {2, 17, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, /* CLK15 */
+};
+
+static void __init init_ioports(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mpc8560_ads_pins); i++) {
+ const struct cpm_pin *pin = &mpc8560_ads_pins[i];
+ cpm2_set_pin(pin->port, pin->pin, pin->flags);
+ }
+
+ cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_RX);
+ cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_TX);
+ cpm2_clk_setup(CPM_CLK_SCC2, CPM_BRG2, CPM_CLK_RX);
+ cpm2_clk_setup(CPM_CLK_SCC2, CPM_BRG2, CPM_CLK_TX);
+ cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK13, CPM_CLK_RX);
+ cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK14, CPM_CLK_TX);
+ cpm2_clk_setup(CPM_CLK_FCC3, CPM_CLK15, CPM_CLK_RX);
+ cpm2_clk_setup(CPM_CLK_FCC3, CPM_CLK16, CPM_CLK_TX);
+}
+#endif
+
+static void __init stx_gp3_setup_arch(void)
+{
+#ifdef CONFIG_PCI
+ struct device_node *np;
+#endif
+
+ if (ppc_md.progress)
+ ppc_md.progress("stx_gp3_setup_arch()", 0);
+
+#ifdef CONFIG_CPM2
+ cpm2_reset();
+ init_ioports();
+#endif
+
+#ifdef CONFIG_PCI
+ for_each_compatible_node(np, "pci", "fsl,mpc8540-pci")
+ fsl_add_bridge(np, 1);
+#endif
+}
+
+static void stx_gp3_show_cpuinfo(struct seq_file *m)
+{
+ uint pvid, svid, phid1;
+ uint memsize = total_memory;
+
+ pvid = mfspr(SPRN_PVR);
+ svid = mfspr(SPRN_SVR);
+
+ seq_printf(m, "Vendor\t\t: RPC Electronics STx \n");
+ seq_printf(m, "PVR\t\t: 0x%x\n", pvid);
+ seq_printf(m, "SVR\t\t: 0x%x\n", svid);
+
+ /* Display cpu Pll setting */
+ phid1 = mfspr(SPRN_HID1);
+ seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f));
+
+ /* Display the amount of memory */
+ seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024));
+}
+
+static struct of_device_id __initdata of_bus_ids[] = {
+ { .name = "soc", },
+ { .type = "soc", },
+ { .name = "cpm", },
+ { .name = "localbus", },
+ {},
+};
+
+static int __init declare_of_platform_devices(void)
+{
+ of_platform_bus_probe(NULL, of_bus_ids, NULL);
+
+ return 0;
+}
+machine_device_initcall(stx_gp3, declare_of_platform_devices);
+
+/*
+ * Called very early, device-tree isn't unflattened
+ */
+static int __init stx_gp3_probe(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ return of_flat_dt_is_compatible(root, "stx,gp3-8560");
+}
+
+define_machine(stx_gp3) {
+ .name = "STX GP3",
+ .probe = stx_gp3_probe,
+ .setup_arch = stx_gp3_setup_arch,
+ .init_IRQ = stx_gp3_pic_init,
+ .show_cpuinfo = stx_gp3_show_cpuinfo,
+ .get_irq = mpic_get_irq,
+ .restart = fsl_rstcr_restart,
+ .calibrate_decr = generic_calibrate_decr,
+ .progress = udbg_progress,
+};
--
1.5.3.7
^ permalink raw reply related
* Re: [PATCH 5/5 v3] Add the memory management driver to RapidIO.
From: Kumar Gala @ 2008-01-24 7:01 UTC (permalink / raw)
To: Zhang Wei; +Cc: linuxppc-dev, paulus, linux-kernel
In-Reply-To: <11854393771067-git-send-email-wei.zhang@freescale.com>
On Jul 26, 2007, at 3:42 AM, Zhang Wei wrote:
> This patch adds the memory management driver to RapidIO.
> The RapidIO system size is changed to automatically detection.
> Add the memory mapping driver to RapidIO basic driver.
> Multi master ports are supported.
> Add a simple Bitmap RapidIO space allocator driver.
Can we break up the functionality here into separate patches. I'm
seeing 4 logical patches:
* system size is changed to automatic detection
* support for multi master ports
* bitmap allocator
* memory map driver
> Signed-off-by: Zhang Wei <wei.zhang@freescale.com>
> ---
> drivers/net/rionet.c | 17 +-
> drivers/rapidio/Kconfig | 18 +-
> drivers/rapidio/Makefile | 1 +
> drivers/rapidio/rio-access.c | 10 +-
> drivers/rapidio/rio-scan.c | 53 +++--
> drivers/rapidio/rio-sysfs.c | 3 +-
> drivers/rapidio/rio.c | 479 ++++++++++++++++++++++++++
> ++++++++-
> drivers/rapidio/rio.h | 9 +-
> drivers/rapidio/sallocator/Kconfig | 9 +
> drivers/rapidio/sallocator/Makefile | 12 +
> drivers/rapidio/sallocator/bitmap.c | 382 ++++++++++++++++++++++++++
> ++
> include/linux/rio.h | 71 +++++-
> include/linux/rio_drv.h | 41 +++-
> 13 files changed, 1046 insertions(+), 59 deletions(-)
> create mode 100644 drivers/rapidio/sallocator/Kconfig
> create mode 100644 drivers/rapidio/sallocator/Makefile
> create mode 100644 drivers/rapidio/sallocator/bitmap.c
[snip]
> +config RAPIDIO_PROC_FS
> + bool "I/O and Memory resource debug"
> + depends on RAPIDIO && PROC_FS
> + default y
> + ---help---
> + Enable this option, it will create a /proc/riores node for
> + monitoring the RapidIO I/O and Memory resource.
> +
This isn't going to be acceptable. You should use sysfs or debugfs
not proc.
- k
^ permalink raw reply
* Re: [PATCH] [POWERPC] 85xx: Port STX GP3 board over from arch/ppc
From: Stephen Rothwell @ 2008-01-24 7:06 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <Pine.LNX.4.64.0801240041530.22238@blarg.am.freescale.net>
[-- Attachment #1: Type: text/plain, Size: 1417 bytes --]
Hi Kumar,
On Thu, 24 Jan 2008 00:43:38 -0600 (CST) Kumar Gala <galak@kernel.crashing.org> wrote:
>
> Let's see what sfr finds wrong with this patch :)
A challenge! :-)
> +++ b/arch/powerpc/platforms/85xx/stx_gp3.c
> +static void __init stx_gp3_pic_init(void)
> + np = of_find_node_by_type(np, "open-pic");
^^
Use NULL explicitly and then you don't need to initialise np.
> + if (of_address_to_resource(np, 0, &r)) {
> + printk(KERN_ERR "Could not map mpic register space\n");
> + of_node_put(np);
> + return;
> + }
> +
> + mpic = mpic_alloc(np, r.start,
> + MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN,
> + 0, 256, " OpenPIC ");
> + BUG_ON(mpic == NULL);
Isn't that a little excessive?
> + irq = irq_of_parse_and_map(np, 0);
> +
> + cpm2_pic_init(np);
> + of_node_put(np);
> + set_irq_chained_handler(irq, cpm2_cascade);
What if irq_of_parse_and_map returns NO_IRQ?
> +static const struct cpm_pin mpc8560_ads_pins[] = {
__initdata and not const, please.
> +static int __init stx_gp3_probe(void)
> +{
> + unsigned long root = of_get_flat_dt_root();
> +
> + return of_flat_dt_is_compatible(root, "stx,gp3-8560");
You should include <asm/prom.h> to use the flattened device tree accessors.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [PATCH v2][POWERPC] Always build setup-bus.c on powerpc
From: Benjamin Herrenschmidt @ 2008-01-24 7:06 UTC (permalink / raw)
To: Kumar Gala; +Cc: Greg KH, linux-pci, Paul Mackerras, linux-kernel, linuxppc-dev
In-Reply-To: <Pine.LNX.4.64.0801240037380.22221@blarg.am.freescale.net>
On Thu, 2008-01-24 at 00:38 -0600, Kumar Gala wrote:
> The common arch/powerpc code calls in to functions in setup-bus.c
> so some builds of ppc32 would fail.
>
> Note, ppc32 usage of setup-irq.c is limited to arch/ppc and should be
> removed when arch/ppc goes away.
>
> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Ack. Funny I didn't catch it on my 4xx testing, I must have always had
HOTPLUG enabled.
> ---
>
> Here's the proper diff, will send this via paulus.
>
> drivers/pci/Makefile | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
> index 5550556..f697f3d 100644
> --- a/drivers/pci/Makefile
> +++ b/drivers/pci/Makefile
> @@ -32,7 +32,7 @@ obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o
> obj-$(CONFIG_PARISC) += setup-bus.o
> obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o
> obj-$(CONFIG_PPC32) += setup-irq.o
> -obj-$(CONFIG_PPC64) += setup-bus.o
> +obj-$(CONFIG_PPC) += setup-bus.o
> obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
> obj-$(CONFIG_X86_VISWS) += setup-irq.o
>
^ permalink raw reply
* Re: ppc32: Weird process scheduling behaviour with 2.6.24-rc
From: Benjamin Herrenschmidt @ 2008-01-24 8:18 UTC (permalink / raw)
To: Michel Dänzer; +Cc: linuxppc-dev, Ingo Molnar, Peter Zijlstra
In-Reply-To: <1201094056.9052.52.camel@thor.sulgenrain.local>
On Wed, 2008-01-23 at 14:14 +0100, Michel Dänzer wrote:
> > Does ppc32 (or your instance thereof) have a high resolution
> > sched_clock()?
>
> I'm not sure (FWIW, we did get support for NO_HZ and HIGH_RES_TIMERS
> in
> 2.6.24-rc as well, but playing with these config options and even
> reverting the code didn't seem to have any effect), can someone from
> the
> linuxppc-dev list answer this?
We do have a hires sched_clock() based on the processor internal
timebase and scaled to ns. Maybe we screwed something up there ? The
implementation is in arch/powerpc/kernel/timer.c
/*
* Scheduler clock - returns current time in nanosec units.
*
* Note: mulhdu(a, b) (multiply high double unsigned) returns
* the high 64 bits of a * b, i.e. (a * b) >> 64, where a and b
* are 64-bit unsigned numbers.
*/
unsigned long long sched_clock(void)
{
if (__USE_RTC())
return get_rtc();
return mulhdu(get_tb() - boot_tb, tb_to_ns_scale) << tb_to_ns_shift;
}
(You can mostly ignore the RTC() case which is the native ns clock of
the old 601 processor.
Ben.
^ permalink raw reply
* Re: ppc32: Weird process scheduling behaviour with 2.6.24-rc
From: Benjamin Herrenschmidt @ 2008-01-24 8:46 UTC (permalink / raw)
To: Peter Zijlstra; +Cc: linuxppc-dev, Ingo Molnar, Michel Dänzer
In-Reply-To: <1201091798.6341.49.camel@lappy>
Could the fact that our sched_clock() returns utter crap if called
before time_init() explain the problem ? If yes, that's an easy fix.
Ben.
^ permalink raw reply
* [PATCH] sysctl: Kill binary sysctl KERN_PPC_L2CR
From: Eric W. Biederman @ 2008-01-24 9:18 UTC (permalink / raw)
To: Andrew Morton; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <20080123140352.e19900d7.akpm@linux-foundation.org>
> From: Stefan Roese <sr@denx.de>
> Subject: ppc: 4xx: sysctl table check failed: /kernel/l2cr .1.31 Missing strategy
>
> I'm seeing this error message when booting an recent arch/ppc kernel on
> 4xx platforms (tested on Ocotea and other 4xx platforms). Booting NFS
> rootfs still works fine, but this message kind of makes me "nervous".
> This is not seen on 4xx arch/powerpc platforms. Here the bootlog:
Because the data field was never filled and a binary sysctl handler
was never written this sysctl has never been usable through the
sys_sysctl interface. So just remove the binary sysctl number.
Making the kernel sanity checks happy.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
---
arch/ppc/kernel/ppc_htab.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c
index aa07b63..9ed36dd 100644
--- a/arch/ppc/kernel/ppc_htab.c
+++ b/arch/ppc/kernel/ppc_htab.c
@@ -436,7 +436,6 @@ int proc_dol2crvec(ctl_table *table, int write, struct file *filp,
*/
static ctl_table htab_ctl_table[]={
{
- .ctl_name = KERN_PPC_L2CR,
.procname = "l2cr",
.mode = 0644,
.proc_handler = &proc_dol2crvec,
--
1.5.3.rc6.17.g1911
^ permalink raw reply related
* Re: [PATCH 0/7] Powerpc support for SBC8560 board
From: Kumar Gala @ 2008-01-24 9:30 UTC (permalink / raw)
To: Paul Gortmaker; +Cc: linuxppc-dev
In-Reply-To: <11997159321859-git-send-email-paul.gortmaker@windriver.com>
On Jan 7, 2008, at 8:25 AM, Paul Gortmaker wrote:
>
> This is a respin of the sbc8560 patches, incorporating the feedback
> and
> suggested changes from everyone. Changes include:
>
> -coding style and missing of_put (Stephen Rothwell)
>
> -minor dtc cleanups for gianfar, mdio, mpic etc. (David Gibson /
> Kumar Gala)
>
> -use for_each macros in legacy_serial to improve readability, and as
> groundwork for next change (me)
>
> -convert dtc "duart" + "device-type="soc" thing to be an epld entry
> listed as compatible with "localbus" -- plus add localbus as an
> acceptable
> parent in legacy_serial (David Gibson / Scott Wood)
>
> -rtc is now a child of the epld/localbus and does not use device_type
> in the dtc, nor in the platform detection code (David Gibson)
>
> -add CPM2 data to dts for FCC ports (on optional daughterboard) based
> on the MPC8560ADS dts (me)
>
> -add dts aliases, conversion to dts-v1 (Kumar)
>
> Patches are as follows:
>
> 1 powerpc-use-for_each-in-legacy_serial.txt
> 2 powerpc-allow-localbus-compatible-serial-ports-for-console-
> device.txt
> 3 sbc8560-add-support-for-Wind-River-SBC8560-in-arch-powerpc.txt
> 4 sbc8560-Add-device-tree-source-for-Wind-River-SBC8560-board.txt
> 5 sbc8560-Convert-WRS-SBC8560-device-tree-to-v1-format.txt
> 6 CPM2-Make-support-for-the-CPM2-optional-on-8560-based-boards.txt
> 7 sbc8560-Add-default-.config-file-for-Wind-River-SBC8560.txt
>
> Thanks again to all those who provided feedback.
Have we closed on these patches?
the sbc8560 is the last board that we need to close on for me to kill
85xx in arch/ppc.
- k
^ permalink raw reply
* [PATCH UCC TDM 1/3 Updated] Platform changes for UCC TDM driver for MPC8323eRDB. Also includes related QE changes and dts entries.
From: Poonam_Aggrwal-b10812 @ 2008-01-24 10:30 UTC (permalink / raw)
To: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev
Cc: michael.barkowski, rich.cutler, ashish.kalra, b10812, timur
Thanks Stephen for your comments, incorporated them.
From: Poonam Aggrwal <b10812@freescale.com>
This patch makes necessary changes in the QE and UCC framework to support
TDM. It also adds support to configure the BRG properly through device
tree entries. Includes the device tree changes for UCC TDM driver as well.
It also includes device tree entries for UCC TDM driver.
Tested on MPC8323ERDB platform.
Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
arch/powerpc/boot/dts/mpc832x_rdb.dts | 58 +++++++
arch/powerpc/sysdev/qe_lib/qe.c | 184 +++++++++++++++++++++--
arch/powerpc/sysdev/qe_lib/ucc.c | 265 +++++++++++++++++++++++++++++++++
arch/powerpc/sysdev/qe_lib/ucc_fast.c | 37 +++++
include/asm-powerpc/qe.h | 8 +
include/asm-powerpc/ucc.h | 4 +
include/asm-powerpc/ucc_fast.h | 4 +
7 files changed, 548 insertions(+), 12 deletions(-)
diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts
index 388c8a7..c0e6283 100644
--- a/arch/powerpc/boot/dts/mpc832x_rdb.dts
+++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts
@@ -105,6 +105,17 @@
device_type = "par_io";
num-ports = <7>;
+ ucc1pio:ucc_pin@01 {
+ pio-map = <
+ /* port pin dir open_drain assignment has_irq */
+ 0 e 2 0 1 0 /* CLK11 */
+ 3 16 1 0 2 0 /* BRG9 */
+ 3 1b 1 0 2 0 /* BRG3 */
+ 0 0 3 0 2 0 /* TDMATxD0 */
+ 0 4 3 0 2 0 /* TDMARxD0 */
+ 3 1b 2 0 1 0>; /* CLK1 */
+ };
+
ucc2pio:ucc_pin@02 {
pio-map = <
/* port pin dir open_drain assignment has_irq */
@@ -169,6 +180,36 @@
};
};
+ clocks {
+ compatible = "fsl,cpm-clocks";
+ /* clock freqs in Hz(for CLK1~CLK24).
+ * CLK11 is 1024KHz,
+ * all other clocks unused
+ * #clock-cells define number of cells
+ * used by the clock-frequency.
+ * right now only #clock cells=1 is
+ * implemented. Provision is there to
+ * handle frequencies >4Gig
+ */
+ #clock-cells = <1>;
+ clock-frequency = <0 0 0 0 0 0
+ 0 0 0 0 d#1024000 0
+ 0 0 0 0 0 0
+ 0 0 0 0 0 0>;
+ };
+
+ brg@640 {
+ compatible = "fsl,cpm-brg";
+ /* input clock sources for all the 16 BRGs.
+ * 1-24 for CLK1 to CLK24.
+ * BRG9 uses CLK11,BRG1 and BRG2-8 use
+ * the QE clock.
+ */
+ fsl,brg-sources = <0 0 0 0 0 0 0 0
+ b 0 0 0 0 0 0 0>;
+ reg = <640 7f>;
+ };
+
spi@4c0 {
device_type = "spi";
compatible = "fsl_spi";
@@ -187,6 +228,23 @@
mode = "cpu";
};
+ ucc@2000 {
+ device_type = "tdm";
+ compatible = "fsl,ucc-tdm";
+ model = "UCC";
+ device-id = <1>;
+ fsl,tdm-num = <1>;
+ fsl,si-num = <1>;
+ fsl,tdm-tx-clk = "CLK1";
+ fsl,tdm-rx-clk = "CLK1";
+ fsl,tdm-tx-sync = "BRG9";
+ fsl,tdm-rx-sync = "BRG9";
+ reg = <2000 200>;
+ interrupts = <20>;
+ interrupt-parent = <&qeic>;
+ pio-handle = <&ucc1pio>;
+ };
+
ucc@3000 {
device_type = "network";
compatible = "ucc_geth";
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index 1df3b4a..9b9971d 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -149,20 +149,170 @@ EXPORT_SYMBOL(qe_issue_cmd);
*/
static unsigned int brg_clk = 0;
-unsigned int get_brg_clk(void)
+u32 get_brg_clk(enum qe_clock brgclk, enum qe_clock *brg_source)
{
- struct device_node *qe;
- if (brg_clk)
- return brg_clk;
+ struct device_node *qe, *brg, *clocks;
+ enum qe_clock brg_src;
+ u32 brg_input_freq = 0;
+ u32 brg_num;
+ int ret;
+ const unsigned int *prop;
- qe = of_find_node_by_type(NULL, "qe");
- if (qe) {
+ *brg_source = 0;
+
+ brg_num = brgclk - QE_BRG1;
+ brg = of_find_compatible_node(NULL, NULL, "fsl,cpm-brg");
+ if (!brg) {
+ printk(KERN_ERR "%s: no brg node in device tree\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+ unsigned int size;
+ prop = of_get_property(brg, "fsl,brg-sources", &size);
+ of_node_put(brg);
+
+ if (!prop) {
+ printk(KERN_ERR "%s: invalid fsl,brg-sources in device tree \n"
+ , __FUNCTION__);
+ return -EINVAL;
+ }
+ brg_src = *(prop + brg_num);
+ if (brg_src == 0) {
+ *brg_source = 0;
+ if (brg_clk > 0)
+ return brg_clk;
+ qe = of_find_node_by_type(NULL, "qe");
+ if (!qe) {
+ printk(KERN_ERR "%s: no qe node in device tree \n"
+ , __FUNCTION__);
+ ret = EINVAL;
+ goto err;
+ }
unsigned int size;
- const u32 *prop = of_get_property(qe, "brg-frequency", &size);
- brg_clk = *prop;
+ prop = of_get_property(qe, "brg-frequency", &size);
+ if (!prop) {
+ printk(KERN_ERR "%s: QE brg-frequency"
+ "not present in device tree\n", __FUNCTION__);
+ ret = -EINVAL;
+ of_node_put(qe);
+ goto err;
+ }
+ if (*prop) {
+ of_node_put(qe);
+ brg_clk = *prop;
+ return *prop;
+ }
+ /*
+ * Older versions of U-Boot do not initialize
+ * the brg-frequency property, so in this case
+ * we assume the BRG frequency is half the QE
+ * bus frequency.
+ */
+ prop = of_get_property(qe, "bus-frequency", NULL);
of_node_put(qe);
- };
- return brg_clk;
+ if (!prop) {
+ printk(KERN_ERR "%s:QE bus-frequency not"
+ " present in device tree\n", __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (*prop) {
+ brg_clk = *prop / 2;
+ return brg_clk;
+ }
+ printk(KERN_ERR "%s: invalid QE bus-frequency in device tree\n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ } else {
+ *brg_source = brg_src + QE_CLK1 - 1;
+ clocks = of_find_compatible_node(NULL, NULL, "fsl,cpm-clocks");
+ if (!clocks) {
+ printk(KERN_ERR "%s: no clocks node in device tree \n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+ prop = of_get_property(clocks, "#clock-cells", &size);
+ /*
+ * clock-cells = 1 only supported right now.
+ */
+ if (!prop || *prop != 1) {
+ printk(KERN_ERR "%s: invalid #clock-cells value"
+ "in device tree \n", __FUNCTION__);
+ of_node_put(clocks);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ prop = of_get_property(clocks, "clock-frequency", &size);
+ if (!prop) {
+ printk(KERN_ERR "%s:no #clock-frequency prop in"
+ "device tree\n", __FUNCTION__);
+ of_node_put(clocks);
+ ret = -EINVAL;
+ goto err;
+ }
+ brg_input_freq = *(prop+(brg_src - 1));
+ of_node_put(clocks);
+ return brg_input_freq;
+ }
+err:
+ return ret;
+}
+
+u32 qe_brg_src(int brg_num, enum qe_clock brg_src)
+{
+ u32 clock_bits, shift;
+
+ clock_bits = 0;
+
+ switch (brg_num) {
+ case 1:
+ case 2:
+ case 5:
+ case 6:
+ switch (brg_src) {
+ case QE_CLK3: clock_bits = 1; break;
+ case QE_CLK5: clock_bits = 2; break;
+ default: break;
+ }
+ break;
+ case 3:
+ case 4:
+ case 7:
+ case 8:
+ switch (brg_src) {
+ case QE_CLK9: clock_bits = 1; break;
+ case QE_CLK15: clock_bits = 2; break;
+ default: break;
+ }
+ break;
+ case 9:
+ case 10:
+ switch (brg_src) {
+ case QE_CLK11: clock_bits = 1; break;
+ default: break;
+ }
+ break;
+ case 11:
+ case 15:
+ case 16:
+ switch (brg_src) {
+ case QE_CLK13: clock_bits = 1; break;
+ default: break;
+ }
+ break;
+ default: clock_bits = 0; break;
+ }
+ shift = 14;
+
+ if (!clock_bits)
+ return -ENOENT;
+
+ clock_bits <<= shift;
+
+ return clock_bits;
}
/* Program the BRG to the given sampling rate and multiplier
@@ -177,11 +327,18 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
{
u32 divisor, tempval;
u32 div16 = 0;
+ u32 brg_clock;
+ enum qe_clock brgsrc;
+ u32 src_bits = 0;
if ((brg < QE_BRG1) || (brg > QE_BRG16))
return -EINVAL;
- divisor = get_brg_clk() / (rate * multiplier);
+ brg_clock = get_brg_clk(brg, &brgsrc);
+ if (brg_clock < 0)
+ return -EINVAL;
+
+ divisor = brg_clock / (rate * multiplier);
if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
div16 = QE_BRGC_DIV16;
@@ -194,8 +351,11 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
if (!div16 && (divisor & 1))
divisor++;
+ if (brgsrc > 0)
+ src_bits = qe_brg_src(brg - QE_BRG1 + 1, brgsrc);
+
tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) |
- QE_BRGC_ENABLE | div16;
+ QE_BRGC_ENABLE | div16 | src_bits;
out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval);
diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c
index 0e348d9..f2de0ed 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc.c
@@ -213,3 +213,268 @@ int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
return 0;
}
+
+int ucc_set_tdm_rxtx_clk(int tdm_num, char *clk_src, enum comm_dir mode)
+{
+ enum qe_clock clock;
+ u32 clock_bits, shift;
+ struct qe_mux *qe_mux_reg = NULL;
+
+ clock_bits = 0;
+ qe_mux_reg = &qe_immr->qmx;
+
+ if ((tdm_num > 3 || tdm_num < 0))
+ return -EINVAL;
+
+ /* The communications direction must be RX or TX */
+ if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX)))
+ return -EINVAL;
+
+ clock = qe_clock_source(clk_src);
+ switch (mode) {
+ case COMM_DIR_RX:
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK3: clock_bits = 6; break;
+ case QE_CLK8: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 28;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK5: clock_bits = 6; break;
+ case QE_CLK10: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 24;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK7: clock_bits = 6; break;
+ case QE_CLK12: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 20;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK9: clock_bits = 6; break;
+ case QE_CLK14: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 16;
+ break;
+ default:
+ break;
+ }
+ break;
+ case COMM_DIR_TX:
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK4: clock_bits = 6; break;
+ case QE_CLK9: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 12;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK6: clock_bits = 6; break;
+ case QE_CLK11: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 8;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK8: clock_bits = 6; break;
+ case QE_CLK13: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 4;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK10: clock_bits = 6; break;
+ case QE_CLK15: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!clock_bits)
+ return -ENOENT;
+
+ clock_bits <<= shift;
+
+ qe_mux_reg->cmxsi1cr_l |= clock_bits;
+
+ return 0;
+}
+
+int ucc_set_tdm_rxtx_sync(int tdm_num, char *sync_src, enum comm_dir mode)
+{
+ enum qe_clock clock;
+ u32 shift, clock_bits;
+ struct qe_mux *qe_mux_reg = NULL;
+ int source;
+
+ source = -1;
+ qe_mux_reg = &qe_immr->qmx;
+
+ if ((tdm_num > 3 || tdm_num < 0))
+ return -EINVAL;
+
+ /* The communications direction must be RX or TX */
+ if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX)))
+ return -EINVAL;
+
+ switch (mode) {
+ case COMM_DIR_RX:
+ if (strcasecmp("RSYNC", sync_src) == 0) {
+ source = 0;
+ shift = 0;
+ break;
+ }
+ clock = qe_clock_source(sync_src);
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 30;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 28;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 26;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 24;
+ break;
+ default:
+ source = -1;
+ break;
+ }
+ break;
+ case COMM_DIR_TX:
+ if (strcasecmp("TSYNC", sync_src) == 0) {
+ source = 0;
+ shift = 0;
+ break;
+ }
+ clock = qe_clock_source(sync_src);
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 14;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 12;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 10;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 8;
+ break;
+ default:
+ source = -1;
+ break;
+ }
+ break;
+ default:
+ source = -1;
+ break;
+ }
+
+ if (source == -1)
+ return -ENOENT;
+
+ clock_bits = (u32) source;
+ clock_bits <<= shift;
+
+
+ qe_mux_reg->cmxsi1syr |= clock_bits;
+
+ return 0;
+}
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
index 3223acb..9c8559f 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
@@ -327,6 +327,43 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
ucc_fast_free(uccf);
return -EINVAL;
}
+ } else {
+ /* TDM Rx clock routing */
+ if ((uf_info->tdm_rx_clk != NULL) &&
+ ucc_set_tdm_rxtx_clk(uf_info->ucc_num,
+ uf_info->tdm_rx_clk, COMM_DIR_RX)) {
+ printk(KERN_ERR "%s: illegal value for TDM RX clock",
+ __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
+ /* TDM Tx clock routing */
+ if ((uf_info->tdm_tx_clk != NULL) &&
+ ucc_set_tdm_rxtx_clk(uf_info->ucc_num,
+ uf_info->tdm_tx_clk, COMM_DIR_TX)) {
+ printk(KERN_ERR "%s: illegal value for TDM TX clock",
+ __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
+ /* TDM Rx sync routing */
+ if ((uf_info->tdm_rx_sync != NULL) &&
+ ucc_set_tdm_rxtx_sync(uf_info->ucc_num,
+ uf_info->tdm_rx_sync, COMM_DIR_RX)) {
+ printk(KERN_ERR "%s: illegal value for TDM RX"
+ "Frame sync", __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
+ /* TDM Tx sync routing */
+ if ((uf_info->tdm_tx_sync != NULL) &&
+ ucc_set_tdm_rxtx_sync(uf_info->ucc_num,
+ uf_info->tdm_tx_sync, COMM_DIR_TX)) {
+ printk(KERN_ERR "%s: illegal value for TDM TX"
+ "Frame sync", __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
}
/* Set interrupt mask register at UCC level. */
diff --git a/include/asm-powerpc/qe.h b/include/asm-powerpc/qe.h
index bcf60be..51de236 100644
--- a/include/asm-powerpc/qe.h
+++ b/include/asm-powerpc/qe.h
@@ -497,6 +497,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
+
/* UPSMR, when used as a UART */
#define UCC_UART_UPSMR_FLC 0x8000
#define UCC_UART_UPSMR_SL 0x4000
diff --git a/include/asm-powerpc/ucc.h b/include/asm-powerpc/ucc.h
index 46b09ba..153db97 100644
--- a/include/asm-powerpc/ucc.h
+++ b/include/asm-powerpc/ucc.h
@@ -42,6 +42,10 @@ int ucc_set_qe_mux_mii_mng(unsigned int ucc_num);
int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
enum comm_dir mode);
+int ucc_set_tdm_rxtx_clk(int tdm_num, char *clk_src, enum comm_dir mode);
+
+int ucc_set_tdm_rxtx_sync(int tdm_num, char *clk_src, enum comm_dir mode);
+
int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask);
/* QE MUX clock routing for UCC
diff --git a/include/asm-powerpc/ucc_fast.h b/include/asm-powerpc/ucc_fast.h
index f529f70..cf35553 100644
--- a/include/asm-powerpc/ucc_fast.h
+++ b/include/asm-powerpc/ucc_fast.h
@@ -152,6 +152,10 @@ struct ucc_fast_info {
enum ucc_fast_rx_decoding_method renc;
enum ucc_fast_transparent_tcrc tcrc;
enum ucc_fast_sync_len synl;
+ const char *tdm_rx_clk;
+ const char *tdm_tx_clk;
+ const char *tdm_rx_sync;
+ const char *tdm_tx_sync;
};
struct ucc_fast_private {
--
1.5.2.4
^ permalink raw reply related
* [PATCH UCC TDM 2/3 ]Updated: UCC TDM driver for QE based MPC83xx platforms.
From: Poonam_Aggrwal-b10812 @ 2008-01-24 10:35 UTC (permalink / raw)
To: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev
Cc: michael.barkowski, rich.cutler, ashish.kalra, b10812, timur
Incorporated Stephen's comments.
From: Poonam Agarwal-b10812 <b10812@freescale.com>
The UCC TDM driver basically multiplexes and demultiplexes data from
different channels. It can interface with for example SLIC kind of devices
to receive TDM data demultiplex it and send to upper modules. At the
transmit end it receives data for different channels multiplexes it and
sends them on the TDM channel. It internally uses TSA( Time Slot Assigner)
which does multiplexing and demultiplexing, UCC to perform SDMA between
host buffers and the TSA, CMX to connect TSA to UCC.
It can be used by a kernel module which can call tdm_register_client to
get access to a TDM device.
The driver is right now a misc driver with no subsystem as such.
There can be a platform independent TDM layer which is planned to be
done after this. TDM bus sort of thing.
The dts file keeps a track of the TDM devices present on the board.
Depending on them the TDM driver initializes those many driver instances
while coming up.
The driver on the upper level can plug to more than one tdm clients
depending on the availablity of TDM devices. At every new request of a TDM
client to bind with a TDM device, a free driver instance is allocated to
the client.
The interface can be described as follows.
tdm_register_client(struct tdm_client *)
This API returns a pointer to the structure tdm_client which is of
type
struct tdm_client {
u32 client_id;
u32 (*tdm_read)(u32 client_id, short chn_id, short
*pcm_buffer, short len);
u32 (*tdm_write)(u32 client_id, short chn_id, short
*pcm_buffer, short len);
wait_queue_head_t *wakeup_event;
}
It consists of:
- client_id: It is basically to identify the particular TDM
device/driver instance.
- tdm_read: It is a function pointer returned by the TDM driver to be
used to read TDM data from a particular TDM channel.
- tdm_write: It is a function pointer returned by the TDM driver to be
used to write TDM data to a particular TDM channel.
- wakeup_event: It is address of a wait_queue event on which the client
keeps on sleeping, and the TDM driver wakes it up periodically. The driver
is configured to
wake up the client after every 10ms.
Once the TDM client gets registered to a TDM driver instance and a TDM
device, it interfaces with the driver using tdm_read, tdm_write and
wakeup_event.
This driver will run on MPC8323E-RDB platforms.
Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
drivers/misc/Kconfig | 14 +
drivers/misc/Makefile | 1 +
drivers/misc/ucc_tdm.c | 1017 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/misc/ucc_tdm.h | 221 +++++++++++
4 files changed, 1253 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/ucc_tdm.c
create mode 100644 drivers/misc/ucc_tdm.h
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b5e67c0..628b14b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -232,4 +232,18 @@ config ATMEL_SSC
If unsure, say N.
+config UCC_TDM
+ bool "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.
+
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 87f2685..6f0c49d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
+obj-$(CONFIG_UCC_TDM) += ucc_tdm.o
diff --git a/drivers/misc/ucc_tdm.c b/drivers/misc/ucc_tdm.c
new file mode 100644
index 0000000..2181132
--- /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 <linux/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");
diff --git a/drivers/misc/ucc_tdm.h b/drivers/misc/ucc_tdm.h
new file mode 100644
index 0000000..eaf2848
--- /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
--
1.5.2.4
^ permalink raw reply related
* checking installed Linux kernel header files... TOO OLD!
From: vinay kumar @ 2008-01-24 13:13 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 5985 bytes --]
Hi All gurus,
I need help.
While installing glibc , i encountered some errors. Kindly guide me of how
to deal with it.
#../glibc-2.3.2/configure --prefix=/opt/buckeye/powerpc-linux
--target=powerpc-linux --host=powerpc-linux --enable-add-ons=linuxthreads
--with-headers=/root/Desktop/systemsim/include/
--with-binutils=/opt/buckeye/powerpc-linux/powerpc-linux/bin
--disable-sanity-checks
configure: WARNING: If you wanted to set the --build type, don't use --host.
If a cross compiler is detected then cross compile mode will be used.
checking build system type... i686-pc-linux-gnu
checking host system type... powerpc-unknown-linux-gnu
checking sysdep dirs... sysdeps/powerpc/powerpc32/elf sysdeps/powerpc/elf
linuxthreads/sysdeps/unix/sysv/linux/powerpc/powerpc32
linuxthreads/sysdeps/unix/sysv/linux/powerpc
linuxthreads/sysdeps/unix/sysv/linux linuxthreads/sysdeps/pthread
sysdeps/pthread linuxthreads/sysdeps/unix/sysv linuxthreads/sysdeps/unix
linuxthreads/sysdeps/powerpc/powerpc32 linuxthreads/sysdeps/powerpc
sysdeps/unix/sysv/linux/powerpc/powerpc32 sysdeps/unix/sysv/linux/powerpc
sysdeps/unix/sysv/linux sysdeps/gnu sysdeps/unix/common sysdeps/unix/mman
sysdeps/unix/inet sysdeps/unix/sysv sysdeps/unix/powerpc sysdeps/unix
sysdeps/posix sysdeps/powerpc/powerpc32/fpu sysdeps/powerpc/powerpc32
sysdeps/wordsize-32 sysdeps/powerpc/soft-fp sysdeps/powerpc/fpu
sysdeps/powerpc sysdeps/ieee754/flt-32 sysdeps/ieee754/dbl-64
sysdeps/ieee754 sysdeps/generic/elf sysdeps/generic
checking for a BSD-compatible install... /usr/bin/install -c
checking whether ln -s works... yes
checking for pwd... /bin/pwd
checking for powerpc-linux-gcc... powerpc-linux-gcc
checking version of powerpc-linux-gcc... 3.3.2, ok
checking for gnumake... no
checking for gmake... gmake
checking version of gmake... 3.80, ok
checking for gnumsgfmt... no
checking for gmsgfmt... no
checking for msgfmt... msgfmt
checking version of msgfmt... 0.14.1, ok
checking for makeinfo... makeinfo
checking version of makeinfo... 4.7, ok
checking for sed... sed
checking version of sed... 4.1.2, ok
checking for powerpc-linux-gcc... (cached) powerpc-linux-gcc
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether powerpc-linux-gcc accepts -g... yes
checking for powerpc-linux-gcc option to accept ANSI C... none needed
checking for gcc... gcc
checking how to run the C preprocessor... powerpc-linux-gcc -E
checking whether /opt/buckeye/powerpc-linux/powerpc-linux/bin/as is GNU
as... yes
checking whether /opt/buckeye/powerpc-linux/powerpc-linux/bin/ld is GNU
ld... yes
checking for /opt/buckeye/powerpc-linux/powerpc-linux/bin/as...
/opt/buckeye/powerpc-linux/powerpc-linux/bin/as
checking version of /opt/buckeye/powerpc-linux/powerpc-linux/bin/as... 2.14,
ok
checking for /opt/buckeye/powerpc-linux/powerpc-linux/bin/ld...
/opt/buckeye/powerpc-linux/powerpc-linux/bin/ld
checking version of /opt/buckeye/powerpc-linux/powerpc-linux/bin/ld... 2.14,
ok
checking whether ranlib is necessary... no
checking LD_LIBRARY_PATH variable... ok
checking whether GCC supports -static-libgcc... -static-libgcc
checking for bash... /bin/sh
checking for gawk... gawk
checking for perl... /usr/bin/perl
checking for install-info... /sbin/install-info
checking for old Debian install-info... no
checking for bison... /usr/bin/bison
checking for signed size_t type... no
checking for libc-friendly stddef.h... yes
checking whether we need to use -P to assemble .S files... no
checking whether .text pseudo-op must be used... yes
checking for assembler global-symbol directive... .globl
checking for .set assembler directive... no
checking for assembler .type directive prefix... @
checking for .symver assembler directive... yes
checking for ld --version-script... yes
checking for .previous assembler directive... yes
checking for .protected and .hidden assembler directive... yes
checking whether __attribute__((visibility())) is supported... yes
checking for broken __attribute__((visibility()))... no
checking for broken __attribute__((alias()))... no
checking whether to put _rtld_local into .sdata section... yes
checking for .preinit_array/.init_array/.fini_array support... yes
checking for -z nodelete option... yes
checking for -z nodlopen option... yes
checking for -z initfirst option... yes
checking for -Bgroup option... yes
checking for -z combreloc... yes
checking whether cc puts quotes around section names... no
checking for assembler .weak directive... yes
checking for ld --no-whole-archive... yes
checking for gcc -fexceptions... yes
checking whether clobbering cr0 causes problems... no
checking for DWARF2 unwind info support... yes
checking for __builtin_expect... yes
checking for __builtin_memset... no
checking for local label subtraction... yes
checking for __thread... no
checking for libgd... no
checking for egrep... grep -E
checking for ANSI C header files... no
checking for sys/types.h... no
checking for sys/stat.h... no
checking for stdlib.h... no
checking for string.h... no
checking for memory.h... no
checking for strings.h... no
checking for inttypes.h... no
checking for stdint.h... no
checking for unistd.h... no
checking for long double... no
checking size of long double... 0
running configure fragment for ../glibc-2.3.2/sysdeps/pthread
running configure fragment for ../glibc-2.3.2/sysdeps/unix/sysv/linux
checking for egrep... (cached) grep -E
*checking installed Linux kernel header files... TOO OLD!
configure: error: GNU libc requires kernel header files from
Linux 2.0.10 or later to be installed before configuring.
The kernel header files are found usually in /usr/include/asm and
/usr/include/linux; make sure these directories use files from
Linux 2.0.10 or later. This check uses <linux/version.h>, so
make sure that file was built correctly when installing the kernel header
files. To use kernel headers not from /usr/include/linux, use the
configure option --with-headers.*
Thanks and regards.
Vinay
[-- Attachment #2: Type: text/html, Size: 6380 bytes --]
^ permalink raw reply
* Reboot at insmod of cf card driver
From: Lehmann, Hans (Ritter Elektronik) @ 2008-01-24 13:07 UTC (permalink / raw)
To: linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 6988 bytes --]
Hi at all,
i have strange problems with my cf_card driver on our MPC5200b board. Because of our board schematic the cf card is mapped to memory space.
The interrupt is routed to IRQ1. IRQ1 is rising edge sensitive.
First the driver was written for Kernel 2.6.14 with device_driver structure and run pretty. Now i have ported it to Kernel 2.6.23 with of_platform_driver structure to get indepent of ppc.
Now my problem:
After I load my modul, the kernel start to reboot at command probe_hwif_init.
The failure occurs exatly in function "actual_try_to_identify" in ide-probe.c when kernel send the identify command to status/command register of ide device "(hwif->outb(cmd, DIE_COMMAND_REG);".
By accident I figured out that no reboot occurs when I change interrupt behauviour (level sensitive). But then several errors occured as followed:
root@RITTER_EL392:/lib/modules> insmod el392_cf_disk_drv.ko
[ 72.306114] IDE: EL392 IDE/ATA driver
[ 72.397169] Base address: d0000000, IO Size: 00020000
[ 72.413994] Memory remapped: 0xd0000000 -> 0xc5040000
[ 72.666923] cmd: 0x0ec , Adresse IDE_REG_COMMAND: 0xc5041006
[ 72.804238] hda: ELITE PRO CF CARD 1GB, CFA DISK drive
[ 73.126941] cmd: 0x0a1 , Adresse IDE_REG_COMMAND: 0xc5041006
[ 73.313920] cmd: 0x0a1 , Adresse IDE_REG_COMMAND: 0xc5041006
[ 73.503065] ide0 at 0xc5041008-0xc504100f,0xc504100f on irq 70
[ 73.521193] ------------[ cut here ]------------
[ 73.530365] Badness at c0105298 [verbose debug info unavailable]
[ 73.542244] NIP: c0105298 LR: c0104260 CTR: 00000000
[ 73.552071] REGS: c374dc30 TRAP: 0700 Not tainted (2.6.23.1-rt5-pcm030-1trunk)
[ 73.566867] MSR: 00029032 <EE,ME,IR,DR> CR: 84004484 XER: 00000000
[ 73.579487] TASK = c36d6000[231] 'insmod' THREAD: c374c000
[ 73.589988] GPR00: 00000001 c374dce0 c36d6000 c02c0ab0 c02396c4 c02bf9b4 c02395ec 00000242
[ 73.606564] GPR08: c36d6000 00000002 00000001 c02c05dc 84004484 100decc4 c50235a8 c3d0c280
[ 73.623139] GPR16: c5022e6c c5022e94 00000000 00000035 00000124 00000000 c5022ac4 c5022ebc
[ 73.639713] GPR24: c0048214 c02c0544 00000015 c028c49c c0240000 c0200000 00000000 c02c0a98
[ 73.656636] NIP [c0105298] kref_get+0xc/0x24
[ 73.665152] LR [c0104260] kobject_get+0x20/0x38
[ 73.674125] Call Trace:
[ 73.678955] [c374dce0] [c02c0428] 0xc02c0428 (unreliable)
[ 73.689660] [c374dcf0] [c01280b4] get_device+0x20/0x38
[ 73.699858] [c374dd00] [c0128f14] device_add+0x6c/0x518
[ 73.710214] [c374dd40] [c0144130] probe_hwif_init_with_fixup+0x80/0xb0
[ 73.723171] [c374dd60] [c5025304] el392_ata_probe+0x210/0x258 [el392_cf_disk_drv]
[ 73.738017] [c374dda0] [c015fc18] of_platform_device_probe+0x58/0x80
[ 73.750632] [c374ddc0] [c012b9ac] driver_probe_device+0xb8/0x1ec
[ 73.762543] [c374dde0] [c012bb64] __driver_attach+0x84/0x88
[ 73.773584] [c374de00] [c012ac88] bus_for_each_dev+0x58/0x94
[ 73.784798] [c374de30] [c012b7bc] driver_attach+0x24/0x34
[ 73.795495] [c374de40] [c012b0e4] bus_add_driver+0x98/0x1d8
[ 73.806536] [c374de60] [c012bd0c] driver_register+0x58/0xa0
[ 73.817577] [c374de70] [c000a0e0] of_register_platform_driver+0x28/0x38
[ 73.830709] [c374de80] [c5028024] el392_ata_init+0x24/0x78 [el392_cf_disk_drv]
[ 73.845022] [c374de90] [c0049550] sys_init_module+0x16c/0x14b0
[ 73.856604] [c374df40] [c000f670] ret_from_syscall+0x0/0x38
[ 73.867662] --- Exception: c01 at 0xff26d98
[ 73.875941] LR = 0x1002ebdc
[ 73.882143] Instruction dump:
[ 73.888009] 409e0010 7c8903a6 4e800421 38000001 7c030378 80010014 38210010 7c0803a6
[ 73.903381] 4e800020 80030000 7c000034 5400d97e <0f000000> 7d201828 31290001 7d20192d
[ 73.923415] Unable to handle kernel paging request for data at address 0x00000024
[ 73.945442] Faulting instruction address: 0xc00b8d08
[ 73.960258] stopped custom tracer.
[ 73.970940] Oops: Kernel access of bad area, sig: 11 [#1]
[ 73.981635] PREEMPT ritter_el392
[ 73.988015] Modules linked in: el392_cf_disk_drv
[ 73.997165] NIP: c00b8d08 LR: c00b8d04 CTR: 00000000
[ 74.006990] REGS: c374dbc0 TRAP: 0300 Not tainted (2.6.23.1-rt5-pcm030-1trunk)
[ 74.021787] MSR: 00009032 <EE,ME,IR,DR> CR: 24004442 XER: 00000000
[ 74.034404] DAR: 00000024, DSISR: 20000000
[ 74.042501] TASK = c36d6000[231] 'insmod' THREAD: c374c000
[ 74.053003] GPR00: c00b8d04 c374dc70 c36d6000 c029b248 00000004 00000000 00000000 00000380
[ 74.069578] GPR08: c36d6000 c02b0000 00000000 00000000 24004488 100decc4 c50235a8 c3d0c280
[ 74.086154] GPR16: c5022e6c c5022e94 00000000 00000035 00000124 00000000 c5022ac4 c5022ebc
[ 74.102730] GPR24: c0048214 c02c0544 c374dcc8 c374dc98 c02c04f0 00000000 c029b248 c374dc98
[ 74.119654] NIP [c00b8d08] sysfs_addrm_start+0x40/0xcc
[ 74.129883] LR [c00b8d04] sysfs_addrm_start+0x3c/0xcc
[ 74.139887] Call Trace:
[ 74.144717] [c374dc70] [c00b8d04] sysfs_addrm_start+0x3c/0xcc (unreliable)
[ 74.158347] [c374dc90] [c00b956c] create_dir+0x50/0xd8
[ 74.168530] [c374dcc0] [c00b962c] sysfs_create_dir+0x38/0x9c
[ 74.179739] [c374dce0] [c01044ac] kobject_shadow_add+0xb0/0x1c0
[ 74.191504] [c374dd00] [c0128f60] device_add+0xb8/0x518
[ 74.201874] [c374dd40] [c0144130] probe_hwif_init_with_fixup+0x80/0xb0
[ 74.214831] [c374dd60] [c5025304] el392_ata_probe+0x210/0x258 [el392_cf_disk_drv]
[ 74.229679] [c374dda0] [c015fc18] of_platform_device_probe+0x58/0x80
[ 74.242300] [c374ddc0] [c012b9ac] driver_probe_device+0xb8/0x1ec
[ 74.254211] [c374dde0] [c012bb64] __driver_attach+0x84/0x88
[ 74.265252] [c374de00] [c012ac88] bus_for_each_dev+0x58/0x94
[ 74.276466] [c374de30] [c012b7bc] driver_attach+0x24/0x34
[ 74.287164] [c374de40] [c012b0e4] bus_add_driver+0x98/0x1d8
[ 74.298205] [c374de60] [c012bd0c] driver_register+0x58/0xa0
[ 74.309247] [c374de70] [c000a0e0] of_register_platform_driver+0x28/0x38
[ 74.322380] [c374de80] [c5028024] el392_ata_init+0x24/0x78 [el392_cf_disk_drv]
[ 74.336693] [c374de90] [c0049550] sys_init_module+0x16c/0x14b0
[ 74.348276] [c374df40] [c000f670] ret_from_syscall+0x0/0x38
[ 74.359334] --- Exception: c01 at 0xff26d98
[ 74.367612] LR = 0x1002ebdc
[ 74.373816] Instruction dump:
[ 74.379683] 7c7f1b78 3bc9b248 90010024 38000000 7c9d2378 9003000c 90030004 90030008
[ 74.395056] 7fc3f378 909f0000 4813b041 3d20c02b <809d0024> 3ca0c00c 8069f5a4 38a5897c
Segmentation fault
root@RITTER_EL392:/lib/modules>
Thanks anybody for help.
Mit freundlichen Grüßen
Hans Lehmann
Dipl.-Ing. Elektrotechnik
RITTER Elektronik GmbH
Leverkuser Strasse 65
D-42897 Remscheid
Tel. +49 (0) 2191 - 67 32 40
Fax +49 (0) 2191 - 67 14 29
Email hans.lehmann@ritter-elektronik.de
Homepage www.ritter-elektronik.de
Geschäftsführer: Manfred A. Wagner, Dr. Uwe Baader
Sitz der Gesellschaft: Oberhausen
HRB 17168 Duisburg USt-ID DE 814009849
<<el392_cf_disk_drv.h>> <<el392_cf_disk_drv.c>>
[-- Attachment #2: el392_cf_disk_drv.h --]
[-- Type: application/octet-stream, Size: 3511 bytes --]
/*
* driver/ide/el392_cf_disk.h
*
* Definitions for RITTER Elektronik EL392_cf_disk driver.
*
* Maintainer : Hans Lehmann <hans.lehmann@ritter-elektronik.de>
*
* Copyright 2007-2008 RITTER-Elektronik <www.ritter-elektronik.de>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#define DATA 0x1008
#define ERROR 0x1000
#define SECTOR_CNT 0x1003
#define SECTOR_NO 0x1002
#define CYL_LOW 0x1005
#define CYL_HIGH 0x1004
#define SELECT 0x1007
#define STATUS 0x1006
#define ALTSTAT_CTL 0x100F
#define DRIVER_NAME "el392_ide"
#define MPC52xx_CRIT_IRQ_NUM 4
#define MPC52xx_MAIN_IRQ_NUM 17
#define MPC52xx_SDMA_IRQ_NUM 17
#define MPC52xx_PERP_IRQ_NUM 23
#define MPC52xx_CRIT_IRQ_BASE 1
#define MPC52xx_MAIN_IRQ_BASE (MPC52xx_CRIT_IRQ_BASE + MPC52xx_CRIT_IRQ_NUM)
#define MPC52xx_SDMA_IRQ_BASE (MPC52xx_MAIN_IRQ_BASE + MPC52xx_MAIN_IRQ_NUM)
#define MPC52xx_PERP_IRQ_BASE (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)
#define MPC52xx_IRQ0 (MPC52xx_CRIT_IRQ_BASE + 0)
#define MPC52xx_SLICE_TIMER_0_IRQ (MPC52xx_CRIT_IRQ_BASE + 1)
#define MPC52xx_HI_INT_IRQ (MPC52xx_CRIT_IRQ_BASE + 2)
#define MPC52xx_CCS_IRQ (MPC52xx_CRIT_IRQ_BASE + 3)
#define MPC52xx_IRQ1 (MPC52xx_MAIN_IRQ_BASE + 1)
#define MPC52xx_IRQ2 (MPC52xx_MAIN_IRQ_BASE + 2)
#define MPC52xx_IRQ3 (MPC52xx_MAIN_IRQ_BASE + 3)
#define MPC52xx_SDMA_IRQ (MPC52xx_PERP_IRQ_BASE + 0)
#define MPC52xx_PSC1_IRQ (MPC52xx_PERP_IRQ_BASE + 1)
#define MPC52xx_PSC2_IRQ (MPC52xx_PERP_IRQ_BASE + 2)
#define MPC52xx_PSC3_IRQ (MPC52xx_PERP_IRQ_BASE + 3)
#define MPC52xx_PSC6_IRQ (MPC52xx_PERP_IRQ_BASE + 4)
#define MPC52xx_IRDA_IRQ (MPC52xx_PERP_IRQ_BASE + 4)
#define MPC52xx_FEC_IRQ (MPC52xx_PERP_IRQ_BASE + 5)
#define MPC52xx_USB_IRQ (MPC52xx_PERP_IRQ_BASE + 6)
#define MPC52xx_ATA_IRQ (MPC52xx_PERP_IRQ_BASE + 7)
#define MPC52xx_PCI_CNTRL_IRQ (MPC52xx_PERP_IRQ_BASE + 8)
#define MPC52xx_PCI_SCIRX_IRQ (MPC52xx_PERP_IRQ_BASE + 9)
#define MPC52xx_PCI_SCITX_IRQ (MPC52xx_PERP_IRQ_BASE + 10)
#define MPC52xx_PSC4_IRQ (MPC52xx_PERP_IRQ_BASE + 11)
#define MPC52xx_PSC5_IRQ (MPC52xx_PERP_IRQ_BASE + 12)
#define MPC52xx_SPI_MODF_IRQ (MPC52xx_PERP_IRQ_BASE + 13)
#define MPC52xx_SPI_SPIF_IRQ (MPC52xx_PERP_IRQ_BASE + 14)
#define MPC52xx_I2C1_IRQ (MPC52xx_PERP_IRQ_BASE + 15)
#define MPC52xx_I2C2_IRQ (MPC52xx_PERP_IRQ_BASE + 16)
#define MPC52xx_MSCAN1_IRQ (MPC52xx_PERP_IRQ_BASE + 17)
#define MPC52xx_MSCAN2_IRQ (MPC52xx_PERP_IRQ_BASE + 18)
#define MPC52xx_IR_RX_IRQ (MPC52xx_PERP_IRQ_BASE + 19)
#define MPC52xx_IR_TX_IRQ (MPC52xx_PERP_IRQ_BASE + 20)
#define MPC52xx_XLB_ARB_IRQ (MPC52xx_PERP_IRQ_BASE + 21)
#define MPC52xx_BDLC_IRQ (MPC52xx_PERP_IRQ_BASE + 22)
static int __devinit el392_ata_probe(struct of_device *op, const struct of_device_id *match);
static int __devexit el392_ata_remove(struct of_device *op);
static struct of_device_id el392_ide_of_match[] = {
{
.name = "ata",
.compatible = "el392_ide",
},
/* {
.name = "ata",
.compatible = "el392_ata",
},*/
{},
};
static struct of_platform_driver el392_ide_of_platform_driver = {
.name = "el392_ide",
.match_table = el392_ide_of_match,
.probe = el392_ata_probe,
.remove = el392_ata_remove,
.driver = {
. name = "el392_ide",
. owner = THIS_MODULE,
},
};
[-- Attachment #3: el392_cf_disk_drv.c --]
[-- Type: application/octet-stream, Size: 5474 bytes --]
/*
* drivers/ide/el392_cf_disk.c
*
* Based upon code written by Benedikt Spranger, (2005)Linutronix
*
* Maintainer : Hans Lehmann <hans.lehmann@ritter-elektronik.de>
*
* Copyright 2007-2008 RITTER-Elektronik <www.ritter-elektronik.de>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <linux/ide.h>
#include <asm/of_device.h>
#include <asm/of_platform.h>
#include <asm/mpc52xx.h>
#include "el392_cf_disk_drv.h"
#include "ritter_el392a.h"
void el392_ide_setup_ports(ide_hwif_t *hwif, void *ioaddr)
{
hwif->hw.io_ports[IDE_DATA_OFFSET] = (unsigned long) ioaddr + DATA;
hwif->hw.io_ports[IDE_ERROR_OFFSET] = (unsigned long) ioaddr + ERROR;
hwif->hw.io_ports[IDE_NSECTOR_OFFSET] = (unsigned long) ioaddr + SECTOR_CNT;
hwif->hw.io_ports[IDE_SECTOR_OFFSET] = (unsigned long) ioaddr + SECTOR_NO;
hwif->hw.io_ports[IDE_LCYL_OFFSET] = (unsigned long) ioaddr + CYL_LOW;
hwif->hw.io_ports[IDE_HCYL_OFFSET] = (unsigned long) ioaddr + CYL_HIGH;
hwif->hw.io_ports[IDE_SELECT_OFFSET] = (unsigned long) ioaddr + SELECT;
hwif->hw.io_ports[IDE_STATUS_OFFSET] = (unsigned long) ioaddr + STATUS;
hwif->hw.io_ports[IDE_CONTROL_OFFSET] = (unsigned long) ioaddr + ALTSTAT_CTL;
}
static int __devinit el392_ata_probe(struct of_device *op, const struct of_device_id *match)
{
int ret = -ENODEV;
int i = 0;
unsigned long res_start, res_length;
void *ioaddr;
ide_hwif_t *hwif;
int ata_irq = NO_IRQ;
struct resource res_mem;
if ((ret = of_address_to_resource(op->node, 0, &res_mem)) != 0)
{
printk(KERN_ERR "EL392_ATA: Can not find IO mem!\n");
goto out;
}
res_start = res_mem.start;
res_length = res_mem.end - res_mem.start + 1;
if (!request_mem_region(res_start, res_length, DRIVER_NAME)) {
printk(KERN_ERR"device busy.\n");
ret = -EBUSY;
goto out;
}
printk("Base address: %08lx, IO Size: %08lx\n",res_start, res_length);
ioaddr = ioremap(res_start, res_length);
if (!ioaddr) {
printk(KERN_ERR "EL392_ATA: ioremap failed.\n");
ret = -ENOMEM;
goto no_mem;
}
printk ("Memory remapped: 0x%08lx -> 0x%08x\n", res_start, (unsigned int)ioaddr);
while (i < MAX_HWIFS && (ide_hwifs[i].present))
++i;
if (i >= MAX_HWIFS) {
printk(KERN_ERR "EL392_ATA: No IDE-slot free\n");
return -ENODEV;
goto no_ide;
}
hwif = &ide_hwifs[i];
el392_ide_setup_ports(hwif, ioaddr);
memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports));
ata_irq = irq_of_parse_and_map(op->node, 0);
if (ata_irq == NO_IRQ) {
printk(KERN_ERR "mpc52xx-ide: Invalid IRQ!\n");
return -EINVAL;
}
hwif->drives[0].autotune = IDE_TUNE_NOAUTO;
hwif->drives[1].autotune = IDE_TUNE_NOAUTO;
hwif->drives[0].unmask = 1;
hwif->drives[0].autodma = hwif->autodma;
hwif->drives[0].no_io_32bit = 1;
hwif->irq = ata_irq;
hwif->mmio = 0;
hwif->no_io_32bit = 1;
hwif->atapi_dma = 0;
hwif->ultra_mask = 0x00;
hwif->mwdma_mask = 0x00;
hwif->swdma_mask = 0x00;
hwif->present = 1;
hwif->noprobe = 0;
hwif->sharing_irq = 0;
hwif->no_lba48 = 1;
hwif->no_lba48_dma = 1;
hwif->chipset = ide_forced;
default_hwif_mmiops(hwif);
dev_set_drvdata(&op->dev, hwif);
probe_hwif_init(hwif);
printk("Wat denn hier lose\n");
/* if (i == -1) {
printk(KERN_INFO "EL392_ATA: Registering IDE failed\n");
ret = -EIO;
goto no_ide;
}*/
// ide_proc_register_port(hwif);
printk(KERN_INFO
"EL392_IDE: Setup successful for %s (mem=%08lx-%08lx irq=%d), i = %d\n",
hwif->name, (unsigned long) res_mem.start, (unsigned long) res_mem.end, hwif->irq, i);
ret = 0;
goto out;
no_ide:
iounmap(ioaddr);
no_mem:
release_mem_region(res_start, res_length);
out:
return 0;
}
static int __devexit el392_ata_remove(struct of_device *op)
{
unsigned long res_start;
unsigned long res_length;
ide_hwif_t *hwif = dev_get_drvdata(&op->dev);
struct resource res_mem;
if (of_address_to_resource(op->node, 0, &res_mem) != 0)
{
printk(KERN_ERR "EL392_ATA: Can not find IO mem!\n");
return -EIO;
}
if (!hwif) {
printk(KERN_ERR "%s: Unable to remove device, please report.\n", DRIVER_NAME);
return -EIO;
}
ide_unregister(hwif->index);
iounmap((void*)hwif->hw.io_ports[0]);
res_start = res_mem.start;
res_length = res_mem.end - res_mem.start;
release_mem_region(res_start, res_length);
return 0;
}
static int __init el392_ata_init(void)
{
printk(KERN_INFO "IDE: EL392 IDE/ATA driver\n");
return of_register_platform_driver(&el392_ide_of_platform_driver);
}
static void __exit el392_ata_exit(void)
{
of_unregister_platform_driver(&el392_ide_of_platform_driver);
printk( KERN_DEBUG "Module EL392_CF_DISK exit\n" );
}
module_init(el392_ata_init);
module_exit(el392_ata_exit);
MODULE_DESCRIPTION("EL392 CF_Card Kernel driver");
MODULE_AUTHOR("Hans Lehmann (hans.lehmann@ritter-elektronik.de)");
MODULE_LICENSE("GPL");
^ permalink raw reply
* Re: [PATCH] [POWERPC] MPC8360E-RDK: device tree, board file and defconfig
From: Anton Vorontsov @ 2008-01-24 13:29 UTC (permalink / raw)
To: Stephen Rothwell; +Cc: linuxppc-dev
In-Reply-To: <20080124132201.c3086756.sfr@canb.auug.org.au>
On Thu, Jan 24, 2008 at 01:22:01PM +1100, Stephen Rothwell wrote:
> On Wed, 23 Jan 2008 23:45:07 +0300 Anton Vorontsov <avorontsov@ru.mvista.com> wrote:
> >
> > +++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c
> > +static int __init mpc836x_rdk_declare_of_platform_devices(void)
> > +{
> > + const struct of_device_id mpc836x_rdk_ids[] = {
>
> Please change this to static, __initdata and not const, thanks. These
> arrays are usually defined outside a function, but that is not really a
> problem.
Pity... we don't need these _ids anywhere outside that function. But
looking into objdump output I see the problem now.
Will change this back, thanks.
> > +static void __init mpc836x_rdk_setup_arch(void)
> > +{
> > + struct device_node *np;
>
> !defined(CONFIG_PCI) && !defined(CONFIG_QUICC_ENGINE) will produce a
> warning here.
Good catch, thanks.
> > + if (ppc_md.progress)
> > + ppc_md.progress("mpc836x_rdk_setup_arch()", 0);
> > +
> > +#ifdef CONFIG_PCI
> > + for_each_compatible_node(np, "pci", "fsl,mpc8349-pci")
> > + mpc83xx_add_bridge(np);
> > +#endif
> > +
> > +#ifdef CONFIG_QUICC_ENGINE
> > + qe_reset();
> > +
> > + np = of_find_compatible_node(NULL, NULL, "fsl,qe-pario");
> > + if (!np) {
> > + pr_warning("QE PIO not initialized!\n");
> > + return;
>
> You need an of_node_put(np) before the return.
Hm, really? I think I don't. But if you insist... :-)
--
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2
^ permalink raw reply
* mpc8247 porting problem
From: chengk_thomson @ 2008-01-24 13:45 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 305 bytes --]
Hello:
I'm porting linux 2.6.18 to a MPC8247 platform.
now I encounter a strange situation. kernel enter a dead loop in __div64_32() which is called by time_init() in start_kernel().
I have modify the dtb file according the customer's board.
does anybody has any idea?
BR
2008-01-24
chengk_thomson
[-- Attachment #2: Type: text/html, Size: 1631 bytes --]
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox