18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2004 Steven J. Hill
48c2ecf20Sopenharmony_ci * Copyright (C) 2001,2002,2003 Broadcom Corporation
58c2ecf20Sopenharmony_ci * Copyright (C) 1995-2000 Simon G. Vogl
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <asm/sibyte/sb1250_regs.h>
148c2ecf20Sopenharmony_ci#include <asm/sibyte/sb1250_smbus.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistruct i2c_algo_sibyte_data {
188c2ecf20Sopenharmony_ci	void *data;		/* private data */
198c2ecf20Sopenharmony_ci	int   bus;		/* which bus */
208c2ecf20Sopenharmony_ci	void *reg_base;		/* CSR base */
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* ----- global defines ----------------------------------------------- */
248c2ecf20Sopenharmony_ci#define SMB_CSR(a,r) ((long)(a->reg_base + r))
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
288c2ecf20Sopenharmony_ci		      unsigned short flags, char read_write,
298c2ecf20Sopenharmony_ci		      u8 command, int size, union i2c_smbus_data * data)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
328c2ecf20Sopenharmony_ci	int data_bytes = 0;
338c2ecf20Sopenharmony_ci	int error;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
368c2ecf20Sopenharmony_ci		;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	switch (size) {
398c2ecf20Sopenharmony_ci	case I2C_SMBUS_QUICK:
408c2ecf20Sopenharmony_ci		csr_out32((V_SMB_ADDR(addr) |
418c2ecf20Sopenharmony_ci			   (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
428c2ecf20Sopenharmony_ci			   V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
438c2ecf20Sopenharmony_ci		break;
448c2ecf20Sopenharmony_ci	case I2C_SMBUS_BYTE:
458c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_READ) {
468c2ecf20Sopenharmony_ci			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
478c2ecf20Sopenharmony_ci				  SMB_CSR(adap, R_SMB_START));
488c2ecf20Sopenharmony_ci			data_bytes = 1;
498c2ecf20Sopenharmony_ci		} else {
508c2ecf20Sopenharmony_ci			csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
518c2ecf20Sopenharmony_ci			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
528c2ecf20Sopenharmony_ci				  SMB_CSR(adap, R_SMB_START));
538c2ecf20Sopenharmony_ci		}
548c2ecf20Sopenharmony_ci		break;
558c2ecf20Sopenharmony_ci	case I2C_SMBUS_BYTE_DATA:
568c2ecf20Sopenharmony_ci		csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
578c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_READ) {
588c2ecf20Sopenharmony_ci			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
598c2ecf20Sopenharmony_ci				  SMB_CSR(adap, R_SMB_START));
608c2ecf20Sopenharmony_ci			data_bytes = 1;
618c2ecf20Sopenharmony_ci		} else {
628c2ecf20Sopenharmony_ci			csr_out32(V_SMB_LB(data->byte),
638c2ecf20Sopenharmony_ci				  SMB_CSR(adap, R_SMB_DATA));
648c2ecf20Sopenharmony_ci			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
658c2ecf20Sopenharmony_ci				  SMB_CSR(adap, R_SMB_START));
668c2ecf20Sopenharmony_ci		}
678c2ecf20Sopenharmony_ci		break;
688c2ecf20Sopenharmony_ci	case I2C_SMBUS_WORD_DATA:
698c2ecf20Sopenharmony_ci		csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
708c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_READ) {
718c2ecf20Sopenharmony_ci			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
728c2ecf20Sopenharmony_ci				  SMB_CSR(adap, R_SMB_START));
738c2ecf20Sopenharmony_ci			data_bytes = 2;
748c2ecf20Sopenharmony_ci		} else {
758c2ecf20Sopenharmony_ci			csr_out32(V_SMB_LB(data->word & 0xff),
768c2ecf20Sopenharmony_ci				  SMB_CSR(adap, R_SMB_DATA));
778c2ecf20Sopenharmony_ci			csr_out32(V_SMB_MB(data->word >> 8),
788c2ecf20Sopenharmony_ci				  SMB_CSR(adap, R_SMB_DATA));
798c2ecf20Sopenharmony_ci			csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
808c2ecf20Sopenharmony_ci				  SMB_CSR(adap, R_SMB_START));
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci		break;
838c2ecf20Sopenharmony_ci	default:
848c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
888c2ecf20Sopenharmony_ci		;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
918c2ecf20Sopenharmony_ci	if (error & M_SMB_ERROR) {
928c2ecf20Sopenharmony_ci		/* Clear error bit by writing a 1 */
938c2ecf20Sopenharmony_ci		csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
948c2ecf20Sopenharmony_ci		return (error & M_SMB_ERROR_TYPE) ? -EIO : -ENXIO;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (data_bytes == 1)
988c2ecf20Sopenharmony_ci		data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
998c2ecf20Sopenharmony_ci	if (data_bytes == 2)
1008c2ecf20Sopenharmony_ci		data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic u32 bit_func(struct i2c_adapter *adap)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
1088c2ecf20Sopenharmony_ci		I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* -----exported algorithm data: -------------------------------------	*/
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic const struct i2c_algorithm i2c_sibyte_algo = {
1158c2ecf20Sopenharmony_ci	.smbus_xfer	= smbus_xfer,
1168c2ecf20Sopenharmony_ci	.functionality	= bit_func,
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/*
1208c2ecf20Sopenharmony_ci * registering functions to load algorithms at runtime
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_cistatic int __init i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* Register new adapter to i2c module... */
1278c2ecf20Sopenharmony_ci	i2c_adap->algo = &i2c_sibyte_algo;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* Set the requested frequency. */
1308c2ecf20Sopenharmony_ci	csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
1318c2ecf20Sopenharmony_ci	csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return i2c_add_numbered_adapter(i2c_adap);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic struct i2c_algo_sibyte_data sibyte_board_data[2] = {
1388c2ecf20Sopenharmony_ci	{ NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) },
1398c2ecf20Sopenharmony_ci	{ NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) }
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic struct i2c_adapter sibyte_board_adapter[2] = {
1438c2ecf20Sopenharmony_ci	{
1448c2ecf20Sopenharmony_ci		.owner		= THIS_MODULE,
1458c2ecf20Sopenharmony_ci		.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
1468c2ecf20Sopenharmony_ci		.algo		= NULL,
1478c2ecf20Sopenharmony_ci		.algo_data	= &sibyte_board_data[0],
1488c2ecf20Sopenharmony_ci		.nr		= 0,
1498c2ecf20Sopenharmony_ci		.name		= "SiByte SMBus 0",
1508c2ecf20Sopenharmony_ci	},
1518c2ecf20Sopenharmony_ci	{
1528c2ecf20Sopenharmony_ci		.owner		= THIS_MODULE,
1538c2ecf20Sopenharmony_ci		.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
1548c2ecf20Sopenharmony_ci		.algo		= NULL,
1558c2ecf20Sopenharmony_ci		.algo_data	= &sibyte_board_data[1],
1568c2ecf20Sopenharmony_ci		.nr		= 1,
1578c2ecf20Sopenharmony_ci		.name		= "SiByte SMBus 1",
1588c2ecf20Sopenharmony_ci	},
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic int __init i2c_sibyte_init(void)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	pr_info("i2c-sibyte: i2c SMBus adapter module for SiByte board\n");
1648c2ecf20Sopenharmony_ci	if (i2c_sibyte_add_bus(&sibyte_board_adapter[0], K_SMB_FREQ_100KHZ) < 0)
1658c2ecf20Sopenharmony_ci		return -ENODEV;
1668c2ecf20Sopenharmony_ci	if (i2c_sibyte_add_bus(&sibyte_board_adapter[1],
1678c2ecf20Sopenharmony_ci			       K_SMB_FREQ_400KHZ) < 0) {
1688c2ecf20Sopenharmony_ci		i2c_del_adapter(&sibyte_board_adapter[0]);
1698c2ecf20Sopenharmony_ci		return -ENODEV;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	return 0;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic void __exit i2c_sibyte_exit(void)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	i2c_del_adapter(&sibyte_board_adapter[0]);
1778c2ecf20Sopenharmony_ci	i2c_del_adapter(&sibyte_board_adapter[1]);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cimodule_init(i2c_sibyte_init);
1818c2ecf20Sopenharmony_cimodule_exit(i2c_sibyte_exit);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kip Walker (Broadcom Corp.)");
1848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
1858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMBus adapter routines for SiByte boards");
1868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
187