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