m3dev

cortex m3 debug tools -- superceded by mdebug
git clone http://frotz.net/git/m3dev.git
Log | Files | Refs | README | LICENSE

usb.c (8931B)


      1 /* usb.c
      2  *
      3  * Copyright 2011 Brian Swetland <swetland@frotz.net>
      4  * 
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 #include <fw/types.h>
     19 #include <fw/lib.h>
     20 #include <fw/io.h>
     21 
     22 #include <arch/hardware.h>
     23 #include <protocol/usb.h>
     24 
     25 void usb_handle_irq(void);
     26 
     27 void irq_usb_lp(void) {
     28 	printx("IRQ USB LP\n");
     29 	for (;;) ;
     30 }
     31 void irq_usb_hp(void) {
     32 	printx("IRQ USB HP\n");
     33 	for (;;) ;
     34 }
     35 
     36 static volatile int _usb_online = 0;
     37 static void *ep1_rx_data;
     38 static volatile int ep1_rx_status;
     39 static volatile int ep1_tx_busy;
     40 
     41 static unsigned ep0rxb = USB_SRAM_BASE + 0x0040; /* 64 bytes */
     42 static unsigned ep0txb = USB_SRAM_BASE + 0x00c0; /* 64 bytes */
     43 static unsigned ep1rxb = USB_SRAM_BASE + 0x0140; /* 64 bytes */
     44 static unsigned ep1txb = USB_SRAM_BASE + 0x01c0; /* 64 bytes */
     45 
     46 #define ADDR2USB(n) (((n) & 0x3FF) >> 1)
     47 
     48 void usb_handle_reset(void) {
     49 	_usb_online = 0;
     50 	ep1_tx_busy = 0;
     51 	ep1_rx_status = -ENODEV;
     52 
     53 	writel(0, USB_BTABLE);
     54 	writel(ADDR2USB(ep0txb), USB_ADDR_TX(0));
     55 	writel(ADDR2USB(ep0rxb), USB_ADDR_RX(0));
     56 	writel(0, USB_COUNT_TX(0));
     57 	writel(USB_RX_SZ_64, USB_COUNT_RX(0));
     58 
     59 	writel(ADDR2USB(ep1txb), USB_ADDR_TX(1));
     60 	writel(ADDR2USB(ep1rxb), USB_ADDR_RX(1));
     61 	writel(0, USB_COUNT_TX(1));
     62 	writel(USB_RX_SZ_64, USB_COUNT_RX(1));
     63 
     64 	writel(0x0 | USB_EPR_TYPE_CONTROL |
     65 		USB_EPR_RX_NAK | USB_EPR_TX_NAK,
     66 		USB_EPR(0));
     67 
     68 	writel(0x1 | USB_EPR_TYPE_BULK |
     69 		USB_EPR_RX_NAK | USB_EPR_TX_NAK, 
     70 		USB_EPR(1));
     71 
     72 	writel(0x00 | USB_DADDR_ENABLE, USB_DADDR);
     73 }
     74 
     75 static u8 _dev00[] = {
     76 	18,		/* size */
     77 	DSC_DEVICE,
     78 	0x00, 0x01,	/* version */
     79 	0xFF,		/* class */
     80 	0x00,		/* subclass */
     81 	0x00,		/* protocol */
     82 	0x40,		/* maxpacket0 */
     83 	0xd1, 0x18,	/* VID */
     84 	0x02, 0x65,	/* PID */
     85 	0x00, 0x01,	/* version */
     86 	0x00,		/* manufacturer string */
     87 	0x00,		/* product string */
     88 	0x00,		/* serialno string */
     89 	0x01,		/* configurations */
     90 };
     91 
     92 static u8 _cfg00[] = {
     93 	9,
     94 	DSC_CONFIG,
     95 	0x20, 0x00,	/* total length */
     96 	0x01,		/* ifc count */
     97 	0x01,		/* configuration value */
     98 	0x00,		/* configuration string */
     99 	0x80,		/* attributes */
    100 	50,		/* mA/2 */
    101 
    102 	9,
    103 	DSC_INTERFACE,
    104 	0x00,		/* interface number */
    105 	0x00,		/* alt setting */
    106 	0x02,		/* ept count */
    107 	0xFF,		/* class */
    108 	0x00,		/* subclass */
    109 	0x00,		/* protocol */
    110 	0x00,		/* interface string */
    111 
    112 	7,
    113 	DSC_ENDPOINT,
    114 	0x81,		/* address */
    115 	0x02,		/* bulk */
    116 	0x40, 0x00,	/* max packet size */
    117 	0x00,		/* interval */
    118 
    119 	7,
    120 	DSC_ENDPOINT,
    121 	0x01,		/* address */
    122 	0x02,		/* bulk */
    123 	0x40, 0x00,	/* max packet size */
    124 	0x00,		/* interval */
    125 
    126 };
    127 
    128 static struct {
    129 	u16 id;
    130 	u16 len;
    131 	u8 *desc;
    132 } dtable[] = {
    133 	{ 0x0100, sizeof(_dev00), _dev00 },
    134 	{ 0x0200, sizeof(_cfg00), _cfg00 },
    135 };
    136 
    137 unsigned load_desc(unsigned id) {
    138 	unsigned n, len;
    139 	for (n = 0; n < (sizeof(dtable)/sizeof(dtable[0])); n++) {
    140 		if (id == dtable[n].id) {
    141 			u16 *src = (u16*) dtable[n].desc;
    142 			u32 *dst = (void*) ep0txb;
    143 			len = dtable[n].len;
    144 			n = (len & 1) + (len >> 1);
    145 			while (n--)
    146 				*dst++ = *src++;
    147 			return len;
    148 		}
    149 	}
    150 	printx("? %h\n", id);
    151 	return 0;
    152 }
    153 
    154 
    155 /* exclude T and W0C bits */
    156 #define EPMASK (USB_EPR_TYPE_MASK | USB_EPR_DBL_BUF | USB_EPR_ADDR_MASK)
    157 
    158 #define EP0_TX_ACK_ADDR	0 /* sending ACK, then changing address */
    159 #define EP0_TX_ACK	1 /* sending ACK */
    160 #define EP0_RX_ACK	2 /* receiving ACK */
    161 #define EP0_TX		3 /* sending data */
    162 #define EP0_RX		4 /* receiving data */
    163 #define EP0_IDLE	5 /* waiting for SETUP */
    164 
    165 static void ep0_recv_ack(unsigned n) {
    166 	writel((n & EPMASK) | USB_EPR_RX_STALL | USB_EPR_STATUS_OUT, USB_EPR(0));
    167 }
    168 static void ep0_send_ack(unsigned n) {
    169 	writel(0, USB_COUNT_TX(0));
    170 	writel((n & EPMASK) | USB_EPR_TX_STALL, USB_EPR(0));
    171 }
    172 
    173 static u8 ep0state = EP0_IDLE;
    174 static u8 newaddr;
    175 
    176 void usb_handle_ep0_tx(unsigned n) {
    177 	switch (ep0state) {
    178 	case EP0_TX_ACK_ADDR:
    179 		writel(newaddr | USB_DADDR_ENABLE, USB_DADDR);
    180 	case EP0_TX_ACK:
    181 		ep0state = EP0_IDLE;
    182 		writel((n & EPMASK), USB_EPR(0));
    183 		break;
    184 	case EP0_TX:
    185 		ep0state = EP0_RX_ACK;
    186 		ep0_recv_ack(n);
    187 		break;
    188 	}
    189 }
    190 
    191 void usb_handle_ep0_rx(unsigned n) {
    192 	switch (ep0state) {
    193 	case EP0_RX_ACK:
    194 		/* ack txn and make sure STATUS_OUT is cleared */
    195 		writel(((n & EPMASK) & (~USB_EPR_STATUS_OUT)) |
    196 			USB_EPR_CTR_TX, USB_EPR(0));
    197 		ep0state = EP0_IDLE;
    198 		break;
    199 	case EP0_RX:
    200 		;
    201 	}
    202 }
    203 
    204 void usb_handle_ep0_setup(unsigned n) {
    205 	u16 req, val, idx, len, x;
    206 
    207 	req = readl(ep0rxb + 0x00);
    208 	val = readl(ep0rxb + 0x04);
    209 	idx = readl(ep0rxb + 0x08);
    210 	len = readl(ep0rxb + 0x0C);
    211 	x = readl(USB_COUNT_RX(0));
    212 
    213 	/* release SETUP latch by acking RX */
    214 	writel((n & EPMASK), USB_EPR(0));
    215 
    216 	switch (req) {
    217 	case GET_DESCRIPTOR:
    218 		x = load_desc(val);
    219 		if (x == 0)
    220 			goto error;
    221 		if (x > len)
    222 			x = len;
    223 		ep0state = EP0_TX;
    224 		writel(x, USB_COUNT_TX(0));
    225 		writel((n & EPMASK) | USB_EPR_TX_STALL, USB_EPR(0));
    226 		return;
    227 	case SET_ADDRESS:
    228 		ep0state = EP0_TX_ACK_ADDR;
    229 		newaddr = val & 0x7F;
    230 		ep0_send_ack(n);
    231 		return;
    232 	case SET_CONFIGURATION:
    233 		ep0state = EP0_TX_ACK;
    234 		ep0_send_ack(n);
    235 		_usb_online = 1; /* TODO: check value */
    236 		return;	
    237 	}
    238 
    239 	/* unknown request */
    240 	printx("? %b %b %h %h %h\n", req, req >> 8, val, idx, len);
    241 
    242 error:
    243 	/* error, stall TX */
    244 	writel((n & EPMASK) | USB_EPR_TX_NAK | USB_EPR_TX_STALL, USB_EPR(0));
    245 }
    246 
    247 void usb_handle_ep0(void) {
    248 	unsigned n = readl(USB_EPR(0));
    249 	if (n & USB_EPR_SETUP) {
    250 		usb_handle_ep0_setup(n);
    251 	} else if (n & USB_EPR_CTR_TX) {
    252 		usb_handle_ep0_tx(n);
    253 	} else if (n & USB_EPR_CTR_RX) {
    254 		usb_handle_ep0_rx(n);
    255 	}
    256 }
    257 
    258 void usb_handle_ep1(void) {
    259 	unsigned n;
    260 	int len;
    261 
    262 	n = readl(USB_EPR(1));
    263 	if (n & USB_EPR_CTR_RX) {
    264 		/* first, clear RX CTR */
    265 		writel((n & EPMASK) | USB_EPR_CTR_TX, USB_EPR(1));
    266 
    267 		u32 *src = (void*) ep1rxb;
    268 		u16 *dst = (void*) ep1_rx_data;
    269 		len = readl(USB_COUNT_RX(1)) & 0x3FF;
    270 		ep1_rx_status = len;
    271 		while (len > 0) {
    272 			*dst++ = *src++;
    273 			len -= 2;
    274 		}
    275 	}
    276 	if (n & USB_EPR_CTR_TX) {
    277 		/* first, clear TX CTR */
    278 		writel((n & EPMASK) | USB_EPR_CTR_RX, USB_EPR(1));
    279 		ep1_tx_busy = 0;
    280 	}
    281 }
    282 
    283 int usb_recv(void *_data, int count) {
    284 	int r, rx = 0;
    285 	unsigned n;
    286 	u8 *data = _data;
    287 
    288 	while (!_usb_online)
    289 		usb_handle_irq();
    290 
    291 	while (count > 0) {
    292 		if (!_usb_online)
    293 			return -ENODEV;
    294 
    295 		ep1_rx_data = data;
    296 		ep1_rx_status = -EBUSY;
    297 
    298 		/* move from NAK to VALID, don't touch any other bits */
    299 		n = readl(USB_EPR(1)) & EPMASK;
    300 		writel(n | USB_EPR_CTR_RX | USB_EPR_CTR_TX | USB_EPR_RX_STALL, USB_EPR(1));
    301 
    302 		while (ep1_rx_status == -EBUSY)
    303 			usb_handle_irq();
    304 
    305 		r = ep1_rx_status;
    306 
    307 		if (r < 0)
    308 			return r;
    309 		if (r > count)
    310 			r = count;
    311 		data += r;
    312 		rx += r;
    313 		count -= r;	
    314 
    315 		/* terminate on short packet */
    316 		if (r != 64)
    317 			break;
    318 	}
    319 
    320 	return rx;
    321 }
    322 
    323 int usb_xmit(void *data, int len) {
    324 	int tx = 0;
    325 	int n;
    326 	u16 *src = data;
    327 
    328 	while (len > 0) {
    329 		u32 *dst = (void*) ep1txb;
    330 		int xfer = (len > 64) ? 64 : len;
    331 
    332 		if (!_usb_online)
    333 			return -ENODEV;
    334 
    335 		while (ep1_tx_busy)
    336 			usb_handle_irq();
    337 
    338 		writel(xfer, USB_COUNT_TX(1));
    339 		//printx("%x <- %x (%x)\n",dst, src, xfer);
    340 		len -= xfer;
    341 		tx += xfer;
    342 
    343 		while (xfer > 0) {
    344 			*dst++ = *src++;
    345 			xfer -= 2;
    346 		}
    347 
    348 		/* move from NAK to VALID, don't touch any other bits */
    349 		n = readl(USB_EPR(1)) & EPMASK;
    350 		writel(n | USB_EPR_CTR_RX | USB_EPR_CTR_TX | USB_EPR_TX_STALL, USB_EPR(1));
    351 
    352 		ep1_tx_busy = 1;
    353 
    354 	}
    355 
    356 	return tx;
    357 }
    358 
    359 void usb_init(unsigned vid, unsigned pid, const char *mfg_string, const char *prod_string) {
    360 	unsigned n;
    361 
    362 	_dev00[8] = vid;
    363 	_dev00[9] = vid >> 8;
    364 	_dev00[10] = pid;
    365 	_dev00[11] = pid >> 8;
    366 
    367 	/* enable GPIOC */
    368 	writel(readl(RCC_APB2ENR) | RCC_APB2_GPIOC, RCC_APB2ENR);
    369 
    370 	/* configure GPIOC-12 */
    371 	writel(1 << 12, GPIOC_BASE + GPIO_BSR);
    372 	n = readl(GPIOC_BASE + GPIO_CRH);
    373 	n = (n & 0xFFF0FFFF) | 0x00050000;
    374 	writel(n, GPIOC_BASE + GPIO_CRH);
    375 
    376 	printx("usb_init()\n");
    377 
    378 	/* enable USB clock */
    379 	writel(readl(RCC_APB1ENR) | RCC_APB1_USB, RCC_APB1ENR);
    380 
    381 	/* reset */
    382 	writel(USB_CR_PDWN | USB_CR_FRES, USB_CR);
    383 	for (n = 0; n < 100000; n++) asm("nop");
    384 	writel(~USB_CR_PDWN, USB_CR); /* power up analog block */
    385 	for (n = 0; n < 100000; n++) asm("nop");
    386 	writel(0, USB_CR);
    387 	writel(0, USB_ISR);
    388 
    389 	usb_handle_reset();
    390 
    391 	/* become active on the bus */
    392 	writel(1 << 12, GPIOC_BASE + GPIO_BRR);
    393 }
    394 
    395 void usb_handle_irq(void) {
    396 	unsigned n;
    397 	for (;;) {
    398 		n = readl(USB_ISR);
    399 		if (n & USB_RESETM) {
    400 			usb_handle_reset();
    401 			writel(~USB_RESETM, USB_ISR);
    402 			continue;
    403 		}
    404 		if (n & USB_CTRM) {
    405 			if ((n & 0x0F) == 0)
    406 				usb_handle_ep0();
    407 			if ((n & 0x0F) == 1)
    408 				usb_handle_ep1();
    409 			writel(~USB_CTRM, USB_ISR);
    410 			continue;
    411 		}
    412 		break;
    413 	}
    414 }
    415