fifo.c 7.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Renesas USB driver
 *
 * Copyright (C) 2011 Renesas Solutions Corp.
 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 *
 * 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
 *
 */
#include <linux/delay.h>
#include <linux/io.h>
#include "./common.h"
#include "./pipe.h"

22 23 24
/*
 *		packet info function
 */
25 26 27 28 29
void usbhs_pkt_init(struct usbhs_pkt *pkt)
{
	INIT_LIST_HEAD(&pkt->node);
}

30 31
void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
		    void *buf, int len, int zero)
32 33 34 35
{
	list_del_init(&pkt->node);
	list_add_tail(&pkt->node, &pipe->list);

36 37 38 39 40
	pkt->pipe	= pipe;
	pkt->buf	= buf;
	pkt->length	= len;
	pkt->zero	= zero;
	pkt->actual	= 0;
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
}

void usbhs_pkt_pop(struct usbhs_pkt *pkt)
{
	list_del_init(&pkt->node);
}

struct usbhs_pkt *usbhs_pkt_get(struct usbhs_pipe *pipe)
{
	if (list_empty(&pipe->list))
		return NULL;

	return list_entry(pipe->list.next, struct usbhs_pkt, node);
}

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
/*
 *		irq enable/disable function
 */
#define usbhsf_irq_empty_ctrl(p, e) usbhsf_irq_callback_ctrl(p, bempsts, e)
#define usbhsf_irq_ready_ctrl(p, e) usbhsf_irq_callback_ctrl(p, brdysts, e)
#define usbhsf_irq_callback_ctrl(pipe, status, enable)			\
	({								\
		struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);	\
		struct usbhs_mod *mod = usbhs_mod_get_current(priv);	\
		u16 status = (1 << usbhs_pipe_number(pipe));		\
		if (!mod)						\
			return;						\
		if (enable)						\
			mod->irq_##status |= status;			\
		else							\
			mod->irq_##status &= ~status;			\
		usbhs_irq_callback_update(priv, mod);			\
	})

static void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
{
	/*
	 * And DCP pipe can NOT use "ready interrupt" for "send"
	 * it should use "empty" interrupt.
	 * see
	 *   "Operation" - "Interrupt Function" - "BRDY Interrupt"
	 *
	 * on the other hand, normal pipe can use "ready interrupt" for "send"
	 * even though it is single/double buffer
	 */
	if (usbhs_pipe_is_dcp(pipe))
		usbhsf_irq_empty_ctrl(pipe, enable);
	else
		usbhsf_irq_ready_ctrl(pipe, enable);
}

static void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
{
	usbhsf_irq_ready_ctrl(pipe, enable);
}

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
/*
 *		FIFO ctrl
 */
static void usbhsf_send_terminator(struct usbhs_pipe *pipe)
{
	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);

	usbhs_bset(priv, CFIFOCTR, BVAL, BVAL);
}

static int usbhsf_fifo_barrier(struct usbhs_priv *priv)
{
	int timeout = 1024;

	do {
		/* The FIFO port is accessible */
		if (usbhs_read(priv, CFIFOCTR) & FRDY)
			return 0;

		udelay(10);
	} while (timeout--);

	return -EBUSY;
}

static void usbhsf_fifo_clear(struct usbhs_pipe *pipe)
{
	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);

	if (!usbhs_pipe_is_dcp(pipe))
		usbhsf_fifo_barrier(priv);

	usbhs_write(priv, CFIFOCTR, BCLR);
}

static int usbhsf_fifo_rcv_len(struct usbhs_priv *priv)
{
	return usbhs_read(priv, CFIFOCTR) & DTLN_MASK;
}

static int usbhsf_fifo_select(struct usbhs_pipe *pipe, int write)
{
	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
	struct device *dev = usbhs_priv_to_dev(priv);
	int timeout = 1024;
	u16 mask = ((1 << 5) | 0xF);		/* mask of ISEL | CURPIPE */
	u16 base = usbhs_pipe_number(pipe);	/* CURPIPE */

	if (usbhs_pipe_is_dcp(pipe))
		base |= (1 == write) << 5;	/* ISEL */

	/* "base" will be used below  */
	usbhs_write(priv, CFIFOSEL, base | MBW_32);

	/* check ISEL and CURPIPE value */
	while (timeout--) {
		if (base == (mask & usbhs_read(priv, CFIFOSEL)))
			return 0;
		udelay(10);
	}

	dev_err(dev, "fifo select error\n");

	return -EIO;
}

/*
 *		PIO fifo functions
 */
int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe)
{
	return usbhsf_fifo_select(pipe, 1);
}

