1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * cx88-vp3054-i2c.c -- support for the secondary I2C bus of the |
4 | * DNTV Live! DVB-T Pro (VP-3054), wired as: |
5 | * GPIO[0] -> SCL, GPIO[1] -> SDA |
6 | * |
7 | * (c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> |
8 | */ |
9 | |
10 | #include "cx88.h" |
11 | #include "cx88-vp3054-i2c.h" |
12 | |
13 | #include <linux/module.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/init.h> |
16 | #include <linux/io.h> |
17 | |
18 | MODULE_DESCRIPTION("driver for cx2388x VP3054 design" ); |
19 | MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>" ); |
20 | MODULE_LICENSE("GPL" ); |
21 | |
22 | /* ----------------------------------------------------------------------- */ |
23 | |
24 | static void vp3054_bit_setscl(void *data, int state) |
25 | { |
26 | struct cx8802_dev *dev = data; |
27 | struct cx88_core *core = dev->core; |
28 | struct vp3054_i2c_state *vp3054_i2c = dev->vp3054; |
29 | |
30 | if (state) { |
31 | vp3054_i2c->state |= 0x0001; /* SCL high */ |
32 | vp3054_i2c->state &= ~0x0100; /* external pullup */ |
33 | } else { |
34 | vp3054_i2c->state &= ~0x0001; /* SCL low */ |
35 | vp3054_i2c->state |= 0x0100; /* drive pin */ |
36 | } |
37 | cx_write(MO_GP0_IO, 0x010000 | vp3054_i2c->state); |
38 | cx_read(MO_GP0_IO); |
39 | } |
40 | |
41 | static void vp3054_bit_setsda(void *data, int state) |
42 | { |
43 | struct cx8802_dev *dev = data; |
44 | struct cx88_core *core = dev->core; |
45 | struct vp3054_i2c_state *vp3054_i2c = dev->vp3054; |
46 | |
47 | if (state) { |
48 | vp3054_i2c->state |= 0x0002; /* SDA high */ |
49 | vp3054_i2c->state &= ~0x0200; /* tristate pin */ |
50 | } else { |
51 | vp3054_i2c->state &= ~0x0002; /* SDA low */ |
52 | vp3054_i2c->state |= 0x0200; /* drive pin */ |
53 | } |
54 | cx_write(MO_GP0_IO, 0x020000 | vp3054_i2c->state); |
55 | cx_read(MO_GP0_IO); |
56 | } |
57 | |
58 | static int vp3054_bit_getscl(void *data) |
59 | { |
60 | struct cx8802_dev *dev = data; |
61 | struct cx88_core *core = dev->core; |
62 | u32 state; |
63 | |
64 | state = cx_read(MO_GP0_IO); |
65 | return (state & 0x01) ? 1 : 0; |
66 | } |
67 | |
68 | static int vp3054_bit_getsda(void *data) |
69 | { |
70 | struct cx8802_dev *dev = data; |
71 | struct cx88_core *core = dev->core; |
72 | u32 state; |
73 | |
74 | state = cx_read(MO_GP0_IO); |
75 | return (state & 0x02) ? 1 : 0; |
76 | } |
77 | |
78 | /* ----------------------------------------------------------------------- */ |
79 | |
80 | static const struct i2c_algo_bit_data vp3054_i2c_algo_template = { |
81 | .setsda = vp3054_bit_setsda, |
82 | .setscl = vp3054_bit_setscl, |
83 | .getsda = vp3054_bit_getsda, |
84 | .getscl = vp3054_bit_getscl, |
85 | .udelay = 16, |
86 | .timeout = 200, |
87 | }; |
88 | |
89 | /* ----------------------------------------------------------------------- */ |
90 | |
91 | int vp3054_i2c_probe(struct cx8802_dev *dev) |
92 | { |
93 | struct cx88_core *core = dev->core; |
94 | struct vp3054_i2c_state *vp3054_i2c; |
95 | int rc; |
96 | |
97 | if (core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO) |
98 | return 0; |
99 | |
100 | vp3054_i2c = kzalloc(size: sizeof(*vp3054_i2c), GFP_KERNEL); |
101 | if (!vp3054_i2c) |
102 | return -ENOMEM; |
103 | dev->vp3054 = vp3054_i2c; |
104 | |
105 | vp3054_i2c->algo = vp3054_i2c_algo_template; |
106 | |
107 | vp3054_i2c->adap.dev.parent = &dev->pci->dev; |
108 | strscpy(vp3054_i2c->adap.name, core->name, |
109 | sizeof(vp3054_i2c->adap.name)); |
110 | vp3054_i2c->adap.owner = THIS_MODULE; |
111 | vp3054_i2c->algo.data = dev; |
112 | i2c_set_adapdata(adap: &vp3054_i2c->adap, data: dev); |
113 | vp3054_i2c->adap.algo_data = &vp3054_i2c->algo; |
114 | |
115 | vp3054_bit_setscl(data: dev, state: 1); |
116 | vp3054_bit_setsda(data: dev, state: 1); |
117 | |
118 | rc = i2c_bit_add_bus(&vp3054_i2c->adap); |
119 | if (rc != 0) { |
120 | pr_err("vp3054_i2c register FAILED\n" ); |
121 | |
122 | kfree(objp: dev->vp3054); |
123 | dev->vp3054 = NULL; |
124 | } |
125 | |
126 | return rc; |
127 | } |
128 | EXPORT_SYMBOL(vp3054_i2c_probe); |
129 | |
130 | void vp3054_i2c_remove(struct cx8802_dev *dev) |
131 | { |
132 | struct vp3054_i2c_state *vp3054_i2c = dev->vp3054; |
133 | |
134 | if (!vp3054_i2c || |
135 | dev->core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO) |
136 | return; |
137 | |
138 | i2c_del_adapter(adap: &vp3054_i2c->adap); |
139 | kfree(objp: vp3054_i2c); |
140 | } |
141 | EXPORT_SYMBOL(vp3054_i2c_remove); |
142 | |