| #ifdef __KERNEL__ |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/module.h> |
| #include <linux/list.h> |
| #include <linux/kthread.h> |
| #else |
| #include "platform.h" |
| #endif |
| |
| #include "pfe_mod.h" |
| #include "pfe_ctrl.h" |
| |
| #include "pfe_ctrl_hal.h" |
| #include "__pfe_ctrl.h" |
| |
| static struct pe_sync_mailbox CLASS_DMEM_SH2(sync_mailbox); |
| static struct pe_sync_mailbox TMU_DMEM_SH2(sync_mailbox); |
| static struct pe_sync_mailbox UTIL_DMEM_SH2(sync_mailbox); |
| |
| static struct pe_msg_mailbox CLASS_DMEM_SH2(msg_mailbox); |
| static struct pe_msg_mailbox TMU_DMEM_SH2(msg_mailbox); |
| static struct pe_msg_mailbox UTIL_DMEM_SH2(msg_mailbox); |
| |
| static int initialized = 0; |
| |
| #define TIMEOUT_MS 1000 |
| |
| int relax(unsigned long end) |
| { |
| #ifdef __KERNEL__ |
| if (time_after(jiffies, end)) { |
| if (time_after(jiffies, end + (TIMEOUT_MS * HZ) / 1000)) { |
| return -1; |
| } |
| |
| if (need_resched()) |
| schedule(); |
| } |
| #else |
| udelay(1); |
| #endif |
| |
| return 0; |
| } |
| |
| /** PE sync stop. |
| * Stops packet processing for a list of PE's (specified using a bitmask). |
| * The caller must hold ctrl->mutex. |
| * |
| * @param ctrl Control context |
| * @param pe_mask Mask of PE id's to stop |
| * |
| */ |
| int pe_sync_stop(struct pfe_ctrl *ctrl, int pe_mask) |
| { |
| struct pe_sync_mailbox *mbox; |
| int pe_stopped = 0; |
| unsigned long end = jiffies + 2; |
| int i; |
| |
| for (i = 0; i < MAX_PE; i++) |
| if (pe_mask & (1 << i)) { |
| mbox = (void *)ctrl->sync_mailbox_baseaddr[i]; |
| |
| pe_dmem_write(i, cpu_to_be32(0x1), (unsigned long)&mbox->stop, 4); |
| } |
| |
| while (pe_stopped != pe_mask) { |
| for (i = 0; i < MAX_PE; i++) |
| if ((pe_mask & (1 << i)) && !(pe_stopped & (1 << i))) { |
| mbox = (void *)ctrl->sync_mailbox_baseaddr[i]; |
| |
| if (pe_dmem_read(i, (unsigned long)&mbox->stopped, 4) & cpu_to_be32(0x1)) |
| pe_stopped |= (1 << i); |
| } |
| |
| if (relax(end) < 0) |
| goto err; |
| } |
| |
| return 0; |
| |
| err: |
| printk(KERN_ERR "%s: timeout, %x %x\n", __func__, pe_mask, pe_stopped); |
| |
| for (i = 0; i < MAX_PE; i++) |
| if (pe_mask & (1 << i)) { |
| mbox = (void *)ctrl->sync_mailbox_baseaddr[i]; |
| |
| pe_dmem_write(i, cpu_to_be32(0x0), (unsigned long)&mbox->stop, 4); |
| } |
| |
| return -EIO; |
| } |
| |
| /** PE start. |
| * Starts packet processing for a list of PE's (specified using a bitmask). |
| * The caller must hold ctrl->mutex. |
| * |
| * @param ctrl Control context |
| * @param pe_mask Mask of PE id's to start |
| * |
| */ |
| void pe_start(struct pfe_ctrl *ctrl, int pe_mask) |
| { |
| struct pe_sync_mailbox *mbox; |
| int i; |
| |
| for (i = 0; i < MAX_PE; i++) |
| if (pe_mask & (1 << i)) { |
| |
| mbox = (void *)ctrl->sync_mailbox_baseaddr[i]; |
| |
| pe_dmem_write(i, cpu_to_be32(0x0), (unsigned long)&mbox->stop, 4); |
| } |
| } |
| |
| |
| /** Sends a control request to a given PE (to copy data to/from internal memory from/to DDR). |
| * The caller must hold ctrl->mutex. |
| * |
| * @param ctrl Control context |
| * @param id PE id |
| * @param dst Physical destination address of data |
| * @param src Physical source address of data |
| * @param len Data length |
| * |
| */ |
| int pe_request(struct pfe_ctrl *ctrl, int id, unsigned short cmd_type, unsigned long dst, unsigned long src, int len) |
| { |
| struct pe_msg_mailbox mbox = { |
| .dst = cpu_to_be32(dst), |
| .src = cpu_to_be32(src), |
| .len = cpu_to_be32(len), |
| .request = cpu_to_be32((cmd_type << 16) | 0x1), |
| }; |
| struct pe_msg_mailbox *pmbox = (void *)ctrl->msg_mailbox_baseaddr[id]; |
| unsigned long end = jiffies + 2; |
| u32 rc; |
| |
| /* This works because .request is written last */ |
| pe_dmem_memcpy_to32(id, (unsigned long)pmbox, &mbox, sizeof(mbox)); |
| |
| while ((rc = pe_dmem_read(id, (unsigned long)&pmbox->request, 4)) & cpu_to_be32(0xffff)) { |
| if (relax(end) < 0) |
| goto err; |
| } |
| |
| rc = be32_to_cpu(rc); |
| |
| return rc >> 16; |
| |
| err: |
| printk(KERN_ERR "%s: timeout, %x\n", __func__, be32_to_cpu(rc)); |
| pe_dmem_write(id, cpu_to_be32(0), (unsigned long)&pmbox->request, 4); |
| return -EIO; |
| } |
| |
| |
| |
| int comcerto_fpp_send_command(u16 fcode, u16 length, u16 *payload, u16 *rlen, u16 *rbuf) |
| { |
| struct pfe_ctrl *ctrl; |
| |
| if (!initialized) { |
| printk(KERN_ERR "pfe control not initialized\n"); |
| *rlen = 2; |
| rbuf[0] = 1; /* ERR_UNKNOWN_COMMAND */ |
| goto out; |
| } |
| |
| ctrl = &pfe->ctrl; |
| |
| mutex_lock(&ctrl->mutex); |
| |
| __pfe_ctrl_cmd_handler(fcode, length, payload, rlen, rbuf); |
| |
| mutex_unlock(&ctrl->mutex); |
| |
| out: |
| return 0; |
| } |
| EXPORT_SYMBOL(comcerto_fpp_send_command); |
| |
| /** |
| * comcerto_fpp_send_command_simple - |
| * |
| * This function is used to send command to FPP in a synchronous way. Calls to the function blocks until a response |
| * from FPP is received. This API can not be used to query data from FPP |
| * |
| * Parameters |
| * fcode: Function code. FPP function code associated to the specified command payload |
| * length: Command length. Length in bytes of the command payload |
| * payload: Command payload. Payload of the command sent to the FPP. 16bits buffer allocated by the client's code and sized up to 256 bytes |
| * |
| * Return values |
| * 0: Success |
| * <0: Linux system failure (check errno for detailed error condition) |
| * >0: FPP returned code |
| */ |
| int comcerto_fpp_send_command_simple(u16 fcode, u16 length, u16 *payload) |
| { |
| u16 rbuf[128]; |
| u16 rlen; |
| int rc; |
| |
| if (!initialized) { |
| printk(KERN_ERR "pfe control not initialized\n"); |
| rbuf[0] = 1; /* ERR_UNKNOWN_COMMAND */ |
| goto out; |
| } |
| |
| rc = comcerto_fpp_send_command(fcode, length, payload, &rlen, rbuf); |
| |
| /* if a command delivery error is detected, do not check command returned code */ |
| if (rc < 0) |
| return rc; |
| |
| out: |
| /* retrieve FPP command returned code. Could be error or acknowledgment */ |
| rc = rbuf[0]; |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(comcerto_fpp_send_command_simple); |
| |
| |
| static void comcerto_fpp_workqueue(struct work_struct *work) |
| { |
| struct pfe_ctrl *ctrl = container_of(work, struct pfe_ctrl, work); |
| struct fpp_msg *msg; |
| unsigned long flags; |
| u16 rbuf[128]; |
| u16 rlen; |
| int rc; |
| |
| spin_lock_irqsave(&ctrl->lock, flags); |
| |
| while (!list_empty(&ctrl->msg_list)) { |
| |
| msg = list_entry(ctrl->msg_list.next, struct fpp_msg, list); |
| |
| list_del(&msg->list); |
| |
| spin_unlock_irqrestore(&ctrl->lock, flags); |
| |
| rc = comcerto_fpp_send_command(msg->fcode, msg->length, msg->payload, &rlen, rbuf); |
| |
| /* send command response to caller's callback */ |
| if (msg->callback != NULL) |
| msg->callback(msg->data, rc, rlen, rbuf); |
| |
| pfe_kfree(msg); |
| |
| spin_lock_irqsave(&ctrl->lock, flags); |
| } |
| |
| spin_unlock_irqrestore(&ctrl->lock, flags); |
| } |
| |
| /** |
| * comcerto_fpp_send_command_atomic - |
| * |
| * This function is used to send command to FPP in an asynchronous way. The Caller specifies a function pointer |
| * that is called by the FPP Comcerto driver when command reponse from FPP engine is received. This API can be also |
| * used to query data from FPP. Queried data are returned through the specified client's callback function |
| * |
| * Parameters |
| * fcode: Function code. FPP function code associated to the specified command payload |
| * length: Command length. Length in bytes of the command payload |
| * payload: Command payload. Payload of the command sent to the FPP. 16bits buffer allocated by the client's code and sized up to 256 bytes |
| * callback: Client's callback handler for FPP response processing |
| * data: Client's private data. Not interpreted by the FPP driver and sent back to the Client as a reference (client's code own usage) |
| * |
| * Return values |
| * 0: Success |
| * <0: Linux system failure (check errno for detailed error condition) |
| **/ |
| int comcerto_fpp_send_command_atomic(u16 fcode, u16 length, u16 *payload, void (*callback)(unsigned long, int, u16, u16 *), unsigned long data) |
| { |
| struct pfe_ctrl *ctrl; |
| struct fpp_msg *msg; |
| unsigned long flags; |
| int rc; |
| |
| if (!initialized) { |
| printk(KERN_ERR "pfe control not initialized\n"); |
| rc = -EIO; |
| goto err0; |
| } |
| |
| ctrl = &pfe->ctrl; |
| |
| if (length > FPP_MAX_MSG_LENGTH) { |
| rc = -EINVAL; |
| goto err0; |
| } |
| |
| msg = pfe_kmalloc(sizeof(struct fpp_msg) + length, GFP_ATOMIC); |
| if (!msg) { |
| rc = -ENOMEM; |
| goto err0; |
| } |
| |
| /* set caller's callback function */ |
| msg->callback = callback; |
| msg->data = data; |
| |
| msg->payload = (u16 *)(msg + 1); |
| |
| msg->fcode = fcode; |
| msg->length = length; |
| memcpy(msg->payload, payload, length); |
| |
| spin_lock_irqsave(&ctrl->lock, flags); |
| |
| list_add(&msg->list, &ctrl->msg_list); |
| |
| spin_unlock_irqrestore(&ctrl->lock, flags); |
| |
| schedule_work(&ctrl->work); |
| |
| return 0; |
| |
| err0: |
| return rc; |
| } |
| |
| EXPORT_SYMBOL(comcerto_fpp_send_command_atomic); |
| |
| |
| |
| /** Sends a control request to TMU PE ). |
| * The caller must hold ctrl->mutex. |
| * |
| * @param ctrl Control context |
| * @param id TMU id |
| * @param tmu_cmd_bitmask Bitmask of commands sent to TMU |
| * |
| */ |
| int tmu_pe_request(struct pfe_ctrl *ctrl, int id, unsigned int tmu_cmd_bitmask) |
| { |
| if ((id < TMU0_ID) || (id > TMU_MAX_ID)) |
| return -EIO; |
| |
| return (pe_request(ctrl, id, 0, tmu_cmd_bitmask, 0, 0)); |
| } |
| |
| |
| |
| |
| |
| static int pfe_ctrl_send_command_simple(u16 fcode, u16 length, u16 *payload) |
| { |
| u16 rbuf[128]; |
| u16 rlen; |
| int rc; |
| |
| /* send command to FE */ |
| comcerto_fpp_send_command(fcode, length, payload, &rlen, rbuf); |
| |
| /* retrieve FE command returned code. Could be error or acknowledgment */ |
| rc = rbuf[0]; |
| |
| return rc; |
| } |
| |
| |
| /** |
| * comcerto_fpp_register_event_cb - |
| * |
| */ |
| int comcerto_fpp_register_event_cb(int (*event_cb)(u16, u16, u16*)) |
| { |
| struct pfe_ctrl *ctrl; |
| |
| if (!initialized) { |
| printk(KERN_ERR "pfe control not initialized\n"); |
| return -EIO; |
| } |
| |
| ctrl = &pfe->ctrl; |
| |
| /* register FCI callback used for asynchrounous event */ |
| ctrl->event_cb = event_cb; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(comcerto_fpp_register_event_cb); |
| |
| |
| int pfe_ctrl_set_eth_state(int id, unsigned int state, unsigned char *mac_addr) |
| { |
| unsigned char msg[20]; |
| int rc; |
| |
| memset(msg, 0, 20); |
| |
| msg[0] = id; |
| |
| if (state) { |
| memcpy(msg + 14, mac_addr, 6); |
| |
| if ((rc = pfe_ctrl_send_command_simple(CMD_TX_ENABLE, 20, (unsigned short *)msg)) != 0) |
| goto err; |
| |
| } else { |
| if ((rc = pfe_ctrl_send_command_simple(CMD_TX_DISABLE, 2, (unsigned short *)msg)) != 0) |
| goto err; |
| } |
| |
| return 0; |
| |
| err: |
| return -1; |
| } |
| |
| |
| int pfe_ctrl_set_lro(char enable) |
| { |
| unsigned short msg = 0; |
| int rc; |
| |
| msg = enable; |
| |
| if ((rc = pfe_ctrl_send_command_simple(CMD_RX_LRO, 2, (unsigned short *)&msg)) != 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| #ifdef CFG_PCAP |
| typedef struct _tQosExptRateCommand { |
| unsigned short expt_iftype; // PCAP |
| unsigned short pkts_per_msec; |
| }QosExptRateCommand, *PQosExptRateCommand; |
| |
| int pfe_ctrl_set_pcap(char enable) |
| { |
| unsigned short msg = 0; |
| int rc; |
| msg = enable; |
| |
| if ((rc = pfe_ctrl_send_command_simple(CMD_PKTCAP_ENABLE, 2, (unsigned short *)&msg)) != 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| int pfe_ctrl_set_pcap_ratelimit(u32 pkts_per_msec) |
| { |
| QosExptRateCommand pcap_ratelimit; |
| int rc; |
| |
| pcap_ratelimit.expt_iftype = EXPT_TYPE_PCAP; |
| pcap_ratelimit.pkts_per_msec = pkts_per_msec; |
| |
| if ((rc = pfe_ctrl_send_command_simple(CMD_QM_EXPT_RATE, sizeof(QosExptRateCommand), (unsigned short *)&pcap_ratelimit)) != 0) |
| return -1; |
| |
| return 0; |
| |
| } |
| #endif |
| /** Control code timer thread. |
| * |
| * A kernel thread is used so that the timer code can be run under the control path mutex. |
| * The thread wakes up regularly and checks if any timer in the timer list as expired. |
| * The timers are re-started automatically. |
| * The code tries to keep the number of times a timer runs per unit time constant on average, |
| * if the thread scheduling is delayed, it's possible for a particular timer to be scheduled in |
| * quick succession to make up for the lost time. |
| * |
| * @param data Pointer to the control context structure |
| * |
| * @return 0 on sucess, a negative value on error |
| * |
| */ |
| static int pfe_ctrl_timer(void *data) |
| { |
| struct pfe_ctrl *ctrl = data; |
| TIMER_ENTRY *timer, *next; |
| |
| printk(KERN_INFO "%s\n", __func__); |
| |
| while (1) |
| { |
| schedule_timeout_interruptible(ctrl->timer_period); |
| |
| mutex_lock(&ctrl->mutex); |
| |
| list_for_each_entry_safe(timer, next, &ctrl->timer_list, list) |
| { |
| if (time_after(jiffies, timer->timeout)) |
| { |
| timer->timeout += timer->period; |
| |
| timer->handler(); |
| } |
| } |
| |
| mutex_unlock(&ctrl->mutex); |
| |
| if (kthread_should_stop()) |
| break; |
| } |
| |
| printk(KERN_INFO "%s exiting\n", __func__); |
| |
| return 0; |
| } |
| |
| |
| int pfe_ctrl_init(struct pfe *pfe) |
| { |
| struct pfe_ctrl *ctrl = &pfe->ctrl; |
| int id; |
| int rc; |
| |
| printk(KERN_INFO "%s\n", __func__); |
| |
| mutex_init(&ctrl->mutex); |
| spin_lock_init(&ctrl->lock); |
| |
| ctrl->timer_period = HZ / TIMER_TICKS_PER_SEC; |
| |
| INIT_LIST_HEAD(&ctrl->timer_list); |
| |
| INIT_WORK(&ctrl->work, comcerto_fpp_workqueue); |
| |
| INIT_LIST_HEAD(&ctrl->msg_list); |
| |
| for (id = CLASS0_ID; id <= CLASS_MAX_ID; id++) { |
| ctrl->sync_mailbox_baseaddr[id] = virt_to_class_dmem(&class_sync_mailbox); |
| ctrl->msg_mailbox_baseaddr[id] = virt_to_class_dmem(&class_msg_mailbox); |
| } |
| |
| for (id = TMU0_ID; id <= TMU_MAX_ID; id++) { |
| ctrl->sync_mailbox_baseaddr[id] = virt_to_tmu_dmem(&tmu_sync_mailbox); |
| ctrl->msg_mailbox_baseaddr[id] = virt_to_tmu_dmem(&tmu_msg_mailbox); |
| } |
| |
| #if !defined(CONFIG_UTIL_DISABLED) |
| ctrl->sync_mailbox_baseaddr[UTIL_ID] = virt_to_util_dmem(&util_sync_mailbox); |
| ctrl->msg_mailbox_baseaddr[UTIL_ID] = virt_to_util_dmem(&util_msg_mailbox); |
| #endif |
| |
| ctrl->hash_array_baseaddr = pfe->ddr_baseaddr + ROUTE_TABLE_BASEADDR; |
| ctrl->hash_array_phys_baseaddr = pfe->ddr_phys_baseaddr + ROUTE_TABLE_BASEADDR; |
| ctrl->ipsec_lmem_phys_baseaddr = CBUS_VIRT_TO_PFE(LMEM_BASE_ADDR + IPSEC_LMEM_BASEADDR); |
| ctrl->ipsec_lmem_baseaddr = (LMEM_BASE_ADDR + IPSEC_LMEM_BASEADDR); |
| |
| ctrl->timer_thread = kthread_create(pfe_ctrl_timer, ctrl, "pfe_ctrl_timer"); |
| if (IS_ERR(ctrl->timer_thread)) |
| { |
| printk (KERN_ERR "%s: kthread_create() failed\n", __func__); |
| rc = PTR_ERR(ctrl->timer_thread); |
| goto err0; |
| } |
| |
| ctrl->dma_pool = dma_pool_create("pfe_dma_pool_256B", pfe->dev, DMA_BUF_SIZE_256, DMA_BUF_MIN_ALIGNMENT, DMA_BUF_BOUNDARY); |
| if (!ctrl->dma_pool) |
| { |
| printk (KERN_ERR "%s: dma_pool_create() failed\n", __func__); |
| rc = -ENOMEM; |
| goto err1; |
| } |
| |
| ctrl->dma_pool_512 = dma_pool_create("pfe_dma_pool_512B", pfe->dev, DMA_BUF_SIZE_512, DMA_BUF_MIN_ALIGNMENT, DMA_BUF_BOUNDARY); |
| if (!ctrl->dma_pool_512) |
| { |
| printk (KERN_ERR "%s: dma_pool_create() failed\n", __func__); |
| rc = -ENOMEM; |
| goto err2; |
| } |
| |
| ctrl->dev = pfe->dev; |
| |
| mutex_lock(&ctrl->mutex); |
| |
| /* Initialize interface to fci */ |
| rc = __pfe_ctrl_init(); |
| |
| mutex_unlock(&ctrl->mutex); |
| |
| if (rc < 0) |
| goto err3; |
| |
| wake_up_process(ctrl->timer_thread); |
| |
| printk(KERN_INFO "%s finished\n", __func__); |
| |
| initialized = 1; |
| |
| return 0; |
| |
| err3: |
| dma_pool_destroy(ctrl->dma_pool_512); |
| |
| err2: |
| dma_pool_destroy(ctrl->dma_pool); |
| |
| err1: |
| kthread_stop(ctrl->timer_thread); |
| |
| err0: |
| return rc; |
| } |
| |
| |
| void pfe_ctrl_exit(struct pfe *pfe) |
| { |
| struct pfe_ctrl *ctrl = &pfe->ctrl; |
| |
| printk(KERN_INFO "%s\n", __func__); |
| |
| initialized = 0; |
| |
| __pfe_ctrl_exit(); |
| |
| dma_pool_destroy(ctrl->dma_pool); |
| |
| dma_pool_destroy(ctrl->dma_pool_512); |
| |
| kthread_stop(ctrl->timer_thread); |
| } |