| /***************************************************************************** |
| * Copyright Statement: |
| * -------------------- |
| * This software is protected by Copyright and the information contained |
| * herein is confidential. The software may not be copied and the information |
| * contained herein may not be used or disclosed except with the written |
| * permission of MediaTek Inc. (C) 2005 |
| * |
| * BY OPENING THIS FILE, BUYER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES |
| * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") |
| * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO BUYER ON |
| * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. |
| * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE |
| * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR |
| * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND BUYER AGREES TO LOOK ONLY TO SUCH |
| * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. MEDIATEK SHALL ALSO |
| * NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE RELEASES MADE TO BUYER'S |
| * SPECIFICATION OR TO CONFORM TO A PARTICULAR STANDARD OR OPEN FORUM. |
| * |
| * BUYER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND CUMULATIVE |
| * LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, |
| * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, |
| * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY BUYER TO |
| * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. |
| * |
| * THE TRANSACTION CONTEMPLATED HEREUNDER SHALL BE CONSTRUED IN ACCORDANCE |
| * WITH THE LAWS OF THE STATE OF CALIFORNIA, USA, EXCLUDING ITS CONFLICT OF |
| * LAWS PRINCIPLES. ANY DISPUTES, CONTROVERSIES OR CLAIMS ARISING THEREOF AND |
| * RELATED THERETO SHALL BE SETTLED BY ARBITRATION IN SAN FRANCISCO, CA, UNDER |
| * THE RULES OF THE INTERNATIONAL CHAMBER OF COMMERCE (ICC). |
| * |
| *****************************************************************************/ |
| |
| /***************************************************************************** |
| * |
| * Filename: |
| * --------- |
| * drv_i2c.c |
| * |
| * |
| * Description: |
| * ------------ |
| * I2C Driver |
| * |
| * Author: |
| * ------- |
| * ------- |
| * |
| *============================================================================ |
| * HISTORY |
| * Below this line, this part is controlled by PVCS VM. DO NOT MODIFY!! |
| *------------------------------------------------------------------------------ |
| * removed! |
| * removed! |
| * removed! |
| * |
| * removed! |
| * removed! |
| * removed! |
| * |
| * removed! |
| * removed! |
| * removed! |
| * |
| * removed! |
| * removed! |
| * removed! |
| * |
| * removed! |
| * removed! |
| * removed! |
| * removed! |
| *------------------------------------------------------------------------------ |
| * Upper this line, this part is controlled by PVCS VM. DO NOT MODIFY!! |
| *============================================================================ |
| *****************************************************************************/ |
| #include "drv_comm.h" |
| #include "reg_base.h" |
| #include "drv_i2c.h" //*/ the new sccb_v2.h should be named i2c.h /*// |
| #include "drvpdn.h" |
| |
| #include "dcl_i2c_owner.h" |
| #include "kal_general_types.h" |
| #include "kal_public_api.h" |
| #include "drvpdn_inline.h" |
| #include <ex_item.h> |
| #include <ex_public.h> |
| |
| #if (!defined(DRV_I2C_OFF)) |
| #if defined(__MD95__) |
| #define I2C_POWER_OFF() do{PDN_SET(PDN_I2C_BCLK);}while(0) /* Power off I2C */ |
| #define I2C_POWER_ON() do{PDN_CLR(PDN_I2C_BCLK);}while(0) /* Power on I2C */ |
| #else |
| #define I2C_POWER_OFF() |
| #define I2C_POWER_ON() |
| #endif |
| /// I2C internal global variables |
| i2c_handle_struct i2c_handle[DCL_I2C_NUM_OF_OWNER]; |
| i2c_status_struct dcl_i2c_status; |
| kal_bool i2c_lock_status = KAL_FALSE; |
| kal_spinlockid i2c_lock_id = 0; |
| |
| |
| //*/ abstract the HW register config API /*// |
| //called in i2c_write -> dcl_i2c_hw_cfg (owner, I2C_TRANSACTION_WRITE, para, datalen, NULL, 0, 1); |
| //called in i2c_read -> dcl_i2c_hw_cfg (owner, I2C_TRANSACTION_READ, NULL, 0, para, datalen, 1); |
| //called in i2c_cont_write -> dcl_i2c_hw_cfg (owner, I2C_TRANSACTION_CONT_WRITE, para, datalen_in_transfer, NULL, 0, transfer_num); |
| //called in i2c_cont_read -> dcl_i2c_hw_cfg (owner, I2C_TRANSACTION_CONT_READ, NULL, 0, para, datalen_in_transfer, transfer_num); |
| //called in i2c_write_and_read -> dcl_i2c_hw_cfg (owner, I2C_TRANSACTION_WRITE_AND_READ, write_buffer, write_len, read_buffer, read_len, 2); |
| void dcl_i2c_hw_cfg (DCL_I2C_OWNER owner, I2C_TRANSACTION_TYPE type, kal_uint8* write_buffer, kal_uint32 write_len, kal_uint8* read_buffer, kal_uint32 read_len, kal_uint32 transfer_num) |
| { |
| i2c_handle_struct* handle=&i2c_handle[owner]; |
| i2c_config_struct* config=&handle->i2c_config; |
| kal_uint32 count_write, count_read, i; |
| |
| if (read_len>DRV_I2C_FIFO_DEPTH || write_len>DRV_I2C_FIFO_DEPTH) //if u want a transcation with len>8, please enable DMA. |
| { |
| ASSERT(0); |
| } |
| count_write = write_len*transfer_num; |
| count_read = read_len*transfer_num; |
| |
| CLEAR_I2C_FIFO; |
| CLEAR_I2C_STA; |
| DISABLE_I2C_INT; |
| |
| SET_I2C_STEP_CNT_DIV(handle->fs_step_cnt_div); |
| SET_I2C_SAMPLE_CNT_DIV(handle->fs_sample_cnt_div); |
| |
| SET_I2C_TRANSACTION_LENGTH(transfer_num); |
| |
| if (handle->i2c_config.transaction_mode == I2C_TRANSACTION_FAST_MODE) |
| { |
| SET_I2C_FAST_MODE; |
| SET_I2C_ST_BETWEEN_TRANSFER; |
| if (config->delay_len>0) |
| { |
| SET_I2C_DELAY_LENGTH(config->delay_len-1); |
| } |
| if ((config->delay_len == 0)&&((type == I2C_TRANSACTION_CONT_WRITE)||(type == I2C_TRANSACTION_CONT_READ))) |
| { |
| SET_I2C_RS_BETWEEN_TRANSFER; |
| } |
| } |
| else if (handle->i2c_config.transaction_mode == I2C_TRANSACTION_HIGH_SPEED_MODE) |
| { |
| SET_I2C_HS_STEP_CNT_DIV(handle->hs_step_cnt_div); |
| SET_I2C_HS_SAMPLE_CNT_DIV(handle->hs_sample_cnt_div); |
| SET_I2C_HS_MODE; |
| SET_I2C_RS_BETWEEN_TRANSFER; |
| } |
| |
| DISABLE_I2C_DIR_CHANGE; |
| DISABLE_I2C_DMA_TRANSFER; |
| |
| if ((type == I2C_TRANSACTION_WRITE)||(type == I2C_TRANSACTION_CONT_WRITE)) |
| { |
| SET_I2C_SLAVE_ADDRESS(config->slave_address,I2C_WRITE_BIT); |
| SET_I2C_TRANSFER_LENGTH(write_len); |
| dcl_i2c_status.number_of_read=0; |
| |
| for (i=0;i<count_write;i++) |
| { |
| DRV_I2C_WriteReg16(REG_I2C_DATA_PORT, *(write_buffer+i)); |
| } |
| } |
| else if ((type == I2C_TRANSACTION_READ)||(type == I2C_TRANSACTION_CONT_READ)) |
| { |
| SET_I2C_SLAVE_ADDRESS(config->slave_address,I2C_READ_BIT); |
| SET_I2C_TRANSFER_LENGTH(read_len); |
| dcl_i2c_status.number_of_read = count_read; |
| dcl_i2c_status.read_buffer=read_buffer; |
| } |
| else if (type == I2C_TRANSACTION_WRITE_AND_READ) |
| { |
| SET_I2C_SLAVE_ADDRESS(config->slave_address,I2C_WRITE_BIT); |
| SET_I2C_TRANSFER_LENGTH(write_len); |
| SET_I2C_AUX_TRANSFER_LENGTH(read_len); |
| ENABLE_I2C_DIR_CHANGE; |
| SET_I2C_RS_BETWEEN_TRANSFER; |
| |
| dcl_i2c_status.number_of_read = read_len; |
| dcl_i2c_status.read_buffer=read_buffer; |
| for (i=0;i<write_len;i++) |
| { |
| DRV_I2C_WriteReg16(REG_I2C_DATA_PORT, (*(write_buffer+i))); |
| } |
| } |
| START_I2C_TRANSACTION; |
| } |
| |
| |
| void _dcl_i2c_status_handler(kal_uint32 sta) |
| { |
| kal_uint32 i; |
| kal_uint8* read_buffer=dcl_i2c_status.read_buffer; |
| kal_uint32 number_of_read=dcl_i2c_status.number_of_read; |
| i2c_handle_struct* handle=&i2c_handle[dcl_i2c_status.owner]; |
| |
| if (sta == I2C_TRANSAC_COMPLETE) //This transaction is done now |
| { |
| for (i=0;i<number_of_read;i++) |
| { |
| read_buffer[i]=(DRV_I2C_ReadReg16(REG_I2C_DATA_PORT) & 0xff); //copy the read data to the previous transcation owner |
| } |
| handle->transaction_result = I2C_TRANSACTION_COMPLETE; |
| } |
| else |
| { |
| if (sta & I2C_TRANSAC_ACK_ERR) |
| handle->transaction_result = I2C_TRANSACTION_ACKERR; |
| else if (sta & I2C_HS_NACK_ERR) |
| handle->transaction_result = I2C_TRANSACTION_HS_NACKERR; |
| else //if got this, check register INTR_STAT to debug |
| handle->transaction_result = I2C_TRANSACTION_FAIL; |
| } |
| dcl_i2c_status.state=I2C_READY_STATE; |
| } |
| |
| void _dcl_i2c_lisr(kal_uint32 vector) |
| { |
| kal_uint32 savedMask; |
| kal_uint32 sta; |
| //test lisr executive time |
| //kal_uint32 start_qbit,end_qbit;// |
| |
| //start_qbit = HW_TDMA_GET_TQCNT();// |
| |
| savedMask = SaveAndSetIRQMask(); |
| |
| sta = DRV_I2C_ReadReg16(REG_I2C_INT_STA); //sta=REG_I2C_INT_STA; |
| |
| { |
| _dcl_i2c_status_handler(sta); |
| } |
| |
| RestoreIRQMask(savedMask); |
| //end_qbit = HW_TDMA_GET_TQCNT();// |
| } |
| |
| |
| void dcl_i2c_init(void) |
| { |
| if(dcl_i2c_status.state != I2C_IDLE_STATE) |
| { |
| return; |
| } |
| |
| dcl_i2c_status.state=I2C_READY_STATE; |
| I2C_POWER_ON(); |
| |
| SET_I2C_HW_MODE; |
| ENABLE_I2C_ACK_ERR_DET; //Always enable ack error detection |
| ENABLE_I2C_NAKERR_DET; //Always enable nack error detection |
| |
| // create mutex |
| if(i2c_lock_status == KAL_FALSE && !INT_QueryExceptionStatus()) |
| { |
| i2c_lock_id = kal_create_spinlock("DRV_I2C"); |
| i2c_lock_status = KAL_TRUE; |
| } |
| MD_TRC(I2C_INIT_MSG,__FUNCTION__); |
| } |
| |
| void dcl_i2c_deinit(void) |
| { |
| RESET_I2C; |
| kal_mem_set(&dcl_i2c_status,0, sizeof(i2c_status_struct)); |
| MD_TRC(I2C_DEINIT_MSG,__FUNCTION__); |
| I2C_POWER_OFF(); |
| } |
| |
| |
| //----------------------------I2C internal arbiteration------------------------------ |
| //--When return from this function, I bit is turned off!!-- |
| kal_uint32 dcl_i2c_wait_transaction_complete_and_lock(DCL_I2C_OWNER owner) |
| { |
| if (!(kal_if_lisr())) |
| { |
| while(1) |
| { |
| if (dcl_i2c_status.state==I2C_READY_STATE) |
| { |
| kal_take_spinlock(i2c_lock_id, KAL_INFINITE_WAIT); |
| dcl_i2c_status.state=I2C_BUSY_STATE; //Lock it |
| dcl_i2c_status.owner=owner; |
| kal_give_spinlock(i2c_lock_id); |
| return 0;//will restore it at: dcl_i2c_wait_transaction_complete |
| } |
| else if (i2c_handle[owner].i2c_config.get_handle_wait==KAL_FALSE) |
| { |
| return 0; |
| } |
| |
| }//end of while |
| } |
| else |
| { |
| if (dcl_i2c_status.state!=I2C_READY_STATE) |
| { |
| ASSERT(0); |
| } |
| |
| dcl_i2c_status.state=I2C_BUSY_STATE; //Lock it |
| dcl_i2c_status.owner=owner; |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| void dcl_i2c_wait_transaction_complete(kal_uint32 savedMask) |
| { |
| kal_uint32 sta=0,timer=0; |
| kal_uint32 i=0; |
| kal_uint8* read_buffer=dcl_i2c_status.read_buffer; |
| kal_uint32 number_of_read=dcl_i2c_status.number_of_read; |
| i2c_handle_struct* handle=&i2c_handle[dcl_i2c_status.owner]; |
| |
| timer = 10000; |
| do |
| { |
| sta = DRV_I2C_ReadReg16(REG_I2C_INT_STA); |
| timer--; |
| } while((!(sta & I2C_TRANSAC_COMPLETE)) && (timer!=0)); |
| |
| if(timer == 0){ |
| MD_TRC(I2C_TRANSACTION_TIMETOUT_MSG,); |
| handle->transaction_result = I2C_TRANSACTION_FAIL; |
| return; |
| } |
| |
| if (sta == I2C_TRANSAC_COMPLETE) //This transaction is done now |
| { |
| for (i=0;i<number_of_read;i++) |
| { |
| read_buffer[i]=(DRV_I2C_ReadReg16(REG_I2C_DATA_PORT) & 0xff); //copy the read data to the previous transcation owner |
| } |
| handle->transaction_result = I2C_TRANSACTION_COMPLETE; |
| MD_TRC(I2C_TRANSACTION_COMPLETE_MSG,); |
| } |
| else |
| { |
| MD_TRC(I2C_TRANSACTION_ERROR_MSG,sta); |
| if (sta & I2C_TRANSAC_ACK_ERR) |
| handle->transaction_result = I2C_TRANSACTION_ACKERR; |
| else if (sta & I2C_HS_NACK_ERR) |
| handle->transaction_result = I2C_TRANSACTION_HS_NACKERR; |
| else //if got this, check register INTR_STAT to debug |
| handle->transaction_result = I2C_TRANSACTION_FAIL; |
| |
| } |
| kal_take_spinlock(i2c_lock_id, KAL_INFINITE_WAIT); |
| dcl_i2c_status.state=I2C_READY_STATE; |
| kal_give_spinlock(i2c_lock_id); |
| |
| } |
| |
| |
| |
| //----------------------------I2C Configuration APIs------------------------------- |
| void dcl_i2c_set_transaction_speed(DCL_I2C_OWNER owner,I2C_TRANSACTION_MODE mode,kal_uint16* Fast_Mode_Speed,kal_uint16* HS_Mode_Speed) |
| { |
| kal_uint32 step_cnt_div; |
| kal_uint32 sample_cnt_div; |
| kal_uint32 temp; |
| |
| //Fast Mode Speed |
| for (sample_cnt_div=1;sample_cnt_div<=8;sample_cnt_div++) |
| { |
| if (NULL != Fast_Mode_Speed) |
| { |
| temp=((*Fast_Mode_Speed)*2*sample_cnt_div); |
| } |
| else |
| { |
| temp = 1*2*sample_cnt_div; |
| } |
| step_cnt_div=(I2C_CLOCK_RATE+temp-1)/temp; //cast the <1 part |
| |
| if (step_cnt_div<=64) |
| { |
| break; |
| } |
| } |
| if (step_cnt_div>64 && sample_cnt_div>8) |
| { |
| step_cnt_div=64; |
| sample_cnt_div=8; |
| } |
| |
| |
| |
| if (NULL != Fast_Mode_Speed) |
| { |
| *Fast_Mode_Speed = I2C_CLOCK_RATE/2/sample_cnt_div/step_cnt_div; |
| i2c_handle[owner].fs_sample_cnt_div=sample_cnt_div-1; |
| i2c_handle[owner].fs_step_cnt_div=step_cnt_div-1; |
| i2c_handle[owner].i2c_config.transaction_mode=I2C_TRANSACTION_FAST_MODE; |
| |
| } |
| |
| //HS Mode Speed |
| if (mode==I2C_TRANSACTION_HIGH_SPEED_MODE) |
| { |
| for (sample_cnt_div=1;sample_cnt_div<=8;sample_cnt_div++) |
| { |
| if (NULL != HS_Mode_Speed) |
| { |
| temp=((*HS_Mode_Speed)*2*sample_cnt_div); |
| } |
| else |
| { |
| temp=(1*2*sample_cnt_div); |
| } |
| step_cnt_div=(I2C_CLOCK_RATE+temp-1)/temp; |
| if (step_cnt_div<=8) |
| { |
| break; |
| } |
| } |
| if (NULL != HS_Mode_Speed) |
| { |
| *HS_Mode_Speed=I2C_CLOCK_RATE/2/sample_cnt_div/step_cnt_div; |
| i2c_handle[owner].hs_sample_cnt_div=sample_cnt_div-1; |
| i2c_handle[owner].hs_step_cnt_div=step_cnt_div-1; |
| i2c_handle[owner].i2c_config.transaction_mode=I2C_TRANSACTION_HIGH_SPEED_MODE; |
| } |
| } |
| } |
| |
| |
| #endif |
| |
| |