18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is provided under a GPLv2 license. When using or 38c2ecf20Sopenharmony_ci * redistributing this file, you may do so under that license. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2016 T-Platforms. All Rights Reserved. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 108c2ecf20Sopenharmony_ci * under the terms and conditions of the GNU General Public License, 118c2ecf20Sopenharmony_ci * version 2, as published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but WITHOUT 148c2ecf20Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 158c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 168c2ecf20Sopenharmony_ci * more details. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License along 198c2ecf20Sopenharmony_ci * with this program; if not, it can be found <http://www.gnu.org/licenses/>. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution in 228c2ecf20Sopenharmony_ci * the file called "COPYING". 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 258c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 268c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 278c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 288c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 298c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 308c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 318c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 328c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 338c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 348c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * IDT PCIe-switch NTB Linux driver 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Contact Information: 398c2ecf20Sopenharmony_ci * Serge Semin <fancer.lancer@gmail.com>, <Sergey.Semin@t-platforms.ru> 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * NOTE of the IDT 89HPESx SMBus-slave interface driver 438c2ecf20Sopenharmony_ci * This driver primarily is developed to have an access to EEPROM device of 448c2ecf20Sopenharmony_ci * IDT PCIe-switches. IDT provides a simple SMBus interface to perform IO- 458c2ecf20Sopenharmony_ci * operations from/to EEPROM, which is located at private (so called Master) 468c2ecf20Sopenharmony_ci * SMBus of switches. Using that interface this the driver creates a simple 478c2ecf20Sopenharmony_ci * binary sysfs-file in the device directory: 488c2ecf20Sopenharmony_ci * /sys/bus/i2c/devices/<bus>-<devaddr>/eeprom 498c2ecf20Sopenharmony_ci * In case if read-only flag is specified in the dts-node of device desription, 508c2ecf20Sopenharmony_ci * User-space applications won't be able to write to the EEPROM sysfs-node. 518c2ecf20Sopenharmony_ci * Additionally IDT 89HPESx SMBus interface has an ability to write/read 528c2ecf20Sopenharmony_ci * data of device CSRs. This driver exposes debugf-file to perform simple IO 538c2ecf20Sopenharmony_ci * operations using that ability for just basic debug purpose. Particularly 548c2ecf20Sopenharmony_ci * next file is created in the specific debugfs-directory: 558c2ecf20Sopenharmony_ci * /sys/kernel/debug/idt_csr/ 568c2ecf20Sopenharmony_ci * Format of the debugfs-node is: 578c2ecf20Sopenharmony_ci * $ cat /sys/kernel/debug/idt_csr/<bus>-<devaddr>/<devname>; 588c2ecf20Sopenharmony_ci * <CSR address>:<CSR value> 598c2ecf20Sopenharmony_ci * So reading the content of the file gives current CSR address and it value. 608c2ecf20Sopenharmony_ci * If User-space application wishes to change current CSR address, 618c2ecf20Sopenharmony_ci * it can just write a proper value to the sysfs-file: 628c2ecf20Sopenharmony_ci * $ echo "<CSR address>" > /sys/kernel/debug/idt_csr/<bus>-<devaddr>/<devname> 638c2ecf20Sopenharmony_ci * If it wants to change the CSR value as well, the format of the write 648c2ecf20Sopenharmony_ci * operation is: 658c2ecf20Sopenharmony_ci * $ echo "<CSR address>:<CSR value>" > \ 668c2ecf20Sopenharmony_ci * /sys/kernel/debug/idt_csr/<bus>-<devaddr>/<devname>; 678c2ecf20Sopenharmony_ci * CSR address and value can be any of hexadecimal, decimal or octal format. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#include <linux/kernel.h> 718c2ecf20Sopenharmony_ci#include <linux/init.h> 728c2ecf20Sopenharmony_ci#include <linux/module.h> 738c2ecf20Sopenharmony_ci#include <linux/types.h> 748c2ecf20Sopenharmony_ci#include <linux/sizes.h> 758c2ecf20Sopenharmony_ci#include <linux/slab.h> 768c2ecf20Sopenharmony_ci#include <linux/mutex.h> 778c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 788c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 798c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 808c2ecf20Sopenharmony_ci#include <linux/property.h> 818c2ecf20Sopenharmony_ci#include <linux/i2c.h> 828c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 838c2ecf20Sopenharmony_ci#include <linux/delay.h> 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define IDT_NAME "89hpesx" 868c2ecf20Sopenharmony_ci#define IDT_89HPESX_DESC "IDT 89HPESx SMBus-slave interface driver" 878c2ecf20Sopenharmony_ci#define IDT_89HPESX_VER "1.0" 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(IDT_89HPESX_DESC); 908c2ecf20Sopenharmony_ciMODULE_VERSION(IDT_89HPESX_VER); 918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 928c2ecf20Sopenharmony_ciMODULE_AUTHOR("T-platforms"); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* 958c2ecf20Sopenharmony_ci * csr_dbgdir - CSR read/write operations Debugfs directory 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic struct dentry *csr_dbgdir; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * struct idt_89hpesx_dev - IDT 89HPESx device data structure 1018c2ecf20Sopenharmony_ci * @eesize: Size of EEPROM in bytes (calculated from "idt,eecompatible") 1028c2ecf20Sopenharmony_ci * @eero: EEPROM Read-only flag 1038c2ecf20Sopenharmony_ci * @eeaddr: EEPROM custom address 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * @inieecmd: Initial cmd value for EEPROM read/write operations 1068c2ecf20Sopenharmony_ci * @inicsrcmd: Initial cmd value for CSR read/write operations 1078c2ecf20Sopenharmony_ci * @iniccode: Initialial command code value for IO-operations 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * @csr: CSR address to perform read operation 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * @smb_write: SMBus write method 1128c2ecf20Sopenharmony_ci * @smb_read: SMBus read method 1138c2ecf20Sopenharmony_ci * @smb_mtx: SMBus mutex 1148c2ecf20Sopenharmony_ci * 1158c2ecf20Sopenharmony_ci * @client: i2c client used to perform IO operations 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * @ee_file: EEPROM read/write sysfs-file 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistruct idt_smb_seq; 1208c2ecf20Sopenharmony_cistruct idt_89hpesx_dev { 1218c2ecf20Sopenharmony_ci u32 eesize; 1228c2ecf20Sopenharmony_ci bool eero; 1238c2ecf20Sopenharmony_ci u8 eeaddr; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci u8 inieecmd; 1268c2ecf20Sopenharmony_ci u8 inicsrcmd; 1278c2ecf20Sopenharmony_ci u8 iniccode; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci u16 csr; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci int (*smb_write)(struct idt_89hpesx_dev *, const struct idt_smb_seq *); 1328c2ecf20Sopenharmony_ci int (*smb_read)(struct idt_89hpesx_dev *, struct idt_smb_seq *); 1338c2ecf20Sopenharmony_ci struct mutex smb_mtx; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci struct i2c_client *client; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci struct bin_attribute *ee_file; 1388c2ecf20Sopenharmony_ci struct dentry *csr_dir; 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * struct idt_smb_seq - sequence of data to be read/written from/to IDT 89HPESx 1438c2ecf20Sopenharmony_ci * @ccode: SMBus command code 1448c2ecf20Sopenharmony_ci * @bytecnt: Byte count of operation 1458c2ecf20Sopenharmony_ci * @data: Data to by written 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistruct idt_smb_seq { 1488c2ecf20Sopenharmony_ci u8 ccode; 1498c2ecf20Sopenharmony_ci u8 bytecnt; 1508c2ecf20Sopenharmony_ci u8 *data; 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* 1548c2ecf20Sopenharmony_ci * struct idt_eeprom_seq - sequence of data to be read/written from/to EEPROM 1558c2ecf20Sopenharmony_ci * @cmd: Transaction CMD 1568c2ecf20Sopenharmony_ci * @eeaddr: EEPROM custom address 1578c2ecf20Sopenharmony_ci * @memaddr: Internal memory address of EEPROM 1588c2ecf20Sopenharmony_ci * @data: Data to be written at the memory address 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_cistruct idt_eeprom_seq { 1618c2ecf20Sopenharmony_ci u8 cmd; 1628c2ecf20Sopenharmony_ci u8 eeaddr; 1638c2ecf20Sopenharmony_ci u16 memaddr; 1648c2ecf20Sopenharmony_ci u8 data; 1658c2ecf20Sopenharmony_ci} __packed; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * struct idt_csr_seq - sequence of data to be read/written from/to CSR 1698c2ecf20Sopenharmony_ci * @cmd: Transaction CMD 1708c2ecf20Sopenharmony_ci * @csraddr: Internal IDT device CSR address 1718c2ecf20Sopenharmony_ci * @data: Data to be read/written from/to the CSR address 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistruct idt_csr_seq { 1748c2ecf20Sopenharmony_ci u8 cmd; 1758c2ecf20Sopenharmony_ci u16 csraddr; 1768c2ecf20Sopenharmony_ci u32 data; 1778c2ecf20Sopenharmony_ci} __packed; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * SMBus command code macros 1818c2ecf20Sopenharmony_ci * @CCODE_END: Indicates the end of transaction 1828c2ecf20Sopenharmony_ci * @CCODE_START: Indicates the start of transaction 1838c2ecf20Sopenharmony_ci * @CCODE_CSR: CSR read/write transaction 1848c2ecf20Sopenharmony_ci * @CCODE_EEPROM: EEPROM read/write transaction 1858c2ecf20Sopenharmony_ci * @CCODE_BYTE: Supplied data has BYTE length 1868c2ecf20Sopenharmony_ci * @CCODE_WORD: Supplied data has WORD length 1878c2ecf20Sopenharmony_ci * @CCODE_BLOCK: Supplied data has variable length passed in bytecnt 1888c2ecf20Sopenharmony_ci * byte right following CCODE byte 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci#define CCODE_END ((u8)0x01) 1918c2ecf20Sopenharmony_ci#define CCODE_START ((u8)0x02) 1928c2ecf20Sopenharmony_ci#define CCODE_CSR ((u8)0x00) 1938c2ecf20Sopenharmony_ci#define CCODE_EEPROM ((u8)0x04) 1948c2ecf20Sopenharmony_ci#define CCODE_BYTE ((u8)0x00) 1958c2ecf20Sopenharmony_ci#define CCODE_WORD ((u8)0x20) 1968c2ecf20Sopenharmony_ci#define CCODE_BLOCK ((u8)0x40) 1978c2ecf20Sopenharmony_ci#define CCODE_PEC ((u8)0x80) 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * EEPROM command macros 2018c2ecf20Sopenharmony_ci * @EEPROM_OP_WRITE: EEPROM write operation 2028c2ecf20Sopenharmony_ci * @EEPROM_OP_READ: EEPROM read operation 2038c2ecf20Sopenharmony_ci * @EEPROM_USA: Use specified address of EEPROM 2048c2ecf20Sopenharmony_ci * @EEPROM_NAERR: EEPROM device is not ready to respond 2058c2ecf20Sopenharmony_ci * @EEPROM_LAERR: EEPROM arbitration loss error 2068c2ecf20Sopenharmony_ci * @EEPROM_MSS: EEPROM misplace start & stop bits error 2078c2ecf20Sopenharmony_ci * @EEPROM_WR_CNT: Bytes count to perform write operation 2088c2ecf20Sopenharmony_ci * @EEPROM_WRRD_CNT: Bytes count to write before reading 2098c2ecf20Sopenharmony_ci * @EEPROM_RD_CNT: Bytes count to perform read operation 2108c2ecf20Sopenharmony_ci * @EEPROM_DEF_SIZE: Fall back size of EEPROM 2118c2ecf20Sopenharmony_ci * @EEPROM_DEF_ADDR: Defatul EEPROM address 2128c2ecf20Sopenharmony_ci * @EEPROM_TOUT: Timeout before retry read operation if eeprom is busy 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci#define EEPROM_OP_WRITE ((u8)0x00) 2158c2ecf20Sopenharmony_ci#define EEPROM_OP_READ ((u8)0x01) 2168c2ecf20Sopenharmony_ci#define EEPROM_USA ((u8)0x02) 2178c2ecf20Sopenharmony_ci#define EEPROM_NAERR ((u8)0x08) 2188c2ecf20Sopenharmony_ci#define EEPROM_LAERR ((u8)0x10) 2198c2ecf20Sopenharmony_ci#define EEPROM_MSS ((u8)0x20) 2208c2ecf20Sopenharmony_ci#define EEPROM_WR_CNT ((u8)5) 2218c2ecf20Sopenharmony_ci#define EEPROM_WRRD_CNT ((u8)4) 2228c2ecf20Sopenharmony_ci#define EEPROM_RD_CNT ((u8)5) 2238c2ecf20Sopenharmony_ci#define EEPROM_DEF_SIZE ((u16)4096) 2248c2ecf20Sopenharmony_ci#define EEPROM_DEF_ADDR ((u8)0x50) 2258c2ecf20Sopenharmony_ci#define EEPROM_TOUT (100) 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* 2288c2ecf20Sopenharmony_ci * CSR command macros 2298c2ecf20Sopenharmony_ci * @CSR_DWE: Enable all four bytes of the operation 2308c2ecf20Sopenharmony_ci * @CSR_OP_WRITE: CSR write operation 2318c2ecf20Sopenharmony_ci * @CSR_OP_READ: CSR read operation 2328c2ecf20Sopenharmony_ci * @CSR_RERR: Read operation error 2338c2ecf20Sopenharmony_ci * @CSR_WERR: Write operation error 2348c2ecf20Sopenharmony_ci * @CSR_WR_CNT: Bytes count to perform write operation 2358c2ecf20Sopenharmony_ci * @CSR_WRRD_CNT: Bytes count to write before reading 2368c2ecf20Sopenharmony_ci * @CSR_RD_CNT: Bytes count to perform read operation 2378c2ecf20Sopenharmony_ci * @CSR_MAX: Maximum CSR address 2388c2ecf20Sopenharmony_ci * @CSR_DEF: Default CSR address 2398c2ecf20Sopenharmony_ci * @CSR_REAL_ADDR: CSR real unshifted address 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci#define CSR_DWE ((u8)0x0F) 2428c2ecf20Sopenharmony_ci#define CSR_OP_WRITE ((u8)0x00) 2438c2ecf20Sopenharmony_ci#define CSR_OP_READ ((u8)0x10) 2448c2ecf20Sopenharmony_ci#define CSR_RERR ((u8)0x40) 2458c2ecf20Sopenharmony_ci#define CSR_WERR ((u8)0x80) 2468c2ecf20Sopenharmony_ci#define CSR_WR_CNT ((u8)7) 2478c2ecf20Sopenharmony_ci#define CSR_WRRD_CNT ((u8)3) 2488c2ecf20Sopenharmony_ci#define CSR_RD_CNT ((u8)7) 2498c2ecf20Sopenharmony_ci#define CSR_MAX ((u32)0x3FFFF) 2508c2ecf20Sopenharmony_ci#define CSR_DEF ((u16)0x0000) 2518c2ecf20Sopenharmony_ci#define CSR_REAL_ADDR(val) ((unsigned int)val << 2) 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* 2548c2ecf20Sopenharmony_ci * IDT 89HPESx basic register 2558c2ecf20Sopenharmony_ci * @IDT_VIDDID_CSR: PCIe VID and DID of IDT 89HPESx 2568c2ecf20Sopenharmony_ci * @IDT_VID_MASK: Mask of VID 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci#define IDT_VIDDID_CSR ((u32)0x0000) 2598c2ecf20Sopenharmony_ci#define IDT_VID_MASK ((u32)0xFFFF) 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/* 2628c2ecf20Sopenharmony_ci * IDT 89HPESx can send NACK when new command is sent before previous one 2638c2ecf20Sopenharmony_ci * fininshed execution. In this case driver retries operation 2648c2ecf20Sopenharmony_ci * certain times. 2658c2ecf20Sopenharmony_ci * @RETRY_CNT: Number of retries before giving up and fail 2668c2ecf20Sopenharmony_ci * @idt_smb_safe: Generate a retry loop on corresponding SMBus method 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci#define RETRY_CNT (128) 2698c2ecf20Sopenharmony_ci#define idt_smb_safe(ops, args...) ({ \ 2708c2ecf20Sopenharmony_ci int __retry = RETRY_CNT; \ 2718c2ecf20Sopenharmony_ci s32 __sts; \ 2728c2ecf20Sopenharmony_ci do { \ 2738c2ecf20Sopenharmony_ci __sts = i2c_smbus_ ## ops ## _data(args); \ 2748c2ecf20Sopenharmony_ci } while (__retry-- && __sts < 0); \ 2758c2ecf20Sopenharmony_ci __sts; \ 2768c2ecf20Sopenharmony_ci}) 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/*=========================================================================== 2798c2ecf20Sopenharmony_ci * i2c bus level IO-operations 2808c2ecf20Sopenharmony_ci *=========================================================================== 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* 2848c2ecf20Sopenharmony_ci * idt_smb_write_byte() - SMBus write method when I2C_SMBUS_BYTE_DATA operation 2858c2ecf20Sopenharmony_ci * is only available 2868c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 2878c2ecf20Sopenharmony_ci * @seq: Sequence of data to be written 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_cistatic int idt_smb_write_byte(struct idt_89hpesx_dev *pdev, 2908c2ecf20Sopenharmony_ci const struct idt_smb_seq *seq) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci s32 sts; 2938c2ecf20Sopenharmony_ci u8 ccode; 2948c2ecf20Sopenharmony_ci int idx; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Loop over the supplied data sending byte one-by-one */ 2978c2ecf20Sopenharmony_ci for (idx = 0; idx < seq->bytecnt; idx++) { 2988c2ecf20Sopenharmony_ci /* Collect the command code byte */ 2998c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_BYTE; 3008c2ecf20Sopenharmony_ci if (idx == 0) 3018c2ecf20Sopenharmony_ci ccode |= CCODE_START; 3028c2ecf20Sopenharmony_ci if (idx == seq->bytecnt - 1) 3038c2ecf20Sopenharmony_ci ccode |= CCODE_END; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Send data to the device */ 3068c2ecf20Sopenharmony_ci sts = idt_smb_safe(write_byte, pdev->client, ccode, 3078c2ecf20Sopenharmony_ci seq->data[idx]); 3088c2ecf20Sopenharmony_ci if (sts != 0) 3098c2ecf20Sopenharmony_ci return (int)sts; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return 0; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* 3168c2ecf20Sopenharmony_ci * idt_smb_read_byte() - SMBus read method when I2C_SMBUS_BYTE_DATA operation 3178c2ecf20Sopenharmony_ci * is only available 3188c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 3198c2ecf20Sopenharmony_ci * @seq: Buffer to read data to 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_cistatic int idt_smb_read_byte(struct idt_89hpesx_dev *pdev, 3228c2ecf20Sopenharmony_ci struct idt_smb_seq *seq) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci s32 sts; 3258c2ecf20Sopenharmony_ci u8 ccode; 3268c2ecf20Sopenharmony_ci int idx; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* Loop over the supplied buffer receiving byte one-by-one */ 3298c2ecf20Sopenharmony_ci for (idx = 0; idx < seq->bytecnt; idx++) { 3308c2ecf20Sopenharmony_ci /* Collect the command code byte */ 3318c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_BYTE; 3328c2ecf20Sopenharmony_ci if (idx == 0) 3338c2ecf20Sopenharmony_ci ccode |= CCODE_START; 3348c2ecf20Sopenharmony_ci if (idx == seq->bytecnt - 1) 3358c2ecf20Sopenharmony_ci ccode |= CCODE_END; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Read data from the device */ 3388c2ecf20Sopenharmony_ci sts = idt_smb_safe(read_byte, pdev->client, ccode); 3398c2ecf20Sopenharmony_ci if (sts < 0) 3408c2ecf20Sopenharmony_ci return (int)sts; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci seq->data[idx] = (u8)sts; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* 3498c2ecf20Sopenharmony_ci * idt_smb_write_word() - SMBus write method when I2C_SMBUS_BYTE_DATA and 3508c2ecf20Sopenharmony_ci * I2C_FUNC_SMBUS_WORD_DATA operations are available 3518c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 3528c2ecf20Sopenharmony_ci * @seq: Sequence of data to be written 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_cistatic int idt_smb_write_word(struct idt_89hpesx_dev *pdev, 3558c2ecf20Sopenharmony_ci const struct idt_smb_seq *seq) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci s32 sts; 3588c2ecf20Sopenharmony_ci u8 ccode; 3598c2ecf20Sopenharmony_ci int idx, evencnt; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Calculate the even count of data to send */ 3628c2ecf20Sopenharmony_ci evencnt = seq->bytecnt - (seq->bytecnt % 2); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Loop over the supplied data sending two bytes at a time */ 3658c2ecf20Sopenharmony_ci for (idx = 0; idx < evencnt; idx += 2) { 3668c2ecf20Sopenharmony_ci /* Collect the command code byte */ 3678c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_WORD; 3688c2ecf20Sopenharmony_ci if (idx == 0) 3698c2ecf20Sopenharmony_ci ccode |= CCODE_START; 3708c2ecf20Sopenharmony_ci if (idx == evencnt - 2) 3718c2ecf20Sopenharmony_ci ccode |= CCODE_END; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Send word data to the device */ 3748c2ecf20Sopenharmony_ci sts = idt_smb_safe(write_word, pdev->client, ccode, 3758c2ecf20Sopenharmony_ci *(u16 *)&seq->data[idx]); 3768c2ecf20Sopenharmony_ci if (sts != 0) 3778c2ecf20Sopenharmony_ci return (int)sts; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* If there is odd number of bytes then send just one last byte */ 3818c2ecf20Sopenharmony_ci if (seq->bytecnt != evencnt) { 3828c2ecf20Sopenharmony_ci /* Collect the command code byte */ 3838c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_BYTE | CCODE_END; 3848c2ecf20Sopenharmony_ci if (idx == 0) 3858c2ecf20Sopenharmony_ci ccode |= CCODE_START; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Send byte data to the device */ 3888c2ecf20Sopenharmony_ci sts = idt_smb_safe(write_byte, pdev->client, ccode, 3898c2ecf20Sopenharmony_ci seq->data[idx]); 3908c2ecf20Sopenharmony_ci if (sts != 0) 3918c2ecf20Sopenharmony_ci return (int)sts; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* 3988c2ecf20Sopenharmony_ci * idt_smb_read_word() - SMBus read method when I2C_SMBUS_BYTE_DATA and 3998c2ecf20Sopenharmony_ci * I2C_FUNC_SMBUS_WORD_DATA operations are available 4008c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 4018c2ecf20Sopenharmony_ci * @seq: Buffer to read data to 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_cistatic int idt_smb_read_word(struct idt_89hpesx_dev *pdev, 4048c2ecf20Sopenharmony_ci struct idt_smb_seq *seq) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci s32 sts; 4078c2ecf20Sopenharmony_ci u8 ccode; 4088c2ecf20Sopenharmony_ci int idx, evencnt; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Calculate the even count of data to send */ 4118c2ecf20Sopenharmony_ci evencnt = seq->bytecnt - (seq->bytecnt % 2); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* Loop over the supplied data reading two bytes at a time */ 4148c2ecf20Sopenharmony_ci for (idx = 0; idx < evencnt; idx += 2) { 4158c2ecf20Sopenharmony_ci /* Collect the command code byte */ 4168c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_WORD; 4178c2ecf20Sopenharmony_ci if (idx == 0) 4188c2ecf20Sopenharmony_ci ccode |= CCODE_START; 4198c2ecf20Sopenharmony_ci if (idx == evencnt - 2) 4208c2ecf20Sopenharmony_ci ccode |= CCODE_END; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* Read word data from the device */ 4238c2ecf20Sopenharmony_ci sts = idt_smb_safe(read_word, pdev->client, ccode); 4248c2ecf20Sopenharmony_ci if (sts < 0) 4258c2ecf20Sopenharmony_ci return (int)sts; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci *(u16 *)&seq->data[idx] = (u16)sts; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* If there is odd number of bytes then receive just one last byte */ 4318c2ecf20Sopenharmony_ci if (seq->bytecnt != evencnt) { 4328c2ecf20Sopenharmony_ci /* Collect the command code byte */ 4338c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_BYTE | CCODE_END; 4348c2ecf20Sopenharmony_ci if (idx == 0) 4358c2ecf20Sopenharmony_ci ccode |= CCODE_START; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Read last data byte from the device */ 4388c2ecf20Sopenharmony_ci sts = idt_smb_safe(read_byte, pdev->client, ccode); 4398c2ecf20Sopenharmony_ci if (sts < 0) 4408c2ecf20Sopenharmony_ci return (int)sts; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci seq->data[idx] = (u8)sts; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci/* 4498c2ecf20Sopenharmony_ci * idt_smb_write_block() - SMBus write method when I2C_SMBUS_BLOCK_DATA 4508c2ecf20Sopenharmony_ci * operation is available 4518c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 4528c2ecf20Sopenharmony_ci * @seq: Sequence of data to be written 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic int idt_smb_write_block(struct idt_89hpesx_dev *pdev, 4558c2ecf20Sopenharmony_ci const struct idt_smb_seq *seq) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci u8 ccode; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* Return error if too much data passed to send */ 4608c2ecf20Sopenharmony_ci if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) 4618c2ecf20Sopenharmony_ci return -EINVAL; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Collect the command code byte */ 4648c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Send block of data to the device */ 4678c2ecf20Sopenharmony_ci return idt_smb_safe(write_block, pdev->client, ccode, seq->bytecnt, 4688c2ecf20Sopenharmony_ci seq->data); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/* 4728c2ecf20Sopenharmony_ci * idt_smb_read_block() - SMBus read method when I2C_SMBUS_BLOCK_DATA 4738c2ecf20Sopenharmony_ci * operation is available 4748c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 4758c2ecf20Sopenharmony_ci * @seq: Buffer to read data to 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_cistatic int idt_smb_read_block(struct idt_89hpesx_dev *pdev, 4788c2ecf20Sopenharmony_ci struct idt_smb_seq *seq) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci s32 sts; 4818c2ecf20Sopenharmony_ci u8 ccode; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* Return error if too much data passed to send */ 4848c2ecf20Sopenharmony_ci if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) 4858c2ecf20Sopenharmony_ci return -EINVAL; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Collect the command code byte */ 4888c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Read block of data from the device */ 4918c2ecf20Sopenharmony_ci sts = idt_smb_safe(read_block, pdev->client, ccode, seq->data); 4928c2ecf20Sopenharmony_ci if (sts != seq->bytecnt) 4938c2ecf20Sopenharmony_ci return (sts < 0 ? sts : -ENODATA); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* 4998c2ecf20Sopenharmony_ci * idt_smb_write_i2c_block() - SMBus write method when I2C_SMBUS_I2C_BLOCK_DATA 5008c2ecf20Sopenharmony_ci * operation is available 5018c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 5028c2ecf20Sopenharmony_ci * @seq: Sequence of data to be written 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * NOTE It's usual SMBus write block operation, except the actual data length is 5058c2ecf20Sopenharmony_ci * sent as first byte of data 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_cistatic int idt_smb_write_i2c_block(struct idt_89hpesx_dev *pdev, 5088c2ecf20Sopenharmony_ci const struct idt_smb_seq *seq) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci u8 ccode, buf[I2C_SMBUS_BLOCK_MAX + 1]; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Return error if too much data passed to send */ 5138c2ecf20Sopenharmony_ci if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) 5148c2ecf20Sopenharmony_ci return -EINVAL; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* Collect the data to send. Length byte must be added prior the data */ 5178c2ecf20Sopenharmony_ci buf[0] = seq->bytecnt; 5188c2ecf20Sopenharmony_ci memcpy(&buf[1], seq->data, seq->bytecnt); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* Collect the command code byte */ 5218c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* Send length and block of data to the device */ 5248c2ecf20Sopenharmony_ci return idt_smb_safe(write_i2c_block, pdev->client, ccode, 5258c2ecf20Sopenharmony_ci seq->bytecnt + 1, buf); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/* 5298c2ecf20Sopenharmony_ci * idt_smb_read_i2c_block() - SMBus read method when I2C_SMBUS_I2C_BLOCK_DATA 5308c2ecf20Sopenharmony_ci * operation is available 5318c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 5328c2ecf20Sopenharmony_ci * @seq: Buffer to read data to 5338c2ecf20Sopenharmony_ci * 5348c2ecf20Sopenharmony_ci * NOTE It's usual SMBus read block operation, except the actual data length is 5358c2ecf20Sopenharmony_ci * retrieved as first byte of data 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_cistatic int idt_smb_read_i2c_block(struct idt_89hpesx_dev *pdev, 5388c2ecf20Sopenharmony_ci struct idt_smb_seq *seq) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci u8 ccode, buf[I2C_SMBUS_BLOCK_MAX + 1]; 5418c2ecf20Sopenharmony_ci s32 sts; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Return error if too much data passed to send */ 5448c2ecf20Sopenharmony_ci if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) 5458c2ecf20Sopenharmony_ci return -EINVAL; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Collect the command code byte */ 5488c2ecf20Sopenharmony_ci ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Read length and block of data from the device */ 5518c2ecf20Sopenharmony_ci sts = idt_smb_safe(read_i2c_block, pdev->client, ccode, 5528c2ecf20Sopenharmony_ci seq->bytecnt + 1, buf); 5538c2ecf20Sopenharmony_ci if (sts != seq->bytecnt + 1) 5548c2ecf20Sopenharmony_ci return (sts < 0 ? sts : -ENODATA); 5558c2ecf20Sopenharmony_ci if (buf[0] != seq->bytecnt) 5568c2ecf20Sopenharmony_ci return -ENODATA; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* Copy retrieved data to the output data buffer */ 5598c2ecf20Sopenharmony_ci memcpy(seq->data, &buf[1], seq->bytecnt); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci return 0; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/*=========================================================================== 5658c2ecf20Sopenharmony_ci * EEPROM IO-operations 5668c2ecf20Sopenharmony_ci *=========================================================================== 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci/* 5708c2ecf20Sopenharmony_ci * idt_eeprom_read_byte() - read just one byte from EEPROM 5718c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 5728c2ecf20Sopenharmony_ci * @memaddr: Start EEPROM memory address 5738c2ecf20Sopenharmony_ci * @data: Data to be written to EEPROM 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_cistatic int idt_eeprom_read_byte(struct idt_89hpesx_dev *pdev, u16 memaddr, 5768c2ecf20Sopenharmony_ci u8 *data) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci struct device *dev = &pdev->client->dev; 5798c2ecf20Sopenharmony_ci struct idt_eeprom_seq eeseq; 5808c2ecf20Sopenharmony_ci struct idt_smb_seq smbseq; 5818c2ecf20Sopenharmony_ci int ret, retry; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Initialize SMBus sequence fields */ 5848c2ecf20Sopenharmony_ci smbseq.ccode = pdev->iniccode | CCODE_EEPROM; 5858c2ecf20Sopenharmony_ci smbseq.data = (u8 *)&eeseq; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* 5888c2ecf20Sopenharmony_ci * Sometimes EEPROM may respond with NACK if it's busy with previous 5898c2ecf20Sopenharmony_ci * operation, so we need to perform a few attempts of read cycle 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_ci retry = RETRY_CNT; 5928c2ecf20Sopenharmony_ci do { 5938c2ecf20Sopenharmony_ci /* Send EEPROM memory address to read data from */ 5948c2ecf20Sopenharmony_ci smbseq.bytecnt = EEPROM_WRRD_CNT; 5958c2ecf20Sopenharmony_ci eeseq.cmd = pdev->inieecmd | EEPROM_OP_READ; 5968c2ecf20Sopenharmony_ci eeseq.eeaddr = pdev->eeaddr; 5978c2ecf20Sopenharmony_ci eeseq.memaddr = cpu_to_le16(memaddr); 5988c2ecf20Sopenharmony_ci ret = pdev->smb_write(pdev, &smbseq); 5998c2ecf20Sopenharmony_ci if (ret != 0) { 6008c2ecf20Sopenharmony_ci dev_err(dev, "Failed to init eeprom addr 0x%02hhx", 6018c2ecf20Sopenharmony_ci memaddr); 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* Perform read operation */ 6068c2ecf20Sopenharmony_ci smbseq.bytecnt = EEPROM_RD_CNT; 6078c2ecf20Sopenharmony_ci ret = pdev->smb_read(pdev, &smbseq); 6088c2ecf20Sopenharmony_ci if (ret != 0) { 6098c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read eeprom data 0x%02hhx", 6108c2ecf20Sopenharmony_ci memaddr); 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* Restart read operation if the device is busy */ 6158c2ecf20Sopenharmony_ci if (retry && (eeseq.cmd & EEPROM_NAERR)) { 6168c2ecf20Sopenharmony_ci dev_dbg(dev, "EEPROM busy, retry reading after %d ms", 6178c2ecf20Sopenharmony_ci EEPROM_TOUT); 6188c2ecf20Sopenharmony_ci msleep(EEPROM_TOUT); 6198c2ecf20Sopenharmony_ci continue; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* Check whether IDT successfully read data from EEPROM */ 6238c2ecf20Sopenharmony_ci if (eeseq.cmd & (EEPROM_NAERR | EEPROM_LAERR | EEPROM_MSS)) { 6248c2ecf20Sopenharmony_ci dev_err(dev, 6258c2ecf20Sopenharmony_ci "Communication with eeprom failed, cmd 0x%hhx", 6268c2ecf20Sopenharmony_ci eeseq.cmd); 6278c2ecf20Sopenharmony_ci ret = -EREMOTEIO; 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* Save retrieved data and exit the loop */ 6328c2ecf20Sopenharmony_ci *data = eeseq.data; 6338c2ecf20Sopenharmony_ci break; 6348c2ecf20Sopenharmony_ci } while (retry--); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Return the status of operation */ 6378c2ecf20Sopenharmony_ci return ret; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci/* 6418c2ecf20Sopenharmony_ci * idt_eeprom_write() - EEPROM write operation 6428c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 6438c2ecf20Sopenharmony_ci * @memaddr: Start EEPROM memory address 6448c2ecf20Sopenharmony_ci * @len: Length of data to be written 6458c2ecf20Sopenharmony_ci * @data: Data to be written to EEPROM 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_cistatic int idt_eeprom_write(struct idt_89hpesx_dev *pdev, u16 memaddr, u16 len, 6488c2ecf20Sopenharmony_ci const u8 *data) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct device *dev = &pdev->client->dev; 6518c2ecf20Sopenharmony_ci struct idt_eeprom_seq eeseq; 6528c2ecf20Sopenharmony_ci struct idt_smb_seq smbseq; 6538c2ecf20Sopenharmony_ci int ret; 6548c2ecf20Sopenharmony_ci u16 idx; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Initialize SMBus sequence fields */ 6578c2ecf20Sopenharmony_ci smbseq.ccode = pdev->iniccode | CCODE_EEPROM; 6588c2ecf20Sopenharmony_ci smbseq.data = (u8 *)&eeseq; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* Send data byte-by-byte, checking if it is successfully written */ 6618c2ecf20Sopenharmony_ci for (idx = 0; idx < len; idx++, memaddr++) { 6628c2ecf20Sopenharmony_ci /* Lock IDT SMBus device */ 6638c2ecf20Sopenharmony_ci mutex_lock(&pdev->smb_mtx); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* Perform write operation */ 6668c2ecf20Sopenharmony_ci smbseq.bytecnt = EEPROM_WR_CNT; 6678c2ecf20Sopenharmony_ci eeseq.cmd = pdev->inieecmd | EEPROM_OP_WRITE; 6688c2ecf20Sopenharmony_ci eeseq.eeaddr = pdev->eeaddr; 6698c2ecf20Sopenharmony_ci eeseq.memaddr = cpu_to_le16(memaddr); 6708c2ecf20Sopenharmony_ci eeseq.data = data[idx]; 6718c2ecf20Sopenharmony_ci ret = pdev->smb_write(pdev, &smbseq); 6728c2ecf20Sopenharmony_ci if (ret != 0) { 6738c2ecf20Sopenharmony_ci dev_err(dev, 6748c2ecf20Sopenharmony_ci "Failed to write 0x%04hx:0x%02hhx to eeprom", 6758c2ecf20Sopenharmony_ci memaddr, data[idx]); 6768c2ecf20Sopenharmony_ci goto err_mutex_unlock; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* 6808c2ecf20Sopenharmony_ci * Check whether the data is successfully written by reading 6818c2ecf20Sopenharmony_ci * from the same EEPROM memory address. 6828c2ecf20Sopenharmony_ci */ 6838c2ecf20Sopenharmony_ci eeseq.data = ~data[idx]; 6848c2ecf20Sopenharmony_ci ret = idt_eeprom_read_byte(pdev, memaddr, &eeseq.data); 6858c2ecf20Sopenharmony_ci if (ret != 0) 6868c2ecf20Sopenharmony_ci goto err_mutex_unlock; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* Check whether the read byte is the same as written one */ 6898c2ecf20Sopenharmony_ci if (eeseq.data != data[idx]) { 6908c2ecf20Sopenharmony_ci dev_err(dev, "Values don't match 0x%02hhx != 0x%02hhx", 6918c2ecf20Sopenharmony_ci eeseq.data, data[idx]); 6928c2ecf20Sopenharmony_ci ret = -EREMOTEIO; 6938c2ecf20Sopenharmony_ci goto err_mutex_unlock; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* Unlock IDT SMBus device */ 6978c2ecf20Sopenharmony_cierr_mutex_unlock: 6988c2ecf20Sopenharmony_ci mutex_unlock(&pdev->smb_mtx); 6998c2ecf20Sopenharmony_ci if (ret != 0) 7008c2ecf20Sopenharmony_ci return ret; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return 0; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci/* 7078c2ecf20Sopenharmony_ci * idt_eeprom_read() - EEPROM read operation 7088c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 7098c2ecf20Sopenharmony_ci * @memaddr: Start EEPROM memory address 7108c2ecf20Sopenharmony_ci * @len: Length of data to read 7118c2ecf20Sopenharmony_ci * @buf: Buffer to read data to 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_cistatic int idt_eeprom_read(struct idt_89hpesx_dev *pdev, u16 memaddr, u16 len, 7148c2ecf20Sopenharmony_ci u8 *buf) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci int ret; 7178c2ecf20Sopenharmony_ci u16 idx; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* Read data byte-by-byte, retrying if it wasn't successful */ 7208c2ecf20Sopenharmony_ci for (idx = 0; idx < len; idx++, memaddr++) { 7218c2ecf20Sopenharmony_ci /* Lock IDT SMBus device */ 7228c2ecf20Sopenharmony_ci mutex_lock(&pdev->smb_mtx); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* Just read the byte to the buffer */ 7258c2ecf20Sopenharmony_ci ret = idt_eeprom_read_byte(pdev, memaddr, &buf[idx]); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Unlock IDT SMBus device */ 7288c2ecf20Sopenharmony_ci mutex_unlock(&pdev->smb_mtx); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* Return error if read operation failed */ 7318c2ecf20Sopenharmony_ci if (ret != 0) 7328c2ecf20Sopenharmony_ci return ret; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci return 0; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci/*=========================================================================== 7398c2ecf20Sopenharmony_ci * CSR IO-operations 7408c2ecf20Sopenharmony_ci *=========================================================================== 7418c2ecf20Sopenharmony_ci */ 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci/* 7448c2ecf20Sopenharmony_ci * idt_csr_write() - CSR write operation 7458c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 7468c2ecf20Sopenharmony_ci * @csraddr: CSR address (with no two LS bits) 7478c2ecf20Sopenharmony_ci * @data: Data to be written to CSR 7488c2ecf20Sopenharmony_ci */ 7498c2ecf20Sopenharmony_cistatic int idt_csr_write(struct idt_89hpesx_dev *pdev, u16 csraddr, 7508c2ecf20Sopenharmony_ci const u32 data) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci struct device *dev = &pdev->client->dev; 7538c2ecf20Sopenharmony_ci struct idt_csr_seq csrseq; 7548c2ecf20Sopenharmony_ci struct idt_smb_seq smbseq; 7558c2ecf20Sopenharmony_ci int ret; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci /* Initialize SMBus sequence fields */ 7588c2ecf20Sopenharmony_ci smbseq.ccode = pdev->iniccode | CCODE_CSR; 7598c2ecf20Sopenharmony_ci smbseq.data = (u8 *)&csrseq; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* Lock IDT SMBus device */ 7628c2ecf20Sopenharmony_ci mutex_lock(&pdev->smb_mtx); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Perform write operation */ 7658c2ecf20Sopenharmony_ci smbseq.bytecnt = CSR_WR_CNT; 7668c2ecf20Sopenharmony_ci csrseq.cmd = pdev->inicsrcmd | CSR_OP_WRITE; 7678c2ecf20Sopenharmony_ci csrseq.csraddr = cpu_to_le16(csraddr); 7688c2ecf20Sopenharmony_ci csrseq.data = cpu_to_le32(data); 7698c2ecf20Sopenharmony_ci ret = pdev->smb_write(pdev, &smbseq); 7708c2ecf20Sopenharmony_ci if (ret != 0) { 7718c2ecf20Sopenharmony_ci dev_err(dev, "Failed to write 0x%04x: 0x%04x to csr", 7728c2ecf20Sopenharmony_ci CSR_REAL_ADDR(csraddr), data); 7738c2ecf20Sopenharmony_ci goto err_mutex_unlock; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* Send CSR address to read data from */ 7778c2ecf20Sopenharmony_ci smbseq.bytecnt = CSR_WRRD_CNT; 7788c2ecf20Sopenharmony_ci csrseq.cmd = pdev->inicsrcmd | CSR_OP_READ; 7798c2ecf20Sopenharmony_ci ret = pdev->smb_write(pdev, &smbseq); 7808c2ecf20Sopenharmony_ci if (ret != 0) { 7818c2ecf20Sopenharmony_ci dev_err(dev, "Failed to init csr address 0x%04x", 7828c2ecf20Sopenharmony_ci CSR_REAL_ADDR(csraddr)); 7838c2ecf20Sopenharmony_ci goto err_mutex_unlock; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* Perform read operation */ 7878c2ecf20Sopenharmony_ci smbseq.bytecnt = CSR_RD_CNT; 7888c2ecf20Sopenharmony_ci ret = pdev->smb_read(pdev, &smbseq); 7898c2ecf20Sopenharmony_ci if (ret != 0) { 7908c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read csr 0x%04x", 7918c2ecf20Sopenharmony_ci CSR_REAL_ADDR(csraddr)); 7928c2ecf20Sopenharmony_ci goto err_mutex_unlock; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* Check whether IDT successfully retrieved CSR data */ 7968c2ecf20Sopenharmony_ci if (csrseq.cmd & (CSR_RERR | CSR_WERR)) { 7978c2ecf20Sopenharmony_ci dev_err(dev, "IDT failed to perform CSR r/w"); 7988c2ecf20Sopenharmony_ci ret = -EREMOTEIO; 7998c2ecf20Sopenharmony_ci goto err_mutex_unlock; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* Unlock IDT SMBus device */ 8038c2ecf20Sopenharmony_cierr_mutex_unlock: 8048c2ecf20Sopenharmony_ci mutex_unlock(&pdev->smb_mtx); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci return ret; 8078c2ecf20Sopenharmony_ci} 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci/* 8108c2ecf20Sopenharmony_ci * idt_csr_read() - CSR read operation 8118c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 8128c2ecf20Sopenharmony_ci * @csraddr: CSR address (with no two LS bits) 8138c2ecf20Sopenharmony_ci * @data: Data to be written to CSR 8148c2ecf20Sopenharmony_ci */ 8158c2ecf20Sopenharmony_cistatic int idt_csr_read(struct idt_89hpesx_dev *pdev, u16 csraddr, u32 *data) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct device *dev = &pdev->client->dev; 8188c2ecf20Sopenharmony_ci struct idt_csr_seq csrseq; 8198c2ecf20Sopenharmony_ci struct idt_smb_seq smbseq; 8208c2ecf20Sopenharmony_ci int ret; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* Initialize SMBus sequence fields */ 8238c2ecf20Sopenharmony_ci smbseq.ccode = pdev->iniccode | CCODE_CSR; 8248c2ecf20Sopenharmony_ci smbseq.data = (u8 *)&csrseq; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* Lock IDT SMBus device */ 8278c2ecf20Sopenharmony_ci mutex_lock(&pdev->smb_mtx); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* Send CSR register address before reading it */ 8308c2ecf20Sopenharmony_ci smbseq.bytecnt = CSR_WRRD_CNT; 8318c2ecf20Sopenharmony_ci csrseq.cmd = pdev->inicsrcmd | CSR_OP_READ; 8328c2ecf20Sopenharmony_ci csrseq.csraddr = cpu_to_le16(csraddr); 8338c2ecf20Sopenharmony_ci ret = pdev->smb_write(pdev, &smbseq); 8348c2ecf20Sopenharmony_ci if (ret != 0) { 8358c2ecf20Sopenharmony_ci dev_err(dev, "Failed to init csr address 0x%04x", 8368c2ecf20Sopenharmony_ci CSR_REAL_ADDR(csraddr)); 8378c2ecf20Sopenharmony_ci goto err_mutex_unlock; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci /* Perform read operation */ 8418c2ecf20Sopenharmony_ci smbseq.bytecnt = CSR_RD_CNT; 8428c2ecf20Sopenharmony_ci ret = pdev->smb_read(pdev, &smbseq); 8438c2ecf20Sopenharmony_ci if (ret != 0) { 8448c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read csr 0x%04hx", 8458c2ecf20Sopenharmony_ci CSR_REAL_ADDR(csraddr)); 8468c2ecf20Sopenharmony_ci goto err_mutex_unlock; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* Check whether IDT successfully retrieved CSR data */ 8508c2ecf20Sopenharmony_ci if (csrseq.cmd & (CSR_RERR | CSR_WERR)) { 8518c2ecf20Sopenharmony_ci dev_err(dev, "IDT failed to perform CSR r/w"); 8528c2ecf20Sopenharmony_ci ret = -EREMOTEIO; 8538c2ecf20Sopenharmony_ci goto err_mutex_unlock; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* Save data retrieved from IDT */ 8578c2ecf20Sopenharmony_ci *data = le32_to_cpu(csrseq.data); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* Unlock IDT SMBus device */ 8608c2ecf20Sopenharmony_cierr_mutex_unlock: 8618c2ecf20Sopenharmony_ci mutex_unlock(&pdev->smb_mtx); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci return ret; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci/*=========================================================================== 8678c2ecf20Sopenharmony_ci * Sysfs/debugfs-nodes IO-operations 8688c2ecf20Sopenharmony_ci *=========================================================================== 8698c2ecf20Sopenharmony_ci */ 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci/* 8728c2ecf20Sopenharmony_ci * eeprom_write() - EEPROM sysfs-node write callback 8738c2ecf20Sopenharmony_ci * @filep: Pointer to the file system node 8748c2ecf20Sopenharmony_ci * @kobj: Pointer to the kernel object related to the sysfs-node 8758c2ecf20Sopenharmony_ci * @attr: Attributes of the file 8768c2ecf20Sopenharmony_ci * @buf: Buffer to write data to 8778c2ecf20Sopenharmony_ci * @off: Offset at which data should be written to 8788c2ecf20Sopenharmony_ci * @count: Number of bytes to write 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_cistatic ssize_t eeprom_write(struct file *filp, struct kobject *kobj, 8818c2ecf20Sopenharmony_ci struct bin_attribute *attr, 8828c2ecf20Sopenharmony_ci char *buf, loff_t off, size_t count) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci struct idt_89hpesx_dev *pdev; 8858c2ecf20Sopenharmony_ci int ret; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci /* Retrieve driver data */ 8888c2ecf20Sopenharmony_ci pdev = dev_get_drvdata(kobj_to_dev(kobj)); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci /* Perform EEPROM write operation */ 8918c2ecf20Sopenharmony_ci ret = idt_eeprom_write(pdev, (u16)off, (u16)count, (u8 *)buf); 8928c2ecf20Sopenharmony_ci return (ret != 0 ? ret : count); 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci/* 8968c2ecf20Sopenharmony_ci * eeprom_read() - EEPROM sysfs-node read callback 8978c2ecf20Sopenharmony_ci * @filep: Pointer to the file system node 8988c2ecf20Sopenharmony_ci * @kobj: Pointer to the kernel object related to the sysfs-node 8998c2ecf20Sopenharmony_ci * @attr: Attributes of the file 9008c2ecf20Sopenharmony_ci * @buf: Buffer to write data to 9018c2ecf20Sopenharmony_ci * @off: Offset at which data should be written to 9028c2ecf20Sopenharmony_ci * @count: Number of bytes to write 9038c2ecf20Sopenharmony_ci */ 9048c2ecf20Sopenharmony_cistatic ssize_t eeprom_read(struct file *filp, struct kobject *kobj, 9058c2ecf20Sopenharmony_ci struct bin_attribute *attr, 9068c2ecf20Sopenharmony_ci char *buf, loff_t off, size_t count) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci struct idt_89hpesx_dev *pdev; 9098c2ecf20Sopenharmony_ci int ret; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci /* Retrieve driver data */ 9128c2ecf20Sopenharmony_ci pdev = dev_get_drvdata(kobj_to_dev(kobj)); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* Perform EEPROM read operation */ 9158c2ecf20Sopenharmony_ci ret = idt_eeprom_read(pdev, (u16)off, (u16)count, (u8 *)buf); 9168c2ecf20Sopenharmony_ci return (ret != 0 ? ret : count); 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci/* 9208c2ecf20Sopenharmony_ci * idt_dbgfs_csr_write() - CSR debugfs-node write callback 9218c2ecf20Sopenharmony_ci * @filep: Pointer to the file system file descriptor 9228c2ecf20Sopenharmony_ci * @buf: Buffer to read data from 9238c2ecf20Sopenharmony_ci * @count: Size of the buffer 9248c2ecf20Sopenharmony_ci * @offp: Offset within the file 9258c2ecf20Sopenharmony_ci * 9268c2ecf20Sopenharmony_ci * It accepts either "0x<reg addr>:0x<value>" for saving register address 9278c2ecf20Sopenharmony_ci * and writing value to specified DWORD register or "0x<reg addr>" for 9288c2ecf20Sopenharmony_ci * just saving register address in order to perform next read operation. 9298c2ecf20Sopenharmony_ci * 9308c2ecf20Sopenharmony_ci * WARNING No spaces are allowed. Incoming string must be strictly formated as: 9318c2ecf20Sopenharmony_ci * "<reg addr>:<value>". Register address must be aligned within 4 bytes 9328c2ecf20Sopenharmony_ci * (one DWORD). 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_cistatic ssize_t idt_dbgfs_csr_write(struct file *filep, const char __user *ubuf, 9358c2ecf20Sopenharmony_ci size_t count, loff_t *offp) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct idt_89hpesx_dev *pdev = filep->private_data; 9388c2ecf20Sopenharmony_ci char *colon_ch, *csraddr_str, *csrval_str; 9398c2ecf20Sopenharmony_ci int ret, csraddr_len; 9408c2ecf20Sopenharmony_ci u32 csraddr, csrval; 9418c2ecf20Sopenharmony_ci char *buf; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (*offp) 9448c2ecf20Sopenharmony_ci return 0; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* Copy data from User-space */ 9478c2ecf20Sopenharmony_ci buf = kmalloc(count + 1, GFP_KERNEL); 9488c2ecf20Sopenharmony_ci if (!buf) 9498c2ecf20Sopenharmony_ci return -ENOMEM; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (copy_from_user(buf, ubuf, count)) { 9528c2ecf20Sopenharmony_ci ret = -EFAULT; 9538c2ecf20Sopenharmony_ci goto free_buf; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci buf[count] = 0; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* Find position of colon in the buffer */ 9588c2ecf20Sopenharmony_ci colon_ch = strnchr(buf, count, ':'); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* 9618c2ecf20Sopenharmony_ci * If there is colon passed then new CSR value should be parsed as 9628c2ecf20Sopenharmony_ci * well, so allocate buffer for CSR address substring. 9638c2ecf20Sopenharmony_ci * If no colon is found, then string must have just one number with 9648c2ecf20Sopenharmony_ci * no new CSR value 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_ci if (colon_ch != NULL) { 9678c2ecf20Sopenharmony_ci csraddr_len = colon_ch - buf; 9688c2ecf20Sopenharmony_ci csraddr_str = 9698c2ecf20Sopenharmony_ci kmalloc(csraddr_len + 1, GFP_KERNEL); 9708c2ecf20Sopenharmony_ci if (csraddr_str == NULL) { 9718c2ecf20Sopenharmony_ci ret = -ENOMEM; 9728c2ecf20Sopenharmony_ci goto free_buf; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci /* Copy the register address to the substring buffer */ 9758c2ecf20Sopenharmony_ci strncpy(csraddr_str, buf, csraddr_len); 9768c2ecf20Sopenharmony_ci csraddr_str[csraddr_len] = '\0'; 9778c2ecf20Sopenharmony_ci /* Register value must follow the colon */ 9788c2ecf20Sopenharmony_ci csrval_str = colon_ch + 1; 9798c2ecf20Sopenharmony_ci } else /* if (str_colon == NULL) */ { 9808c2ecf20Sopenharmony_ci csraddr_str = (char *)buf; /* Just to shut warning up */ 9818c2ecf20Sopenharmony_ci csraddr_len = strnlen(csraddr_str, count); 9828c2ecf20Sopenharmony_ci csrval_str = NULL; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* Convert CSR address to u32 value */ 9868c2ecf20Sopenharmony_ci ret = kstrtou32(csraddr_str, 0, &csraddr); 9878c2ecf20Sopenharmony_ci if (ret != 0) 9888c2ecf20Sopenharmony_ci goto free_csraddr_str; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* Check whether passed register address is valid */ 9918c2ecf20Sopenharmony_ci if (csraddr > CSR_MAX || !IS_ALIGNED(csraddr, SZ_4)) { 9928c2ecf20Sopenharmony_ci ret = -EINVAL; 9938c2ecf20Sopenharmony_ci goto free_csraddr_str; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* Shift register address to the right so to have u16 address */ 9978c2ecf20Sopenharmony_ci pdev->csr = (csraddr >> 2); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* Parse new CSR value and send it to IDT, if colon has been found */ 10008c2ecf20Sopenharmony_ci if (colon_ch != NULL) { 10018c2ecf20Sopenharmony_ci ret = kstrtou32(csrval_str, 0, &csrval); 10028c2ecf20Sopenharmony_ci if (ret != 0) 10038c2ecf20Sopenharmony_ci goto free_csraddr_str; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci ret = idt_csr_write(pdev, pdev->csr, csrval); 10068c2ecf20Sopenharmony_ci if (ret != 0) 10078c2ecf20Sopenharmony_ci goto free_csraddr_str; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* Free memory only if colon has been found */ 10118c2ecf20Sopenharmony_cifree_csraddr_str: 10128c2ecf20Sopenharmony_ci if (colon_ch != NULL) 10138c2ecf20Sopenharmony_ci kfree(csraddr_str); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* Free buffer allocated for data retrieved from User-space */ 10168c2ecf20Sopenharmony_cifree_buf: 10178c2ecf20Sopenharmony_ci kfree(buf); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci return (ret != 0 ? ret : count); 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci/* 10238c2ecf20Sopenharmony_ci * idt_dbgfs_csr_read() - CSR debugfs-node read callback 10248c2ecf20Sopenharmony_ci * @filep: Pointer to the file system file descriptor 10258c2ecf20Sopenharmony_ci * @buf: Buffer to write data to 10268c2ecf20Sopenharmony_ci * @count: Size of the buffer 10278c2ecf20Sopenharmony_ci * @offp: Offset within the file 10288c2ecf20Sopenharmony_ci * 10298c2ecf20Sopenharmony_ci * It just prints the pair "0x<reg addr>:0x<value>" to passed buffer. 10308c2ecf20Sopenharmony_ci */ 10318c2ecf20Sopenharmony_ci#define CSRBUF_SIZE ((size_t)32) 10328c2ecf20Sopenharmony_cistatic ssize_t idt_dbgfs_csr_read(struct file *filep, char __user *ubuf, 10338c2ecf20Sopenharmony_ci size_t count, loff_t *offp) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct idt_89hpesx_dev *pdev = filep->private_data; 10368c2ecf20Sopenharmony_ci u32 csraddr, csrval; 10378c2ecf20Sopenharmony_ci char buf[CSRBUF_SIZE]; 10388c2ecf20Sopenharmony_ci int ret, size; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* Perform CSR read operation */ 10418c2ecf20Sopenharmony_ci ret = idt_csr_read(pdev, pdev->csr, &csrval); 10428c2ecf20Sopenharmony_ci if (ret != 0) 10438c2ecf20Sopenharmony_ci return ret; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* Shift register address to the left so to have real address */ 10468c2ecf20Sopenharmony_ci csraddr = ((u32)pdev->csr << 2); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci /* Print the "0x<reg addr>:0x<value>" to buffer */ 10498c2ecf20Sopenharmony_ci size = snprintf(buf, CSRBUF_SIZE, "0x%05x:0x%08x\n", 10508c2ecf20Sopenharmony_ci (unsigned int)csraddr, (unsigned int)csrval); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* Copy data to User-space */ 10538c2ecf20Sopenharmony_ci return simple_read_from_buffer(ubuf, count, offp, buf, size); 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci/* 10578c2ecf20Sopenharmony_ci * eeprom_attribute - EEPROM sysfs-node attributes 10588c2ecf20Sopenharmony_ci * 10598c2ecf20Sopenharmony_ci * NOTE Size will be changed in compliance with OF node. EEPROM attribute will 10608c2ecf20Sopenharmony_ci * be read-only as well if the corresponding flag is specified in OF node. 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_cistatic BIN_ATTR_RW(eeprom, EEPROM_DEF_SIZE); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci/* 10658c2ecf20Sopenharmony_ci * csr_dbgfs_ops - CSR debugfs-node read/write operations 10668c2ecf20Sopenharmony_ci */ 10678c2ecf20Sopenharmony_cistatic const struct file_operations csr_dbgfs_ops = { 10688c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10698c2ecf20Sopenharmony_ci .open = simple_open, 10708c2ecf20Sopenharmony_ci .write = idt_dbgfs_csr_write, 10718c2ecf20Sopenharmony_ci .read = idt_dbgfs_csr_read 10728c2ecf20Sopenharmony_ci}; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci/*=========================================================================== 10758c2ecf20Sopenharmony_ci * Driver init/deinit methods 10768c2ecf20Sopenharmony_ci *=========================================================================== 10778c2ecf20Sopenharmony_ci */ 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci/* 10808c2ecf20Sopenharmony_ci * idt_set_defval() - disable EEPROM access by default 10818c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 10828c2ecf20Sopenharmony_ci */ 10838c2ecf20Sopenharmony_cistatic void idt_set_defval(struct idt_89hpesx_dev *pdev) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci /* If OF info is missing then use next values */ 10868c2ecf20Sopenharmony_ci pdev->eesize = 0; 10878c2ecf20Sopenharmony_ci pdev->eero = true; 10888c2ecf20Sopenharmony_ci pdev->inieecmd = 0; 10898c2ecf20Sopenharmony_ci pdev->eeaddr = 0; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic const struct i2c_device_id ee_ids[]; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci/* 10958c2ecf20Sopenharmony_ci * idt_ee_match_id() - check whether the node belongs to compatible EEPROMs 10968c2ecf20Sopenharmony_ci */ 10978c2ecf20Sopenharmony_cistatic const struct i2c_device_id *idt_ee_match_id(struct fwnode_handle *fwnode) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci const struct i2c_device_id *id = ee_ids; 11008c2ecf20Sopenharmony_ci const char *compatible, *p; 11018c2ecf20Sopenharmony_ci char devname[I2C_NAME_SIZE]; 11028c2ecf20Sopenharmony_ci int ret; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci ret = fwnode_property_read_string(fwnode, "compatible", &compatible); 11058c2ecf20Sopenharmony_ci if (ret) 11068c2ecf20Sopenharmony_ci return NULL; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci p = strchr(compatible, ','); 11098c2ecf20Sopenharmony_ci strlcpy(devname, p ? p + 1 : compatible, sizeof(devname)); 11108c2ecf20Sopenharmony_ci /* Search through the device name */ 11118c2ecf20Sopenharmony_ci while (id->name[0]) { 11128c2ecf20Sopenharmony_ci if (strcmp(devname, id->name) == 0) 11138c2ecf20Sopenharmony_ci return id; 11148c2ecf20Sopenharmony_ci id++; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci return NULL; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci/* 11208c2ecf20Sopenharmony_ci * idt_get_fw_data() - get IDT i2c-device parameters from device tree 11218c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 11228c2ecf20Sopenharmony_ci */ 11238c2ecf20Sopenharmony_cistatic void idt_get_fw_data(struct idt_89hpesx_dev *pdev) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci struct device *dev = &pdev->client->dev; 11268c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode; 11278c2ecf20Sopenharmony_ci const struct i2c_device_id *ee_id = NULL; 11288c2ecf20Sopenharmony_ci u32 eeprom_addr; 11298c2ecf20Sopenharmony_ci int ret; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci device_for_each_child_node(dev, fwnode) { 11328c2ecf20Sopenharmony_ci ee_id = idt_ee_match_id(fwnode); 11338c2ecf20Sopenharmony_ci if (ee_id) 11348c2ecf20Sopenharmony_ci break; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci dev_warn(dev, "Skip unsupported EEPROM device %pfw\n", fwnode); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* If there is no fwnode EEPROM device, then set zero size */ 11408c2ecf20Sopenharmony_ci if (!ee_id) { 11418c2ecf20Sopenharmony_ci dev_warn(dev, "No fwnode, EEPROM access disabled"); 11428c2ecf20Sopenharmony_ci idt_set_defval(pdev); 11438c2ecf20Sopenharmony_ci return; 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci /* Retrieve EEPROM size */ 11478c2ecf20Sopenharmony_ci pdev->eesize = (u32)ee_id->driver_data; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* Get custom EEPROM address from 'reg' attribute */ 11508c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32(fwnode, "reg", &eeprom_addr); 11518c2ecf20Sopenharmony_ci if (ret || (eeprom_addr == 0)) { 11528c2ecf20Sopenharmony_ci dev_warn(dev, "No EEPROM reg found, use default address 0x%x", 11538c2ecf20Sopenharmony_ci EEPROM_DEF_ADDR); 11548c2ecf20Sopenharmony_ci pdev->inieecmd = 0; 11558c2ecf20Sopenharmony_ci pdev->eeaddr = EEPROM_DEF_ADDR << 1; 11568c2ecf20Sopenharmony_ci } else { 11578c2ecf20Sopenharmony_ci pdev->inieecmd = EEPROM_USA; 11588c2ecf20Sopenharmony_ci pdev->eeaddr = eeprom_addr << 1; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* Check EEPROM 'read-only' flag */ 11628c2ecf20Sopenharmony_ci if (fwnode_property_read_bool(fwnode, "read-only")) 11638c2ecf20Sopenharmony_ci pdev->eero = true; 11648c2ecf20Sopenharmony_ci else /* if (!fwnode_property_read_bool(node, "read-only")) */ 11658c2ecf20Sopenharmony_ci pdev->eero = false; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci fwnode_handle_put(fwnode); 11688c2ecf20Sopenharmony_ci dev_info(dev, "EEPROM of %d bytes found by 0x%x", 11698c2ecf20Sopenharmony_ci pdev->eesize, pdev->eeaddr); 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci/* 11738c2ecf20Sopenharmony_ci * idt_create_pdev() - create and init data structure of the driver 11748c2ecf20Sopenharmony_ci * @client: i2c client of IDT PCIe-switch device 11758c2ecf20Sopenharmony_ci */ 11768c2ecf20Sopenharmony_cistatic struct idt_89hpesx_dev *idt_create_pdev(struct i2c_client *client) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct idt_89hpesx_dev *pdev; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* Allocate memory for driver data */ 11818c2ecf20Sopenharmony_ci pdev = devm_kmalloc(&client->dev, sizeof(struct idt_89hpesx_dev), 11828c2ecf20Sopenharmony_ci GFP_KERNEL); 11838c2ecf20Sopenharmony_ci if (pdev == NULL) 11848c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* Initialize basic fields of the data */ 11878c2ecf20Sopenharmony_ci pdev->client = client; 11888c2ecf20Sopenharmony_ci i2c_set_clientdata(client, pdev); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* Read firmware nodes information */ 11918c2ecf20Sopenharmony_ci idt_get_fw_data(pdev); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* Initialize basic CSR CMD field - use full DWORD-sized r/w ops */ 11948c2ecf20Sopenharmony_ci pdev->inicsrcmd = CSR_DWE; 11958c2ecf20Sopenharmony_ci pdev->csr = CSR_DEF; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* Enable Packet Error Checking if it's supported by adapter */ 11988c2ecf20Sopenharmony_ci if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) { 11998c2ecf20Sopenharmony_ci pdev->iniccode = CCODE_PEC; 12008c2ecf20Sopenharmony_ci client->flags |= I2C_CLIENT_PEC; 12018c2ecf20Sopenharmony_ci } else /* PEC is unsupported */ { 12028c2ecf20Sopenharmony_ci pdev->iniccode = 0; 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci return pdev; 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci/* 12098c2ecf20Sopenharmony_ci * idt_free_pdev() - free data structure of the driver 12108c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 12118c2ecf20Sopenharmony_ci */ 12128c2ecf20Sopenharmony_cistatic void idt_free_pdev(struct idt_89hpesx_dev *pdev) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci /* Clear driver data from device private field */ 12158c2ecf20Sopenharmony_ci i2c_set_clientdata(pdev->client, NULL); 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci/* 12198c2ecf20Sopenharmony_ci * idt_set_smbus_ops() - set supported SMBus operations 12208c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 12218c2ecf20Sopenharmony_ci * Return status of smbus check operations 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_cistatic int idt_set_smbus_ops(struct idt_89hpesx_dev *pdev) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = pdev->client->adapter; 12268c2ecf20Sopenharmony_ci struct device *dev = &pdev->client->dev; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* Check i2c adapter read functionality */ 12298c2ecf20Sopenharmony_ci if (i2c_check_functionality(adapter, 12308c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { 12318c2ecf20Sopenharmony_ci pdev->smb_read = idt_smb_read_block; 12328c2ecf20Sopenharmony_ci dev_dbg(dev, "SMBus block-read op chosen"); 12338c2ecf20Sopenharmony_ci } else if (i2c_check_functionality(adapter, 12348c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { 12358c2ecf20Sopenharmony_ci pdev->smb_read = idt_smb_read_i2c_block; 12368c2ecf20Sopenharmony_ci dev_dbg(dev, "SMBus i2c-block-read op chosen"); 12378c2ecf20Sopenharmony_ci } else if (i2c_check_functionality(adapter, 12388c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_WORD_DATA) && 12398c2ecf20Sopenharmony_ci i2c_check_functionality(adapter, 12408c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_BYTE_DATA)) { 12418c2ecf20Sopenharmony_ci pdev->smb_read = idt_smb_read_word; 12428c2ecf20Sopenharmony_ci dev_warn(dev, "Use slow word/byte SMBus read ops"); 12438c2ecf20Sopenharmony_ci } else if (i2c_check_functionality(adapter, 12448c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_BYTE_DATA)) { 12458c2ecf20Sopenharmony_ci pdev->smb_read = idt_smb_read_byte; 12468c2ecf20Sopenharmony_ci dev_warn(dev, "Use slow byte SMBus read op"); 12478c2ecf20Sopenharmony_ci } else /* no supported smbus read operations */ { 12488c2ecf20Sopenharmony_ci dev_err(dev, "No supported SMBus read op"); 12498c2ecf20Sopenharmony_ci return -EPFNOSUPPORT; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci /* Check i2c adapter write functionality */ 12538c2ecf20Sopenharmony_ci if (i2c_check_functionality(adapter, 12548c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) { 12558c2ecf20Sopenharmony_ci pdev->smb_write = idt_smb_write_block; 12568c2ecf20Sopenharmony_ci dev_dbg(dev, "SMBus block-write op chosen"); 12578c2ecf20Sopenharmony_ci } else if (i2c_check_functionality(adapter, 12588c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { 12598c2ecf20Sopenharmony_ci pdev->smb_write = idt_smb_write_i2c_block; 12608c2ecf20Sopenharmony_ci dev_dbg(dev, "SMBus i2c-block-write op chosen"); 12618c2ecf20Sopenharmony_ci } else if (i2c_check_functionality(adapter, 12628c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WRITE_WORD_DATA) && 12638c2ecf20Sopenharmony_ci i2c_check_functionality(adapter, 12648c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { 12658c2ecf20Sopenharmony_ci pdev->smb_write = idt_smb_write_word; 12668c2ecf20Sopenharmony_ci dev_warn(dev, "Use slow word/byte SMBus write op"); 12678c2ecf20Sopenharmony_ci } else if (i2c_check_functionality(adapter, 12688c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { 12698c2ecf20Sopenharmony_ci pdev->smb_write = idt_smb_write_byte; 12708c2ecf20Sopenharmony_ci dev_warn(dev, "Use slow byte SMBus write op"); 12718c2ecf20Sopenharmony_ci } else /* no supported smbus write operations */ { 12728c2ecf20Sopenharmony_ci dev_err(dev, "No supported SMBus write op"); 12738c2ecf20Sopenharmony_ci return -EPFNOSUPPORT; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci /* Initialize IDT SMBus slave interface mutex */ 12778c2ecf20Sopenharmony_ci mutex_init(&pdev->smb_mtx); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci return 0; 12808c2ecf20Sopenharmony_ci} 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci/* 12838c2ecf20Sopenharmony_ci * idt_check_dev() - check whether it's really IDT 89HPESx device 12848c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 12858c2ecf20Sopenharmony_ci * Return status of i2c adapter check operation 12868c2ecf20Sopenharmony_ci */ 12878c2ecf20Sopenharmony_cistatic int idt_check_dev(struct idt_89hpesx_dev *pdev) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct device *dev = &pdev->client->dev; 12908c2ecf20Sopenharmony_ci u32 viddid; 12918c2ecf20Sopenharmony_ci int ret; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci /* Read VID and DID directly from IDT memory space */ 12948c2ecf20Sopenharmony_ci ret = idt_csr_read(pdev, IDT_VIDDID_CSR, &viddid); 12958c2ecf20Sopenharmony_ci if (ret != 0) { 12968c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read VID/DID"); 12978c2ecf20Sopenharmony_ci return ret; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci /* Check whether it's IDT device */ 13018c2ecf20Sopenharmony_ci if ((viddid & IDT_VID_MASK) != PCI_VENDOR_ID_IDT) { 13028c2ecf20Sopenharmony_ci dev_err(dev, "Got unsupported VID/DID: 0x%08x", viddid); 13038c2ecf20Sopenharmony_ci return -ENODEV; 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci dev_info(dev, "Found IDT 89HPES device VID:0x%04x, DID:0x%04x", 13078c2ecf20Sopenharmony_ci (viddid & IDT_VID_MASK), (viddid >> 16)); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci return 0; 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci/* 13138c2ecf20Sopenharmony_ci * idt_create_sysfs_files() - create sysfs attribute files 13148c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 13158c2ecf20Sopenharmony_ci * Return status of operation 13168c2ecf20Sopenharmony_ci */ 13178c2ecf20Sopenharmony_cistatic int idt_create_sysfs_files(struct idt_89hpesx_dev *pdev) 13188c2ecf20Sopenharmony_ci{ 13198c2ecf20Sopenharmony_ci struct device *dev = &pdev->client->dev; 13208c2ecf20Sopenharmony_ci int ret; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* Don't do anything if EEPROM isn't accessible */ 13238c2ecf20Sopenharmony_ci if (pdev->eesize == 0) { 13248c2ecf20Sopenharmony_ci dev_dbg(dev, "Skip creating sysfs-files"); 13258c2ecf20Sopenharmony_ci return 0; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* Allocate memory for attribute file */ 13298c2ecf20Sopenharmony_ci pdev->ee_file = devm_kmalloc(dev, sizeof(*pdev->ee_file), GFP_KERNEL); 13308c2ecf20Sopenharmony_ci if (!pdev->ee_file) 13318c2ecf20Sopenharmony_ci return -ENOMEM; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci /* Copy the declared EEPROM attr structure to change some of fields */ 13348c2ecf20Sopenharmony_ci memcpy(pdev->ee_file, &bin_attr_eeprom, sizeof(*pdev->ee_file)); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci /* In case of read-only EEPROM get rid of write ability */ 13378c2ecf20Sopenharmony_ci if (pdev->eero) { 13388c2ecf20Sopenharmony_ci pdev->ee_file->attr.mode &= ~0200; 13398c2ecf20Sopenharmony_ci pdev->ee_file->write = NULL; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci /* Create EEPROM sysfs file */ 13428c2ecf20Sopenharmony_ci pdev->ee_file->size = pdev->eesize; 13438c2ecf20Sopenharmony_ci ret = sysfs_create_bin_file(&dev->kobj, pdev->ee_file); 13448c2ecf20Sopenharmony_ci if (ret != 0) { 13458c2ecf20Sopenharmony_ci dev_err(dev, "Failed to create EEPROM sysfs-node"); 13468c2ecf20Sopenharmony_ci return ret; 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci return 0; 13508c2ecf20Sopenharmony_ci} 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci/* 13538c2ecf20Sopenharmony_ci * idt_remove_sysfs_files() - remove sysfs attribute files 13548c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_cistatic void idt_remove_sysfs_files(struct idt_89hpesx_dev *pdev) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct device *dev = &pdev->client->dev; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci /* Don't do anything if EEPROM wasn't accessible */ 13618c2ecf20Sopenharmony_ci if (pdev->eesize == 0) 13628c2ecf20Sopenharmony_ci return; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci /* Remove EEPROM sysfs file */ 13658c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&dev->kobj, pdev->ee_file); 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci/* 13698c2ecf20Sopenharmony_ci * idt_create_dbgfs_files() - create debugfs files 13708c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 13718c2ecf20Sopenharmony_ci */ 13728c2ecf20Sopenharmony_ci#define CSRNAME_LEN ((size_t)32) 13738c2ecf20Sopenharmony_cistatic void idt_create_dbgfs_files(struct idt_89hpesx_dev *pdev) 13748c2ecf20Sopenharmony_ci{ 13758c2ecf20Sopenharmony_ci struct i2c_client *cli = pdev->client; 13768c2ecf20Sopenharmony_ci char fname[CSRNAME_LEN]; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* Create Debugfs directory for CSR file */ 13798c2ecf20Sopenharmony_ci snprintf(fname, CSRNAME_LEN, "%d-%04hx", cli->adapter->nr, cli->addr); 13808c2ecf20Sopenharmony_ci pdev->csr_dir = debugfs_create_dir(fname, csr_dbgdir); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* Create Debugfs file for CSR read/write operations */ 13838c2ecf20Sopenharmony_ci debugfs_create_file(cli->name, 0600, pdev->csr_dir, pdev, 13848c2ecf20Sopenharmony_ci &csr_dbgfs_ops); 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci/* 13888c2ecf20Sopenharmony_ci * idt_remove_dbgfs_files() - remove debugfs files 13898c2ecf20Sopenharmony_ci * @pdev: Pointer to the driver data 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_cistatic void idt_remove_dbgfs_files(struct idt_89hpesx_dev *pdev) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci /* Remove CSR directory and it sysfs-node */ 13948c2ecf20Sopenharmony_ci debugfs_remove_recursive(pdev->csr_dir); 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci/* 13988c2ecf20Sopenharmony_ci * idt_probe() - IDT 89HPESx driver probe() callback method 13998c2ecf20Sopenharmony_ci */ 14008c2ecf20Sopenharmony_cistatic int idt_probe(struct i2c_client *client, const struct i2c_device_id *id) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci struct idt_89hpesx_dev *pdev; 14038c2ecf20Sopenharmony_ci int ret; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci /* Create driver data */ 14068c2ecf20Sopenharmony_ci pdev = idt_create_pdev(client); 14078c2ecf20Sopenharmony_ci if (IS_ERR(pdev)) 14088c2ecf20Sopenharmony_ci return PTR_ERR(pdev); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* Set SMBus operations */ 14118c2ecf20Sopenharmony_ci ret = idt_set_smbus_ops(pdev); 14128c2ecf20Sopenharmony_ci if (ret != 0) 14138c2ecf20Sopenharmony_ci goto err_free_pdev; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci /* Check whether it is truly IDT 89HPESx device */ 14168c2ecf20Sopenharmony_ci ret = idt_check_dev(pdev); 14178c2ecf20Sopenharmony_ci if (ret != 0) 14188c2ecf20Sopenharmony_ci goto err_free_pdev; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci /* Create sysfs files */ 14218c2ecf20Sopenharmony_ci ret = idt_create_sysfs_files(pdev); 14228c2ecf20Sopenharmony_ci if (ret != 0) 14238c2ecf20Sopenharmony_ci goto err_free_pdev; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci /* Create debugfs files */ 14268c2ecf20Sopenharmony_ci idt_create_dbgfs_files(pdev); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci return 0; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_cierr_free_pdev: 14318c2ecf20Sopenharmony_ci idt_free_pdev(pdev); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci return ret; 14348c2ecf20Sopenharmony_ci} 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci/* 14378c2ecf20Sopenharmony_ci * idt_remove() - IDT 89HPESx driver remove() callback method 14388c2ecf20Sopenharmony_ci */ 14398c2ecf20Sopenharmony_cistatic int idt_remove(struct i2c_client *client) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci struct idt_89hpesx_dev *pdev = i2c_get_clientdata(client); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci /* Remove debugfs files first */ 14448c2ecf20Sopenharmony_ci idt_remove_dbgfs_files(pdev); 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci /* Remove sysfs files */ 14478c2ecf20Sopenharmony_ci idt_remove_sysfs_files(pdev); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci /* Discard driver data structure */ 14508c2ecf20Sopenharmony_ci idt_free_pdev(pdev); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci return 0; 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci/* 14568c2ecf20Sopenharmony_ci * ee_ids - array of supported EEPROMs 14578c2ecf20Sopenharmony_ci */ 14588c2ecf20Sopenharmony_cistatic const struct i2c_device_id ee_ids[] = { 14598c2ecf20Sopenharmony_ci { "24c32", 4096}, 14608c2ecf20Sopenharmony_ci { "24c64", 8192}, 14618c2ecf20Sopenharmony_ci { "24c128", 16384}, 14628c2ecf20Sopenharmony_ci { "24c256", 32768}, 14638c2ecf20Sopenharmony_ci { "24c512", 65536}, 14648c2ecf20Sopenharmony_ci {} 14658c2ecf20Sopenharmony_ci}; 14668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ee_ids); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci/* 14698c2ecf20Sopenharmony_ci * idt_ids - supported IDT 89HPESx devices 14708c2ecf20Sopenharmony_ci */ 14718c2ecf20Sopenharmony_cistatic const struct i2c_device_id idt_ids[] = { 14728c2ecf20Sopenharmony_ci { "89hpes8nt2", 0 }, 14738c2ecf20Sopenharmony_ci { "89hpes12nt3", 0 }, 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci { "89hpes24nt6ag2", 0 }, 14768c2ecf20Sopenharmony_ci { "89hpes32nt8ag2", 0 }, 14778c2ecf20Sopenharmony_ci { "89hpes32nt8bg2", 0 }, 14788c2ecf20Sopenharmony_ci { "89hpes12nt12g2", 0 }, 14798c2ecf20Sopenharmony_ci { "89hpes16nt16g2", 0 }, 14808c2ecf20Sopenharmony_ci { "89hpes24nt24g2", 0 }, 14818c2ecf20Sopenharmony_ci { "89hpes32nt24ag2", 0 }, 14828c2ecf20Sopenharmony_ci { "89hpes32nt24bg2", 0 }, 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci { "89hpes12n3", 0 }, 14858c2ecf20Sopenharmony_ci { "89hpes12n3a", 0 }, 14868c2ecf20Sopenharmony_ci { "89hpes24n3", 0 }, 14878c2ecf20Sopenharmony_ci { "89hpes24n3a", 0 }, 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci { "89hpes32h8", 0 }, 14908c2ecf20Sopenharmony_ci { "89hpes32h8g2", 0 }, 14918c2ecf20Sopenharmony_ci { "89hpes48h12", 0 }, 14928c2ecf20Sopenharmony_ci { "89hpes48h12g2", 0 }, 14938c2ecf20Sopenharmony_ci { "89hpes48h12ag2", 0 }, 14948c2ecf20Sopenharmony_ci { "89hpes16h16", 0 }, 14958c2ecf20Sopenharmony_ci { "89hpes22h16", 0 }, 14968c2ecf20Sopenharmony_ci { "89hpes22h16g2", 0 }, 14978c2ecf20Sopenharmony_ci { "89hpes34h16", 0 }, 14988c2ecf20Sopenharmony_ci { "89hpes34h16g2", 0 }, 14998c2ecf20Sopenharmony_ci { "89hpes64h16", 0 }, 15008c2ecf20Sopenharmony_ci { "89hpes64h16g2", 0 }, 15018c2ecf20Sopenharmony_ci { "89hpes64h16ag2", 0 }, 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci /* { "89hpes3t3", 0 }, // No SMBus-slave iface */ 15048c2ecf20Sopenharmony_ci { "89hpes12t3g2", 0 }, 15058c2ecf20Sopenharmony_ci { "89hpes24t3g2", 0 }, 15068c2ecf20Sopenharmony_ci /* { "89hpes4t4", 0 }, // No SMBus-slave iface */ 15078c2ecf20Sopenharmony_ci { "89hpes16t4", 0 }, 15088c2ecf20Sopenharmony_ci { "89hpes4t4g2", 0 }, 15098c2ecf20Sopenharmony_ci { "89hpes10t4g2", 0 }, 15108c2ecf20Sopenharmony_ci { "89hpes16t4g2", 0 }, 15118c2ecf20Sopenharmony_ci { "89hpes16t4ag2", 0 }, 15128c2ecf20Sopenharmony_ci { "89hpes5t5", 0 }, 15138c2ecf20Sopenharmony_ci { "89hpes6t5", 0 }, 15148c2ecf20Sopenharmony_ci { "89hpes8t5", 0 }, 15158c2ecf20Sopenharmony_ci { "89hpes8t5a", 0 }, 15168c2ecf20Sopenharmony_ci { "89hpes24t6", 0 }, 15178c2ecf20Sopenharmony_ci { "89hpes6t6g2", 0 }, 15188c2ecf20Sopenharmony_ci { "89hpes24t6g2", 0 }, 15198c2ecf20Sopenharmony_ci { "89hpes16t7", 0 }, 15208c2ecf20Sopenharmony_ci { "89hpes32t8", 0 }, 15218c2ecf20Sopenharmony_ci { "89hpes32t8g2", 0 }, 15228c2ecf20Sopenharmony_ci { "89hpes48t12", 0 }, 15238c2ecf20Sopenharmony_ci { "89hpes48t12g2", 0 }, 15248c2ecf20Sopenharmony_ci { /* END OF LIST */ } 15258c2ecf20Sopenharmony_ci}; 15268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, idt_ids); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_cistatic const struct of_device_id idt_of_match[] = { 15298c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes8nt2", }, 15308c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes12nt3", }, 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes24nt6ag2", }, 15338c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes32nt8ag2", }, 15348c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes32nt8bg2", }, 15358c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes12nt12g2", }, 15368c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes16nt16g2", }, 15378c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes24nt24g2", }, 15388c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes32nt24ag2", }, 15398c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes32nt24bg2", }, 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes12n3", }, 15428c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes12n3a", }, 15438c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes24n3", }, 15448c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes24n3a", }, 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes32h8", }, 15478c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes32h8g2", }, 15488c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes48h12", }, 15498c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes48h12g2", }, 15508c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes48h12ag2", }, 15518c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes16h16", }, 15528c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes22h16", }, 15538c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes22h16g2", }, 15548c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes34h16", }, 15558c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes34h16g2", }, 15568c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes64h16", }, 15578c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes64h16g2", }, 15588c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes64h16ag2", }, 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes12t3g2", }, 15618c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes24t3g2", }, 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes16t4", }, 15648c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes4t4g2", }, 15658c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes10t4g2", }, 15668c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes16t4g2", }, 15678c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes16t4ag2", }, 15688c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes5t5", }, 15698c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes6t5", }, 15708c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes8t5", }, 15718c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes8t5a", }, 15728c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes24t6", }, 15738c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes6t6g2", }, 15748c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes24t6g2", }, 15758c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes16t7", }, 15768c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes32t8", }, 15778c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes32t8g2", }, 15788c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes48t12", }, 15798c2ecf20Sopenharmony_ci { .compatible = "idt,89hpes48t12g2", }, 15808c2ecf20Sopenharmony_ci { }, 15818c2ecf20Sopenharmony_ci}; 15828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, idt_of_match); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci/* 15858c2ecf20Sopenharmony_ci * idt_driver - IDT 89HPESx driver structure 15868c2ecf20Sopenharmony_ci */ 15878c2ecf20Sopenharmony_cistatic struct i2c_driver idt_driver = { 15888c2ecf20Sopenharmony_ci .driver = { 15898c2ecf20Sopenharmony_ci .name = IDT_NAME, 15908c2ecf20Sopenharmony_ci .of_match_table = idt_of_match, 15918c2ecf20Sopenharmony_ci }, 15928c2ecf20Sopenharmony_ci .probe = idt_probe, 15938c2ecf20Sopenharmony_ci .remove = idt_remove, 15948c2ecf20Sopenharmony_ci .id_table = idt_ids, 15958c2ecf20Sopenharmony_ci}; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci/* 15988c2ecf20Sopenharmony_ci * idt_init() - IDT 89HPESx driver init() callback method 15998c2ecf20Sopenharmony_ci */ 16008c2ecf20Sopenharmony_cistatic int __init idt_init(void) 16018c2ecf20Sopenharmony_ci{ 16028c2ecf20Sopenharmony_ci /* Create Debugfs directory first */ 16038c2ecf20Sopenharmony_ci if (debugfs_initialized()) 16048c2ecf20Sopenharmony_ci csr_dbgdir = debugfs_create_dir("idt_csr", NULL); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci /* Add new i2c-device driver */ 16078c2ecf20Sopenharmony_ci return i2c_add_driver(&idt_driver); 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_cimodule_init(idt_init); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci/* 16128c2ecf20Sopenharmony_ci * idt_exit() - IDT 89HPESx driver exit() callback method 16138c2ecf20Sopenharmony_ci */ 16148c2ecf20Sopenharmony_cistatic void __exit idt_exit(void) 16158c2ecf20Sopenharmony_ci{ 16168c2ecf20Sopenharmony_ci /* Discard debugfs directory and all files if any */ 16178c2ecf20Sopenharmony_ci debugfs_remove_recursive(csr_dbgdir); 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci /* Unregister i2c-device driver */ 16208c2ecf20Sopenharmony_ci i2c_del_driver(&idt_driver); 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_cimodule_exit(idt_exit); 1623