/* * SPDX-FileCopyrightText: Copyright (c) 2005-2019 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: MIT * * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #define __NO_VERSION__ #include #include "os-interface.h" #include "nv-linux.h" #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) static int nv_i2c_algo_master_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num) { nv_state_t *nv = (nv_state_t *)adapter->algo_data; unsigned int i = 0; int rc; NV_STATUS rmStatus = NV_OK; nvidia_stack_t *sp = NULL; const unsigned int supported_i2c_flags = I2C_M_RD #if defined(I2C_M_DMA_SAFE) | I2C_M_DMA_SAFE #endif ; rc = nv_kmem_cache_alloc_stack(&sp); if (rc != 0) { return rc; } rc = -EIO; for (i = 0; ((i < (unsigned int)num) && (rmStatus == NV_OK)); i++) { if (msgs[i].flags & ~supported_i2c_flags) { /* we only support basic I2C reads/writes, reject any other commands */ rc = -EINVAL; nv_printf(NV_DBG_ERRORS, "NVRM: Unsupported I2C flags used. (flags:0x%08x)\n", msgs[i].flags); rmStatus = NV_ERR_INVALID_ARGUMENT; } else { rmStatus = rm_i2c_transfer(sp, nv, (void *)adapter, (msgs[i].flags & I2C_M_RD) ? NV_I2C_CMD_READ : NV_I2C_CMD_WRITE, (NvU8)(msgs[i].addr & 0x7f), 0, (NvU32)(msgs[i].len & 0xffffUL), (NvU8 *)msgs[i].buf); } } nv_kmem_cache_free_stack(sp); return (rmStatus != NV_OK) ? rc : num; } static int nv_i2c_algo_smbus_xfer( struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data ) { nv_state_t *nv = (nv_state_t *)adapter->algo_data; int rc; NV_STATUS rmStatus = NV_OK; nvidia_stack_t *sp = NULL; rc = nv_kmem_cache_alloc_stack(&sp); if (rc != 0) { return rc; } rc = -EIO; switch (size) { case I2C_SMBUS_QUICK: rmStatus = rm_i2c_transfer(sp, nv, (void *)adapter, (read_write == I2C_SMBUS_READ) ? NV_I2C_CMD_SMBUS_QUICK_READ : NV_I2C_CMD_SMBUS_QUICK_WRITE, (NvU8)(addr & 0x7f), 0, 0, NULL); break; case I2C_SMBUS_BYTE: if (read_write == I2C_SMBUS_READ) { rmStatus = rm_i2c_transfer(sp, nv, (void *)adapter, NV_I2C_CMD_READ, (NvU8)(addr & 0x7f), 0, 1, (NvU8 *)&data->byte); } else { u8 data = command; rmStatus = rm_i2c_transfer(sp, nv, (void *)adapter, NV_I2C_CMD_WRITE, (NvU8)(addr & 0x7f), 0, 1, (NvU8 *)&data); } break; case I2C_SMBUS_BYTE_DATA: rmStatus = rm_i2c_transfer(sp, nv, (void *)adapter, (read_write == I2C_SMBUS_READ) ? NV_I2C_CMD_SMBUS_READ : NV_I2C_CMD_SMBUS_WRITE, (NvU8)(addr & 0x7f), (NvU8)command, 1, (NvU8 *)&data->byte); break; case I2C_SMBUS_WORD_DATA: if (read_write != I2C_SMBUS_READ) { data->block[1] = (data->word & 0xff); data->block[2] = (data->word >> 8); } rmStatus = rm_i2c_transfer(sp, nv, (void *)adapter, (read_write == I2C_SMBUS_READ) ? NV_I2C_CMD_SMBUS_READ : NV_I2C_CMD_SMBUS_WRITE, (NvU8)(addr & 0x7f), (NvU8)command, 2, (NvU8 *)&data->block[1]); if (read_write == I2C_SMBUS_READ) { data->word = ((NvU16)data->block[1]) | ((NvU16)data->block[2] << 8); } break; case I2C_SMBUS_BLOCK_DATA: rmStatus = rm_i2c_transfer(sp, nv, (void *)adapter, (read_write == I2C_SMBUS_READ) ? NV_I2C_CMD_SMBUS_BLOCK_READ : NV_I2C_CMD_SMBUS_BLOCK_WRITE, (NvU8)(addr & 0x7f), (NvU8)command, sizeof(data->block), (NvU8 *)data->block); break; default: rc = -EINVAL; rmStatus = NV_ERR_INVALID_ARGUMENT; } nv_kmem_cache_free_stack(sp); return (rmStatus != NV_OK) ? rc : 0; } static u32 nv_i2c_algo_functionality(struct i2c_adapter *adapter) { nv_state_t *nv = (nv_state_t *)adapter->algo_data; u32 ret = I2C_FUNC_I2C; nvidia_stack_t *sp = NULL; if (nv_kmem_cache_alloc_stack(&sp) != 0) { return 0; } if (rm_i2c_is_smbus_capable(sp, nv, adapter)) { ret |= (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA); } nv_kmem_cache_free_stack(sp); return ret; } static struct i2c_algorithm nv_i2c_algo = { .master_xfer = nv_i2c_algo_master_xfer, .smbus_xfer = nv_i2c_algo_smbus_xfer, .functionality = nv_i2c_algo_functionality, }; struct i2c_adapter nv_i2c_adapter_prototype = { .owner = THIS_MODULE, .algo = &nv_i2c_algo, .algo_data = NULL, }; void* NV_API_CALL nv_i2c_add_adapter(nv_state_t *nv, NvU32 port) { NV_STATUS rmStatus; nv_linux_state_t *nvl = NV_GET_NVL_FROM_NV_STATE(nv); struct i2c_adapter *pI2cAdapter = NULL; int osstatus = 0; // get a i2c adapter rmStatus = os_alloc_mem((void **)&pI2cAdapter,sizeof(struct i2c_adapter)); if (rmStatus != NV_OK) return NULL; // fill in with default structure os_mem_copy(pI2cAdapter, &nv_i2c_adapter_prototype, sizeof(struct i2c_adapter)); pI2cAdapter->dev.parent = nvl->dev; if (nvl->pci_dev != NULL) { snprintf(pI2cAdapter->name, sizeof(pI2cAdapter->name), "NVIDIA i2c adapter %u at %x:%02x.%u", port, nv->pci_info.bus, nv->pci_info.slot, PCI_FUNC(nvl->pci_dev->devfn)); } else { snprintf(pI2cAdapter->name, sizeof(pI2cAdapter->name), "NVIDIA SOC i2c adapter %u", port); } // add our data to the structure pI2cAdapter->algo_data = (void *)nv; // attempt to register with the kernel osstatus = i2c_add_adapter(pI2cAdapter); if (osstatus) { // free the memory and NULL the ptr os_free_mem(pI2cAdapter); pI2cAdapter = NULL; } return ((void *)pI2cAdapter); } void NV_API_CALL nv_i2c_del_adapter(nv_state_t *nv, void *data) { struct i2c_adapter *pI2cAdapter = (struct i2c_adapter *)data; if (pI2cAdapter) { // release with the OS i2c_del_adapter(pI2cAdapter); os_free_mem(pI2cAdapter); } } #else // defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) void NV_API_CALL nv_i2c_del_adapter(nv_state_t *nv, void *data) { } void* NV_API_CALL nv_i2c_add_adapter(nv_state_t *nv, NvU32 port) { return NULL; } #endif // defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)