18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2011 Advanced Micro Devices, Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Authors: Alex Deucher 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <drm/radeon_drm.h> 278c2ecf20Sopenharmony_ci#include "radeon.h" 288c2ecf20Sopenharmony_ci#include "atom.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define TARGET_HW_I2C_CLOCK 50 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* these are a limitation of ProcessI2cChannelTransaction not the hw */ 338c2ecf20Sopenharmony_ci#define ATOM_MAX_HW_I2C_WRITE 3 348c2ecf20Sopenharmony_ci#define ATOM_MAX_HW_I2C_READ 255 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int radeon_process_i2c_ch(struct radeon_i2c_chan *chan, 378c2ecf20Sopenharmony_ci u8 slave_addr, u8 flags, 388c2ecf20Sopenharmony_ci u8 *buf, int num) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct drm_device *dev = chan->dev; 418c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 428c2ecf20Sopenharmony_ci PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args; 438c2ecf20Sopenharmony_ci int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction); 448c2ecf20Sopenharmony_ci unsigned char *base; 458c2ecf20Sopenharmony_ci u16 out = cpu_to_le16(0); 468c2ecf20Sopenharmony_ci int r = 0; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci memset(&args, 0, sizeof(args)); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci mutex_lock(&chan->mutex); 518c2ecf20Sopenharmony_ci mutex_lock(&rdev->mode_info.atom_context->scratch_mutex); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci base = (unsigned char *)rdev->mode_info.atom_context->scratch; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (flags & HW_I2C_WRITE) { 568c2ecf20Sopenharmony_ci if (num > ATOM_MAX_HW_I2C_WRITE) { 578c2ecf20Sopenharmony_ci DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 3)\n", num); 588c2ecf20Sopenharmony_ci r = -EINVAL; 598c2ecf20Sopenharmony_ci goto done; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci if (buf == NULL) 628c2ecf20Sopenharmony_ci args.ucRegIndex = 0; 638c2ecf20Sopenharmony_ci else 648c2ecf20Sopenharmony_ci args.ucRegIndex = buf[0]; 658c2ecf20Sopenharmony_ci if (num) 668c2ecf20Sopenharmony_ci num--; 678c2ecf20Sopenharmony_ci if (num) 688c2ecf20Sopenharmony_ci memcpy(&out, &buf[1], num); 698c2ecf20Sopenharmony_ci args.lpI2CDataOut = cpu_to_le16(out); 708c2ecf20Sopenharmony_ci } else { 718c2ecf20Sopenharmony_ci args.ucRegIndex = 0; 728c2ecf20Sopenharmony_ci args.lpI2CDataOut = 0; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci args.ucFlag = flags; 768c2ecf20Sopenharmony_ci args.ucI2CSpeed = TARGET_HW_I2C_CLOCK; 778c2ecf20Sopenharmony_ci args.ucTransBytes = num; 788c2ecf20Sopenharmony_ci args.ucSlaveAddr = slave_addr << 1; 798c2ecf20Sopenharmony_ci args.ucLineNumber = chan->rec.i2c_id; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* error */ 848c2ecf20Sopenharmony_ci if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) { 858c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("hw_i2c error\n"); 868c2ecf20Sopenharmony_ci r = -EIO; 878c2ecf20Sopenharmony_ci goto done; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (!(flags & HW_I2C_WRITE)) 918c2ecf20Sopenharmony_ci radeon_atom_copy_swap(buf, base, num, false); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cidone: 948c2ecf20Sopenharmony_ci mutex_unlock(&rdev->mode_info.atom_context->scratch_mutex); 958c2ecf20Sopenharmony_ci mutex_unlock(&chan->mutex); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return r; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciint radeon_atom_hw_i2c_xfer(struct i2c_adapter *i2c_adap, 1018c2ecf20Sopenharmony_ci struct i2c_msg *msgs, int num) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); 1048c2ecf20Sopenharmony_ci struct i2c_msg *p; 1058c2ecf20Sopenharmony_ci int i, remaining, current_count, buffer_offset, max_bytes, ret; 1068c2ecf20Sopenharmony_ci u8 flags; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* check for bus probe */ 1098c2ecf20Sopenharmony_ci p = &msgs[0]; 1108c2ecf20Sopenharmony_ci if ((num == 1) && (p->len == 0)) { 1118c2ecf20Sopenharmony_ci ret = radeon_process_i2c_ch(i2c, 1128c2ecf20Sopenharmony_ci p->addr, HW_I2C_WRITE, 1138c2ecf20Sopenharmony_ci NULL, 0); 1148c2ecf20Sopenharmony_ci if (ret) 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci else 1178c2ecf20Sopenharmony_ci return num; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 1218c2ecf20Sopenharmony_ci p = &msgs[i]; 1228c2ecf20Sopenharmony_ci remaining = p->len; 1238c2ecf20Sopenharmony_ci buffer_offset = 0; 1248c2ecf20Sopenharmony_ci /* max_bytes are a limitation of ProcessI2cChannelTransaction not the hw */ 1258c2ecf20Sopenharmony_ci if (p->flags & I2C_M_RD) { 1268c2ecf20Sopenharmony_ci max_bytes = ATOM_MAX_HW_I2C_READ; 1278c2ecf20Sopenharmony_ci flags = HW_I2C_READ; 1288c2ecf20Sopenharmony_ci } else { 1298c2ecf20Sopenharmony_ci max_bytes = ATOM_MAX_HW_I2C_WRITE; 1308c2ecf20Sopenharmony_ci flags = HW_I2C_WRITE; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci while (remaining) { 1338c2ecf20Sopenharmony_ci if (remaining > max_bytes) 1348c2ecf20Sopenharmony_ci current_count = max_bytes; 1358c2ecf20Sopenharmony_ci else 1368c2ecf20Sopenharmony_ci current_count = remaining; 1378c2ecf20Sopenharmony_ci ret = radeon_process_i2c_ch(i2c, 1388c2ecf20Sopenharmony_ci p->addr, flags, 1398c2ecf20Sopenharmony_ci &p->buf[buffer_offset], current_count); 1408c2ecf20Sopenharmony_ci if (ret) 1418c2ecf20Sopenharmony_ci return ret; 1428c2ecf20Sopenharmony_ci remaining -= current_count; 1438c2ecf20Sopenharmony_ci buffer_offset += current_count; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return num; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciu32 radeon_atom_hw_i2c_func(struct i2c_adapter *adap) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 155