aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2013-02-14 20:59:41 -0500
committerBen Skeggs <bskeggs@redhat.com>2013-02-20 01:00:58 -0500
commit31a34aa421032cfe3b2b892c929e7539e747a7ac (patch)
tree35155f9322a363e42d5bd69b8323143a30a39a18
parenteaa8e7ab99d1b33db9362f35c1d65df8df39dea9 (diff)
drm/nouveau/i2c: aux channels not necessarily on nvio
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/Makefile1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/i2c.h5
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c168
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c165
5 files changed, 193 insertions, 148 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 4208db9b1388..29f8cc5f46d8 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -95,6 +95,7 @@ nouveau-y += core/subdev/gpio/nve0.o
95nouveau-y += core/subdev/i2c/base.o 95nouveau-y += core/subdev/i2c/base.o
96nouveau-y += core/subdev/i2c/aux.o 96nouveau-y += core/subdev/i2c/aux.o
97nouveau-y += core/subdev/i2c/bit.o 97nouveau-y += core/subdev/i2c/bit.o
98nouveau-y += core/subdev/i2c/nv94.o
98nouveau-y += core/subdev/ibus/nvc0.o 99nouveau-y += core/subdev/ibus/nvc0.o
99nouveau-y += core/subdev/ibus/nve0.o 100nouveau-y += core/subdev/ibus/nve0.o
100nouveau-y += core/subdev/instmem/base.o 101nouveau-y += core/subdev/instmem/base.o
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
index b93ab01e3785..d2c067a794aa 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
@@ -21,6 +21,8 @@ struct nouveau_i2c_port {
21 u32 drive; 21 u32 drive;
22 u32 sense; 22 u32 sense;
23 u32 state; 23 u32 state;
24 void (*aux_mux)(struct nouveau_i2c_port *);
25 int (*aux)(struct nouveau_i2c_port *, u8, u32, u8 *, u8);
24}; 26};
25 27
26struct nouveau_i2c { 28struct nouveau_i2c {
@@ -57,4 +59,7 @@ int nv_wraux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
57extern const struct i2c_algorithm nouveau_i2c_bit_algo; 59extern const struct i2c_algorithm nouveau_i2c_bit_algo;
58extern const struct i2c_algorithm nouveau_i2c_aux_algo; 60extern const struct i2c_algorithm nouveau_i2c_aux_algo;
59 61
62void nv94_aux_mux(struct nouveau_i2c_port *);
63int nv94_aux(struct nouveau_i2c_port *, u8, u32, u8 *, u8);
64
60#endif 65#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c
index 1a428743cf8e..7ad0d94db4f7 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c
@@ -24,166 +24,39 @@
24 24
25#include <subdev/i2c.h> 25#include <subdev/i2c.h>
26 26
27/******************************************************************************
28 * aux channel util functions
29 *****************************************************************************/
30#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
31#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
32
33static void
34auxch_fini(struct nouveau_i2c *aux, int ch)
35{
36 nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
37}
38
39static int
40auxch_init(struct nouveau_i2c *aux, int ch)
41{
42 const u32 unksel = 1; /* nfi which to use, or if it matters.. */
43 const u32 ureq = unksel ? 0x00100000 : 0x00200000;
44 const u32 urep = unksel ? 0x01000000 : 0x02000000;
45 u32 ctrl, timeout;
46
47 /* wait up to 1ms for any previous transaction to be done... */
48 timeout = 1000;
49 do {
50 ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
51 udelay(1);
52 if (!timeout--) {
53 AUX_ERR("begin idle timeout 0x%08x\n", ctrl);
54 return -EBUSY;
55 }
56 } while (ctrl & 0x03010000);
57
58 /* set some magic, and wait up to 1ms for it to appear */
59 nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
60 timeout = 1000;
61 do {
62 ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
63 udelay(1);
64 if (!timeout--) {
65 AUX_ERR("magic wait 0x%08x\n", ctrl);
66 auxch_fini(aux, ch);
67 return -EBUSY;
68 }
69 } while ((ctrl & 0x03000000) != urep);
70
71 return 0;
72}
73
74static int
75auxch_tx(struct nouveau_i2c *aux, int ch, u8 type, u32 addr, u8 *data, u8 size)
76{
77 u32 ctrl, stat, timeout, retries;
78 u32 xbuf[4] = {};
79 int ret, i;
80
81 AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
82
83 ret = auxch_init(aux, ch);
84 if (ret)
85 goto out;
86
87 stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50));
88 if (!(stat & 0x10000000)) {
89 AUX_DBG("sink not detected\n");
90 ret = -ENXIO;
91 goto out;
92 }
93
94 if (!(type & 1)) {
95 memcpy(xbuf, data, size);
96 for (i = 0; i < 16; i += 4) {
97 AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
98 nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
99 }
100 }
101
102 ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
103 ctrl &= ~0x0001f0ff;
104 ctrl |= type << 12;
105 ctrl |= size - 1;
106 nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
107
108 /* retry transaction a number of times on failure... */
109 ret = -EREMOTEIO;
110 for (retries = 0; retries < 32; retries++) {
111 /* reset, and delay a while if this is a retry */
112 nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
113 nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
114 if (retries)
115 udelay(400);
116
117 /* transaction request, wait up to 1ms for it to complete */
118 nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
119
120 timeout = 1000;
121 do {
122 ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
123 udelay(1);
124 if (!timeout--) {
125 AUX_ERR("tx req timeout 0x%08x\n", ctrl);
126 goto out;
127 }
128 } while (ctrl & 0x00010000);
129
130 /* read status, and check if transaction completed ok */
131 stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
132 if (!(stat & 0x000f0f00)) {
133 ret = 0;
134 break;
135 }
136
137 AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
138 }
139
140 if (type & 1) {
141 for (i = 0; i < 16; i += 4) {
142 xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i);
143 AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
144 }
145 memcpy(data, xbuf, size);
146 }
147
148out:
149 auxch_fini(aux, ch);
150 return ret;
151}
152
153static void
154auxch_mux(struct nouveau_i2c_port *port)
155{
156 if (port->dcb & 0x00000100) {
157 u32 reg = 0x00e500 + (port->drive * 0x50);
158 /* nfi, but neither auxch or i2c work if it's 1 */
159 nv_mask(port->i2c, reg + 0x0c, 0x00000001, 0x00000000);
160 /* nfi, but switches auxch vs normal i2c */
161 nv_mask(port->i2c, reg + 0x00, 0x0000f003, 0x00002002);
162 }
163}
164
165int 27int
166nv_rdaux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size) 28nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
167{ 29{
168 auxch_mux(auxch); 30 if (port->aux) {
169 return auxch_tx(auxch->i2c, auxch->drive, 9, addr, data, size); 31 if (port->aux_mux)
32 port->aux_mux(port);
33 return port->aux(port, 9, addr, data, size);
34 }
35 return -ENODEV;
170} 36}
171 37
172int 38int
173nv_wraux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size) 39nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
174{ 40{
175 auxch_mux(auxch); 41 if (port->aux) {
176 return auxch_tx(auxch->i2c, auxch->drive, 8, addr, data, size); 42 if (port->aux_mux)
43 port->aux_mux(port);
44 return port->aux(port, 8, addr, data, size);
45 }
46 return -ENODEV;
177} 47}
178 48
179static int 49static int
180aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 50aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
181{ 51{
182 struct nouveau_i2c_port *auxch = (struct nouveau_i2c_port *)adap; 52 struct nouveau_i2c_port *port = (struct nouveau_i2c_port *)adap;
183 struct i2c_msg *msg = msgs; 53 struct i2c_msg *msg = msgs;
184 int ret, mcnt = num; 54 int ret, mcnt = num;
185 55
186 auxch_mux(auxch); 56 if (!port->aux)
57 return -ENODEV;
58 if ( port->aux_mux)
59 port->aux_mux(port);
187 60
188 while (mcnt--) { 61 while (mcnt--) {
189 u8 remaining = msg->len; 62 u8 remaining = msg->len;
@@ -201,8 +74,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
201 if (mcnt || remaining > 16) 74 if (mcnt || remaining > 16)
202 cmd |= 4; /* MOT */ 75 cmd |= 4; /* MOT */
203 76
204 ret = auxch_tx(auxch->i2c, auxch->drive, cmd, 77 ret = port->aux(port, cmd, msg->addr, ptr, cnt);
205 msg->addr, ptr, cnt);
206 if (ret < 0) 78 if (ret < 0)
207 return ret; 79 return ret;
208 80
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
index e09af329120e..bc8ec7ace9d1 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
@@ -303,6 +303,8 @@ nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
303 port->drive = info.drive & 0x0f; 303 port->drive = info.drive & 0x0f;
304 port->sense = port->drive; 304 port->sense = port->drive;
305 port->adapter.algo = &nouveau_i2c_aux_algo; 305 port->adapter.algo = &nouveau_i2c_aux_algo;
306 port->aux_mux = nv94_aux_mux;
307 port->aux = nv94_aux;
306 break; 308 break;
307 default: 309 default:
308 break; 310 break;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c
new file mode 100644
index 000000000000..4c498532d793
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c
@@ -0,0 +1,165 @@
1/*
2 * Copyright 2009 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25#include <subdev/i2c.h>
26
27/******************************************************************************
28 * aux channel util functions
29 *****************************************************************************/
30#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
31#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
32
33static void
34auxch_fini(struct nouveau_i2c *aux, int ch)
35{
36 nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
37}
38
39static int
40auxch_init(struct nouveau_i2c *aux, int ch)
41{
42 const u32 unksel = 1; /* nfi which to use, or if it matters.. */
43 const u32 ureq = unksel ? 0x00100000 : 0x00200000;
44 const u32 urep = unksel ? 0x01000000 : 0x02000000;
45 u32 ctrl, timeout;
46
47 /* wait up to 1ms for any previous transaction to be done... */
48 timeout = 1000;
49 do {
50 ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
51 udelay(1);
52 if (!timeout--) {
53 AUX_ERR("begin idle timeout 0x%08x\n", ctrl);
54 return -EBUSY;
55 }
56 } while (ctrl & 0x03010000);
57
58 /* set some magic, and wait up to 1ms for it to appear */
59 nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
60 timeout = 1000;
61 do {
62 ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
63 udelay(1);
64 if (!timeout--) {
65 AUX_ERR("magic wait 0x%08x\n", ctrl);
66 auxch_fini(aux, ch);
67 return -EBUSY;
68 }
69 } while ((ctrl & 0x03000000) != urep);
70
71 return 0;
72}
73
74int
75nv94_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size)
76{
77 struct nouveau_i2c *aux = port->i2c;
78 u32 ctrl, stat, timeout, retries;
79 u32 xbuf[4] = {};
80 int ch = port->drive;
81 int ret, i;
82
83 AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
84
85 ret = auxch_init(aux, ch);
86 if (ret)
87 goto out;
88
89 stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50));
90 if (!(stat & 0x10000000)) {
91 AUX_DBG("sink not detected\n");
92 ret = -ENXIO;
93 goto out;
94 }
95
96 if (!(type & 1)) {
97 memcpy(xbuf, data, size);
98 for (i = 0; i < 16; i += 4) {
99 AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
100 nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
101 }
102 }
103
104 ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
105 ctrl &= ~0x0001f0ff;
106 ctrl |= type << 12;
107 ctrl |= size - 1;
108 nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
109
110 /* retry transaction a number of times on failure... */
111 ret = -EREMOTEIO;
112 for (retries = 0; retries < 32; retries++) {
113 /* reset, and delay a while if this is a retry */
114 nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
115 nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
116 if (retries)
117 udelay(400);
118
119 /* transaction request, wait up to 1ms for it to complete */
120 nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
121
122 timeout = 1000;
123 do {
124 ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
125 udelay(1);
126 if (!timeout--) {
127 AUX_ERR("tx req timeout 0x%08x\n", ctrl);
128 goto out;
129 }
130 } while (ctrl & 0x00010000);
131
132 /* read status, and check if transaction completed ok */
133 stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
134 if (!(stat & 0x000f0f00)) {
135 ret = 0;
136 break;
137 }
138
139 AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
140 }
141
142 if (type & 1) {
143 for (i = 0; i < 16; i += 4) {
144 xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i);
145 AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
146 }
147 memcpy(data, xbuf, size);
148 }
149
150out:
151 auxch_fini(aux, ch);
152 return ret;
153}
154
155void
156nv94_aux_mux(struct nouveau_i2c_port *port)
157{
158 if (port->dcb & 0x00000100) {
159 u32 reg = 0x00e500 + (port->drive * 0x50);
160 /* nfi, but neither auxch or i2c work if it's 1 */
161 nv_mask(port->i2c, reg + 0x0c, 0x00000001, 0x00000000);
162 /* nfi, but switches auxch vs normal i2c */
163 nv_mask(port->i2c, reg + 0x00, 0x0000f003, 0x00002002);
164 }
165}