2012-01-20 14:50:18 -05:00
/*
* Copyright 2011 Advanced Micro Devices , Inc .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) BE LIABLE FOR ANY CLAIM , DAMAGES OR
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE .
*
* Authors : Alex Deucher
*
*/
2019-06-08 10:02:41 +02:00
2012-10-02 18:01:07 +01:00
# include <drm/radeon_drm.h>
2012-01-20 14:50:18 -05:00
# include "radeon.h"
# include "atom.h"
# define TARGET_HW_I2C_CLOCK 50
/* these are a limitation of ProcessI2cChannelTransaction not the hw */
2013-08-21 13:48:12 -04:00
# define ATOM_MAX_HW_I2C_WRITE 3
2012-01-20 14:50:18 -05:00
# define ATOM_MAX_HW_I2C_READ 255
static int radeon_process_i2c_ch ( struct radeon_i2c_chan * chan ,
u8 slave_addr , u8 flags ,
2018-04-12 21:33:33 +02:00
u8 * buf , int num )
2012-01-20 14:50:18 -05:00
{
struct drm_device * dev = chan - > dev ;
struct radeon_device * rdev = dev - > dev_private ;
PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args ;
int index = GetIndexIntoMasterTable ( COMMAND , ProcessI2cChannelTransaction ) ;
unsigned char * base ;
2013-12-03 17:16:49 -05:00
u16 out = cpu_to_le16 ( 0 ) ;
2014-05-08 10:58:04 -04:00
int r = 0 ;
2012-01-20 14:50:18 -05:00
memset ( & args , 0 , sizeof ( args ) ) ;
2014-05-08 10:58:04 -04:00
mutex_lock ( & chan - > mutex ) ;
2014-11-11 09:16:15 +10:00
mutex_lock ( & rdev - > mode_info . atom_context - > scratch_mutex ) ;
2014-05-08 10:58:04 -04:00
2012-01-20 14:50:18 -05:00
base = ( unsigned char * ) rdev - > mode_info . atom_context - > scratch ;
if ( flags & HW_I2C_WRITE ) {
if ( num > ATOM_MAX_HW_I2C_WRITE ) {
2013-08-21 13:48:12 -04:00
DRM_ERROR ( " hw i2c: tried to write too many bytes (%d vs 3) \n " , num ) ;
2014-05-08 10:58:04 -04:00
r = - EINVAL ;
goto done ;
2012-01-20 14:50:18 -05:00
}
2013-12-03 17:16:49 -05:00
if ( buf = = NULL )
args . ucRegIndex = 0 ;
else
args . ucRegIndex = buf [ 0 ] ;
if ( num )
2013-11-06 17:42:02 -05:00
num - - ;
2013-12-03 17:16:49 -05:00
if ( num )
2013-11-06 17:42:02 -05:00
memcpy ( & out , & buf [ 1 ] , num ) ;
2012-01-20 14:50:18 -05:00
args . lpI2CDataOut = cpu_to_le16 ( out ) ;
} else {
if ( num > ATOM_MAX_HW_I2C_READ ) {
DRM_ERROR ( " hw i2c: tried to read too many bytes (%d vs 255) \n " , num ) ;
2014-05-08 10:58:04 -04:00
r = - EINVAL ;
goto done ;
2012-01-20 14:50:18 -05:00
}
2013-08-21 13:48:12 -04:00
args . ucRegIndex = 0 ;
args . lpI2CDataOut = 0 ;
2012-01-20 14:50:18 -05:00
}
2013-08-21 13:48:12 -04:00
args . ucFlag = flags ;
2012-01-20 14:50:18 -05:00
args . ucI2CSpeed = TARGET_HW_I2C_CLOCK ;
args . ucTransBytes = num ;
args . ucSlaveAddr = slave_addr < < 1 ;
args . ucLineNumber = chan - > rec . i2c_id ;
2014-11-11 09:16:15 +10:00
atom_execute_table_scratch_unlocked ( rdev - > mode_info . atom_context , index , ( uint32_t * ) & args ) ;
2012-01-20 14:50:18 -05:00
/* error */
if ( args . ucStatus ! = HW_ASSISTED_I2C_STATUS_SUCCESS ) {
DRM_DEBUG_KMS ( " hw_i2c error \n " ) ;
2014-05-08 10:58:04 -04:00
r = - EIO ;
goto done ;
2012-01-20 14:50:18 -05:00
}
if ( ! ( flags & HW_I2C_WRITE ) )
2013-08-07 19:34:53 -04:00
radeon_atom_copy_swap ( buf , base , num , false ) ;
2012-01-20 14:50:18 -05:00
2014-05-08 10:58:04 -04:00
done :
2014-11-11 09:16:15 +10:00
mutex_unlock ( & rdev - > mode_info . atom_context - > scratch_mutex ) ;
2014-05-08 10:58:04 -04:00
mutex_unlock ( & chan - > mutex ) ;
return r ;
2012-01-20 14:50:18 -05:00
}
int radeon_atom_hw_i2c_xfer ( struct i2c_adapter * i2c_adap ,
struct i2c_msg * msgs , int num )
{
struct radeon_i2c_chan * i2c = i2c_get_adapdata ( i2c_adap ) ;
struct i2c_msg * p ;
int i , remaining , current_count , buffer_offset , max_bytes , ret ;
2013-12-03 17:16:49 -05:00
u8 flags ;
2012-01-20 14:50:18 -05:00
/* check for bus probe */
p = & msgs [ 0 ] ;
if ( ( num = = 1 ) & & ( p - > len = = 0 ) ) {
ret = radeon_process_i2c_ch ( i2c ,
p - > addr , HW_I2C_WRITE ,
2013-12-03 17:16:49 -05:00
NULL , 0 ) ;
2012-01-20 14:50:18 -05:00
if ( ret )
return ret ;
else
return num ;
}
for ( i = 0 ; i < num ; i + + ) {
p = & msgs [ i ] ;
remaining = p - > len ;
buffer_offset = 0 ;
/* max_bytes are a limitation of ProcessI2cChannelTransaction not the hw */
if ( p - > flags & I2C_M_RD ) {
max_bytes = ATOM_MAX_HW_I2C_READ ;
flags = HW_I2C_READ ;
} else {
max_bytes = ATOM_MAX_HW_I2C_WRITE ;
flags = HW_I2C_WRITE ;
}
while ( remaining ) {
if ( remaining > max_bytes )
current_count = max_bytes ;
else
current_count = remaining ;
ret = radeon_process_i2c_ch ( i2c ,
p - > addr , flags ,
& p - > buf [ buffer_offset ] , current_count ) ;
if ( ret )
return ret ;
remaining - = current_count ;
buffer_offset + = current_count ;
}
}
return num ;
}
u32 radeon_atom_hw_i2c_func ( struct i2c_adapter * adap )
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL ;
}