18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Original author:
68c2ecf20Sopenharmony_ci * Ben Collins <bcollins@ubuntu.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Additional work by:
98c2ecf20Sopenharmony_ci * John Brooks <john.brooks@bluecherry.net>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c
138c2ecf20Sopenharmony_ci * channel. The bus can only handle one i2c event at a time. The below handles
148c2ecf20Sopenharmony_ci * this all wrong. We should be using the status registers to see if the bus
158c2ecf20Sopenharmony_ci * is in use, and have a global lock to check the status register. Also,
168c2ecf20Sopenharmony_ci * the bulk of the work should be handled out-of-interrupt. The ugly loops
178c2ecf20Sopenharmony_ci * that occur during interrupt scare me. The ISR should merely signal
188c2ecf20Sopenharmony_ci * thread context, ACK the interrupt, and move on. -- BenC */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "solo6x10.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciu8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct i2c_msg msgs[2];
288c2ecf20Sopenharmony_ci	u8 data;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	msgs[0].flags = 0;
318c2ecf20Sopenharmony_ci	msgs[0].addr = addr;
328c2ecf20Sopenharmony_ci	msgs[0].len = 1;
338c2ecf20Sopenharmony_ci	msgs[0].buf = &off;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	msgs[1].flags = I2C_M_RD;
368c2ecf20Sopenharmony_ci	msgs[1].addr = addr;
378c2ecf20Sopenharmony_ci	msgs[1].len = 1;
388c2ecf20Sopenharmony_ci	msgs[1].buf = &data;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	return data;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_civoid solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr,
468c2ecf20Sopenharmony_ci			u8 off, u8 data)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct i2c_msg msgs;
498c2ecf20Sopenharmony_ci	u8 buf[2];
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	buf[0] = off;
528c2ecf20Sopenharmony_ci	buf[1] = data;
538c2ecf20Sopenharmony_ci	msgs.flags = 0;
548c2ecf20Sopenharmony_ci	msgs.addr = addr;
558c2ecf20Sopenharmony_ci	msgs.len = 2;
568c2ecf20Sopenharmony_ci	msgs.buf = buf;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void solo_i2c_flush(struct solo_dev *solo_dev, int wr)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	u32 ctrl;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (solo_dev->i2c_state == IIC_STATE_START)
688c2ecf20Sopenharmony_ci		ctrl |= SOLO_IIC_START;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (wr) {
718c2ecf20Sopenharmony_ci		ctrl |= SOLO_IIC_WRITE;
728c2ecf20Sopenharmony_ci	} else {
738c2ecf20Sopenharmony_ci		ctrl |= SOLO_IIC_READ;
748c2ecf20Sopenharmony_ci		if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK))
758c2ecf20Sopenharmony_ci			ctrl |= SOLO_IIC_ACK_EN;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len)
798c2ecf20Sopenharmony_ci		ctrl |= SOLO_IIC_STOP;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic void solo_i2c_start(struct solo_dev *solo_dev)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	u32 addr = solo_dev->i2c_msg->addr << 1;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (solo_dev->i2c_msg->flags & I2C_M_RD)
898c2ecf20Sopenharmony_ci		addr |= 1;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	solo_dev->i2c_state = IIC_STATE_START;
928c2ecf20Sopenharmony_ci	solo_reg_write(solo_dev, SOLO_IIC_TXD, addr);
938c2ecf20Sopenharmony_ci	solo_i2c_flush(solo_dev, 1);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void solo_i2c_stop(struct solo_dev *solo_dev)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	solo_irq_off(solo_dev, SOLO_IRQ_IIC);
998c2ecf20Sopenharmony_ci	solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0);
1008c2ecf20Sopenharmony_ci	solo_dev->i2c_state = IIC_STATE_STOP;
1018c2ecf20Sopenharmony_ci	wake_up(&solo_dev->i2c_wait);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int solo_i2c_handle_read(struct solo_dev *solo_dev)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ciprepare_read:
1078c2ecf20Sopenharmony_ci	if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) {
1088c2ecf20Sopenharmony_ci		solo_i2c_flush(solo_dev, 0);
1098c2ecf20Sopenharmony_ci		return 0;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	solo_dev->i2c_msg_ptr = 0;
1138c2ecf20Sopenharmony_ci	solo_dev->i2c_msg++;
1148c2ecf20Sopenharmony_ci	solo_dev->i2c_msg_num--;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (solo_dev->i2c_msg_num == 0) {
1178c2ecf20Sopenharmony_ci		solo_i2c_stop(solo_dev);
1188c2ecf20Sopenharmony_ci		return 0;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) {
1228c2ecf20Sopenharmony_ci		solo_i2c_start(solo_dev);
1238c2ecf20Sopenharmony_ci	} else {
1248c2ecf20Sopenharmony_ci		if (solo_dev->i2c_msg->flags & I2C_M_RD)
1258c2ecf20Sopenharmony_ci			goto prepare_read;
1268c2ecf20Sopenharmony_ci		else
1278c2ecf20Sopenharmony_ci			solo_i2c_stop(solo_dev);
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int solo_i2c_handle_write(struct solo_dev *solo_dev)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ciretry_write:
1368c2ecf20Sopenharmony_ci	if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) {
1378c2ecf20Sopenharmony_ci		solo_reg_write(solo_dev, SOLO_IIC_TXD,
1388c2ecf20Sopenharmony_ci			       solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]);
1398c2ecf20Sopenharmony_ci		solo_dev->i2c_msg_ptr++;
1408c2ecf20Sopenharmony_ci		solo_i2c_flush(solo_dev, 1);
1418c2ecf20Sopenharmony_ci		return 0;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	solo_dev->i2c_msg_ptr = 0;
1458c2ecf20Sopenharmony_ci	solo_dev->i2c_msg++;
1468c2ecf20Sopenharmony_ci	solo_dev->i2c_msg_num--;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (solo_dev->i2c_msg_num == 0) {
1498c2ecf20Sopenharmony_ci		solo_i2c_stop(solo_dev);
1508c2ecf20Sopenharmony_ci		return 0;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) {
1548c2ecf20Sopenharmony_ci		solo_i2c_start(solo_dev);
1558c2ecf20Sopenharmony_ci	} else {
1568c2ecf20Sopenharmony_ci		if (solo_dev->i2c_msg->flags & I2C_M_RD)
1578c2ecf20Sopenharmony_ci			solo_i2c_stop(solo_dev);
1588c2ecf20Sopenharmony_ci		else
1598c2ecf20Sopenharmony_ci			goto retry_write;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ciint solo_i2c_isr(struct solo_dev *solo_dev)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL);
1688c2ecf20Sopenharmony_ci	int ret = -EINVAL;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR)
1728c2ecf20Sopenharmony_ci	    || solo_dev->i2c_id < 0) {
1738c2ecf20Sopenharmony_ci		solo_i2c_stop(solo_dev);
1748c2ecf20Sopenharmony_ci		return -ENXIO;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	switch (solo_dev->i2c_state) {
1788c2ecf20Sopenharmony_ci	case IIC_STATE_START:
1798c2ecf20Sopenharmony_ci		if (solo_dev->i2c_msg->flags & I2C_M_RD) {
1808c2ecf20Sopenharmony_ci			solo_dev->i2c_state = IIC_STATE_READ;
1818c2ecf20Sopenharmony_ci			ret = solo_i2c_handle_read(solo_dev);
1828c2ecf20Sopenharmony_ci			break;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		solo_dev->i2c_state = IIC_STATE_WRITE;
1868c2ecf20Sopenharmony_ci		fallthrough;
1878c2ecf20Sopenharmony_ci	case IIC_STATE_WRITE:
1888c2ecf20Sopenharmony_ci		ret = solo_i2c_handle_write(solo_dev);
1898c2ecf20Sopenharmony_ci		break;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	case IIC_STATE_READ:
1928c2ecf20Sopenharmony_ci		solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] =
1938c2ecf20Sopenharmony_ci			solo_reg_read(solo_dev, SOLO_IIC_RXD);
1948c2ecf20Sopenharmony_ci		solo_dev->i2c_msg_ptr++;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		ret = solo_i2c_handle_read(solo_dev);
1978c2ecf20Sopenharmony_ci		break;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	default:
2008c2ecf20Sopenharmony_ci		solo_i2c_stop(solo_dev);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return ret;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int solo_i2c_master_xfer(struct i2c_adapter *adap,
2078c2ecf20Sopenharmony_ci				struct i2c_msg msgs[], int num)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct solo_dev *solo_dev = adap->algo_data;
2108c2ecf20Sopenharmony_ci	unsigned long timeout;
2118c2ecf20Sopenharmony_ci	int ret;
2128c2ecf20Sopenharmony_ci	int i;
2138c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
2168c2ecf20Sopenharmony_ci		if (&solo_dev->i2c_adap[i] == adap)
2178c2ecf20Sopenharmony_ci			break;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (i == SOLO_I2C_ADAPTERS)
2218c2ecf20Sopenharmony_ci		return num; /* XXX Right return value for failure? */
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	mutex_lock(&solo_dev->i2c_mutex);
2248c2ecf20Sopenharmony_ci	solo_dev->i2c_id = i;
2258c2ecf20Sopenharmony_ci	solo_dev->i2c_msg = msgs;
2268c2ecf20Sopenharmony_ci	solo_dev->i2c_msg_num = num;
2278c2ecf20Sopenharmony_ci	solo_dev->i2c_msg_ptr = 0;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0);
2308c2ecf20Sopenharmony_ci	solo_irq_on(solo_dev, SOLO_IRQ_IIC);
2318c2ecf20Sopenharmony_ci	solo_i2c_start(solo_dev);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	timeout = HZ / 2;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	for (;;) {
2368c2ecf20Sopenharmony_ci		prepare_to_wait(&solo_dev->i2c_wait, &wait,
2378c2ecf20Sopenharmony_ci				TASK_INTERRUPTIBLE);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		if (solo_dev->i2c_state == IIC_STATE_STOP)
2408c2ecf20Sopenharmony_ci			break;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		timeout = schedule_timeout(timeout);
2438c2ecf20Sopenharmony_ci		if (!timeout)
2448c2ecf20Sopenharmony_ci			break;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci		if (signal_pending(current))
2478c2ecf20Sopenharmony_ci			break;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	finish_wait(&solo_dev->i2c_wait, &wait);
2518c2ecf20Sopenharmony_ci	ret = num - solo_dev->i2c_msg_num;
2528c2ecf20Sopenharmony_ci	solo_dev->i2c_state = IIC_STATE_IDLE;
2538c2ecf20Sopenharmony_ci	solo_dev->i2c_id = -1;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	mutex_unlock(&solo_dev->i2c_mutex);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return ret;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic u32 solo_i2c_functionality(struct i2c_adapter *adap)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	return I2C_FUNC_I2C;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic const struct i2c_algorithm solo_i2c_algo = {
2668c2ecf20Sopenharmony_ci	.master_xfer	= solo_i2c_master_xfer,
2678c2ecf20Sopenharmony_ci	.functionality	= solo_i2c_functionality,
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ciint solo_i2c_init(struct solo_dev *solo_dev)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	int i;
2738c2ecf20Sopenharmony_ci	int ret;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	solo_reg_write(solo_dev, SOLO_IIC_CFG,
2768c2ecf20Sopenharmony_ci		       SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	solo_dev->i2c_id = -1;
2798c2ecf20Sopenharmony_ci	solo_dev->i2c_state = IIC_STATE_IDLE;
2808c2ecf20Sopenharmony_ci	init_waitqueue_head(&solo_dev->i2c_wait);
2818c2ecf20Sopenharmony_ci	mutex_init(&solo_dev->i2c_mutex);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
2848c2ecf20Sopenharmony_ci		struct i2c_adapter *adap = &solo_dev->i2c_adap[i];
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d",
2878c2ecf20Sopenharmony_ci			 SOLO6X10_NAME, i);
2888c2ecf20Sopenharmony_ci		adap->algo = &solo_i2c_algo;
2898c2ecf20Sopenharmony_ci		adap->algo_data = solo_dev;
2908c2ecf20Sopenharmony_ci		adap->retries = 1;
2918c2ecf20Sopenharmony_ci		adap->dev.parent = &solo_dev->pdev->dev;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		ret = i2c_add_adapter(adap);
2948c2ecf20Sopenharmony_ci		if (ret) {
2958c2ecf20Sopenharmony_ci			adap->algo_data = NULL;
2968c2ecf20Sopenharmony_ci			break;
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (ret) {
3018c2ecf20Sopenharmony_ci		for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
3028c2ecf20Sopenharmony_ci			if (!solo_dev->i2c_adap[i].algo_data)
3038c2ecf20Sopenharmony_ci				break;
3048c2ecf20Sopenharmony_ci			i2c_del_adapter(&solo_dev->i2c_adap[i]);
3058c2ecf20Sopenharmony_ci			solo_dev->i2c_adap[i].algo_data = NULL;
3068c2ecf20Sopenharmony_ci		}
3078c2ecf20Sopenharmony_ci		return ret;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return 0;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_civoid solo_i2c_exit(struct solo_dev *solo_dev)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	int i;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
3188c2ecf20Sopenharmony_ci		if (!solo_dev->i2c_adap[i].algo_data)
3198c2ecf20Sopenharmony_ci			continue;
3208c2ecf20Sopenharmony_ci		i2c_del_adapter(&solo_dev->i2c_adap[i]);
3218c2ecf20Sopenharmony_ci		solo_dev->i2c_adap[i].algo_data = NULL;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci}
324