xref: /openbmc/linux/drivers/media/pci/solo6x10/solo6x10-i2c.c (revision cbecf716ca618fd44feda6bd9a64a8179d031fc5)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
228cae868SHans Verkuil /*
3cf293a4fSAlexander A. Klimov  * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>
428cae868SHans Verkuil  *
528cae868SHans Verkuil  * Original author:
628cae868SHans Verkuil  * Ben Collins <bcollins@ubuntu.com>
728cae868SHans Verkuil  *
828cae868SHans Verkuil  * Additional work by:
928cae868SHans Verkuil  * John Brooks <john.brooks@bluecherry.net>
1028cae868SHans Verkuil  */
1128cae868SHans Verkuil 
1228cae868SHans Verkuil /* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c
1328cae868SHans Verkuil  * channel. The bus can only handle one i2c event at a time. The below handles
1428cae868SHans Verkuil  * this all wrong. We should be using the status registers to see if the bus
1528cae868SHans Verkuil  * is in use, and have a global lock to check the status register. Also,
1628cae868SHans Verkuil  * the bulk of the work should be handled out-of-interrupt. The ugly loops
1728cae868SHans Verkuil  * that occur during interrupt scare me. The ISR should merely signal
1828cae868SHans Verkuil  * thread context, ACK the interrupt, and move on. -- BenC */
1928cae868SHans Verkuil 
2028cae868SHans Verkuil #include <linux/kernel.h>
21174cd4b1SIngo Molnar #include <linux/sched/signal.h>
2228cae868SHans Verkuil 
2328cae868SHans Verkuil #include "solo6x10.h"
2428cae868SHans Verkuil 
solo_i2c_readbyte(struct solo_dev * solo_dev,int id,u8 addr,u8 off)2528cae868SHans Verkuil u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off)
2628cae868SHans Verkuil {
2728cae868SHans Verkuil 	struct i2c_msg msgs[2];
2828cae868SHans Verkuil 	u8 data;
2928cae868SHans Verkuil 
3028cae868SHans Verkuil 	msgs[0].flags = 0;
3128cae868SHans Verkuil 	msgs[0].addr = addr;
3228cae868SHans Verkuil 	msgs[0].len = 1;
3328cae868SHans Verkuil 	msgs[0].buf = &off;
3428cae868SHans Verkuil 
3528cae868SHans Verkuil 	msgs[1].flags = I2C_M_RD;
3628cae868SHans Verkuil 	msgs[1].addr = addr;
3728cae868SHans Verkuil 	msgs[1].len = 1;
3828cae868SHans Verkuil 	msgs[1].buf = &data;
3928cae868SHans Verkuil 
4028cae868SHans Verkuil 	i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2);
4128cae868SHans Verkuil 
4228cae868SHans Verkuil 	return data;
4328cae868SHans Verkuil }
4428cae868SHans Verkuil 
solo_i2c_writebyte(struct solo_dev * solo_dev,int id,u8 addr,u8 off,u8 data)4528cae868SHans Verkuil void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr,
4628cae868SHans Verkuil 			u8 off, u8 data)
4728cae868SHans Verkuil {
4828cae868SHans Verkuil 	struct i2c_msg msgs;
4928cae868SHans Verkuil 	u8 buf[2];
5028cae868SHans Verkuil 
5128cae868SHans Verkuil 	buf[0] = off;
5228cae868SHans Verkuil 	buf[1] = data;
5328cae868SHans Verkuil 	msgs.flags = 0;
5428cae868SHans Verkuil 	msgs.addr = addr;
5528cae868SHans Verkuil 	msgs.len = 2;
5628cae868SHans Verkuil 	msgs.buf = buf;
5728cae868SHans Verkuil 
5828cae868SHans Verkuil 	i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1);
5928cae868SHans Verkuil }
6028cae868SHans Verkuil 
solo_i2c_flush(struct solo_dev * solo_dev,int wr)6128cae868SHans Verkuil static void solo_i2c_flush(struct solo_dev *solo_dev, int wr)
6228cae868SHans Verkuil {
6328cae868SHans Verkuil 	u32 ctrl;
6428cae868SHans Verkuil 
6528cae868SHans Verkuil 	ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id);
6628cae868SHans Verkuil 
6728cae868SHans Verkuil 	if (solo_dev->i2c_state == IIC_STATE_START)
6828cae868SHans Verkuil 		ctrl |= SOLO_IIC_START;
6928cae868SHans Verkuil 
7028cae868SHans Verkuil 	if (wr) {
7128cae868SHans Verkuil 		ctrl |= SOLO_IIC_WRITE;
7228cae868SHans Verkuil 	} else {
7328cae868SHans Verkuil 		ctrl |= SOLO_IIC_READ;
7428cae868SHans Verkuil 		if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK))
7528cae868SHans Verkuil 			ctrl |= SOLO_IIC_ACK_EN;
7628cae868SHans Verkuil 	}
7728cae868SHans Verkuil 
7828cae868SHans Verkuil 	if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len)
7928cae868SHans Verkuil 		ctrl |= SOLO_IIC_STOP;
8028cae868SHans Verkuil 
8128cae868SHans Verkuil 	solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl);
8228cae868SHans Verkuil }
8328cae868SHans Verkuil 
solo_i2c_start(struct solo_dev * solo_dev)8428cae868SHans Verkuil static void solo_i2c_start(struct solo_dev *solo_dev)
8528cae868SHans Verkuil {
8628cae868SHans Verkuil 	u32 addr = solo_dev->i2c_msg->addr << 1;
8728cae868SHans Verkuil 
8828cae868SHans Verkuil 	if (solo_dev->i2c_msg->flags & I2C_M_RD)
8928cae868SHans Verkuil 		addr |= 1;
9028cae868SHans Verkuil 
9128cae868SHans Verkuil 	solo_dev->i2c_state = IIC_STATE_START;
9228cae868SHans Verkuil 	solo_reg_write(solo_dev, SOLO_IIC_TXD, addr);
9328cae868SHans Verkuil 	solo_i2c_flush(solo_dev, 1);
9428cae868SHans Verkuil }
9528cae868SHans Verkuil 
solo_i2c_stop(struct solo_dev * solo_dev)9628cae868SHans Verkuil static void solo_i2c_stop(struct solo_dev *solo_dev)
9728cae868SHans Verkuil {
9828cae868SHans Verkuil 	solo_irq_off(solo_dev, SOLO_IRQ_IIC);
9928cae868SHans Verkuil 	solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0);
10028cae868SHans Verkuil 	solo_dev->i2c_state = IIC_STATE_STOP;
10128cae868SHans Verkuil 	wake_up(&solo_dev->i2c_wait);
10228cae868SHans Verkuil }
10328cae868SHans Verkuil 
solo_i2c_handle_read(struct solo_dev * solo_dev)10428cae868SHans Verkuil static int solo_i2c_handle_read(struct solo_dev *solo_dev)
10528cae868SHans Verkuil {
10628cae868SHans Verkuil prepare_read:
10728cae868SHans Verkuil 	if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) {
10828cae868SHans Verkuil 		solo_i2c_flush(solo_dev, 0);
10928cae868SHans Verkuil 		return 0;
11028cae868SHans Verkuil 	}
11128cae868SHans Verkuil 
11228cae868SHans Verkuil 	solo_dev->i2c_msg_ptr = 0;
11328cae868SHans Verkuil 	solo_dev->i2c_msg++;
11428cae868SHans Verkuil 	solo_dev->i2c_msg_num--;
11528cae868SHans Verkuil 
11628cae868SHans Verkuil 	if (solo_dev->i2c_msg_num == 0) {
11728cae868SHans Verkuil 		solo_i2c_stop(solo_dev);
11828cae868SHans Verkuil 		return 0;
11928cae868SHans Verkuil 	}
12028cae868SHans Verkuil 
12128cae868SHans Verkuil 	if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) {
12228cae868SHans Verkuil 		solo_i2c_start(solo_dev);
12328cae868SHans Verkuil 	} else {
12428cae868SHans Verkuil 		if (solo_dev->i2c_msg->flags & I2C_M_RD)
12528cae868SHans Verkuil 			goto prepare_read;
12628cae868SHans Verkuil 		else
12728cae868SHans Verkuil 			solo_i2c_stop(solo_dev);
12828cae868SHans Verkuil 	}
12928cae868SHans Verkuil 
13028cae868SHans Verkuil 	return 0;
13128cae868SHans Verkuil }
13228cae868SHans Verkuil 
solo_i2c_handle_write(struct solo_dev * solo_dev)13328cae868SHans Verkuil static int solo_i2c_handle_write(struct solo_dev *solo_dev)
13428cae868SHans Verkuil {
13528cae868SHans Verkuil retry_write:
13628cae868SHans Verkuil 	if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) {
13728cae868SHans Verkuil 		solo_reg_write(solo_dev, SOLO_IIC_TXD,
13828cae868SHans Verkuil 			       solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]);
13928cae868SHans Verkuil 		solo_dev->i2c_msg_ptr++;
14028cae868SHans Verkuil 		solo_i2c_flush(solo_dev, 1);
14128cae868SHans Verkuil 		return 0;
14228cae868SHans Verkuil 	}
14328cae868SHans Verkuil 
14428cae868SHans Verkuil 	solo_dev->i2c_msg_ptr = 0;
14528cae868SHans Verkuil 	solo_dev->i2c_msg++;
14628cae868SHans Verkuil 	solo_dev->i2c_msg_num--;
14728cae868SHans Verkuil 
14828cae868SHans Verkuil 	if (solo_dev->i2c_msg_num == 0) {
14928cae868SHans Verkuil 		solo_i2c_stop(solo_dev);
15028cae868SHans Verkuil 		return 0;
15128cae868SHans Verkuil 	}
15228cae868SHans Verkuil 
15328cae868SHans Verkuil 	if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) {
15428cae868SHans Verkuil 		solo_i2c_start(solo_dev);
15528cae868SHans Verkuil 	} else {
15628cae868SHans Verkuil 		if (solo_dev->i2c_msg->flags & I2C_M_RD)
15728cae868SHans Verkuil 			solo_i2c_stop(solo_dev);
15828cae868SHans Verkuil 		else
15928cae868SHans Verkuil 			goto retry_write;
16028cae868SHans Verkuil 	}
16128cae868SHans Verkuil 
16228cae868SHans Verkuil 	return 0;
16328cae868SHans Verkuil }
16428cae868SHans Verkuil 
solo_i2c_isr(struct solo_dev * solo_dev)16528cae868SHans Verkuil int solo_i2c_isr(struct solo_dev *solo_dev)
16628cae868SHans Verkuil {
16728cae868SHans Verkuil 	u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL);
16828cae868SHans Verkuil 	int ret = -EINVAL;
16928cae868SHans Verkuil 
17028cae868SHans Verkuil 
17128cae868SHans Verkuil 	if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR)
17228cae868SHans Verkuil 	    || solo_dev->i2c_id < 0) {
17328cae868SHans Verkuil 		solo_i2c_stop(solo_dev);
17428cae868SHans Verkuil 		return -ENXIO;
17528cae868SHans Verkuil 	}
17628cae868SHans Verkuil 
17728cae868SHans Verkuil 	switch (solo_dev->i2c_state) {
17828cae868SHans Verkuil 	case IIC_STATE_START:
17928cae868SHans Verkuil 		if (solo_dev->i2c_msg->flags & I2C_M_RD) {
18028cae868SHans Verkuil 			solo_dev->i2c_state = IIC_STATE_READ;
18128cae868SHans Verkuil 			ret = solo_i2c_handle_read(solo_dev);
18228cae868SHans Verkuil 			break;
18328cae868SHans Verkuil 		}
18428cae868SHans Verkuil 
18528cae868SHans Verkuil 		solo_dev->i2c_state = IIC_STATE_WRITE;
186*1771e9fbSGustavo A. R. Silva 		fallthrough;
18728cae868SHans Verkuil 	case IIC_STATE_WRITE:
18828cae868SHans Verkuil 		ret = solo_i2c_handle_write(solo_dev);
18928cae868SHans Verkuil 		break;
19028cae868SHans Verkuil 
19128cae868SHans Verkuil 	case IIC_STATE_READ:
19228cae868SHans Verkuil 		solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] =
19328cae868SHans Verkuil 			solo_reg_read(solo_dev, SOLO_IIC_RXD);
19428cae868SHans Verkuil 		solo_dev->i2c_msg_ptr++;
19528cae868SHans Verkuil 
19628cae868SHans Verkuil 		ret = solo_i2c_handle_read(solo_dev);
19728cae868SHans Verkuil 		break;
19828cae868SHans Verkuil 
19928cae868SHans Verkuil 	default:
20028cae868SHans Verkuil 		solo_i2c_stop(solo_dev);
20128cae868SHans Verkuil 	}
20228cae868SHans Verkuil 
20328cae868SHans Verkuil 	return ret;
20428cae868SHans Verkuil }
20528cae868SHans Verkuil 
solo_i2c_master_xfer(struct i2c_adapter * adap,struct i2c_msg msgs[],int num)20628cae868SHans Verkuil static int solo_i2c_master_xfer(struct i2c_adapter *adap,
20728cae868SHans Verkuil 				struct i2c_msg msgs[], int num)
20828cae868SHans Verkuil {
20928cae868SHans Verkuil 	struct solo_dev *solo_dev = adap->algo_data;
21028cae868SHans Verkuil 	unsigned long timeout;
21128cae868SHans Verkuil 	int ret;
21228cae868SHans Verkuil 	int i;
21328cae868SHans Verkuil 	DEFINE_WAIT(wait);
21428cae868SHans Verkuil 
21528cae868SHans Verkuil 	for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
21628cae868SHans Verkuil 		if (&solo_dev->i2c_adap[i] == adap)
21728cae868SHans Verkuil 			break;
21828cae868SHans Verkuil 	}
21928cae868SHans Verkuil 
22028cae868SHans Verkuil 	if (i == SOLO_I2C_ADAPTERS)
22128cae868SHans Verkuil 		return num; /* XXX Right return value for failure? */
22228cae868SHans Verkuil 
22328cae868SHans Verkuil 	mutex_lock(&solo_dev->i2c_mutex);
22428cae868SHans Verkuil 	solo_dev->i2c_id = i;
22528cae868SHans Verkuil 	solo_dev->i2c_msg = msgs;
22628cae868SHans Verkuil 	solo_dev->i2c_msg_num = num;
22728cae868SHans Verkuil 	solo_dev->i2c_msg_ptr = 0;
22828cae868SHans Verkuil 
22928cae868SHans Verkuil 	solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0);
23028cae868SHans Verkuil 	solo_irq_on(solo_dev, SOLO_IRQ_IIC);
23128cae868SHans Verkuil 	solo_i2c_start(solo_dev);
23228cae868SHans Verkuil 
23328cae868SHans Verkuil 	timeout = HZ / 2;
23428cae868SHans Verkuil 
23528cae868SHans Verkuil 	for (;;) {
23628cae868SHans Verkuil 		prepare_to_wait(&solo_dev->i2c_wait, &wait,
23728cae868SHans Verkuil 				TASK_INTERRUPTIBLE);
23828cae868SHans Verkuil 
23928cae868SHans Verkuil 		if (solo_dev->i2c_state == IIC_STATE_STOP)
24028cae868SHans Verkuil 			break;
24128cae868SHans Verkuil 
24228cae868SHans Verkuil 		timeout = schedule_timeout(timeout);
24328cae868SHans Verkuil 		if (!timeout)
24428cae868SHans Verkuil 			break;
24528cae868SHans Verkuil 
24628cae868SHans Verkuil 		if (signal_pending(current))
24728cae868SHans Verkuil 			break;
24828cae868SHans Verkuil 	}
24928cae868SHans Verkuil 
25028cae868SHans Verkuil 	finish_wait(&solo_dev->i2c_wait, &wait);
25128cae868SHans Verkuil 	ret = num - solo_dev->i2c_msg_num;
25228cae868SHans Verkuil 	solo_dev->i2c_state = IIC_STATE_IDLE;
25328cae868SHans Verkuil 	solo_dev->i2c_id = -1;
25428cae868SHans Verkuil 
25528cae868SHans Verkuil 	mutex_unlock(&solo_dev->i2c_mutex);
25628cae868SHans Verkuil 
25728cae868SHans Verkuil 	return ret;
25828cae868SHans Verkuil }
25928cae868SHans Verkuil 
solo_i2c_functionality(struct i2c_adapter * adap)26028cae868SHans Verkuil static u32 solo_i2c_functionality(struct i2c_adapter *adap)
26128cae868SHans Verkuil {
26228cae868SHans Verkuil 	return I2C_FUNC_I2C;
26328cae868SHans Verkuil }
26428cae868SHans Verkuil 
26528cae868SHans Verkuil static const struct i2c_algorithm solo_i2c_algo = {
26628cae868SHans Verkuil 	.master_xfer	= solo_i2c_master_xfer,
26728cae868SHans Verkuil 	.functionality	= solo_i2c_functionality,
26828cae868SHans Verkuil };
26928cae868SHans Verkuil 
solo_i2c_init(struct solo_dev * solo_dev)27028cae868SHans Verkuil int solo_i2c_init(struct solo_dev *solo_dev)
27128cae868SHans Verkuil {
27228cae868SHans Verkuil 	int i;
27328cae868SHans Verkuil 	int ret;
27428cae868SHans Verkuil 
27528cae868SHans Verkuil 	solo_reg_write(solo_dev, SOLO_IIC_CFG,
27628cae868SHans Verkuil 		       SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE);
27728cae868SHans Verkuil 
27828cae868SHans Verkuil 	solo_dev->i2c_id = -1;
27928cae868SHans Verkuil 	solo_dev->i2c_state = IIC_STATE_IDLE;
28028cae868SHans Verkuil 	init_waitqueue_head(&solo_dev->i2c_wait);
28128cae868SHans Verkuil 	mutex_init(&solo_dev->i2c_mutex);
28228cae868SHans Verkuil 
28328cae868SHans Verkuil 	for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
28428cae868SHans Verkuil 		struct i2c_adapter *adap = &solo_dev->i2c_adap[i];
28528cae868SHans Verkuil 
28628cae868SHans Verkuil 		snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d",
28728cae868SHans Verkuil 			 SOLO6X10_NAME, i);
28828cae868SHans Verkuil 		adap->algo = &solo_i2c_algo;
28928cae868SHans Verkuil 		adap->algo_data = solo_dev;
29028cae868SHans Verkuil 		adap->retries = 1;
29128cae868SHans Verkuil 		adap->dev.parent = &solo_dev->pdev->dev;
29228cae868SHans Verkuil 
29328cae868SHans Verkuil 		ret = i2c_add_adapter(adap);
29428cae868SHans Verkuil 		if (ret) {
29528cae868SHans Verkuil 			adap->algo_data = NULL;
29628cae868SHans Verkuil 			break;
29728cae868SHans Verkuil 		}
29828cae868SHans Verkuil 	}
29928cae868SHans Verkuil 
30028cae868SHans Verkuil 	if (ret) {
30128cae868SHans Verkuil 		for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
30228cae868SHans Verkuil 			if (!solo_dev->i2c_adap[i].algo_data)
30328cae868SHans Verkuil 				break;
30428cae868SHans Verkuil 			i2c_del_adapter(&solo_dev->i2c_adap[i]);
30528cae868SHans Verkuil 			solo_dev->i2c_adap[i].algo_data = NULL;
30628cae868SHans Verkuil 		}
30728cae868SHans Verkuil 		return ret;
30828cae868SHans Verkuil 	}
30928cae868SHans Verkuil 
31028cae868SHans Verkuil 	return 0;
31128cae868SHans Verkuil }
31228cae868SHans Verkuil 
solo_i2c_exit(struct solo_dev * solo_dev)31328cae868SHans Verkuil void solo_i2c_exit(struct solo_dev *solo_dev)
31428cae868SHans Verkuil {
31528cae868SHans Verkuil 	int i;
31628cae868SHans Verkuil 
31728cae868SHans Verkuil 	for (i = 0; i < SOLO_I2C_ADAPTERS; i++) {
31828cae868SHans Verkuil 		if (!solo_dev->i2c_adap[i].algo_data)
31928cae868SHans Verkuil 			continue;
32028cae868SHans Verkuil 		i2c_del_adapter(&solo_dev->i2c_adap[i]);
32128cae868SHans Verkuil 		solo_dev->i2c_adap[i].algo_data = NULL;
32228cae868SHans Verkuil 	}
32328cae868SHans Verkuil }
324