aboutsummaryrefslogtreecommitdiffstats
path: root/package/ifxmips-atm/src/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'package/ifxmips-atm/src/core.c')
-rw-r--r--package/ifxmips-atm/src/core.c800
1 files changed, 800 insertions, 0 deletions
diff --git a/package/ifxmips-atm/src/core.c b/package/ifxmips-atm/src/core.c
new file mode 100644
index 0000000000..95b05f3dbd
--- /dev/null
+++ b/package/ifxmips-atm/src/core.c
@@ -0,0 +1,800 @@
+#include <asm/mach-ifxmips/cgu.h>
+#include <linux/module.h>
+#include <linux/atmdev.h>
+#include <linux/irq.h>
+
+#include "common.h"
+#include "proc.h"
+
+// our main struct
+struct ppe_dev ppe_dev;
+
+static int port_max_connection[2] = {7, 7}; /* Maximum number of connections for ports (0-14) */
+static int port_cell_rate_up[2] = {3200, 3200}; /* Maximum TX cell rate for ports */
+static int qsb_tau = 1;
+static int qsb_srvm = 0x0f;
+static int qsb_tstep = 4;
+static int write_descriptor_delay = 0x20;
+static int aal5_fill_pattern = 0x007E;
+static int aal5r_max_packet_size = 0x0700;
+static int aal5r_min_packet_size = 0x0000;
+static int aal5s_max_packet_size = 0x0700;
+static int aal5s_min_packet_size = 0x0000;
+static int aal5r_drop_error_packet = 1;
+static int dma_rx_descriptor_length = 48;
+static int dma_tx_descriptor_length = 64;
+static int dma_rx_clp1_descriptor_threshold = 38;
+
+//module_param(port_max_connection, "2-2i");
+//module_param(port_cell_rate_up, "2-2i");
+module_param(qsb_tau, int, 0);
+module_param(qsb_srvm, int, 0);
+module_param(qsb_tstep, int, 0);
+module_param(write_descriptor_delay, int, 0);
+module_param(aal5_fill_pattern, int, 0);
+module_param(aal5r_max_packet_size, int, 0);
+module_param(aal5r_min_packet_size, int, 0);
+module_param(aal5s_max_packet_size, int, 0);
+module_param(aal5s_min_packet_size, int, 0);
+module_param(aal5r_drop_error_packet, int, 0);
+module_param(dma_rx_descriptor_length, int, 0);
+module_param(dma_tx_descriptor_length, int, 0);
+module_param(dma_rx_clp1_descriptor_threshold, int, 0);
+
+MODULE_PARM_DESC(port_cell_rate_up, "ATM port upstream rate in cells/s");
+MODULE_PARM_DESC(port_max_connection, "Maximum atm connection for port (0-1)");
+MODULE_PARM_DESC(qsb_tau, "Cell delay variation. Value must be > 0");
+MODULE_PARM_DESC(qsb_srvm, "Maximum burst size");
+MODULE_PARM_DESC(qsb_tstep, "n*32 cycles per sbs cycles n=1,2,4");
+MODULE_PARM_DESC(write_descriptor_delay, "PPE core clock cycles between descriptor write and effectiveness in external RAM");
+MODULE_PARM_DESC(a5_fill_pattern, "Filling pattern (PAD) for AAL5 frames");
+MODULE_PARM_DESC(aal5r_max_packet_size, "Max packet size in byte for downstream AAL5 frames");
+MODULE_PARM_DESC(aal5r_min_packet_size, "Min packet size in byte for downstream AAL5 frames");
+MODULE_PARM_DESC(aal5s_max_packet_size, "Max packet size in byte for upstream AAL5 frames");
+MODULE_PARM_DESC(aal5s_min_packet_size, "Min packet size in byte for upstream AAL5 frames");
+MODULE_PARM_DESC(aal5r_drop_error_packet, "Non-zero value to drop error packet for downstream");
+MODULE_PARM_DESC(dma_rx_descriptor_length, "Number of descriptor assigned to DMA RX channel (>16)");
+MODULE_PARM_DESC(dma_tx_descriptor_length, "Number of descriptor assigned to DMA TX channel (>16)");
+MODULE_PARM_DESC(dma_rx_clp1_descriptor_threshold, "Descriptor threshold for cells with cell loss priority 1");
+
+void init_rx_tables(void)
+{
+ int i, j;
+ struct wrx_queue_config wrx_queue_config = {0};
+ struct wrx_dma_channel_config wrx_dma_channel_config = {0};
+ struct htu_entry htu_entry = {0};
+ struct htu_result htu_result = {0};
+
+ struct htu_mask htu_mask = { set: 0x03,
+ pid_mask: 0x00,
+ vpi_mask: 0x00,
+ vci_mask: 0x00,
+ pti_mask: 0x00,
+ clear: 0x00};
+
+ /*
+ * General Registers
+ */
+ *CFG_WRX_HTUTS = ppe_dev.max_connections + OAM_HTU_ENTRY_NUMBER;
+ *CFG_WRX_QNUM = ppe_dev.max_connections + OAM_RX_QUEUE_NUMBER + QSB_QUEUE_NUMBER_BASE;
+ *CFG_WRX_DCHNUM = ppe_dev.dma.rx_total_channel_used;
+ *WRX_DMACH_ON = (1 << ppe_dev.dma.rx_total_channel_used) - 1;
+ *WRX_HUNT_BITTH = DEFAULT_RX_HUNT_BITTH;
+
+ /*
+ * WRX Queue Configuration Table
+ */
+ wrx_queue_config.uumask = 0;
+ wrx_queue_config.cpimask = 0;
+ wrx_queue_config.uuexp = 0;
+ wrx_queue_config.cpiexp = 0;
+ wrx_queue_config.mfs = ppe_dev.aal5.rx_max_packet_size; // rx_buffer_size
+ wrx_queue_config.oversize = ppe_dev.aal5.rx_max_packet_size;
+ wrx_queue_config.undersize = ppe_dev.aal5.rx_min_packet_size;
+ wrx_queue_config.errdp = ppe_dev.aal5.rx_drop_error_packet;
+ for ( i = 0; i < QSB_QUEUE_NUMBER_BASE; i++ )
+ *WRX_QUEUE_CONFIG(i) = wrx_queue_config;
+ for ( j = 0; j < ppe_dev.max_connections; j++ )
+ {
+#if !defined(ENABLE_RX_QOS) || !ENABLE_RX_QOS
+ /* If RX QoS is disabled, the DMA channel must be fixed. */
+ wrx_queue_config.dmach = ppe_dev.connection[i].rx_dma_channel;
+#endif // !defined(ENABLE_RX_QOS) || !ENABLE_RX_QOS
+ *WRX_QUEUE_CONFIG(i++) = wrx_queue_config;
+ }
+ /* OAM RX Queue */
+ for ( j = 0; j < OAM_RX_DMA_CHANNEL_NUMBER; j++ )
+ {
+#if defined(ENABLE_RX_QOS) && ENABLE_RX_QOS
+ wrx_queue_config.dmach = RX_DMA_CH_OAM;
+#else
+ wrx_queue_config.dmach = ppe_dev.oam_rx_dma_channel + j;
+#endif // defined(ENABLE_RX_QOS) && ENABLE_RX_QOS
+ *WRX_QUEUE_CONFIG(i++) = wrx_queue_config;
+ }
+
+ wrx_dma_channel_config.deslen = ppe_dev.dma.rx_descriptor_number;
+ wrx_dma_channel_config.chrl = 0;
+ wrx_dma_channel_config.clp1th = ppe_dev.dma.rx_clp1_desc_threshold;
+ wrx_dma_channel_config.mode = WRX_DMA_CHANNEL_COUNTER_MODE;
+ wrx_dma_channel_config.rlcfg = WRX_DMA_BUF_LEN_PER_DESCRIPTOR;
+ for ( i = 0; i < ppe_dev.dma.rx_total_channel_used; i++ )
+ {
+ wrx_dma_channel_config.desba = (((u32)ppe_dev.dma.rx_descriptor_base >> 2) & 0x0FFFFFFF) + ppe_dev.dma.rx_descriptor_number * i * (sizeof(struct rx_descriptor) >> 2);
+ *WRX_DMA_CHANNEL_CONFIG(i) = wrx_dma_channel_config;
+ }
+
+ /*
+ * HTU Tables
+ */
+ for ( i = 0; i < ppe_dev.max_connections; i++ )
+ {
+ htu_result.qid = (unsigned int)i;
+
+ *HTU_ENTRY(i + OAM_HTU_ENTRY_NUMBER) = htu_entry;
+ *HTU_MASK(i + OAM_HTU_ENTRY_NUMBER) = htu_mask;
+ *HTU_RESULT(i + OAM_HTU_ENTRY_NUMBER) = htu_result;
+ }
+ /* OAM HTU Entry */
+ htu_entry.vci = 0x03;
+ htu_mask.pid_mask = 0x03;
+ htu_mask.vpi_mask = 0xFF;
+ htu_mask.vci_mask = 0x0000;
+ htu_mask.pti_mask = 0x07;
+ htu_result.cellid = ppe_dev.oam_rx_queue;
+ htu_result.type = 1;
+ htu_result.ven = 1;
+ htu_result.qid = ppe_dev.oam_rx_queue;
+ *HTU_RESULT(OAM_F4_SEG_HTU_ENTRY) = htu_result;
+ *HTU_MASK(OAM_F4_SEG_HTU_ENTRY) = htu_mask;
+ *HTU_ENTRY(OAM_F4_SEG_HTU_ENTRY) = htu_entry;
+ htu_entry.vci = 0x04;
+ htu_result.cellid = ppe_dev.oam_rx_queue;
+ htu_result.type = 1;
+ htu_result.ven = 1;
+ htu_result.qid = ppe_dev.oam_rx_queue;
+ *HTU_RESULT(OAM_F4_TOT_HTU_ENTRY) = htu_result;
+ *HTU_MASK(OAM_F4_TOT_HTU_ENTRY) = htu_mask;
+ *HTU_ENTRY(OAM_F4_TOT_HTU_ENTRY) = htu_entry;
+ htu_entry.vci = 0x00;
+ htu_entry.pti = 0x04;
+ htu_mask.vci_mask = 0xFFFF;
+ htu_mask.pti_mask = 0x01;
+ htu_result.cellid = ppe_dev.oam_rx_queue;
+ htu_result.type = 1;
+ htu_result.ven = 1;
+ htu_result.qid = ppe_dev.oam_rx_queue;
+ *HTU_RESULT(OAM_F5_HTU_ENTRY) = htu_result;
+ *HTU_MASK(OAM_F5_HTU_ENTRY) = htu_mask;
+ *HTU_ENTRY(OAM_F5_HTU_ENTRY) = htu_entry;
+}
+
+void init_tx_tables(void)
+{
+ int i, j;
+ struct wtx_queue_config wtx_queue_config = {0};
+ struct wtx_dma_channel_config wtx_dma_channel_config = {0};
+
+ struct wtx_port_config wtx_port_config = { res1: 0,
+ qid: 0,
+ qsben: 1};
+
+ /*
+ * General Registers
+ */
+ *CFG_WTX_DCHNUM = ppe_dev.dma.tx_total_channel_used + QSB_QUEUE_NUMBER_BASE;
+ *WTX_DMACH_ON = ((1 << (ppe_dev.dma.tx_total_channel_used + QSB_QUEUE_NUMBER_BASE)) - 1) ^ ((1 << QSB_QUEUE_NUMBER_BASE) - 1);
+ *CFG_WRDES_DELAY = ppe_dev.dma.write_descriptor_delay;
+
+ /*
+ * WTX Port Configuration Table
+ */
+#if !defined(DISABLE_QSB) || !DISABLE_QSB
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ *WTX_PORT_CONFIG(i) = wtx_port_config;
+#else
+ wtx_port_config.qsben = 0;
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ {
+ wtx_port_config.qid = ppe_dev.port[i].connection_base;
+ *WTX_PORT_CONFIG(i) = wtx_port_config;
+
+printk("port %d: qid = %d, qsb disabled\n", i, wtx_port_config.qid);
+ }
+#endif
+
+ /*
+ * WTX Queue Configuration Table
+ */
+ wtx_queue_config.res1 = 0;
+ wtx_queue_config.res2 = 0;
+// wtx_queue_config.type = 0x03;
+ wtx_queue_config.type = 0x0;
+#if !defined(DISABLE_QSB) || !DISABLE_QSB
+ wtx_queue_config.qsben = 1;
+#else
+ wtx_queue_config.qsben = 0;
+#endif
+ wtx_queue_config.sbid = 0;
+ for ( i = 0; i < QSB_QUEUE_NUMBER_BASE; i++ )
+ *WTX_QUEUE_CONFIG(i) = wtx_queue_config;
+ for ( j = 0; j < ppe_dev.max_connections; j++ )
+ {
+ wtx_queue_config.sbid = ppe_dev.connection[i].port & 0x01; /* assign QSB to TX queue */
+ *WTX_QUEUE_CONFIG(i) = wtx_queue_config;
+ i++;
+ }
+ /* OAM TX Queue */
+// wtx_queue_config.type = 0x01;
+ wtx_queue_config.type = 0x00;
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ {
+ wtx_queue_config.sbid = i & 0x01;
+ for ( j = 0; j < OAM_TX_QUEUE_NUMBER_PER_PORT; j++ )
+ *WTX_QUEUE_CONFIG(ppe_dev.port[i].oam_tx_queue + j) = wtx_queue_config;
+ }
+
+ wtx_dma_channel_config.mode = WRX_DMA_CHANNEL_COUNTER_MODE;
+ wtx_dma_channel_config.deslen = 0;
+ wtx_dma_channel_config.desba = 0;
+ for ( i = 0; i < QSB_QUEUE_NUMBER_BASE; i++ )
+ *WTX_DMA_CHANNEL_CONFIG(i) = wtx_dma_channel_config;
+ /* normal connection and OAM channel */
+ wtx_dma_channel_config.deslen = ppe_dev.dma.tx_descriptor_number;
+ for ( j = 0; j < ppe_dev.dma.tx_total_channel_used; j++ )
+ {
+ wtx_dma_channel_config.desba = (((u32)ppe_dev.dma.tx_descriptor_base >> 2) & 0x0FFFFFFF) + ppe_dev.dma.tx_descriptor_number * j * (sizeof(struct tx_descriptor) >> 2);
+ *WTX_DMA_CHANNEL_CONFIG(i++) = wtx_dma_channel_config;
+ }
+}
+
+static inline void qsb_global_set(void)
+{
+ int i, j;
+ u32 qsb_clk = cgu_get_fpi_bus_clock(2);
+ u32 tmp1, tmp2, tmp3;
+ union qsb_queue_parameter_table qsb_queue_parameter_table = {{0}};
+ union qsb_queue_vbr_parameter_table qsb_queue_vbr_parameter_table = {{0}};
+ int qsb_qid;
+
+ *QSB_ICDV = QSB_ICDV_TAU_SET(ppe_dev.qsb.tau);
+ *QSB_SBL = QSB_SBL_SBL_SET(ppe_dev.qsb.sbl);
+ *QSB_CFG = QSB_CFG_TSTEPC_SET(ppe_dev.qsb.tstepc >> 1);
+
+ /*
+ * set SCT and SPT per port
+ */
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ if ( ppe_dev.port[i].max_connections != 0 && ppe_dev.port[i].tx_max_cell_rate != 0 )
+ {
+ tmp1 = ((qsb_clk * ppe_dev.qsb.tstepc) >> 1) / ppe_dev.port[i].tx_max_cell_rate;
+ tmp2 = tmp1 >> 6; /* integer value of Tsb */
+ tmp3 = (tmp1 & ((1 << 6) - 1)) + 1; /* fractional part of Tsb */
+ /* carry over to integer part (?) */
+ if ( tmp3 == (1 << 6) )
+ {
+ tmp3 = 0;
+ tmp2++;
+ }
+ if ( tmp2 == 0 )
+ tmp2 = tmp3 = 1;
+ /* 1. set mask */
+ /* 2. write value to data transfer register */
+ /* 3. start the tranfer */
+ /* SCT (FracRate) */
+ *QSB_RTM = QSB_RTM_DM_SET(QSB_SET_SCT_MASK);
+ *QSB_RTD = QSB_RTD_TTV_SET(tmp3);
+ *QSB_RAMAC = QSB_RAMAC_RW_SET(QSB_RAMAC_RW_WRITE) | QSB_RAMAC_TSEL_SET(QSB_RAMAC_TSEL_SCT) | QSB_RAMAC_LH_SET(QSB_RAMAC_LH_LOW) | QSB_RAMAC_TESEL_SET(i & 0x01);
+ /* SPT (SBV + PN + IntRage) */
+ *QSB_RTM = QSB_RTM_DM_SET(QSB_SET_SPT_MASK);
+ *QSB_RTD = QSB_RTD_TTV_SET(QSB_SPT_SBV_VALID | QSB_SPT_PN_SET(i & 0x01) | QSB_SPT_INTRATE_SET(tmp2));
+ *QSB_RAMAC = QSB_RAMAC_RW_SET(QSB_RAMAC_RW_WRITE) | QSB_RAMAC_TSEL_SET(QSB_RAMAC_TSEL_SPT) | QSB_RAMAC_LH_SET(QSB_RAMAC_LH_LOW) | QSB_RAMAC_TESEL_SET(i & 0x01);
+ }
+
+ /*
+ * set OAM TX queue
+ */
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ if ( ppe_dev.port[i].max_connections != 0 )
+ {
+ tmp1 = ((qsb_clk * ppe_dev.qsb.tstepc) >> 1) / ppe_dev.port[i].tx_max_cell_rate;
+ tmp2 = tmp1 >> 6; /* integer value of Tsb */
+ tmp3 = (tmp1 & ((1 << 6) - 1)) + 1; /* fractional part of Tsb */
+ /* carry over to integer part (?) */
+ if ( tmp3 == (1 << 6) )
+ {
+ tmp3 = 0;
+ tmp2++;
+ }
+ if ( tmp2 == 0 )
+ tmp2 = tmp3 = 1;
+ /* 1. set mask */
+ /* 2. write value to data transfer register */
+ /* 3. start the tranfer */
+ /* SCT (FracRate) */
+ *QSB_RTM = QSB_RTM_DM_SET(QSB_SET_SCT_MASK);
+ *QSB_RTD = QSB_RTD_TTV_SET(tmp3);
+ *QSB_RAMAC = QSB_RAMAC_RW_SET(QSB_RAMAC_RW_WRITE) | QSB_RAMAC_TSEL_SET(QSB_RAMAC_TSEL_SCT) | QSB_RAMAC_LH_SET(QSB_RAMAC_LH_LOW) | QSB_RAMAC_TESEL_SET(i & 0x01);
+
+ /* SPT (SBV + PN + IntRage) */
+ *QSB_RTM = QSB_RTM_DM_SET(QSB_SET_SPT_MASK);
+ *QSB_RTD = QSB_RTD_TTV_SET(QSB_SPT_SBV_VALID | QSB_SPT_PN_SET(i & 0x01) | QSB_SPT_INTRATE_SET(tmp2));
+ *QSB_RAMAC = QSB_RAMAC_RW_SET(QSB_RAMAC_RW_WRITE) | QSB_RAMAC_TSEL_SET(QSB_RAMAC_TSEL_SPT) | QSB_RAMAC_LH_SET(QSB_RAMAC_LH_LOW) | QSB_RAMAC_TESEL_SET(i & 0x01);
+ }
+
+ /*
+ * * set OAM TX queue
+ * */
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ if ( ppe_dev.port[i].max_connections != 0 )
+ for ( j = 0; j < OAM_TX_QUEUE_NUMBER_PER_PORT; j++ )
+ {
+ qsb_qid = ppe_dev.port[i].oam_tx_queue + j;
+
+ /* disable PCR limiter */
+ qsb_queue_parameter_table.bit.tp = 0;
+ /* set WFQ as real time queue */
+ qsb_queue_parameter_table.bit.wfqf = 0;
+ /* disable leaky bucket shaper */
+ qsb_queue_vbr_parameter_table.bit.taus = 0;
+ qsb_queue_vbr_parameter_table.bit.ts = 0;
+
+ /* Queue Parameter Table (QPT) */
+ *QSB_RTM = QSB_RTM_DM_SET(QSB_QPT_SET_MASK);
+ *QSB_RTD = QSB_RTD_TTV_SET(qsb_queue_parameter_table.dword);
+ *QSB_RAMAC = QSB_RAMAC_RW_SET(QSB_RAMAC_RW_WRITE) | QSB_RAMAC_TSEL_SET(QSB_RAMAC_TSEL_QPT) | QSB_RAMAC_LH_SET(QSB_RAMAC_LH_LOW) | QSB_RAMAC_TESEL_SET(qsb_qid);
+ /* Queue VBR Paramter Table (QVPT) */
+ *QSB_RTM = QSB_RTM_DM_SET(QSB_QVPT_SET_MASK);
+ *QSB_RTD = QSB_RTD_TTV_SET(qsb_queue_vbr_parameter_table.dword);
+ *QSB_RAMAC = QSB_RAMAC_RW_SET(QSB_RAMAC_RW_WRITE) | QSB_RAMAC_TSEL_SET(QSB_RAMAC_TSEL_VBR) | QSB_RAMAC_LH_SET(QSB_RAMAC_LH_LOW) | QSB_RAMAC_TESEL_SET(qsb_qid);
+ }
+}
+
+static inline void clear_ppe_dev(void)
+{
+ int i;
+
+ for (i = 0; i < ppe_dev.dma.tx_total_channel_used; i++ )
+ {
+ int conn = i + QSB_QUEUE_NUMBER_BASE;
+ int desc_base;
+ struct sk_buff *skb;
+
+ while(ppe_dev.dma.tx_desc_release_pos[conn] != ppe_dev.dma.tx_desc_alloc_pos[conn])
+ {
+ desc_base = ppe_dev.dma.tx_descriptor_number * (conn - QSB_QUEUE_NUMBER_BASE) + ppe_dev.dma.tx_desc_release_pos[conn];
+ if(!ppe_dev.dma.tx_descriptor_base[desc_base].own)
+ {
+ skb = ppe_dev.dma.tx_skb_pointers[desc_base];
+ atm_free_tx_skb_vcc(skb);
+
+ // pretend PP32 hold owner bit, so that won't be released more than once, so allocation process don't check this bit
+ ppe_dev.dma.tx_descriptor_base[desc_base].own = 1;
+ }
+ if (++ppe_dev.dma.tx_desc_release_pos[conn] == ppe_dev.dma.tx_descriptor_number)
+ ppe_dev.dma.tx_desc_release_pos[conn] = 0;
+ }
+ }
+
+ for (i = ppe_dev.dma.rx_total_channel_used * ppe_dev.dma.rx_descriptor_number - 1; i >= 0; i--)
+ dev_kfree_skb_any(*(struct sk_buff **)(((ppe_dev.dma.rx_descriptor_base[i].dataptr << 2) | KSEG0) - 4));
+
+ kfree(ppe_dev.dma.tx_skb_pointers);
+ kfree(ppe_dev.dma.tx_descriptor_addr);
+ kfree(ppe_dev.dma.rx_descriptor_addr);
+}
+
+static inline int init_ppe_dev(void)
+{
+ int i, j;
+ int rx_desc, tx_desc;
+ int conn;
+ int oam_tx_queue;
+#if !defined(ENABLE_RX_QOS) || !ENABLE_RX_QOS
+ int rx_dma_channel_base;
+ int rx_dma_channel_assigned;
+#endif // !defined(ENABLE_RX_QOS) || !ENABLE_RX_QOS
+
+ struct rx_descriptor rx_descriptor = { own: 1,
+ c: 0,
+ sop: 1,
+ eop: 1,
+ res1: 0,
+ byteoff:0,
+ res2: 0,
+ id: 0,
+ err: 0,
+ datalen:0,
+ res3: 0,
+ dataptr:0};
+
+ struct tx_descriptor tx_descriptor = { own: 1, // pretend it's hold by PP32
+ c: 0,
+ sop: 1,
+ eop: 1,
+ byteoff:0,
+ res1: 0,
+ iscell: 0,
+ clp: 0,
+ datalen:0,
+ res2: 0,
+ dataptr:0};
+
+ memset(&ppe_dev, 0, sizeof(ppe_dev));
+
+ /*
+ * Setup AAL5 members, buffer size must be larger than max packet size plus overhead.
+ */
+ ppe_dev.aal5.padding_byte = (u8)aal5_fill_pattern;
+ ppe_dev.aal5.rx_max_packet_size = (u32)aal5r_max_packet_size;
+ ppe_dev.aal5.rx_min_packet_size = (u32)aal5r_min_packet_size;
+ ppe_dev.aal5.rx_buffer_size = ((u32)(aal5r_max_packet_size > CELL_SIZE ? aal5r_max_packet_size + MAX_RX_FRAME_EXTRA_BYTES : CELL_SIZE + MAX_RX_FRAME_EXTRA_BYTES) + DMA_ALIGNMENT - 1) & ~(DMA_ALIGNMENT - 1);
+ ppe_dev.aal5.tx_max_packet_size = (u32)aal5s_max_packet_size;
+ ppe_dev.aal5.tx_min_packet_size = (u32)aal5s_min_packet_size;
+ ppe_dev.aal5.tx_buffer_size = ((u32)(aal5s_max_packet_size > CELL_SIZE ? aal5s_max_packet_size + MAX_TX_FRAME_EXTRA_BYTES : CELL_SIZE + MAX_TX_FRAME_EXTRA_BYTES) + DMA_ALIGNMENT - 1) & ~(DMA_ALIGNMENT - 1);
+ ppe_dev.aal5.rx_drop_error_packet = aal5r_drop_error_packet ? 1 : 0;
+
+ /*
+ * Setup QSB members, please refer to Amazon spec 15.4 to get the value calculation formula.
+ */
+ ppe_dev.qsb.tau = (u32)qsb_tau;
+ ppe_dev.qsb.tstepc = (u32)qsb_tstep;
+ ppe_dev.qsb.sbl = (u32)qsb_srvm;
+
+ /*
+ * Setup port, connection, other members.
+ */
+ conn = 0;
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ {
+ /* first connection ID of port */
+ ppe_dev.port[i].connection_base = conn + QSB_QUEUE_NUMBER_BASE;
+ /* max number of connections of port */
+ ppe_dev.port[i].max_connections = (u32)port_max_connection[i];
+ /* max cell rate the port has */
+ ppe_dev.port[i].tx_max_cell_rate = (u32)port_cell_rate_up[i];
+
+ /* link connection ID to port ID */
+ for ( j = port_max_connection[i] - 1; j >= 0; j-- )
+ ppe_dev.connection[conn++ + QSB_QUEUE_NUMBER_BASE].port = i;
+ }
+ /* total connection numbers of all ports */
+ ppe_dev.max_connections = conn;
+ /* OAM RX queue ID, which is the first available connection ID after */
+ /* connections assigned to ports. */
+ ppe_dev.oam_rx_queue = conn + QSB_QUEUE_NUMBER_BASE;
+
+#if defined(ENABLE_RX_QOS) && ENABLE_RX_QOS
+ oam_tx_queue = conn;
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ if ( port_max_connection[i] != 0 )
+ {
+ ppe_dev.port[i].oam_tx_queue = oam_tx_queue + QSB_QUEUE_NUMBER_BASE;
+
+ for ( j = 0; j < OAM_TX_QUEUE_NUMBER_PER_PORT; j++ )
+ /* Since connection ID is one to one mapped to RX/TX queue ID, the connection */
+ /* structure must be reserved for OAM RX/TX queues, and member "port" is set */
+ /* according to port to which OAM TX queue is connected. */
+ ppe_dev.connection[oam_tx_queue++ + QSB_QUEUE_NUMBER_BASE].port = i;
+ }
+ /* DMA RX channel assigned to OAM RX queue */
+ ppe_dev.oam_rx_dma_channel = RX_DMA_CH_OAM;
+ /* DMA RX channel will be assigned dynamically when VCC is open. */
+#else // defined(ENABLE_RX_QOS) && ENABLE_RX_QOS
+ rx_dma_channel_base = 0;
+ oam_tx_queue = conn;
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ if ( port_max_connection[i] != 0 )
+ {
+ /* Calculate the number of DMA RX channels could be assigned to port. */
+ rx_dma_channel_assigned = i == ATM_PORT_NUMBER - 1
+ ? (MAX_RX_DMA_CHANNEL_NUMBER - OAM_RX_DMA_CHANNEL_NUMBER) - rx_dma_channel_base
+ : (ppe_dev.port[i].max_connections * (MAX_RX_DMA_CHANNEL_NUMBER - OAM_RX_DMA_CHANNEL_NUMBER) + ppe_dev.max_connections / 2) / ppe_dev.max_connections;
+ /* Amend the number, which could be zero. */
+ if ( rx_dma_channel_assigned == 0 )
+ rx_dma_channel_assigned = 1;
+ /* Calculate the first DMA RX channel ID could be assigned to port. */
+ if ( rx_dma_channel_base + rx_dma_channel_assigned > MAX_RX_DMA_CHANNEL_NUMBER - OAM_RX_DMA_CHANNEL_NUMBER )
+ rx_dma_channel_base = MAX_RX_DMA_CHANNEL_NUMBER - OAM_RX_DMA_CHANNEL_NUMBER - rx_dma_channel_assigned;
+
+ /* first DMA RX channel ID */
+ ppe_dev.port[i].rx_dma_channel_base = rx_dma_channel_base;
+ /* number of DMA RX channels assigned to this port */
+ ppe_dev.port[i].rx_dma_channel_assigned = rx_dma_channel_assigned;
+ /* OAM TX queue ID, which must be assigned after connections assigned to ports */
+ ppe_dev.port[i].oam_tx_queue = oam_tx_queue + QSB_QUEUE_NUMBER_BASE;
+
+ rx_dma_channel_base += rx_dma_channel_assigned;
+
+ for ( j = 0; j < OAM_TX_QUEUE_NUMBER_PER_PORT; j++ )
+ /* Since connection ID is one to one mapped to RX/TX queue ID, the connection */
+ /* structure must be reserved for OAM RX/TX queues, and member "port" is set */
+ /* according to port to which OAM TX queue is connected. */
+ ppe_dev.connection[oam_tx_queue++ + QSB_QUEUE_NUMBER_BASE].port = i;
+ }
+ /* DMA RX channel assigned to OAM RX queue */
+ ppe_dev.oam_rx_dma_channel = rx_dma_channel_base;
+
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ for ( j = 0; j < port_max_connection[i]; j++ )
+ /* Assign DMA RX channel to RX queues. One channel could be assigned to more than one queue. */
+ ppe_dev.connection[ppe_dev.port[i].connection_base + j].rx_dma_channel = ppe_dev.port[i].rx_dma_channel_base + j % ppe_dev.port[i].rx_dma_channel_assigned;
+#endif // defined(ENABLE_RX_QOS) && ENABLE_RX_QOS
+
+ /* initialize semaphore used by open and close */
+ sema_init(&ppe_dev.sem, 1);
+ /* descriptor number of RX DMA channel */
+ ppe_dev.dma.rx_descriptor_number = dma_rx_descriptor_length;
+ /* descriptor number of TX DMA channel */
+ ppe_dev.dma.tx_descriptor_number = dma_tx_descriptor_length;
+ /* If used descriptors are more than this value, cell with CLP1 is dropped. */
+ ppe_dev.dma.rx_clp1_desc_threshold = dma_rx_clp1_descriptor_threshold;
+
+ /* delay on descriptor write path */
+ ppe_dev.dma.write_descriptor_delay = write_descriptor_delay;
+
+ /* total DMA RX channel used */
+#if defined(ENABLE_RX_QOS) && ENABLE_RX_QOS
+ ppe_dev.dma.rx_total_channel_used = RX_DMA_CH_TOTAL;
+#else
+ ppe_dev.dma.rx_total_channel_used = rx_dma_channel_base + OAM_RX_DMA_CHANNEL_NUMBER;
+#endif // defined(ENABLE_RX_QOS) && ENABLE_RX_QOS
+ /* total DMA TX channel used (exclude channel reserved by QSB) */
+ ppe_dev.dma.tx_total_channel_used = oam_tx_queue;
+
+ /* allocate memory for RX descriptors */
+ ppe_dev.dma.rx_descriptor_addr = kmalloc(ppe_dev.dma.rx_total_channel_used * ppe_dev.dma.rx_descriptor_number * sizeof(struct rx_descriptor) + 4, GFP_KERNEL | GFP_DMA);
+ if ( !ppe_dev.dma.rx_descriptor_addr )
+ goto RX_DESCRIPTOR_BASE_ALLOCATE_FAIL;
+ /* do alignment (DWORD) */
+ ppe_dev.dma.rx_descriptor_base = (struct rx_descriptor *)(((u32)ppe_dev.dma.rx_descriptor_addr + 0x03) & ~0x03);
+ ppe_dev.dma.rx_descriptor_base = (struct rx_descriptor *)((u32)ppe_dev.dma.rx_descriptor_base | KSEG1); // no cache
+
+ /* allocate memory for TX descriptors */
+ ppe_dev.dma.tx_descriptor_addr = kmalloc(ppe_dev.dma.tx_total_channel_used * ppe_dev.dma.tx_descriptor_number * sizeof(struct tx_descriptor) + 4, GFP_KERNEL | GFP_DMA);
+ if ( !ppe_dev.dma.tx_descriptor_addr )
+ goto TX_DESCRIPTOR_BASE_ALLOCATE_FAIL;
+ /* do alignment (DWORD) */
+ ppe_dev.dma.tx_descriptor_base = (struct tx_descriptor *)(((u32)ppe_dev.dma.tx_descriptor_addr + 0x03) & ~0x03);
+ ppe_dev.dma.tx_descriptor_base = (struct tx_descriptor *)((u32)ppe_dev.dma.tx_descriptor_base | KSEG1); // no cache
+ /* allocate pointers to TX sk_buff */
+ ppe_dev.dma.tx_skb_pointers = kmalloc(ppe_dev.dma.tx_total_channel_used * ppe_dev.dma.tx_descriptor_number * sizeof(struct sk_buff *), GFP_KERNEL);
+ if ( !ppe_dev.dma.tx_skb_pointers )
+ goto TX_SKB_POINTER_ALLOCATE_FAIL;
+ memset(ppe_dev.dma.tx_skb_pointers, 0, ppe_dev.dma.tx_total_channel_used * ppe_dev.dma.tx_descriptor_number * sizeof(struct sk_buff *));
+
+ /* Allocate RX sk_buff and fill up RX descriptors. */
+ rx_descriptor.datalen = ppe_dev.aal5.rx_buffer_size;
+ for ( rx_desc = ppe_dev.dma.rx_total_channel_used * ppe_dev.dma.rx_descriptor_number - 1; rx_desc >= 0; rx_desc-- )
+ {
+ struct sk_buff *skb;
+ skb = alloc_skb_rx();
+ if ( skb == NULL )
+ panic("sk buffer is used up\n");
+ rx_descriptor.dataptr = (u32)skb->data >> 2;
+ ppe_dev.dma.rx_descriptor_base[rx_desc] = rx_descriptor;
+
+ }
+
+ /* Fill up TX descriptors. */
+ tx_descriptor.datalen = ppe_dev.aal5.tx_buffer_size;
+ for ( tx_desc = ppe_dev.dma.tx_total_channel_used * ppe_dev.dma.tx_descriptor_number - 1; tx_desc >= 0; tx_desc-- )
+ ppe_dev.dma.tx_descriptor_base[tx_desc] = tx_descriptor;
+
+ return 0;
+
+TX_SKB_POINTER_ALLOCATE_FAIL:
+ kfree(ppe_dev.dma.tx_descriptor_addr);
+TX_DESCRIPTOR_BASE_ALLOCATE_FAIL:
+ kfree(ppe_dev.dma.rx_descriptor_addr);
+RX_DESCRIPTOR_BASE_ALLOCATE_FAIL:
+ return -ENOMEM;
+}
+
+
+static inline void clear_share_buffer(void)
+{
+ volatile u32 *p = SB_RAM0_ADDR(0);
+ unsigned int i;
+
+ /* write all zeros only */
+ for ( i = 0; i < SB_RAM0_DWLEN + SB_RAM1_DWLEN + SB_RAM2_DWLEN + SB_RAM3_DWLEN; i++ )
+ *p++ = 0;
+}
+
+
+static inline void check_parameters(void)
+{
+ int i;
+ int enabled_port_number;
+ int unassigned_queue_number;
+ int assigned_queue_number;
+
+ enabled_port_number = 0;
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ if ( port_max_connection[i] < 1 )
+ port_max_connection[i] = 0;
+ else
+ enabled_port_number++;
+ /* If the max connection number of a port is not 0, the port is enabled */
+ /* and at lease two connection ID must be reserved for this port. One of */
+ /* them is used as OAM TX path. */
+ unassigned_queue_number = MAX_QUEUE_NUMBER - QSB_QUEUE_NUMBER_BASE;
+ for ( i = 0; i < ATM_PORT_NUMBER; i++ )
+ if ( port_max_connection[i] > 0 )
+ {
+ enabled_port_number--;
+ assigned_queue_number = unassigned_queue_number - enabled_port_number * (1 + OAM_TX_QUEUE_NUMBER_PER_PORT) - OAM_TX_QUEUE_NUMBER_PER_PORT;
+ if ( assigned_queue_number > MAX_QUEUE_NUMBER_PER_PORT - OAM_TX_QUEUE_NUMBER_PER_PORT )
+ assigned_queue_number = MAX_QUEUE_NUMBER_PER_PORT - OAM_TX_QUEUE_NUMBER_PER_PORT;
+ if ( port_max_connection[i] > assigned_queue_number )
+ {
+ port_max_connection[i] = assigned_queue_number;
+ unassigned_queue_number -= assigned_queue_number;
+ }
+ else
+ unassigned_queue_number -= port_max_connection[i];
+ }
+
+ /* Please refer to Amazon spec 15.4 for setting these values. */
+ if ( qsb_tau < 1 )
+ qsb_tau = 1;
+ if ( qsb_tstep < 1 )
+ qsb_tstep = 1;
+ else if ( qsb_tstep > 4 )
+ qsb_tstep = 4;
+ else if ( qsb_tstep == 3 )
+ qsb_tstep = 2;
+
+ /* There is a delay between PPE write descriptor and descriptor is */
+ /* really stored in memory. Host also has this delay when writing */
+ /* descriptor. So PPE will use this value to determine if the write */
+ /* operation makes effect. */
+ if ( write_descriptor_delay < 0 )
+ write_descriptor_delay = 0;
+
+ if ( aal5_fill_pattern < 0 )
+ aal5_fill_pattern = 0;
+ else
+ aal5_fill_pattern &= 0xFF;
+
+ /* Because of the limitation of length field in descriptors, the packet */
+ /* size could not be larger than 64K minus overhead size. */
+ if ( aal5r_max_packet_size < 0 )
+ aal5r_max_packet_size = 0;
+ else if ( aal5r_max_packet_size >= 65536 - MAX_RX_FRAME_EXTRA_BYTES )
+ aal5r_max_packet_size = 65536 - MAX_RX_FRAME_EXTRA_BYTES;
+ if ( aal5r_min_packet_size < 0 )
+ aal5r_min_packet_size = 0;
+ else if ( aal5r_min_packet_size > aal5r_max_packet_size )
+ aal5r_min_packet_size = aal5r_max_packet_size;
+ if ( aal5s_max_packet_size < 0 )
+ aal5s_max_packet_size = 0;
+ else if ( aal5s_max_packet_size >= 65536 - MAX_TX_FRAME_EXTRA_BYTES )
+ aal5s_max_packet_size = 65536 - MAX_TX_FRAME_EXTRA_BYTES;
+ if ( aal5s_min_packet_size < 0 )
+ aal5s_min_packet_size = 0;
+ else if ( aal5s_min_packet_size > aal5s_max_packet_size )
+ aal5s_min_packet_size = aal5s_max_packet_size;
+
+ if ( dma_rx_descriptor_length < 2 )
+ dma_rx_descriptor_length = 2;
+ if ( dma_tx_descriptor_length < 2 )
+ dma_tx_descriptor_length = 2;
+ if ( dma_rx_clp1_descriptor_threshold < 0 )
+ dma_rx_clp1_descriptor_threshold = 0;
+ else if ( dma_rx_clp1_descriptor_threshold > dma_rx_descriptor_length )
+ dma_rx_clp1_descriptor_threshold = dma_rx_descriptor_length;
+}
+
+static struct atmdev_ops ppe_atm_ops = {
+ owner: THIS_MODULE,
+ open: ppe_open,
+ close: ppe_close,
+ ioctl: ppe_ioctl,
+ send: ppe_send,
+ send_oam: ppe_send_oam,
+ change_qos: ppe_change_qos,
+};
+
+int __init danube_ppe_init(void)
+{
+ int ret;
+ int port_num;
+
+ check_parameters();
+
+ ret = init_ppe_dev();
+ if ( ret )
+ goto INIT_PPE_DEV_FAIL;
+
+ clear_share_buffer();
+ init_rx_tables();
+ init_tx_tables();
+printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
+
+ for ( port_num = 0; port_num < ATM_PORT_NUMBER; port_num++ )
+ if ( ppe_dev.port[port_num].max_connections != 0 )
+ {
+ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
+ ppe_dev.port[port_num].dev = atm_dev_register("danube_atm", &ppe_atm_ops, -1, 0UL);
+ if ( !ppe_dev.port[port_num].dev )
+ {
+ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
+ ret = -EIO;
+ goto ATM_DEV_REGISTER_FAIL;
+ }
+ else
+ {
+ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
+ ppe_dev.port[port_num].dev->ci_range.vpi_bits = 8;
+ ppe_dev.port[port_num].dev->ci_range.vci_bits = 16;
+ ppe_dev.port[port_num].dev->link_rate = ppe_dev.port[port_num].tx_max_cell_rate;
+ ppe_dev.port[port_num].dev->dev_data = (void*)port_num;
+ }
+ }
+ /* register interrupt handler */
+ ret = request_irq(IFXMIPS_PPE_MBOX_INT, mailbox_irq_handler, IRQF_DISABLED, "ppe_mailbox_isr", NULL);
+ if ( ret )
+ {
+ if ( ret == -EBUSY )
+ printk("ppe: IRQ may be occupied by ETH2 driver, please reconfig to disable it.\n");
+ goto REQUEST_IRQ_IFXMIPS_PPE_MBOX_INT_FAIL;
+ }
+ disable_irq(IFXMIPS_PPE_MBOX_INT);
+
+ #if defined(CONFIG_PCI) && defined(USE_FIX_FOR_PCI_PPE) && USE_FIX_FOR_PCI_PPE
+ ret = request_irq(PPE_MAILBOX_IGU0_INT, pci_fix_irq_handler, SA_INTERRUPT, "ppe_pci_fix_isr", NULL);
+ if ( ret )
+ printk("failed in registering mailbox 0 interrupt (pci fix)\n");
+ #endif // defined(CONFIG_PCI) && defined(USE_FIX_FOR_PCI_PPE) && USE_FIX_FOR_PCI_PPE
+
+ ret = pp32_start();
+ if ( ret )
+ goto PP32_START_FAIL;
+
+ qsb_global_set();
+ HTU_ENTRY(OAM_F4_SEG_HTU_ENTRY)->vld = 1;
+ HTU_ENTRY(OAM_F4_TOT_HTU_ENTRY)->vld = 1;
+ HTU_ENTRY(OAM_F5_HTU_ENTRY)->vld = 1;
+
+ /* create proc file */
+ proc_file_create();
+
+ printk("ppe: ATM init succeeded (firmware version 1.1.0.2.1.13\n");
+ return 0;
+
+PP32_START_FAIL:
+
+ free_irq(IFXMIPS_PPE_MBOX_INT, NULL);
+REQUEST_IRQ_IFXMIPS_PPE_MBOX_INT_FAIL:
+ATM_DEV_REGISTER_FAIL:
+ clear_ppe_dev();
+INIT_PPE_DEV_FAIL:
+ printk("ppe: ATM init failed\n");
+ return ret;
+}
+
+void __exit danube_ppe_exit(void)
+{
+ int port_num;
+ register int l;
+ proc_file_delete();
+ HTU_ENTRY(OAM_F4_SEG_HTU_ENTRY)->vld = 0;
+ HTU_ENTRY(OAM_F4_TOT_HTU_ENTRY)->vld = 0;
+ HTU_ENTRY(OAM_F5_HTU_ENTRY)->vld = 0;
+ /* idle for a while to finish running HTU search */
+ for (l = 0; l < IDLE_CYCLE_NUMBER; l++ );
+ pp32_stop();
+ free_irq(IFXMIPS_PPE_MBOX_INT, NULL);
+ for ( port_num = 0; port_num < ATM_PORT_NUMBER; port_num++ )
+ if ( ppe_dev.port[port_num].max_connections != 0 )
+ atm_dev_deregister(ppe_dev.port[port_num].dev);
+ clear_ppe_dev();
+}
+
+module_init(danube_ppe_init);
+module_exit(danube_ppe_exit);
+
+MODULE_LICENSE("GPL");
+