i2c-sibyte.c (5032B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2004 Steven J. Hill 4 * Copyright (C) 2001,2002,2003 Broadcom Corporation 5 * Copyright (C) 1995-2000 Simon G. Vogl 6 */ 7 8#include <linux/kernel.h> 9#include <linux/module.h> 10#include <linux/init.h> 11#include <linux/i2c.h> 12#include <linux/io.h> 13#include <asm/sibyte/sb1250_regs.h> 14#include <asm/sibyte/sb1250_smbus.h> 15 16 17struct i2c_algo_sibyte_data { 18 void *data; /* private data */ 19 int bus; /* which bus */ 20 void *reg_base; /* CSR base */ 21}; 22 23/* ----- global defines ----------------------------------------------- */ 24#define SMB_CSR(a,r) ((long)(a->reg_base + r)) 25 26 27static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr, 28 unsigned short flags, char read_write, 29 u8 command, int size, union i2c_smbus_data * data) 30{ 31 struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; 32 int data_bytes = 0; 33 int error; 34 35 while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) 36 ; 37 38 switch (size) { 39 case I2C_SMBUS_QUICK: 40 csr_out32((V_SMB_ADDR(addr) | 41 (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) | 42 V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START)); 43 break; 44 case I2C_SMBUS_BYTE: 45 if (read_write == I2C_SMBUS_READ) { 46 csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE), 47 SMB_CSR(adap, R_SMB_START)); 48 data_bytes = 1; 49 } else { 50 csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); 51 csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE), 52 SMB_CSR(adap, R_SMB_START)); 53 } 54 break; 55 case I2C_SMBUS_BYTE_DATA: 56 csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); 57 if (read_write == I2C_SMBUS_READ) { 58 csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE), 59 SMB_CSR(adap, R_SMB_START)); 60 data_bytes = 1; 61 } else { 62 csr_out32(V_SMB_LB(data->byte), 63 SMB_CSR(adap, R_SMB_DATA)); 64 csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), 65 SMB_CSR(adap, R_SMB_START)); 66 } 67 break; 68 case I2C_SMBUS_WORD_DATA: 69 csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); 70 if (read_write == I2C_SMBUS_READ) { 71 csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE), 72 SMB_CSR(adap, R_SMB_START)); 73 data_bytes = 2; 74 } else { 75 csr_out32(V_SMB_LB(data->word & 0xff), 76 SMB_CSR(adap, R_SMB_DATA)); 77 csr_out32(V_SMB_MB(data->word >> 8), 78 SMB_CSR(adap, R_SMB_DATA)); 79 csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), 80 SMB_CSR(adap, R_SMB_START)); 81 } 82 break; 83 default: 84 return -EOPNOTSUPP; 85 } 86 87 while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) 88 ; 89 90 error = csr_in32(SMB_CSR(adap, R_SMB_STATUS)); 91 if (error & M_SMB_ERROR) { 92 /* Clear error bit by writing a 1 */ 93 csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS)); 94 return (error & M_SMB_ERROR_TYPE) ? -EIO : -ENXIO; 95 } 96 97 if (data_bytes == 1) 98 data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff; 99 if (data_bytes == 2) 100 data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff; 101 102 return 0; 103} 104 105static u32 bit_func(struct i2c_adapter *adap) 106{ 107 return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 108 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA); 109} 110 111 112/* -----exported algorithm data: ------------------------------------- */ 113 114static const struct i2c_algorithm i2c_sibyte_algo = { 115 .smbus_xfer = smbus_xfer, 116 .functionality = bit_func, 117}; 118 119/* 120 * registering functions to load algorithms at runtime 121 */ 122static int __init i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed) 123{ 124 struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; 125 126 /* Register new adapter to i2c module... */ 127 i2c_adap->algo = &i2c_sibyte_algo; 128 129 /* Set the requested frequency. */ 130 csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ)); 131 csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL)); 132 133 return i2c_add_numbered_adapter(i2c_adap); 134} 135 136 137static struct i2c_algo_sibyte_data sibyte_board_data[2] = { 138 { NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) }, 139 { NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) } 140}; 141 142static struct i2c_adapter sibyte_board_adapter[2] = { 143 { 144 .owner = THIS_MODULE, 145 .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 146 .algo = NULL, 147 .algo_data = &sibyte_board_data[0], 148 .nr = 0, 149 .name = "SiByte SMBus 0", 150 }, 151 { 152 .owner = THIS_MODULE, 153 .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 154 .algo = NULL, 155 .algo_data = &sibyte_board_data[1], 156 .nr = 1, 157 .name = "SiByte SMBus 1", 158 }, 159}; 160 161static int __init i2c_sibyte_init(void) 162{ 163 pr_info("i2c-sibyte: i2c SMBus adapter module for SiByte board\n"); 164 if (i2c_sibyte_add_bus(&sibyte_board_adapter[0], K_SMB_FREQ_100KHZ) < 0) 165 return -ENODEV; 166 if (i2c_sibyte_add_bus(&sibyte_board_adapter[1], 167 K_SMB_FREQ_400KHZ) < 0) { 168 i2c_del_adapter(&sibyte_board_adapter[0]); 169 return -ENODEV; 170 } 171 return 0; 172} 173 174static void __exit i2c_sibyte_exit(void) 175{ 176 i2c_del_adapter(&sibyte_board_adapter[0]); 177 i2c_del_adapter(&sibyte_board_adapter[1]); 178} 179 180module_init(i2c_sibyte_init); 181module_exit(i2c_sibyte_exit); 182 183MODULE_AUTHOR("Kip Walker (Broadcom Corp.)"); 184MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>"); 185MODULE_DESCRIPTION("SMBus adapter routines for SiByte boards"); 186MODULE_LICENSE("GPL");