cy7c67200_300_pcd.c

来自「linux嵌入式课程实践中的一个关于声卡驱动程序 。」· C语言 代码 · 共 2,215 行 · 第 1/5 页

C
2,215
字号
/******************************************************************************* * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */   #include <linux/config.h>#include <linux/module.h>#include "../usbd-export.h"#include "../usbd-build.h"// #include "../usbd-module.h"MODULE_AUTHOR("Cypress Semiconductor");MODULE_DESCRIPTION("CY7C67200_300 Peripheral Bus Interface");#include <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/timer.h>#include <asm/atomic.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/types.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/delay.h>#include "../usbd.h"#include "../usbd-func.h"#include "../usbd-bus.h"#include "../usbd-inline.h"#include "usbd-bi.h"#include "udc.h"#include "../../67300.h"#include "../../lcp_cmd.h"#include "../../hwfixes.h"#include "../../cy7c67200_300_common.h"#include "../../cy7c67200_300_lcd.h"#include "../../cy7c67200_300_otg.h"#include "../../cy7c67200_300_debug.h"#include "cy7c67200_300_pcd.h"extern void de1_modinit(void);extern void de3_modinit(void);extern void de4_modinit(void);#define MIN(a,b) ((a) < (b) ? (a) : (b))#define MAX(a,b) ((a) > (b) ? (a) : (b))typedef enum{    act_susp_state_1 = 0,    act_susp_state_2 = 1,    act_susp_state_3 = 2} Act_Susp_States;/* Local functions *///static int is_host_mode(cy_priv_t *, int);static void ep_receive_data(struct usb_endpoint_instance *, int, cy_priv_t *);static int load_configuration_data(cy_priv_t *);static void susb_init_int_done(unsigned short response, unsigned short value,                                int, cy_priv_t * cy_priv);static int pcd_dbg_on = 0;#define pcd_dbg(format, arg...) \    if( pcd_dbg_on != 0 ) \        printk(KERN_DEBUG __FILE__ ":"  "%d: " format "\n" ,__LINE__, ## arg)/*    This bus interface needs to support two peripheral ports.  Each port will have     its own private data to keep track of the current state, address, etc.  Each     port will register as a separate bus interface with the peripheral core.      So, from the upper layer it will appear as two separate bus interfaces, but     many of the functions will be shared between the two ports.*///static struct usbd_urb * ep0_urb;static struct timer_list vbus_timer;static sie_info *sie;static sie_info sie_init_data[] ={    {        susb_recv_interrupt:          SUSB1_RECEIVE_INT,        susb_stall_interrupt:         SUSB1_STALL_INT,        susb_standard_int:            SUSB1_STANDARD_INT,        susb_standard_loader_vec:     SUSB1_STANDARD_LOADER_VEC,        susb_vendor_interrupt:        SUSB1_VENDOR_INT,        susb_vendor_loader_vec:       SUSB1_VENDOR_LOADER_VEC,        susb_class_interrupt:         SUSB1_CLASS_INT,        susb_class_loader_vec:        SUSB1_CLASS_LOADER_VEC,        susb_finish_interrupt:        SUSB1_FINISH_INT,        susb_dev_desc_vec:            SUSB1_DEV_DESC_VEC,        susb_config_desc_vec:         SUSB1_CONFIG_DESC_VEC,        susb_string_desc_vec:         SUSB1_STRING_DESC_VEC,        susb_parse_config_interrupt:  SUSB1_PARSE_CONFIG_INT,        susb_loader_interrupt:        SUSB1_LOADER_INT,        susb_delta_config_interrupt:  SUSB1_DELTA_CONFIG_INT,        susb_init_interrupt:          SUSB_INIT_INT,        susb_remote_wakeup_interrupt: REMOTE_WAKEUP_INT,        susb_start_srp_interrupt:     START_SRP_INT,        recv_buffer_length:           RECV_BUFF_LENGTH,        send_buffer_length:           SEND_BUFF_LENGTH,        bus:                          0,        cy_priv:                      0,        usb_address:                  0,        udc_suspended:                TRUE,        sie_number:                   SIE0    },    {        susb_recv_interrupt:          SUSB2_RECEIVE_INT,        susb_stall_interrupt:         SUSB2_STALL_INT,        susb_standard_int:            SUSB2_STANDARD_INT,        susb_standard_loader_vec:     SUSB2_STANDARD_LOADER_VEC,        susb_vendor_interrupt:        SUSB2_VENDOR_INT,        susb_vendor_loader_vec:       SUSB2_VENDOR_LOADER_VEC,        susb_class_interrupt:         SUSB2_CLASS_INT,        susb_class_loader_vec:        SUSB2_CLASS_LOADER_VEC,        susb_finish_interrupt:        SUSB2_FINISH_INT,        susb_dev_desc_vec:            SUSB2_DEV_DESC_VEC,        susb_config_desc_vec:         SUSB2_CONFIG_DESC_VEC,        susb_string_desc_vec:         SUSB2_STRING_DESC_VEC,        susb_parse_config_interrupt:  SUSB2_PARSE_CONFIG_INT,        susb_loader_interrupt:        SUSB2_LOADER_INT,        susb_delta_config_interrupt:  SUSB2_DELTA_CONFIG_INT,        susb_init_interrupt:          SUSB_INIT_INT,        susb_remote_wakeup_interrupt: REMOTE_WAKEUP_INT,        susb_start_srp_interrupt:     START_SRP_INT,        recv_buffer_length:           RECV_BUFF_LENGTH,        send_buffer_length:           SEND_BUFF_LENGTH,        bus:                          0,        cy_priv:                      0,        usb_address:                  0,        udc_suspended:                TRUE,        sie_number:                   SIE1    }};/****************************************************************************** * FUNCTION: pcd_init * * DESCRIPTION: Records boot time configuration of desired sie and design *              example to be used later. */int pcd_init(int sie_num, int de_num, cy_priv_t *cy_priv){    size_t i;    unsigned short mem_start = cy_priv->cy_buf_addr;    pcd_dbg("pcd_init enter");    /* Check for valid SIE */    if( sie_num >= sizeof(sie_init_data)/sizeof(sie_info) )    {        return ERROR;    }    /* Initialize some SIE members */    sie = &sie_init_data[sie_num];    sie->cy_priv = cy_priv;    cy_priv->pcdi = sie;        if( sie_num == SIE0 )    {        sie->sie_dev_desc_location    = SIE0_DEVICE_DESCRIPTOR_LOCATION;        sie->sie_config_desc_location = SIE0_CONFIG_DESCRIPTOR_LOCATION;        sie->sie_string_desc_location = SIE0_STRING_DESCRIPTOR_LOCATION;        sie->recv_struct_location     = SIE0_RECV_STRUCT_ADDR;        sie->recv_buffer_location     = SIE0_RECV_BUFF_ADDR;        sie->send_struct_location     = SIE0_SEND_STRUCT_ADDR;        sie->send_buffer_location     = SIE0_SEND_BUFF_ADDR;        cy_priv->cy_buf_addr          = SIE0_MEM_END;    }    else    {        sie->sie_dev_desc_location    = SIE1_DEVICE_DESCRIPTOR_LOCATION;        sie->sie_config_desc_location = SIE1_CONFIG_DESCRIPTOR_LOCATION;        sie->sie_string_desc_location = SIE1_STRING_DESCRIPTOR_LOCATION;        sie->recv_struct_location     = SIE1_RECV_STRUCT_ADDR;        sie->recv_buffer_location     = SIE1_RECV_BUFF_ADDR;        sie->send_struct_location     = SIE1_SEND_STRUCT_ADDR;        sie->send_buffer_location     = SIE1_SEND_BUFF_ADDR;        cy_priv->cy_buf_addr          = SIE1_MEM_END;    }    cy_err("PCD memory usage: start:0x%04x, end+1:0x%04x, size:0x%04x",             mem_start, cy_priv->cy_buf_addr, cy_priv->cy_buf_addr - mem_start);            for(i=0; i<UDC_MAX_ENDPOINTS; i++)    {        sie->rcv_data_pend_ep[i] = 0;    }    /* Initialize device core */    if (usbd_device_init()) {        cy_err("usbd_device_init failed");        sie = 0;        return ERROR;    }    /* setup function driver based upon design example */    switch( de_num )    {    case DE1:        {        lcd_int_data_t int_data;        pcd_dbg("Initializing peripheral function driver for DE1");        de1_modinit();                // Initialize BIOS for debug port        int_data.int_num = SUSB_INIT_INT;        int_data.reg[1]  = FULL_SPEED;        int_data.reg[2]  = 2;   // SIE1                pcd_dbg("Initializing debug port");        lcd_exec_interrupt(&int_data, 0, 0, cy_priv);        }        break;    case DE3:        pcd_dbg("Initializing peripheral function driver for DE3");        de3_modinit();        break;    case DE4:        pcd_dbg("Initializing peripheral function driver for DE4");        de4_modinit();        break;    default:        cy_err("unhandled design example %d", de_num);        return ERROR;        break;    }    /* Initialize bus interface */    if( bi_modinit() )     {        cy_err("bi_modinit failed");        sie = 0;        return ERROR;    }    init_timer(&vbus_timer);    return SUCCESS;}/***************************************************************** * * Function Name: pcd_switch_role * * Description: This function activates/deactivates the peripheral *              code when doing a role switch. * * Input: role - the role to switch to. * * Return: none  *                 *****************************************************************/int pcd_switch_role(int role, cy_priv_t *cy_priv){    unsigned short intStat;    lcd_int_data_t int_data;    int i;    pcd_dbg("pcd_switch_role enter");    if( cy_priv == 0 || sie == 0 )    {        return ERROR;    }    switch(role)    {    case PERIPHERAL_ROLE:        pcd_dbg("pcd role switch to PERIPHERAL_ROLE");        // Setup interrupt steering        lcd_read_reg(HPI_SIE_IE, &intStat, cy_priv);        intStat &= ~SOFEOP1_TO_HPI_EN;        intStat &= ~SOFEOP2_TO_HPI_EN;        intStat |=  SOFEOP1_TO_CPU_EN;        intStat |=  SOFEOP2_TO_CPU_EN;        lcd_write_reg(HPI_SIE_IE, intStat, cy_priv);        // Save descriptors to BIOS        if( load_configuration_data(cy_priv) == ERROR )        {            cy_err("load_configuration_data failed");            return ERROR;        }        // Initialize BIOS for slave        int_data.int_num = SUSB_INIT_INT;        int_data.reg[1] = FULL_SPEED;        int_data.reg[2] = sie->sie_number + 1;                if( lcd_exec_interrupt(&int_data, susb_init_int_done, 0, cy_priv) ==                 ERROR )        {            cy_err("lcd_exec_interrupt failed");            return ERROR;        }        HWTrace(0x7001);        for(i=0; i<10; i++)        {            lcd_read_reg(USB1_CTL_REG, &intStat, cy_priv);            if( (intStat & (A_DP_STAT | A_DM_STAT)) ) break;            mdelay(1);        }        // Turn on VBUS and ID interrupts        lcd_read_reg (SIE1_INT_EN_REG, &intStat, cy_priv);        intStat |= VBUS_IRQ_EN;        intStat |= ID_IRQ_EN;        intStat |= 0x0800; // MSE, IROS pg 103        lcd_write_reg(SIE1_INT_EN_REG, intStat, cy_priv);        break;    case HOST_ROLE:        pcd_dbg("pcd role switch to HOST_ROLE");        usbd_device_event_irq(sie->bus->device, DEVICE_BUS_INACTIVE, 0);           usbd_device_event(sie->bus->device, DEVICE_RESET, 0);        break;    case HOST_INACTIVE_ROLE:        pcd_dbg("pcd role switch to HOST_INACTIVE_ROLE");        usbd_device_event_irq(sie->bus->device, DEVICE_BUS_INACTIVE, 0);           usbd_device_event(sie->bus->device, DEVICE_RESET, 0);                // Setup interrupt steering        lcd_read_reg(HPI_SIE_IE, &intStat, cy_priv);        intStat &= ~SOFEOP1_TO_HPI_EN;        intStat &= ~SOFEOP2_TO_HPI_EN;        intStat |=  SOFEOP1_TO_CPU_EN;        intStat |=  SOFEOP2_TO_CPU_EN;        lcd_write_reg(HPI_SIE_IE, intStat, cy_priv);                // Turn on VBUS and ID interrupts        lcd_read_reg (SIE1_INT_EN_REG, &intStat, cy_priv);        intStat |= VBUS_IRQ_EN;        intStat |= ID_IRQ_EN;        intStat |= 0x800; // MSE, IROS pg 103        intStat &= ~A_CHG_IRQ_EN;        intStat &= ~B_CHG_IRQ_EN;        lcd_write_reg(SIE1_INT_EN_REG, intStat, cy_priv);        // for initialization when we don't get an IRQ for connection at        // reset        pcd_irq_vbus(cy_priv);          break;    }    return SUCCESS;}/*****************************************************************/void susb_init_int_done(unsigned short response, unsigned short value,                         int opt_arg, cy_priv_t * cy_priv){#if 0    unsigned short ctl_reg;    // Need to set to host mode to be able to control D+    lcd_read_reg(USB1_CTL_REG, &ctl_reg, cy_priv);    pcd_dbg("usb1_ctl_reg-1: 0x%04x", ctl_reg);    lcd_write_reg(USB1_CTL_REG, ctl_reg | MODE_SEL, cy_priv);    lcd_read_reg(USB1_CTL_REG, &ctl_reg, cy_priv); // debug    pcd_dbg("usb1_ctl_reg-2: 0x%04x", ctl_reg);    lcd_read_reg(OTG_CTL_REG, &ctl_reg, cy_priv);    if( !(ctl_reg & OTG_DATA_STAT) )    {        pcd_dbg("Init: D+ off");        ctl_reg &= ~DPLUS_PULLUP_EN;        //ctl_reg &= ~DPLUS_PULLDOWN_EN;        //ctl_reg |= DPLUS_PULLDOWN_EN;        lcd_write_reg(OTG_CTL_REG, ctl_reg, cy_priv);    }#endif}/***************************************************************** * * Function Name: pcd_signal_srp * * Description: This function signals to the host via SRP. * * Input: vbus_pulse_time - time in milliseconds to pulse vbus. * * Return: ERROR/SUCCESS *                 *****************************************************************/#if 0void signal_srp_done(unsigned short response, unsigned short value,                      int opt_arg, cy_priv_t * cy_priv){    otg_t * otg = cy_priv->otg;    if( response == COMM_ACK )

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?