aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/host/r8a66597.h
blob: f28782d20eef0eab2a0f7c124146e3c154ba8e2f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*
 * R8A66597 HCD (Host Controller Driver)
 *
 * Copyright (C) 2006-2007 Renesas Solutions Corp.
 * Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
 * Portions Copyright (C) 2004-2005 David Brownell
 * Portions Copyright (C) 1999 Roman Weissgaerber
 *
 * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifndef __R8A66597_H__
#define __R8A66597_H__

#ifdef CONFIG_HAVE_CLK
#include <linux/clk.h>
#endif

#include <linux/usb/r8a66597.h>

#define R8A66597_MAX_NUM_PIPE		10
#define R8A66597_BUF_BSIZE		8
#define R8A66597_MAX_DEVICE		10
#define R8A66597_MAX_ROOT_HUB		2
#define R8A66597_MAX_SAMPLING		5
#define R8A66597_RH_POLL_TIME		10
#define R8A66597_MAX_DMA_CHANNEL	2
#define R8A66597_PIPE_NO_DMA		R8A66597_MAX_DMA_CHANNEL
#define check_bulk_or_isoc(pipenum)	((pipenum >= 1 && pipenum <= 5))
#define check_interrupt(pipenum)	((pipenum >= 6 && pipenum <= 9))
#define make_devsel(addr)		(addr << 12)

struct r8a66597_pipe_info {
	unsigned long timer_interval;
	u16 pipenum;
	u16 address;	/* R8A66597 HCD usb address */
	u16 epnum;
	u16 maxpacket;
	u16 type;
	u16 bufnum;
	u16 buf_bsize;
	u16 interval;
	u16 dir_in;
};

struct r8a66597_pipe {
	struct r8a66597_pipe_info info;

	unsigned long fifoaddr;
	unsigned long fifosel;
	unsigned long fifoctr;
	unsigned long pipectr;
	unsigned long pipetre;
	unsigned long pipetrn;
};

struct r8a66597_td {
	struct r8a66597_pipe *pipe;
	struct urb *urb;
	struct list_head queue;

	u16 type;
	u16 pipenum;
	int iso_cnt;

	u16 address;		/* R8A66597's USB address */
	u16 maxpacket;

	unsigned zero_packet:1;
	unsigned short_packet:1;
	unsigned set_address:1;
};

struct r8a66597_device {
	u16	address;	/* R8A66597's USB address */
	u16	hub_port;
	u16	root_port;

	unsigned short ep_in_toggle;
	unsigned short ep_out_toggle;
	unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
	unsigned char dma_map;

	enum usb_device_state state;

	struct usb_device *udev;
	int usb_address;
	struct list_head device_list;
};

struct r8a66597_root_hub {
	u32 port;
	u16 old_syssts;
	int scount;

	struct r8a66597_device	*dev;
};

struct r8a66597 {
	spinlock_t lock;
	void __iomem *reg;
#ifdef CONFIG_HAVE_CLK
	struct clk *clk;
#endif
	struct r8a66597_platdata	*pdata;
	struct r8a66597_device		device0;
	struct r8a66597_root_hub	root_hub[R8A66597_MAX_ROOT_HUB];
	struct list_head		pipe_queue[R8A66597_MAX_NUM_PIPE];

	struct timer_list rh_timer;
	struct timer_list td_timer[R8A66597_MAX_NUM_PIPE];
	struct timer_list interval_timer[R8A66597_MAX_NUM_PIPE];

	unsigned short address_map;
	unsigned short timeout_map;
	unsigned short interval_map;
	unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
	unsigned char dma_map;
	unsigned int max_root_hub;

	struct list_head child_device;
	unsigned long child_connect_map[4];

	unsigned bus_suspended:1;
	unsigned irq_sense_low:1;
};

static inline struct r8a66597 *hcd_to_r8a66597(struct usb_hcd *hcd)
{
	return (struct r8a66597 *)(hcd->hcd_priv);
}

static inline struct usb_hcd *r8a66597_to_hcd(struct r8a66597 *r8a66597)
{
	return container_of((void *)r8a66597, struct usb_hcd, hcd_priv);
}

static inline struct r8a66597_td *r8a66597_get_td(struct r8a66597 *r8a66597,
						  u16 pipenum)
{
	if (unlikely(list_empty(&r8a66597->pipe_queue[pipenum])))
		return NULL;

	return list_entry(r8a66597->pipe_queue[pipenum].next,
			  struct r8a66597_td, queue);
}

static inline struct urb *r8a66597_get_urb(struct r8a66597 *r8a66597,
					   u16 pipenum)
{
	struct r8a66597_td *td;

	td = r8a66597_get_td(r8a66597, pipenum);
	return (td ? td->urb : NULL);
}

static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
{
	return ioread16(r8a66597->reg + offset);
}

static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
				      unsigned long offset, u16 *buf,
				      int len)
{
	void __iomem *fifoaddr = r8a66597->reg + offset;
	unsigned long count;

	if (r8a66597->pdata->on_chip) {
		count = len / 4;
		ioread32_rep(fifoaddr, buf, count);

		if (len & 0x00000003) {
			unsigned long tmp = ioread32(fifoaddr);
			memcpy((unsigned char *)buf + count * 4, &tmp,
			       len & 0x03);
		}
	} else {
		len = (len + 1) / 2;
		ioread16_rep(fifoaddr, buf, len);
	}
}