171
int usbhs_fifo_write(struct usbhs_pkt *pkt)
172
{
173
	struct usbhs_pipe *pipe = pkt->pipe;
174
	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
175
	struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
176
	struct device *dev = usbhs_priv_to_dev(priv);
177
	void __iomem *addr = priv->base + CFIFO;
178
	u8 *buf;
179 180
	int maxp = usbhs_pipe_get_maxpacket(pipe);
	int total_len;
181
	int i, ret, len;
182
	int is_short, is_done;
183 184 185

	ret = usbhs_pipe_is_accessible(pipe);
	if (ret < 0)
186
		goto usbhs_fifo_write_busy;
187 188 189

	ret = usbhsf_fifo_select(pipe, 1);
	if (ret < 0)
190
		goto usbhs_fifo_write_busy;
191 192 193

	ret = usbhsf_fifo_barrier(priv);
	if (ret < 0)
194
		goto usbhs_fifo_write_busy;
195

196 197 198 199 200
	buf		= pkt->buf    + pkt->actual;
	len		= pkt->length - pkt->actual;
	len		= min(len, maxp);
	total_len	= len;
	is_short	= total_len < maxp;
201 202 203 204 205 206

	/*
	 * FIXME
	 *
	 * 32-bit access only
	 */
207
	if (len >= 4 && !((unsigned long)buf & 0x03)) {
208 209 210 211 212 213 214 215 216
		iowrite32_rep(addr, buf, len / 4);
		len %= 4;
		buf += total_len - len;
	}

	/* the rest operation */
	for (i = 0; i < len; i++)
		iowrite8(buf[i], addr + (0x03 - (i & 0x03)));

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
	/*
	 * variable update
	 */
	pkt->actual += total_len;

	if (pkt->actual < pkt->length)
		is_done = 0;		/* there are remainder data */
	else if (is_short)
		is_done = 1;		/* short packet */
	else
		is_done = !pkt->zero;	/* send zero packet ? */

	/*
	 * pipe/irq handling
	 */
	if (is_short)
233 234
		usbhsf_send_terminator(pipe);

235
	usbhsf_tx_irq_ctrl(pipe, !is_done);
236 237
	usbhs_pipe_enable(pipe);

238 239 240 241 242 243 244 245 246 247 248 249 250
	dev_dbg(dev, "  send %d (%d/ %d/ %d/ %d)\n",
		usbhs_pipe_number(pipe),
		pkt->length, pkt->actual, is_done, pkt->zero);

	/*
	 * Transmission end
	 */
	if (is_done) {
		if (usbhs_pipe_is_dcp(pipe))
			usbhs_dcp_control_transfer_done(pipe);

		if (info->tx_done)
			info->tx_done(pkt);
251 252 253
	}

	return 0;
254 255 256 257 258 259 260 261 262

usbhs_fifo_write_busy:
	/*
	 * pipe is busy.
	 * retry in interrupt
	 */
	usbhsf_tx_irq_ctrl(pipe, 1);

	return ret;
263 264 265 266 267 268 269 270 271 272 273 274 275 276
}

int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe)
{
	int ret;

	/*
	 * select pipe and enable it to prepare packet receive
	 */
	ret = usbhsf_fifo_select(pipe, 0);
	if (ret < 0)
		return ret;

	usbhs_pipe_enable(pipe);
277
	usbhsf_rx_irq_ctrl(pipe, 1);
278 279 280 281

	return ret;
}

282
int usbhs_fifo_read(struct usbhs_pkt *pkt)
283
{
284
	struct usbhs_pipe *pipe = pkt->pipe;
285
	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
286
	struct device *dev = usbhs_priv_to_dev(priv);
287
	void __iomem *addr = priv->base + CFIFO;
288 289 290
	u8 *buf;
	u32 data = 0;
	int maxp = usbhs_pipe_get_maxpacket(pipe);
291
	int rcv_len, len;
292
	int i, ret;
293
	int total_len = 0;
294
	int is_done = 0;
295 296 297 298 299 300 301 302 303 304 305

	ret = usbhsf_fifo_select(pipe, 0);
	if (ret < 0)
		return ret;

	ret = usbhsf_fifo_barrier(priv);
	if (ret < 0)
		return ret;

	rcv_len = usbhsf_fifo_rcv_len(priv);

306 307 308 309 310
	buf		= pkt->buf    + pkt->actual;
	len		= pkt->length - pkt->actual;
	len		= min(len, rcv_len);
	total_len	= len;

311 312 313 314 315 316 317 318
	/*
	 * Buffer clear if Zero-Length packet
	 *
	 * see
	 * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
	 */
	if (0 == rcv_len) {
		usbhsf_fifo_clear(pipe);
319
		goto usbhs_fifo_read_end;
320 321 322 323 324 325 326
	}

	/*
	 * FIXME
	 *
	 * 32-bit access only
	 */
327
	if (len >= 4 && !((unsigned long)buf & 0x03)) {
328 329
		ioread32_rep(addr, buf, len / 4);
		len %= 4;
330
		buf += total_len - len;
331 332 333 334 335 336 337 338 339 340
	}

	/* the rest operation */
	for (i = 0; i < len; i++) {
		if (!(i & 0x03))
			data = ioread32(addr);

		buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
	}

341 342
	pkt->actual += total_len;

343
usbhs_fifo_read_end:
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
	if ((pkt->actual == pkt->length) ||	/* receive all data */
	    (total_len < maxp))			/* short packet */
		is_done = 1;

	dev_dbg(dev, "  recv %d (%d/ %d/ %d/ %d)\n",
		usbhs_pipe_number(pipe),
		pkt->length, pkt->actual, is_done, pkt->zero);

	if (is_done) {
		struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);

		usbhsf_rx_irq_ctrl(pipe, 0);
		usbhs_pipe_disable(pipe);

		if (info->rx_done)
			info->rx_done(pkt);
360 361 362
	}

	return 0;
363
}