1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  BCM1250 SMBus controller driver		File: sb1250_smbus.c
5    *
6    *  Basic operations for BCM1250 SMBus controller.
7    *
8    *  Author:  Mitch Lichtenberg
9    *
10    *********************************************************************
11    *
12    *  Copyright 2000,2001,2002,2003
13    *  Broadcom Corporation. All rights reserved.
14    *
15    *  This software is furnished under license and may be used and
16    *  copied only in accordance with the following terms and
17    *  conditions.  Subject to these conditions, you may download,
18    *  copy, install, use, modify and distribute modified or unmodified
19    *  copies of this software in source and/or binary form.  No title
20    *  or ownership is transferred hereby.
21    *
22    *  1) Any source code used, modified or distributed must reproduce
23    *     and retain this copyright notice and list of conditions
24    *     as they appear in the source file.
25    *
26    *  2) No right is granted to use any trade name, trademark, or
27    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
28    *     name may not be used to endorse or promote products derived
29    *     from this software without the prior written permission of
30    *     Broadcom Corporation.
31    *
32    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
33    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
34    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
36    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
37    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
38    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
42    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
44    *     THE POSSIBILITY OF SUCH DAMAGE.
45    ********************************************************************* */
46
47#include "cfe.h"
48#include "sbmips.h"
49
50#include "cfe_smbus.h"
51
52#include "sb1250_defs.h"
53#include "sb1250_regs.h"
54#include "sb1250_smbus.h"
55
56/*  *********************************************************************
57    *  Types
58    ********************************************************************* */
59
60
61typedef struct sb1250_smbus_softc_s {
62    long base;				/* physical address of channel */
63} sb1250_smbus_softc_t;
64
65/*  *********************************************************************
66    *  Forward declarations
67    ********************************************************************* */
68
69
70static int sb1250_smbus_qcmd(cfe_smbus_channel_t *chan,uint8_t slave,int rw);
71static int sb1250_smbus_xact(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t cmd,uint8_t *buf,int len);
72static int sb1250_smbus_read(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t *buf,int len);
73static int sb1250_smbus_write(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t *buf,int len);
74static int sb1250_smbus_init(cfe_smbus_channel_t *chan);
75static cfe_smbus_channel_t *sb1250_smbus_attach(cfe_smbus_t *ops,uint64_t probe_a,uint64_t probe_b);
76
77/*
78 * SMBus Extended mode read write routines
79 */
80
81static int sb1250_smbus_xread(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t cmd,uint8_t numcmd,uint8_t numdat,int *buf);
82static int sb1250_smbus_xwrite(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t cmd,uint8_t numcmd,uint8_t numdat,int *buf);
83
84/*  *********************************************************************
85    *  SMBus operations
86    ********************************************************************* */
87
88cfe_smbus_t sb1250_smbus = {
89    sb1250_smbus_attach,
90    sb1250_smbus_init,
91    sb1250_smbus_write,
92    sb1250_smbus_read,
93    sb1250_smbus_xact,
94    sb1250_smbus_qcmd,
95    sb1250_smbus_xread,
96    sb1250_smbus_xwrite
97};
98
99
100static int sb1250_smbus_waitready(sb1250_smbus_softc_t *softc)
101{
102    uint64_t status;
103    int cnt  = 10000000;	/* about 1 second at 1Ghz */
104
105    while (cnt > 0) {
106	status = SBREADCSR(softc->base+R_SMB_STATUS);
107	if (status & M_SMB_BUSY) {
108	    cnt--;
109	    continue;
110	    }
111	break;
112	}
113
114    if (cnt == 0) return CFE_ERR_TIMEOUT;
115
116    if (status & M_SMB_ERROR) {
117	SBWRITECSR(softc->base+R_SMB_STATUS,(status & M_SMB_ERROR));
118	return CFE_ERR_NOTREADY;
119	}
120    return 0;
121}
122
123static cfe_smbus_channel_t *sb1250_smbus_attach(cfe_smbus_t *ops,uint64_t probe_a,uint64_t probe_b)
124{
125    cfe_smbus_channel_t *chan;
126    sb1250_smbus_softc_t *softc;
127
128    chan = KMALLOC(sizeof(cfe_smbus_channel_t)+sizeof(sb1250_smbus_softc_t),0);
129
130    if (!chan) return NULL;
131
132    chan->ops = ops;
133    chan->softc = (void *) (chan+1);
134
135    softc = chan->softc;
136    softc->base = probe_a;
137
138    return chan;
139}
140
141static int sb1250_smbus_init(cfe_smbus_channel_t *chan)
142{
143    sb1250_smbus_softc_t *softc = chan->softc;
144
145    /*
146     * Assume 100KHz for all devices.  We don't need to go fast
147     * ever.
148     *
149     * Also turn off direct mode and disable interrupts.
150     */
151
152    SBWRITECSR(softc->base+R_SMB_FREQ,K_SMB_FREQ_100KHZ);
153    SBWRITECSR(softc->base+R_SMB_CONTROL,0);
154
155    /*
156     * XXX Could switch to bit-bang mode here and send a bunch
157     * of null clocks to reset devices.
158     */
159
160    return 0;
161}
162
163
164
165static int sb1250_smbus_write(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t *buf,int len)
166{
167    int err;
168    sb1250_smbus_softc_t *softc = chan->softc;
169
170    /*
171     * Make sure the bus is idle (ignore error here)
172     */
173
174    sb1250_smbus_waitready(softc);
175
176    /*
177     * Depending on how many bytes we're writing, fill in the various
178     * SMB registers and execute the command.
179     */
180
181    switch (len) {
182	case 1:		/* "command" byte alone */
183	    SBWRITECSR(softc->base+R_SMB_CMD,buf[0]);
184	    SBWRITECSR(softc->base+R_SMB_START,V_SMB_TT(K_SMB_TT_WR1BYTE) | ((uint64_t)slave));
185	    break;
186	case 2:		/* "command" byte plus a data byte */
187	    SBWRITECSR(softc->base+R_SMB_CMD,buf[0]);
188	    SBWRITECSR(softc->base+R_SMB_DATA,buf[1]);
189	    SBWRITECSR(softc->base+R_SMB_START,V_SMB_TT(K_SMB_TT_WR2BYTE) | ((uint64_t)slave));
190	    break;
191	case 3:		/* "command" byte plus 2 data bytes */
192	    SBWRITECSR(softc->base+R_SMB_CMD,buf[0]);
193	    SBWRITECSR(softc->base+R_SMB_DATA,((uint64_t)(buf[1])) | (((uint64_t)buf[2]) << 8));
194	    SBWRITECSR(softc->base+R_SMB_START,V_SMB_TT(K_SMB_TT_WR3BYTE) | ((uint64_t)slave));
195	    break;
196	default:
197	    return CFE_ERR_INV_PARAM;
198	    break;
199	}
200
201    /*
202     * Wait for command to complete.
203     */
204
205    err = sb1250_smbus_waitready(softc);
206    if (err < 0) return err;
207
208    return 0;
209}
210
211static int sb1250_smbus_read(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t *buf,int len)
212{
213    int err;
214    sb1250_smbus_softc_t *softc = chan->softc;
215
216    while (len > 0) {
217	err = sb1250_smbus_waitready(softc);
218	if (err < 0) return err;
219
220	SBWRITECSR(softc->base+R_SMB_START,V_SMB_TT(K_SMB_TT_RD1BYTE) | ((uint64_t)slave));
221
222	err = sb1250_smbus_waitready(softc);
223	if (err < 0) return err;
224
225	*buf++ = (uint8_t) SBREADCSR(softc->base+R_SMB_DATA);
226	len--;
227	}
228
229    return 0;
230}
231
232static int sb1250_smbus_xwrite(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t cmd,uint8_t numcmd,uint8_t numdat,int *buf)
233{
234    sb1250_smbus_softc_t *softc = chan->softc;
235    int t;
236    int err ;
237
238    /*
239     * Make sure the bus is idle (ignore error here)
240     */
241
242    sb1250_smbus_waitready(softc);
243
244    /*
245     * Do extended mode write access
246     */
247
248    SBWRITECSR(softc->base+R_SMB_CMD,cmd);
249    t = buf[0] + (buf[1]<<8) ;
250    SBWRITECSR(softc->base+R_SMB_DATA,t);
251    // if numdat>2 SBWRITECSR(softc->base+R_SMB_?,ext);
252
253    SBWRITECSR(softc->base+ R_SMB_START, M_SMB_EXTEND |  V_SMB_AFMT(numcmd) | V_SMB_DFMT(numdat) | ((uint64_t)slave));
254
255    err = sb1250_smbus_waitready(softc);
256    if (err < 0) return err;
257
258   return 0;
259}
260
261static int sb1250_smbus_xread(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t cmd,uint8_t numcmd,uint8_t numdat,int *buf)
262{
263    sb1250_smbus_softc_t *softc = chan->softc;
264
265    /*
266     * Make sure the bus is idle (ignore error here)
267     */
268
269    sb1250_smbus_waitready(softc);
270
271    /*
272     * Do extended mode read access
273     */
274
275    SBWRITECSR(softc->base+R_SMB_CMD,cmd);
276    SBWRITECSR(softc->base+ R_SMB_START, M_SMB_EXTEND | M_SMB_DIR | V_SMB_AFMT(numcmd) | V_SMB_DFMT(numdat) | ((uint64_t)slave));
277
278    sb1250_smbus_waitready(softc);
279
280    *buf = (int) SBREADCSR(softc->base+R_SMB_DATA);
281
282    return 0;
283}
284
285static int sb1250_smbus_xact(cfe_smbus_channel_t *chan,uint8_t slave,uint8_t cmd,uint8_t *buf,int len)
286{
287    sb1250_smbus_softc_t *softc = chan->softc;
288    uint64_t data;
289    int err;
290
291    /*
292     * Make sure the bus is idle (ignore error here)
293     */
294
295    sb1250_smbus_waitready(softc);
296
297    /*
298     * The "command" byte goes out...
299     */
300
301    SBWRITECSR(softc->base+R_SMB_CMD,cmd);
302
303    /*
304     * And a variable number of bytes come back.
305     */
306
307    switch (len) {
308	case 1:
309	    SBWRITECSR(softc->base+R_SMB_START,V_SMB_TT(K_SMB_TT_CMD_RD1BYTE) | ((uint64_t)slave));
310	    break;
311	case 2:
312	    SBWRITECSR(softc->base+R_SMB_START,V_SMB_TT(K_SMB_TT_CMD_RD2BYTE) | ((uint64_t)slave));
313	    break;
314	default:
315	    return CFE_ERR_INV_PARAM;
316	}
317
318    err = sb1250_smbus_waitready(softc);
319    if (err < 0) return err;
320
321    /*
322     * Get data from device and unpack it for application.
323     */
324
325    data = SBREADCSR(softc->base+R_SMB_DATA);
326
327    switch (len) {
328	case 1:
329	    *buf++ = (data & 0xFF);
330	    break;
331	case 2:
332	    *buf++ = (data & 0xFF);
333	    data >>= 8;
334	    *buf++ = (data & 0xFF);
335	    break;
336	}
337
338    return 0;
339
340}
341
342static int sb1250_smbus_qcmd(cfe_smbus_channel_t *chan,uint8_t slave,int rw)
343{
344    int err;
345    sb1250_smbus_softc_t *softc = chan->softc;
346
347    /*
348     * Make sure the bus is idle (ignore error here)
349     */
350
351    sb1250_smbus_waitready(softc);
352
353    SBWRITECSR(softc->base+R_SMB_START,V_SMB_TT(K_SMB_TT_QUICKCMD) | ((uint64_t)slave) |
354	       (rw ? M_SMB_QDATA : 0));
355
356    err = sb1250_smbus_waitready(softc);
357
358    return err;
359}
360