static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
				  unsigned long offset)
{
	iowrite16(val, r8a66597->reg + offset);
}

static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
				 u16 val, u16 pat, unsigned long offset)
{
	u16 tmp;
	tmp = r8a66597_read(r8a66597, offset);
	tmp = tmp & (~pat);
	tmp = tmp | val;
	r8a66597_write(r8a66597, tmp, offset);
}

#define r8a66597_bclr(r8a66597, val, offset)	\
			r8a66597_mdfy(r8a66597, 0, val, offset)
#define r8a66597_bset(r8a66597, val, offset)	\
			r8a66597_mdfy(r8a66597, val, 0, offset)

static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
				       struct r8a66597_pipe *pipe, u16 *buf,
				       int len)
{
	void __iomem *fifoaddr = r8a66597->reg + pipe->fifoaddr;
	unsigned long count;
	unsigned char *pb;
	int i;

	if (r8a66597->pdata->on_chip) {
		count = len / 4;
		iowrite32_rep(fifoaddr, buf, count);

		if (len & 0x00000003) {
			pb = (unsigned char *)buf + count * 4;
			for (i = 0; i < (len & 0x00000003); i++) {
				if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)
					iowrite8(pb[i], fifoaddr + i);
				else
					iowrite8(pb[i], fifoaddr + 3 - i);
			}
		}
	} else {
		int odd = len & 0x0001;

		len = len / 2;
		iowrite16_rep(fifoaddr, buf, len);
		if (unlikely(odd)) {
			buf = &buf[len];
			if (r8a66597->pdata->wr0_shorted_to_wr1)
				r8a66597_bclr(r8a66597, MBW_16, pipe->fifosel);
			iowrite8((unsigned char)*buf, fifoaddr);
			if (r8a66597->pdata->wr0_shorted_to_wr1)
				r8a66597_bset(r8a66597, MBW_16, pipe->fifosel);
		}
	}
}

static inline unsigned long get_syscfg_reg(int port)
{
	return port == 0 ? SYSCFG0 : SYSCFG1;
}

static inline unsigned long get_syssts_reg(int port)
{
	return port == 0 ? SYSSTS0 : SYSSTS1;
}

static inline unsigned long get_dvstctr_reg(int port)
{
	return port == 0 ? DVSTCTR0 : DVSTCTR1;
}

static inline unsigned long get_dmacfg_reg(int port)
{
	return port == 0 ? DMA0CFG : DMA1CFG;
}

static inline unsigned long get_intenb_reg(int port)
{
	return port == 0 ? INTENB1 : INTENB2;
}

static inline unsigned long get_intsts_reg(int port)
{
	return port == 0 ? INTSTS1 : INTSTS2;
}

static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port)
{
	unsigned long dvstctr_reg = get_dvstctr_reg(port);

	return r8a66597_read(r8a66597, dvstctr_reg) & RHST;
}

static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
				       int power)
{
	unsigned long dvstctr_reg = get_dvstctr_reg(port);

	if (r8a66597->pdata->port_power) {
		r8a66597->pdata->port_power(port, power);
	} else {
		if (power)
			r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
		else
			r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
	}
}

static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata)
{
	u16 clock = 0;

	switch (pdata->xtal) {
	case R8A66597_PLATDATA_XTAL_12MHZ:
		clock = XTAL12;
		break;
	case R8A66597_PLATDATA_XTAL_24MHZ:
		clock = XTAL24;
		break;
	case R8A66597_PLATDATA_XTAL_48MHZ:
		clock = XTAL48;
		break;
	default:
		printk(KERN_ERR "r8a66597: platdata clock is wrong.\n");
		break;
	}

	return clock;
}

#define get_pipectr_addr(pipenum)	(PIPE1CTR + (pipenum - 1) * 2)
#define get_pipetre_addr(pipenum)	(PIPE1TRE + (pipenum - 1) * 4)
#define get_pipetrn_addr(pipenum)	(PIPE1TRN + (pipenum - 1) * 4)
#define get_devadd_addr(address)	(DEVADD0 + address * 2)

#define enable_irq_ready(r8a66597, pipenum)	\
	enable_pipe_irq(r8a66597, pipenum, BRDYENB)
#define disable_irq_ready(r8a66597, pipenum)	\
	disable_pipe_irq(r8a66597, pipenum, BRDYENB)
#define enable_irq_empty(r8a66597, pipenum)	\
	enable_pipe_irq(r8a66597, pipenum, BEMPENB)
#define disable_irq_empty(r8a66597, pipenum)	\
	disable_pipe_irq(r8a66597, pipenum, BEMPENB)
#define enable_irq_nrdy(r8a66597, pipenum)	\
	enable_pipe_irq(r8a66597, pipenum, NRDYENB)
#define disable_irq_nrdy(r8a66597, pipenum)	\
	disable_pipe_irq(r8a66597, pipenum, NRDYENB)

#endif	/* __R8A66597_H__ */