1/* -*- linux-c -*- */
2
3/* plx9050.c
4 * Copyright (C) 2000 by Francois Wautier
5 * based on code from Bjorn Davis
6 *
7 * Read and write command for the eprom attached to
8 * the PLX9050
9 */
10
11/* Modifications and extensions
12 * Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version
17 * 2 of the License, or (at your option) any later version.
18 **/
19
20/* We handle PCI devices */
21#include <linux/pci.h>
22
23/* We need to use ioremap */
24#include <asm/io.h>
25
26#include <linux/delay.h>
27
28				/* Joachim Martillo modified this file */
29				/* so that it had no dependencies on specific */
30				/* Aurora adapter card or ESSC* structures*/
31				/* The original file use TRUE for 1 and */
32				/* FALSE for 0.  This convention conflicted */
33				/* with other conventions throughout LINUX */
34				/* also TRUE was used for setting an eprom */
35				/* bit which is a slight semantic confusion. */
36				/* I just used 0 and 1 */
37#include "Reg9050.h"
38
39/*
40 * Write a single bit to the serial EPROM interface.
41 */
42
43				/* eprom_ctl is the */
44				/* address of the 9050 */
45				/* eprom control register */
46				/* The original & operation */
47				/* looks wrong.  I am surprised */
48				/* the code worked */
49				/* but I left the parentheses */
50				/* because readl, writel etc */
51				/* are macros*/
52
53				/* The following function */
54				/* assumes the proper bit */
55				/* in the serial eprom */
56				/* has already been selected*/
57
58				/* The 9050 registers are 32 bits */
59				/* hence the readl and writel */
60				/* macros are invoked*/
61
62				/* eprom_ctl must be a virtual */
63				/* address*/
64
65static void plx9050_eprom_wbit(unsigned int* eprom_ctl, unsigned int val)
66{
67	unsigned int	 ctrl;
68
69	/* get the initial value of the CTRL register */
70	ctrl = readl((eprom_ctl));
71
72	/* set or clear the data bit */
73	if (val)
74	{
75		ctrl |= PLX_CTRL_SEPWD;
76	}
77	else
78	{
79		ctrl &= ~PLX_CTRL_SEPWD;
80	}
81
82	writel(ctrl, (eprom_ctl));
83
84	udelay(1);
85
86	/* Toggle the clock line */
87	/* gets to the next bit */
88	/* in the serial eprom */
89	ctrl |= PLX_CTRL_SEPCLK;
90	writel(ctrl, (eprom_ctl));
91
92	udelay(1);
93
94	/* Toggle the clock line */
95	ctrl &= ~PLX_CTRL_SEPCLK;
96	writel(ctrl, (eprom_ctl));
97	udelay(1);
98}
99
100/*
101 * Run a serial EPROM command.  Returns 1 on success,
102 *  0 otherwise.
103 */
104
105/* This routine does the write of data but only sets up */
106/* for a read*/
107/* the write goes from most significant to least significant */
108unsigned int plx9050_eprom_cmd(unsigned int* eprom_ctl, unsigned char cmd, unsigned char addr, unsigned short data)
109{
110	unsigned int ctrl;
111	unsigned char shiftb;
112	unsigned short shiftw;
113	unsigned int l, v;
114	unsigned char ret;
115	int i;
116
117	ret = 1;
118	shiftb = addr << (NM93_BITS_PER_BYTE - NM93_ADDRBITS); /* looks a bizarre way to mask out unused bits */
119
120	ctrl = readl((eprom_ctl));
121
122	ctrl &= ~(PLX_CTRL_SEPCLK | PLX_CTRL_SEPWD);
123	writel(ctrl, (eprom_ctl));
124	udelay(1);
125
126	ctrl |= PLX_CTRL_SEPCS;
127	writel(ctrl, (eprom_ctl));
128
129	plx9050_eprom_wbit(eprom_ctl, 1);
130
131	/*
132	 * Clock out the command
133	 */
134
135	plx9050_eprom_wbit(eprom_ctl, (cmd & 0x02) != 0);
136	plx9050_eprom_wbit(eprom_ctl, (cmd & 0x01) != 0);
137
138	/*
139	 * Clock out the address
140	 */
141
142	i = NM93_ADDRBITS;
143	while (i != 0)		/* here we get to the correct */
144				/* short in the serial eprom*/
145	{
146		/* printf("Loop #1\n"); */
147		plx9050_eprom_wbit(eprom_ctl, (shiftb & 0x80) != 0);
148
149		shiftb <<= 1;
150		i--;
151	}
152
153	if (cmd == NM93_WRITECMD)	/* now do the write if */
154		/* a write is to be done*/
155	{
156		/* write data? */
157		/*
158		 * Clock out the data
159		 */
160
161		shiftw = data;
162
163		i = NM93_BITS_PER_WORD;
164		while (i != 0) {
165			/* printf("Loop #2\n"); */
166			plx9050_eprom_wbit(eprom_ctl, (shiftw & 0x8000) != 0);
167
168			shiftw <<= 1;
169			i--;
170		}
171
172		/*
173		 * De-assert chip select for a short period of time
174		 */
175		ctrl = readl((eprom_ctl));
176
177		ctrl &= ~PLX_CTRL_SEPCS;
178		writel(ctrl, (eprom_ctl));
179		udelay(2);
180
181		/*
182		 * Re-assert chip select
183		 */
184		ctrl |= PLX_CTRL_SEPCS;
185		writel(ctrl, (eprom_ctl));
186
187		/*
188		 * Wait for a low to high transition of DO
189		 */
190
191		i = 20000;
192		ctrl = readl((eprom_ctl));
193		l = (ctrl & PLX_CTRL_SEPRD);
194
195		while (i != 0)
196		{
197			/* printf("Loop #3\n"); */
198			ctrl = readl((eprom_ctl));
199			v = (ctrl & PLX_CTRL_SEPRD);
200			if (v != 0 && l == 0)
201			{
202				break;
203			}
204			l = v;
205			udelay(1);
206			i--;
207		}
208
209		if (i == 0)
210		{
211			printk("plx9050: eprom didn't go low to high");
212			ret = 0;
213		}
214	}
215
216	if (cmd != NM93_READCMD)	/* not a read -- terminate */
217	{
218		/*
219		 * De-assert the chip select.
220		 */
221
222		ctrl = readl((eprom_ctl));
223		ctrl &= ~PLX_CTRL_SEPCS;
224		writel(ctrl,(eprom_ctl));
225	}
226	/* otherwise left in read state */
227	return ret;
228}
229
230/*
231 * Read the serial EPROM.  Returns 1 on success, 0 on failure.
232 * reads in shorts (i.e., 16 bits at a time.)
233 *
234 */
235
236unsigned int
237plx9050_eprom_read(unsigned int* eprom_ctl, unsigned short *ptr, unsigned char addr, unsigned short len)
238{
239	unsigned short shiftw;
240	int i;
241	unsigned int ctrl;
242
243	if (!plx9050_eprom_cmd(eprom_ctl, NM93_READCMD, addr, (unsigned short) 0x0)) /* set up read */
244	{
245		return 0;
246	}
247
248	ctrl = readl((eprom_ctl));	/* synchronize */
249
250	while (len-- > 0)		/* now read one word at a time */
251	{
252		shiftw = 0;
253
254		ctrl &= ~PLX_CTRL_SEPCLK;
255		writel(ctrl, (eprom_ctl));
256
257		udelay(1);
258
259		i = NM93_BITS_PER_WORD;
260		while (1)			/* now read one bit at a time, */
261			/* left shifting each bit */
262		{
263			ctrl |= PLX_CTRL_SEPCLK;
264			writel(ctrl, (eprom_ctl));
265
266			udelay(1);
267
268			ctrl = readl((eprom_ctl));
269
270
271			if ((ctrl & PLX_CTRL_SEPRD) != 0)
272			{
273				shiftw |= 0x1;
274			}
275
276			i--;
277			if (i == 0)
278			{
279				break;
280			}
281			shiftw <<= 1;
282
283			ctrl &= ~PLX_CTRL_SEPCLK;
284			writel(ctrl, (eprom_ctl));
285			udelay(1);
286		}
287
288		*ptr++ = shiftw;
289	}
290
291	ctrl &= ~PLX_CTRL_SEPCS;
292	writel(ctrl, (eprom_ctl));
293
294	udelay(1);
295	ctrl &= ~PLX_CTRL_SEPCLK;
296	writel(ctrl, (eprom_ctl));
297
298	return 1;
299}
300