18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Bestcomm FEC tasks driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2003-2004 MontaVista, Software, Inc. 78c2ecf20Sopenharmony_ci * ( by Dale Farnsworth <dfarnsworth@mvista.com> ) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License 108c2ecf20Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any 118c2ecf20Sopenharmony_ci * kind, whether express or implied. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <asm/io.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/fsl/bestcomm/bestcomm.h> 208c2ecf20Sopenharmony_ci#include <linux/fsl/bestcomm/bestcomm_priv.h> 218c2ecf20Sopenharmony_ci#include <linux/fsl/bestcomm/fec.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* ======================================================================== */ 258c2ecf20Sopenharmony_ci/* Task image/var/inc */ 268c2ecf20Sopenharmony_ci/* ======================================================================== */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* fec tasks images */ 298c2ecf20Sopenharmony_ciextern u32 bcom_fec_rx_task[]; 308c2ecf20Sopenharmony_ciextern u32 bcom_fec_tx_task[]; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* rx task vars that need to be set before enabling the task */ 338c2ecf20Sopenharmony_cistruct bcom_fec_rx_var { 348c2ecf20Sopenharmony_ci u32 enable; /* (u16*) address of task's control register */ 358c2ecf20Sopenharmony_ci u32 fifo; /* (u32*) address of fec's fifo */ 368c2ecf20Sopenharmony_ci u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ 378c2ecf20Sopenharmony_ci u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ 388c2ecf20Sopenharmony_ci u32 bd_start; /* (struct bcom_bd*) current bd */ 398c2ecf20Sopenharmony_ci u32 buffer_size; /* size of receive buffer */ 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* rx task incs that need to be set before enabling the task */ 438c2ecf20Sopenharmony_cistruct bcom_fec_rx_inc { 448c2ecf20Sopenharmony_ci u16 pad0; 458c2ecf20Sopenharmony_ci s16 incr_bytes; 468c2ecf20Sopenharmony_ci u16 pad1; 478c2ecf20Sopenharmony_ci s16 incr_dst; 488c2ecf20Sopenharmony_ci u16 pad2; 498c2ecf20Sopenharmony_ci s16 incr_dst_ma; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* tx task vars that need to be set before enabling the task */ 538c2ecf20Sopenharmony_cistruct bcom_fec_tx_var { 548c2ecf20Sopenharmony_ci u32 DRD; /* (u32*) address of self-modified DRD */ 558c2ecf20Sopenharmony_ci u32 fifo; /* (u32*) address of fec's fifo */ 568c2ecf20Sopenharmony_ci u32 enable; /* (u16*) address of task's control register */ 578c2ecf20Sopenharmony_ci u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ 588c2ecf20Sopenharmony_ci u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ 598c2ecf20Sopenharmony_ci u32 bd_start; /* (struct bcom_bd*) current bd */ 608c2ecf20Sopenharmony_ci u32 buffer_size; /* set by uCode for each packet */ 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* tx task incs that need to be set before enabling the task */ 648c2ecf20Sopenharmony_cistruct bcom_fec_tx_inc { 658c2ecf20Sopenharmony_ci u16 pad0; 668c2ecf20Sopenharmony_ci s16 incr_bytes; 678c2ecf20Sopenharmony_ci u16 pad1; 688c2ecf20Sopenharmony_ci s16 incr_src; 698c2ecf20Sopenharmony_ci u16 pad2; 708c2ecf20Sopenharmony_ci s16 incr_src_ma; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* private structure in the task */ 748c2ecf20Sopenharmony_cistruct bcom_fec_priv { 758c2ecf20Sopenharmony_ci phys_addr_t fifo; 768c2ecf20Sopenharmony_ci int maxbufsize; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* ======================================================================== */ 818c2ecf20Sopenharmony_ci/* Task support code */ 828c2ecf20Sopenharmony_ci/* ======================================================================== */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistruct bcom_task * 858c2ecf20Sopenharmony_cibcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct bcom_task *tsk; 888c2ecf20Sopenharmony_ci struct bcom_fec_priv *priv; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd), 918c2ecf20Sopenharmony_ci sizeof(struct bcom_fec_priv)); 928c2ecf20Sopenharmony_ci if (!tsk) 938c2ecf20Sopenharmony_ci return NULL; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci tsk->flags = BCOM_FLAGS_NONE; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci priv = tsk->priv; 988c2ecf20Sopenharmony_ci priv->fifo = fifo; 998c2ecf20Sopenharmony_ci priv->maxbufsize = maxbufsize; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (bcom_fec_rx_reset(tsk)) { 1028c2ecf20Sopenharmony_ci bcom_task_free(tsk); 1038c2ecf20Sopenharmony_ci return NULL; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return tsk; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcom_fec_rx_init); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciint 1118c2ecf20Sopenharmony_cibcom_fec_rx_reset(struct bcom_task *tsk) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct bcom_fec_priv *priv = tsk->priv; 1148c2ecf20Sopenharmony_ci struct bcom_fec_rx_var *var; 1158c2ecf20Sopenharmony_ci struct bcom_fec_rx_inc *inc; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Shutdown the task */ 1188c2ecf20Sopenharmony_ci bcom_disable_task(tsk->tasknum); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Reset the microcode */ 1218c2ecf20Sopenharmony_ci var = (struct bcom_fec_rx_var *) bcom_task_var(tsk->tasknum); 1228c2ecf20Sopenharmony_ci inc = (struct bcom_fec_rx_inc *) bcom_task_inc(tsk->tasknum); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (bcom_load_image(tsk->tasknum, bcom_fec_rx_task)) 1258c2ecf20Sopenharmony_ci return -1; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci var->enable = bcom_eng->regs_base + 1288c2ecf20Sopenharmony_ci offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); 1298c2ecf20Sopenharmony_ci var->fifo = (u32) priv->fifo; 1308c2ecf20Sopenharmony_ci var->bd_base = tsk->bd_pa; 1318c2ecf20Sopenharmony_ci var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); 1328c2ecf20Sopenharmony_ci var->bd_start = tsk->bd_pa; 1338c2ecf20Sopenharmony_ci var->buffer_size = priv->maxbufsize; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */ 1368c2ecf20Sopenharmony_ci inc->incr_dst = sizeof(u32); /* task image, but we stick */ 1378c2ecf20Sopenharmony_ci inc->incr_dst_ma= sizeof(u8); /* to the official ones */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Reset the BDs */ 1408c2ecf20Sopenharmony_ci tsk->index = 0; 1418c2ecf20Sopenharmony_ci tsk->outdex = 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci memset_io(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Configure some stuff */ 1468c2ecf20Sopenharmony_ci bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_RX_BD_PRAGMA); 1478c2ecf20Sopenharmony_ci bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_RX], BCOM_IPR_FEC_RX); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci out_be32(&bcom_eng->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */ 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcom_fec_rx_reset); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_civoid 1588c2ecf20Sopenharmony_cibcom_fec_rx_release(struct bcom_task *tsk) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci /* Nothing special for the FEC tasks */ 1618c2ecf20Sopenharmony_ci bcom_task_free(tsk); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcom_fec_rx_release); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Return 2nd to last DRD */ 1688c2ecf20Sopenharmony_ci /* This is an ugly hack, but at least it's only done 1698c2ecf20Sopenharmony_ci once at initialization */ 1708c2ecf20Sopenharmony_cistatic u32 *self_modified_drd(int tasknum) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci u32 *desc; 1738c2ecf20Sopenharmony_ci int num_descs; 1748c2ecf20Sopenharmony_ci int drd_count; 1758c2ecf20Sopenharmony_ci int i; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci num_descs = bcom_task_num_descs(tasknum); 1788c2ecf20Sopenharmony_ci desc = bcom_task_desc(tasknum) + num_descs - 1; 1798c2ecf20Sopenharmony_ci drd_count = 0; 1808c2ecf20Sopenharmony_ci for (i=0; i<num_descs; i++, desc--) 1818c2ecf20Sopenharmony_ci if (bcom_desc_is_drd(*desc) && ++drd_count == 3) 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci return desc; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistruct bcom_task * 1878c2ecf20Sopenharmony_cibcom_fec_tx_init(int queue_len, phys_addr_t fifo) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct bcom_task *tsk; 1908c2ecf20Sopenharmony_ci struct bcom_fec_priv *priv; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd), 1938c2ecf20Sopenharmony_ci sizeof(struct bcom_fec_priv)); 1948c2ecf20Sopenharmony_ci if (!tsk) 1958c2ecf20Sopenharmony_ci return NULL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci tsk->flags = BCOM_FLAGS_ENABLE_TASK; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci priv = tsk->priv; 2008c2ecf20Sopenharmony_ci priv->fifo = fifo; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (bcom_fec_tx_reset(tsk)) { 2038c2ecf20Sopenharmony_ci bcom_task_free(tsk); 2048c2ecf20Sopenharmony_ci return NULL; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return tsk; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcom_fec_tx_init); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciint 2128c2ecf20Sopenharmony_cibcom_fec_tx_reset(struct bcom_task *tsk) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct bcom_fec_priv *priv = tsk->priv; 2158c2ecf20Sopenharmony_ci struct bcom_fec_tx_var *var; 2168c2ecf20Sopenharmony_ci struct bcom_fec_tx_inc *inc; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* Shutdown the task */ 2198c2ecf20Sopenharmony_ci bcom_disable_task(tsk->tasknum); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Reset the microcode */ 2228c2ecf20Sopenharmony_ci var = (struct bcom_fec_tx_var *) bcom_task_var(tsk->tasknum); 2238c2ecf20Sopenharmony_ci inc = (struct bcom_fec_tx_inc *) bcom_task_inc(tsk->tasknum); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (bcom_load_image(tsk->tasknum, bcom_fec_tx_task)) 2268c2ecf20Sopenharmony_ci return -1; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci var->enable = bcom_eng->regs_base + 2298c2ecf20Sopenharmony_ci offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); 2308c2ecf20Sopenharmony_ci var->fifo = (u32) priv->fifo; 2318c2ecf20Sopenharmony_ci var->DRD = bcom_sram_va2pa(self_modified_drd(tsk->tasknum)); 2328c2ecf20Sopenharmony_ci var->bd_base = tsk->bd_pa; 2338c2ecf20Sopenharmony_ci var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); 2348c2ecf20Sopenharmony_ci var->bd_start = tsk->bd_pa; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */ 2378c2ecf20Sopenharmony_ci inc->incr_src = sizeof(u32); /* task image, but we stick */ 2388c2ecf20Sopenharmony_ci inc->incr_src_ma= sizeof(u8); /* to the official ones */ 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Reset the BDs */ 2418c2ecf20Sopenharmony_ci tsk->index = 0; 2428c2ecf20Sopenharmony_ci tsk->outdex = 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci memset_io(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Configure some stuff */ 2478c2ecf20Sopenharmony_ci bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_TX_BD_PRAGMA); 2488c2ecf20Sopenharmony_ci bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_TX], BCOM_IPR_FEC_TX); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci out_be32(&bcom_eng->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcom_fec_tx_reset); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_civoid 2598c2ecf20Sopenharmony_cibcom_fec_tx_release(struct bcom_task *tsk) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci /* Nothing special for the FEC tasks */ 2628c2ecf20Sopenharmony_ci bcom_task_free(tsk); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcom_fec_tx_release); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BestComm FEC tasks driver"); 2688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>"); 2698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2708c2ecf20Sopenharmony_ci 271