162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Coral-P(A)/Lime I2C adapter driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/fb.h>
962306a36Sopenharmony_ci#include <linux/i2c.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/export.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "mb862xxfb.h"
1562306a36Sopenharmony_ci#include "mb862xx_reg.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int mb862xx_i2c_wait_event(struct i2c_adapter *adap)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	struct mb862xxfb_par *par = adap->algo_data;
2062306a36Sopenharmony_ci	u32 reg;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	do {
2362306a36Sopenharmony_ci		udelay(10);
2462306a36Sopenharmony_ci		reg = inreg(i2c, GC_I2C_BCR);
2562306a36Sopenharmony_ci		if (reg & (I2C_INT | I2C_BER))
2662306a36Sopenharmony_ci			break;
2762306a36Sopenharmony_ci	} while (1);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	return (reg & I2C_BER) ? 0 : 1;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int mb862xx_i2c_do_address(struct i2c_adapter *adap, int addr)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct mb862xxfb_par *par = adap->algo_data;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	outreg(i2c, GC_I2C_DAR, addr);
3762306a36Sopenharmony_ci	outreg(i2c, GC_I2C_CCR, I2C_CLOCK_AND_ENABLE);
3862306a36Sopenharmony_ci	outreg(i2c, GC_I2C_BCR, par->i2c_rs ? I2C_REPEATED_START : I2C_START);
3962306a36Sopenharmony_ci	if (!mb862xx_i2c_wait_event(adap))
4062306a36Sopenharmony_ci		return -EIO;
4162306a36Sopenharmony_ci	par->i2c_rs = !(inreg(i2c, GC_I2C_BSR) & I2C_LRB);
4262306a36Sopenharmony_ci	return par->i2c_rs;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int mb862xx_i2c_write_byte(struct i2c_adapter *adap, u8 byte)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct mb862xxfb_par *par = adap->algo_data;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	outreg(i2c, GC_I2C_DAR, byte);
5062306a36Sopenharmony_ci	outreg(i2c, GC_I2C_BCR, I2C_START);
5162306a36Sopenharmony_ci	if (!mb862xx_i2c_wait_event(adap))
5262306a36Sopenharmony_ci		return -EIO;
5362306a36Sopenharmony_ci	return !(inreg(i2c, GC_I2C_BSR) & I2C_LRB);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int mb862xx_i2c_read_byte(struct i2c_adapter *adap, u8 *byte, int last)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct mb862xxfb_par *par = adap->algo_data;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	outreg(i2c, GC_I2C_BCR, I2C_START | (last ? 0 : I2C_ACK));
6162306a36Sopenharmony_ci	if (!mb862xx_i2c_wait_event(adap))
6262306a36Sopenharmony_ci		return 0;
6362306a36Sopenharmony_ci	*byte = inreg(i2c, GC_I2C_DAR);
6462306a36Sopenharmony_ci	return 1;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void mb862xx_i2c_stop(struct i2c_adapter *adap)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct mb862xxfb_par *par = adap->algo_data;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	outreg(i2c, GC_I2C_BCR, I2C_STOP);
7262306a36Sopenharmony_ci	outreg(i2c, GC_I2C_CCR, I2C_DISABLE);
7362306a36Sopenharmony_ci	par->i2c_rs = 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int mb862xx_i2c_read(struct i2c_adapter *adap, struct i2c_msg *m)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	int i, ret = 0;
7962306a36Sopenharmony_ci	int last = m->len - 1;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	for (i = 0; i < m->len; i++) {
8262306a36Sopenharmony_ci		if (!mb862xx_i2c_read_byte(adap, &m->buf[i], i == last)) {
8362306a36Sopenharmony_ci			ret = -EIO;
8462306a36Sopenharmony_ci			break;
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	return ret;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int mb862xx_i2c_write(struct i2c_adapter *adap, struct i2c_msg *m)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	int i, ret = 0;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	for (i = 0; i < m->len; i++) {
9562306a36Sopenharmony_ci		if (!mb862xx_i2c_write_byte(adap, m->buf[i])) {
9662306a36Sopenharmony_ci			ret = -EIO;
9762306a36Sopenharmony_ci			break;
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	return ret;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int mb862xx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
10462306a36Sopenharmony_ci			int num)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct mb862xxfb_par *par = adap->algo_data;
10762306a36Sopenharmony_ci	struct i2c_msg *m;
10862306a36Sopenharmony_ci	int addr;
10962306a36Sopenharmony_ci	int i = 0, err = 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	dev_dbg(par->dev, "%s: %d msgs\n", __func__, num);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
11462306a36Sopenharmony_ci		m = &msgs[i];
11562306a36Sopenharmony_ci		if (!m->len) {
11662306a36Sopenharmony_ci			dev_dbg(par->dev, "%s: null msgs\n", __func__);
11762306a36Sopenharmony_ci			continue;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci		addr = m->addr;
12062306a36Sopenharmony_ci		if (m->flags & I2C_M_RD)
12162306a36Sopenharmony_ci			addr |= 1;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		err = mb862xx_i2c_do_address(adap, addr);
12462306a36Sopenharmony_ci		if (err < 0)
12562306a36Sopenharmony_ci			break;
12662306a36Sopenharmony_ci		if (m->flags & I2C_M_RD)
12762306a36Sopenharmony_ci			err = mb862xx_i2c_read(adap, m);
12862306a36Sopenharmony_ci		else
12962306a36Sopenharmony_ci			err = mb862xx_i2c_write(adap, m);
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (i)
13362306a36Sopenharmony_ci		mb862xx_i2c_stop(adap);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return (err < 0) ? err : i;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic u32 mb862xx_func(struct i2c_adapter *adap)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	return I2C_FUNC_SMBUS_BYTE_DATA;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic const struct i2c_algorithm mb862xx_algo = {
14462306a36Sopenharmony_ci	.master_xfer	= mb862xx_xfer,
14562306a36Sopenharmony_ci	.functionality	= mb862xx_func,
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic struct i2c_adapter mb862xx_i2c_adapter = {
14962306a36Sopenharmony_ci	.name		= "MB862xx I2C adapter",
15062306a36Sopenharmony_ci	.algo		= &mb862xx_algo,
15162306a36Sopenharmony_ci	.owner		= THIS_MODULE,
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciint mb862xx_i2c_init(struct mb862xxfb_par *par)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	mb862xx_i2c_adapter.algo_data = par;
15762306a36Sopenharmony_ci	par->adap = &mb862xx_i2c_adapter;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return i2c_add_adapter(par->adap);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_civoid mb862xx_i2c_exit(struct mb862xxfb_par *par)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	if (par->adap) {
16562306a36Sopenharmony_ci		i2c_del_adapter(par->adap);
16662306a36Sopenharmony_ci		par->adap = NULL;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci}
169