162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * cx88-vp3054-i2c.c -- support for the secondary I2C bus of the
462306a36Sopenharmony_ci *			DNTV Live! DVB-T Pro (VP-3054), wired as:
562306a36Sopenharmony_ci *			GPIO[0] -> SCL, GPIO[1] -> SDA
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * (c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "cx88.h"
1162306a36Sopenharmony_ci#include "cx88-vp3054-i2c.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciMODULE_DESCRIPTION("driver for cx2388x VP3054 design");
1962306a36Sopenharmony_ciMODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
2062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic void vp3054_bit_setscl(void *data, int state)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct cx8802_dev *dev = data;
2762306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
2862306a36Sopenharmony_ci	struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (state) {
3162306a36Sopenharmony_ci		vp3054_i2c->state |=  0x0001;	/* SCL high */
3262306a36Sopenharmony_ci		vp3054_i2c->state &= ~0x0100;	/* external pullup */
3362306a36Sopenharmony_ci	} else {
3462306a36Sopenharmony_ci		vp3054_i2c->state &= ~0x0001;	/* SCL low */
3562306a36Sopenharmony_ci		vp3054_i2c->state |=  0x0100;	/* drive pin */
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci	cx_write(MO_GP0_IO, 0x010000 | vp3054_i2c->state);
3862306a36Sopenharmony_ci	cx_read(MO_GP0_IO);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void vp3054_bit_setsda(void *data, int state)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct cx8802_dev *dev = data;
4462306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
4562306a36Sopenharmony_ci	struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (state) {
4862306a36Sopenharmony_ci		vp3054_i2c->state |=  0x0002;	/* SDA high */
4962306a36Sopenharmony_ci		vp3054_i2c->state &= ~0x0200;	/* tristate pin */
5062306a36Sopenharmony_ci	} else {
5162306a36Sopenharmony_ci		vp3054_i2c->state &= ~0x0002;	/* SDA low */
5262306a36Sopenharmony_ci		vp3054_i2c->state |=  0x0200;	/* drive pin */
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	cx_write(MO_GP0_IO, 0x020000 | vp3054_i2c->state);
5562306a36Sopenharmony_ci	cx_read(MO_GP0_IO);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int vp3054_bit_getscl(void *data)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct cx8802_dev *dev = data;
6162306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
6262306a36Sopenharmony_ci	u32 state;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	state = cx_read(MO_GP0_IO);
6562306a36Sopenharmony_ci	return (state & 0x01) ? 1 : 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int vp3054_bit_getsda(void *data)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct cx8802_dev *dev = data;
7162306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
7262306a36Sopenharmony_ci	u32 state;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	state = cx_read(MO_GP0_IO);
7562306a36Sopenharmony_ci	return (state & 0x02) ? 1 : 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic const struct i2c_algo_bit_data vp3054_i2c_algo_template = {
8162306a36Sopenharmony_ci	.setsda  = vp3054_bit_setsda,
8262306a36Sopenharmony_ci	.setscl  = vp3054_bit_setscl,
8362306a36Sopenharmony_ci	.getsda  = vp3054_bit_getsda,
8462306a36Sopenharmony_ci	.getscl  = vp3054_bit_getscl,
8562306a36Sopenharmony_ci	.udelay  = 16,
8662306a36Sopenharmony_ci	.timeout = 200,
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciint vp3054_i2c_probe(struct cx8802_dev *dev)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
9462306a36Sopenharmony_ci	struct vp3054_i2c_state *vp3054_i2c;
9562306a36Sopenharmony_ci	int rc;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO)
9862306a36Sopenharmony_ci		return 0;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	vp3054_i2c = kzalloc(sizeof(*vp3054_i2c), GFP_KERNEL);
10162306a36Sopenharmony_ci	if (!vp3054_i2c)
10262306a36Sopenharmony_ci		return -ENOMEM;
10362306a36Sopenharmony_ci	dev->vp3054 = vp3054_i2c;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	vp3054_i2c->algo = vp3054_i2c_algo_template;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	vp3054_i2c->adap.dev.parent = &dev->pci->dev;
10862306a36Sopenharmony_ci	strscpy(vp3054_i2c->adap.name, core->name,
10962306a36Sopenharmony_ci		sizeof(vp3054_i2c->adap.name));
11062306a36Sopenharmony_ci	vp3054_i2c->adap.owner = THIS_MODULE;
11162306a36Sopenharmony_ci	vp3054_i2c->algo.data = dev;
11262306a36Sopenharmony_ci	i2c_set_adapdata(&vp3054_i2c->adap, dev);
11362306a36Sopenharmony_ci	vp3054_i2c->adap.algo_data = &vp3054_i2c->algo;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	vp3054_bit_setscl(dev, 1);
11662306a36Sopenharmony_ci	vp3054_bit_setsda(dev, 1);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	rc = i2c_bit_add_bus(&vp3054_i2c->adap);
11962306a36Sopenharmony_ci	if (rc != 0) {
12062306a36Sopenharmony_ci		pr_err("vp3054_i2c register FAILED\n");
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		kfree(dev->vp3054);
12362306a36Sopenharmony_ci		dev->vp3054 = NULL;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return rc;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ciEXPORT_SYMBOL(vp3054_i2c_probe);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_civoid vp3054_i2c_remove(struct cx8802_dev *dev)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (!vp3054_i2c ||
13562306a36Sopenharmony_ci	    dev->core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO)
13662306a36Sopenharmony_ci		return;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	i2c_del_adapter(&vp3054_i2c->adap);
13962306a36Sopenharmony_ci	kfree(vp3054_i2c);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ciEXPORT_SYMBOL(vp3054_i2c_remove);
142