This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM335x Kernel panic with 3G USB dongle

Other Parts Discussed in Thread: AM3354

Hello AM335x Gurus,

after a lot of reading and installing, I got a HUAWEI E3131 3G USB dongle to work with my PhyBoard WEGA SBC - Processor is AM3354.
I'm using a YOCTO distribution - provided from the manufactuere PHYTEC. There is an official linux-ti kernel running with version: 3.12.30.

Unfortunately the Internet connection lasts only for a few seconds. Then the Kernel get's Panic :-(

root@phyboard-wega-am335x-2:~# ping heise.de
PING heise.de (193.99.144.80): 56 data bytes
64 bytes from 193.99.144.80: seq=0 ttl=241 time=447.424 ms
64 bytes from 193.99.144.80: seq=1 ttl=241 time=494.518 ms
64 bytes from 193.99.144.80: seq=2 ttl=241 time=435.364 ms
64 bytes from 193.99.144.80: seq=3 ttl=241 time=377.298 ms
64 bytes from 193.99.144.80: seq=4 ttl=241 time=516.077 ms
64 bytes from 193.99.144.80: seq=5 ttl=241 time=755.049 ms
[  134.821457] skbuff: skb_over_panic: text:c043d820 len:1926 put:1926 head:cc8b9500 data:cc8b9542 tail:0xcc8b9cc8 end:0xcc8b9b40 dev:wwan0
[  134.834385] ------------[ cut here ]------------
[  134.839243] Kernel BUG at c052e518 [verbose debug info unavailable]
[  134.845827] Internal error: Oops - BUG: 0 [#1] SMP ARM
[  134.851228] Modules linked in: snd_soc_evm snd_soc_omap snd_pcm_dmaengine snd_soc_davinci_mcasp snd_soc_davinci snd_soc_tlv320aic3x snd_so           c_core spidev snd_compress regmap_spi snd_pcm snd_page_alloc snd_timer snd soundcore ipv6
[  134.872621] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.12.30-AM335x-PD15.3.2 #3
[  134.880393] task: c0883720 ti: c0878000 task.ti: c0878000
[  134.886084] PC is at skb_panic+0x5c/0x68
[  134.890221] LR is at irq_work_queue+0xec/0x100
[  134.894899] pc : [<c052e518>]    lr : [<c00b4894>]    psr: 600f0193
[  134.894899] sp : c0879d70  ip : c0879c60  fp : c0879d9c
[  134.906951] r10: c0878000  r9 : c090f900  r8 : cdbfd4f0
[  134.912443] r7 : c07ca878  r6 : cc8b9500  r5 : 00000786  r4 : cc8b9542
[  134.919299] r3 : 00000001  r2 : 00000000  r1 : 002f0000  r0 : 0000007c
[  134.926160] Flags: nZCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
[  134.933929] Control: 10c5387d  Table: 8c8e0019  DAC: 00000015
[  134.939966] Process swapper/0 (pid: 0, stack limit = 0xc0878248)
[  134.946276] Stack: (0xc0879d70 to 0xc087a000)
[  134.950867] 9d60:                                     00000786 cc8b9500 cc8b9542 cc8b9cc8
[  134.959467] 9d80: cc8b9b40 cc8ec000 cc8b9542 cc8b9b40 c0879dbc c0879da0 c052e57c c052e4c8
[  134.968068] 9da0: cc8ec4c0 cce0f3c0 cdde1cc0 00000000 c0879de4 c0879dc0 c043d820 c052e530
[  134.976667] 9dc0: cce0f3c0 cdbfd400 cce0f3c0 600f0113 00000000 00000000 c0879e04 c0879de8
[  134.985265] 9de0: c04489f4 c043d800 cce0f3d4 c0879e08 cdbfd4ec cdbfd4f4 c0879e34 c0879e08
[  134.993864] 9e00: c0449510 c0448998 c0879e08 c0879e08 c0879e64 cdbfd4fc cdbfd500 00000000
[  135.002463] 9e20: c0874384 c0880c10 c0879e6c c0879e38 c004f584 c0449484 00000000 00000000
[  135.011060] 9e40: 00000001 00000001 c087a098 00000018 c0878000 00000018 00000100 00000006
[  135.019659] 9e60: c0879ebc c0879e70 c004f718 c004f4ec c0880c10 cd805a80 c0879ea4 00200000
[  135.028256] 9e80: ffffbf7a 0000000a c0878030 00000000 c0878030 600f0193 c0878000 c0880c10
[  135.036855] 9ea0: 00000000 00000021 c0878000 00000000 c0879ed4 c0879ec0 c004f8cc c004f638
[  135.045453] 9ec0: 00000000 c0878018 c0879eec c0879ed8 c004fb78 c004f884 00000112 c0874ef0
[  135.054051] 9ee0: c0879f14 c0879ef0 c0015554 c004fae0 00000080 fa200000 c0879f38 c090e798
[  135.062653] 9f00: 00000021 c0608f4c c0879f34 c0879f18 c0008818 c0015504 c001592c 600f0013
[  135.071251] 9f20: ffffffff c0879f6c c0879f8c c0879f38 c05fe8c0 c000879c ffffffed 002f0000
[  135.079851] 9f40: c0881638 c00260a0 c0878010 c0878000 c08808f0 c0880950 c0608f4c c0878000
[  135.088450] 9f60: 00000000 c0879f8c c0879f80 c0879f80 c001593c c001592c 600f0013 ffffffff
[  135.097048] 9f80: c0879fa4 c0879f90 c0085578 c0015908 00000000 c08591d8 c0879fb4 c0879fa8
[  135.105647] 9fa0: c05f4368 c008548c c0879ff4 c0879fb8 c0813ae8 c05f430c ffffffff ffffffff
[  135.114245] 9fc0: c08135d0 00000000 00000000 c08591d8 00000000 10c5387d c08808e8 c08591d4
[  135.122844] 9fe0: c0884728 80004059 00000000 c0879ff8 80008074 c0813834 00000000 00000000
[  135.131424] Backtrace:
[  135.134033] [<c052e4bc>] (skb_panic+0x0/0x68) from [<c052e57c>] (skb_put+0x58/0x5c)
[  135.142070]  r7:cc8b9b40
[  135.144779] [<c052e524>] (skb_put+0x0/0x5c) from [<c043d820>] (rx_complete+0x2c/0x210)
[  135.153091]  r7:00000000 r6:cdde1cc0 r5:cce0f3c0 r4:cc8ec4c0
[  135.159117] [<c043d7f4>] (rx_complete+0x0/0x210) from [<c04489f4>] (__usb_hcd_giveback_urb+0x68/0x104)
[  135.168885]  r7:00000000 r6:00000000 r5:600f0113 r4:cce0f3c0
[  135.174902] [<c044898c>] (__usb_hcd_giveback_urb+0x0/0x104) from [<c0449510>] (usb_giveback_urb_bh+0x98/0xe4)
[  135.185306]  r6:cdbfd4f4 r5:cdbfd4ec r4:c0879e08 r3:cce0f3d4
[  135.191329] [<c0449478>] (usb_giveback_urb_bh+0x0/0xe4) from [<c004f584>] (tasklet_action+0xa4/0x14c)
[  135.201121]  r8:c0880c10 r7:c0874384 r6:00000000 r5:cdbfd500 r4:cdbfd4fc
[  135.208327] [<c004f4e0>] (tasklet_action+0x0/0x14c) from [<c004f718>] (__do_softirq+0xec/0x1e8)
[  135.217588] [<c004f62c>] (__do_softirq+0x0/0x1e8) from [<c004f8cc>] (do_softirq+0x54/0x60)
[  135.226386] [<c004f878>] (do_softirq+0x0/0x60) from [<c004fb78>] (irq_exit+0xa4/0xf8)
[  135.234704]  r4:c0878018 r3:00000000
[  135.238568] [<c004fad4>] (irq_exit+0x0/0xf8) from [<c0015554>] (handle_IRQ+0x5c/0xbc)
[  135.246886]  r4:c0874ef0 r3:00000112
[  135.250738] [<c00154f8>] (handle_IRQ+0x0/0xbc) from [<c0008818>] (omap3_intc_handle_irq+0x88/0xa4)
[  135.260254]  r8:c0608f4c r7:00000021 r6:c090e798 r5:c0879f38 r4:fa200000
r3:00000080
[  135.268682] [<c0008790>] (omap3_intc_handle_irq+0x0/0xa4) from [<c05fe8c0>] (__irq_svc+0x40/0x54)
[  135.278109] Exception stack(0xc0879f38 to 0xc0879f80)
[  135.283486] 9f20:                                                       ffffffed 002f0000
[  135.292189] 9f40: c0881638 c00260a0 c0878010 c0878000 c08808f0 c0880950 c0608f4c c0878000
[  135.300891] 9f60: 00000000 c0879f8c c0879f80 c0879f80 c001593c c001592c 600f0013 ffffffff
[  135.309576]  r7:c0879f6c r6:ffffffff r5:600f0013 r4:c001592c
[  135.315674] [<c00158fc>] (arch_cpu_idle+0x0/0x4c) from [<c0085578>] (cpu_startup_entry+0xf8/0x150)
[  135.325222] [<c0085480>] (cpu_startup_entry+0x0/0x150) from [<c05f4368>] (rest_init+0x68/0x80)
[  135.334369]  r7:c08591d8 r3:00000000
[  135.338235] [<c05f4300>] (rest_init+0x0/0x80) from [<c0813ae8>] (start_kernel+0x2c0/0x31c)
[  135.347041] [<c0813828>] (start_kernel+0x0/0x31c) from [<80008074>] (0x80008074)
[  135.354911] Code: e58d4008 e58de00c e59f0008 eb0324c8 (e7f001f2)
[  135.361406] ---[ end trace 3e41909440eba8ce ]---
[  135.366319] Kernel panic - not syncing: Fatal exception in interrupt

I found some other issues regarding similar problems with usb 3G modems here in the forum. But their configurations does not match my.
What will be the best try for me to fix this issue? Patching the kernel as mentioned here?:



Or just change some kernel config? It's attached here:
zImage.config

Thanks in advance for your help!

Mario

  • Hi,

    I will ask the USB experts to comment.
  • Thanks Biser!

    By the way - I also tried the 3G usb stick on my BeagleBone Black with current Debian Linux - without any problems...
  • You could try the procedures suggested in the other post you refer. Kernel v3.12 is rather old and a transitional version. A lot of issues have been fixed in newer releases. Anyway, it won't hurt if you try the patches on processors.wiki.ti.com/.../Sitara_Linux_MUSB_Issues
  • Mario,

    Your issue is very similar to the one in the forum thread you referred, which is fixed with patches in the mentioned wiki. So please patch the kernel as Biser suggested.

    What is the Debian kernel version you tested on BBB?
  • Ok I first try to upgrade the kernel - I started a regarding question to the YOCTO BSP maintainer (PHYTEC).
    If this is not possible, then I try the mentioned patches in AMSDK 07.00.00.00.

    My BBB kernel version is: 3.8.13-bone79 - much older???!!!

    best regards
    Mario
  • Yeah, 3.8.13 is much older, I doubt MUSB CPPI DMA is supported at all in that kernel. You can check it with the following command:

    $ zcat /proc/config.gz | grep CPPI41

    If nothing set, the old kernel does not use DMA for USB.
  • You are right - on the BBB there is no setting replied after the mentioned command.
    But on the WEGA Board I got:

    root@phyboard-wega-am335x-2:~# zcat /proc/config.gz | grep CPPI41
    CONFIG_USB_TI_CPPI41_DMA=y
    CONFIG_TI_CPPI41=y

    So you mean I should apply the DMA related patches - like:

    7.2. MUSB CPPI DMA driver does not handle RX Zero-Length Packet (ZLP).
    7.6. Fire hrtimer too late for pre-mature TX DMA completion interrupt for smaller packets.
    a. File:0001-Revert-usb-musb-musb cppi41-Handle-ISOCH-differently.patch.gz.
    b. [usb: musb: cppi41: fire hrtimer according to programmed channel length].

    Thanks for your help ...
  • Please apply patch #7.2, #7.4, #7.6, #8.1 one by one.
  • Hi Bin,

    I applied all mentioned patches. Number #7.6 (b) failed:

    root@vagrant-ubuntu-trusty-64:~/yocto/build/tmp-glibc/work-shared/phyboard-wega-am335x-2/kernel-source# cat ~/0005-usb-musb-cppi41-fire_hrtimer_according_to_programmed_channel_length.patch | patch -p1
    patching file drivers/usb/musb/musb_cppi41.c
    Reversed (or previously applied) patch detected!  Assume -R? [n]
    Apply anyway? [n] y
    Hunk #1 FAILED at 200.
    Hunk #2 FAILED at 274.
    2 out of 2 hunks FAILED -- saving rejects to file drivers/usb/musb/musb_cppi41.c.rej
    root@vagrant-ubuntu-trusty-64:~/yocto/build/tmp-glibc/work-shared/phyboard-wega-am335x-2/kernel-source#


    I attached the resulting .rej file:

    --- drivers/usb/musb/musb_cppi41.c
    +++ drivers/usb/musb/musb_cppi41.c
    @@ -200,7 +200,7 @@
     	if (!list_empty(&controller->early_tx_list)) {
     		ret = HRTIMER_RESTART;
     		hrtimer_forward_now(&controller->early_tx,
    -				ktime_set(0, 150 * NSEC_PER_USEC));
    +				ktime_set(0, 50 * NSEC_PER_USEC));
     	}
     
     	spin_unlock_irqrestore(&musb->lock, flags);
    @@ -274,8 +274,10 @@
     		list_add_tail(&cppi41_channel->tx_check,
     				&controller->early_tx_list);
     		if (!hrtimer_active(&controller->early_tx)) {
    +			unsigned long usecs = cppi41_channel->total_len / 10;
    +
     			hrtimer_start_range_ns(&controller->early_tx,
    -				ktime_set(0, 140 * NSEC_PER_USEC),
    +				ktime_set(0, usecs * NSEC_PER_USEC),
     				40 * NSEC_PER_USEC,
     				HRTIMER_MODE_REL);
     		}
    

    and the patched source file:

    #include <linux/device.h>
    #include <linux/dma-mapping.h>
    #include <linux/dmaengine.h>
    #include <linux/sizes.h>
    #include <linux/platform_device.h>
    #include <linux/of.h>
    
    #include "musb_core.h"
    
    #define RNDIS_REG(x) (0x80 + ((x - 1) * 4))
    
    #define EP_MODE_AUTOREG_NONE		0
    #define EP_MODE_AUTOREG_ALL_NEOP	1
    #define EP_MODE_AUTOREG_ALWAYS		3
    
    #define EP_MODE_DMA_TRANSPARENT		0
    #define EP_MODE_DMA_RNDIS		1
    #define EP_MODE_DMA_GEN_RNDIS		3
    
    #define USB_CTRL_TX_MODE	0x70
    #define USB_CTRL_RX_MODE	0x74
    #define USB_CTRL_AUTOREQ	0xd0
    #define USB_TDOWN		0xd8
    
    struct cppi41_dma_channel {
    	struct dma_channel channel;
    	struct cppi41_dma_controller *controller;
    	struct musb_hw_ep *hw_ep;
    	struct dma_chan *dc;
    	dma_cookie_t cookie;
    	u8 port_num;
    	u8 is_tx;
    	u8 is_allocated;
    	u8 usb_toggle;
    
    	dma_addr_t buf_addr;
    	u32 total_len;
    	u32 prog_len;
    	u32 transferred;
    	u32 packet_sz;
    	struct list_head tx_check;
    	int tx_zlp;
    };
    
    #define MUSB_DMA_NUM_CHANNELS 15
    
    struct cppi41_dma_controller {
    	struct dma_controller controller;
    	struct cppi41_dma_channel rx_channel[MUSB_DMA_NUM_CHANNELS];
    	struct cppi41_dma_channel tx_channel[MUSB_DMA_NUM_CHANNELS];
    	struct musb *musb;
    	struct hrtimer early_tx;
    	struct list_head early_tx_list;
    	u32 rx_mode;
    	u32 tx_mode;
    	u32 auto_req;
    };
    
    static void save_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
    {
    	u16 csr;
    	u8 toggle;
    
    	if (cppi41_channel->is_tx)
    		return;
    	if (!is_host_active(cppi41_channel->controller->musb))
    		return;
    
    	csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR);
    	toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0;
    
    	cppi41_channel->usb_toggle = toggle;
    }
    
    static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
    {
    	u16 csr;
    	u8 toggle;
    
    	if (cppi41_channel->is_tx)
    		return;
    	if (!is_host_active(cppi41_channel->controller->musb))
    		return;
    
    	csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR);
    	toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0;
    
    	/*
    	 * AM335x Advisory 1.0.13: Due to internal synchronisation error the
    	 * data toggle may reset from DATA1 to DATA0 during receiving data from
    	 * more than one endpoint.
    	 */
    	if (!toggle && toggle == cppi41_channel->usb_toggle) {
    		csr |= MUSB_RXCSR_H_DATATOGGLE | MUSB_RXCSR_H_WR_DATATOGGLE;
    		musb_writew(cppi41_channel->hw_ep->regs, MUSB_RXCSR, csr);
    		dev_dbg(cppi41_channel->controller->musb->controller,
    				"Restoring DATA1 toggle.\n");
    	}
    
    	cppi41_channel->usb_toggle = toggle;
    }
    
    static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep)
    {
    	u8		epnum = hw_ep->epnum;
    	struct musb	*musb = hw_ep->musb;
    	void __iomem	*epio = musb->endpoints[epnum].regs;
    	u16		csr;
    
    	csr = musb_readw(epio, MUSB_TXCSR);
    	if (csr & MUSB_TXCSR_TXPKTRDY)
    		return false;
    	return true;
    }
    
    static void cppi41_dma_callback(void *private_data);
    
    static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
    {
    	struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
    	struct musb *musb = hw_ep->musb;
    	void __iomem *epio = hw_ep->regs;
    	u16 csr;
    
    	if (!cppi41_channel->prog_len ||
    	    (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)) {
    
    		/* done, complete */
    		cppi41_channel->channel.actual_len =
    			cppi41_channel->transferred;
    		cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE;
    		cppi41_channel->channel.rx_packet_done = true;
    
    		/*
    		 * transmit ZLP using PIO mode for transfers which size is
    		 * multiple of EP packet size.
    		 */
    		if (cppi41_channel->tx_zlp && (cppi41_channel->transferred %
    					cppi41_channel->packet_sz) == 0) {
    			musb_ep_select(musb->mregs, hw_ep->epnum);
    			csr = MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY;
    			musb_writew(epio, MUSB_TXCSR, csr);
    		}
    		musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx);
    	} else {
    		/* next iteration, reload */
    		struct dma_chan *dc = cppi41_channel->dc;
    		struct dma_async_tx_descriptor *dma_desc;
    		enum dma_transfer_direction direction;
    		u32 remain_bytes;
    
    		cppi41_channel->buf_addr += cppi41_channel->packet_sz;
    
    		remain_bytes = cppi41_channel->total_len;
    		remain_bytes -= cppi41_channel->transferred;
    		remain_bytes = min(remain_bytes, cppi41_channel->packet_sz);
    		cppi41_channel->prog_len = remain_bytes;
    
    		direction = cppi41_channel->is_tx ? DMA_MEM_TO_DEV
    			: DMA_DEV_TO_MEM;
    		dma_desc = dmaengine_prep_slave_single(dc,
    				cppi41_channel->buf_addr,
    				remain_bytes,
    				direction,
    				DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    		if (WARN_ON(!dma_desc))
    			return;
    
    		dma_desc->callback = cppi41_dma_callback;
    		dma_desc->callback_param = &cppi41_channel->channel;
    		cppi41_channel->cookie = dma_desc->tx_submit(dma_desc);
    		dma_async_issue_pending(dc);
    
    		if (!cppi41_channel->is_tx) {
    			csr = musb_readw(epio, MUSB_RXCSR);
    			csr |= MUSB_RXCSR_H_REQPKT;
    			musb_writew(epio, MUSB_RXCSR, csr);
    		}
    	}
    }
    
    static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
    {
    	struct cppi41_dma_controller *controller;
    	struct cppi41_dma_channel *cppi41_channel, *n;
    	struct musb *musb;
    	unsigned long flags;
    	enum hrtimer_restart ret = HRTIMER_NORESTART;
    
    	controller = container_of(timer, struct cppi41_dma_controller,
    			early_tx);
    	musb = controller->musb;
    
    	spin_lock_irqsave(&musb->lock, flags);
    	list_for_each_entry_safe(cppi41_channel, n, &controller->early_tx_list,
    			tx_check) {
    		bool empty;
    		struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
    
    		empty = musb_is_tx_fifo_empty(hw_ep);
    		if (empty) {
    			list_del_init(&cppi41_channel->tx_check);
    			cppi41_trans_done(cppi41_channel);
    		}
    	}
    
    	if (!list_empty(&controller->early_tx_list)) {
    		ret = HRTIMER_RESTART;
    		hrtimer_forward_now(&controller->early_tx,
    				ktime_set(0, 50 * NSEC_PER_USEC));
    	}
    
    	spin_unlock_irqrestore(&musb->lock, flags);
    	return ret;
    }
    
    static void cppi41_dma_callback(void *private_data)
    {
    	struct dma_channel *channel = private_data;
    	struct cppi41_dma_channel *cppi41_channel = channel->private_data;
    	struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
    	struct musb *musb = hw_ep->musb;
    	unsigned long flags;
    	struct dma_tx_state txstate;
    	u32 transferred;
    	bool empty;
    
    	spin_lock_irqsave(&musb->lock, flags);
    
    	dmaengine_tx_status(cppi41_channel->dc, cppi41_channel->cookie,
    			&txstate);
    	transferred = cppi41_channel->prog_len - txstate.residue;
    	cppi41_channel->transferred += transferred;
    
    	dev_dbg(musb->controller, "DMA transfer done on hw_ep=%d bytes=%d/%d\n",
    		hw_ep->epnum, cppi41_channel->transferred,
    		cppi41_channel->total_len);
    
    	update_rx_toggle(cppi41_channel);
    
    	if (cppi41_channel->transferred == cppi41_channel->total_len ||
    			transferred < cppi41_channel->packet_sz)
    		cppi41_channel->prog_len = 0;
    
    	empty = musb_is_tx_fifo_empty(hw_ep);
    	if (empty) {
    		cppi41_trans_done(cppi41_channel);
    	} else {
    		struct cppi41_dma_controller *controller;
    		/*
    		 * On AM335x it has been observed that the TX interrupt fires
    		 * too early that means the TXFIFO is not yet empty but the DMA
    		 * engine says that it is done with the transfer. We don't
    		 * receive a FIFO empty interrupt so the only thing we can do is
    		 * to poll for the bit. On HS it usually takes 2us, on FS around
    		 * 110us - 150us depending on the transfer size.
    		 * We spin on HS (no longer than than 25us and setup a timer on
    		 * FS to check for the bit and complete the transfer.
    		 */
    		controller = cppi41_channel->controller;
    
    		if (musb->g.speed == USB_SPEED_HIGH) {
    			unsigned wait = 25;
    
    			do {
    				empty = musb_is_tx_fifo_empty(hw_ep);
    				if (empty)
    					break;
    				wait--;
    				if (!wait)
    					break;
    				udelay(1);
    			} while (1);
    
    			empty = musb_is_tx_fifo_empty(hw_ep);
    			if (empty) {
    				cppi41_trans_done(cppi41_channel);
    				goto out;
    			}
    		}
    		list_add_tail(&cppi41_channel->tx_check,
    				&controller->early_tx_list);
    		if (!hrtimer_is_queued(&controller->early_tx)) {
    			unsigned long usecs = cppi41_channel->total_len / 10;
    
    			hrtimer_start_range_ns(&controller->early_tx,
    				ktime_set(0, usecs * NSEC_PER_USEC),
    				40 * NSEC_PER_USEC,
    				HRTIMER_MODE_REL);
    		}
    	}
    out:
    	spin_unlock_irqrestore(&musb->lock, flags);
    }
    
    static u32 update_ep_mode(unsigned ep, unsigned mode, u32 old)
    {
    	unsigned shift;
    
    	shift = (ep - 1) * 2;
    	old &= ~(3 << shift);
    	old |= mode << shift;
    	return old;
    }
    
    static void cppi41_set_dma_mode(struct cppi41_dma_channel *cppi41_channel,
    		unsigned mode)
    {
    	struct cppi41_dma_controller *controller = cppi41_channel->controller;
    	u32 port;
    	u32 new_mode;
    	u32 old_mode;
    
    	if (cppi41_channel->is_tx)
    		old_mode = controller->tx_mode;
    	else
    		old_mode = controller->rx_mode;
    	port = cppi41_channel->port_num;
    	new_mode = update_ep_mode(port, mode, old_mode);
    
    	if (new_mode == old_mode)
    		return;
    	if (cppi41_channel->is_tx) {
    		controller->tx_mode = new_mode;
    		musb_writel(controller->musb->ctrl_base, USB_CTRL_TX_MODE,
    				new_mode);
    	} else {
    		controller->rx_mode = new_mode;
    		musb_writel(controller->musb->ctrl_base, USB_CTRL_RX_MODE,
    				new_mode);
    	}
    }
    
    static void cppi41_set_autoreq_mode(struct cppi41_dma_channel *cppi41_channel,
    		unsigned mode)
    {
    	struct cppi41_dma_controller *controller = cppi41_channel->controller;
    	u32 port;
    	u32 new_mode;
    	u32 old_mode;
    
    	old_mode = controller->auto_req;
    	port = cppi41_channel->port_num;
    	new_mode = update_ep_mode(port, mode, old_mode);
    
    	if (new_mode == old_mode)
    		return;
    	controller->auto_req = new_mode;
    	musb_writel(controller->musb->ctrl_base, USB_CTRL_AUTOREQ, new_mode);
    }
    
    static bool cppi41_configure_channel(struct dma_channel *channel,
    				u16 packet_sz, u8 mode,
    				dma_addr_t dma_addr, u32 len)
    {
    	struct cppi41_dma_channel *cppi41_channel = channel->private_data;
    	struct dma_chan *dc = cppi41_channel->dc;
    	struct dma_async_tx_descriptor *dma_desc;
    	enum dma_transfer_direction direction;
    	struct musb *musb = cppi41_channel->controller->musb;
    	unsigned use_gen_rndis = 0;
    
    	dev_dbg(musb->controller,
    		"configure ep%d/%x packet_sz=%d, mode=%d, dma_addr=0x%llx, len=%d is_tx=%d\n",
    		cppi41_channel->port_num, RNDIS_REG(cppi41_channel->port_num),
    		packet_sz, mode, (unsigned long long) dma_addr,
    		len, cppi41_channel->is_tx);
    
    	cppi41_channel->buf_addr = dma_addr;
    	cppi41_channel->total_len = len;
    	cppi41_channel->transferred = 0;
    	cppi41_channel->packet_sz = packet_sz;
    	cppi41_channel->tx_zlp = (cppi41_channel->is_tx && mode) ? 1 : 0;
    
    	/*
    	 * Due to AM335x' Advisory 1.0.13 we are not allowed to transfer more
    	 * than max packet size at a time.
    	 */
    	if (cppi41_channel->is_tx)
    		use_gen_rndis = 1;
    
    	if (use_gen_rndis) {
    		/* RNDIS mode */
    		if (len > packet_sz) {
    			musb_writel(musb->ctrl_base,
    				RNDIS_REG(cppi41_channel->port_num), len);
    			/* gen rndis */
    			cppi41_set_dma_mode(cppi41_channel,
    					EP_MODE_DMA_GEN_RNDIS);
    
    			/* auto req */
    			cppi41_set_autoreq_mode(cppi41_channel,
    					EP_MODE_AUTOREG_ALL_NEOP);
    		} else {
    			musb_writel(musb->ctrl_base,
    					RNDIS_REG(cppi41_channel->port_num), 0);
    			cppi41_set_dma_mode(cppi41_channel,
    					EP_MODE_DMA_TRANSPARENT);
    			cppi41_set_autoreq_mode(cppi41_channel,
    					EP_MODE_AUTOREG_NONE);
    		}
    	} else {
    		/* fallback mode */
    		cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT);
    		cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREG_NONE);
    		len = min_t(u32, packet_sz, len);
    	}
    	cppi41_channel->prog_len = len;
    	direction = cppi41_channel->is_tx ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
    	dma_desc = dmaengine_prep_slave_single(dc, dma_addr, len, direction,
    			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    	if (!dma_desc)
    		return false;
    
    	dma_desc->callback = cppi41_dma_callback;
    	dma_desc->callback_param = channel;
    	cppi41_channel->cookie = dma_desc->tx_submit(dma_desc);
    	cppi41_channel->channel.rx_packet_done = false;
    
    	save_rx_toggle(cppi41_channel);
    	dma_async_issue_pending(dc);
    	return true;
    }
    
    static struct dma_channel *cppi41_dma_channel_allocate(struct dma_controller *c,
    				struct musb_hw_ep *hw_ep, u8 is_tx)
    {
    	struct cppi41_dma_controller *controller = container_of(c,
    			struct cppi41_dma_controller, controller);
    	struct cppi41_dma_channel *cppi41_channel = NULL;
    	u8 ch_num = hw_ep->epnum - 1;
    
    	if (ch_num >= MUSB_DMA_NUM_CHANNELS)
    		return NULL;
    
    	if (is_tx)
    		cppi41_channel = &controller->tx_channel[ch_num];
    	else
    		cppi41_channel = &controller->rx_channel[ch_num];
    
    	if (!cppi41_channel->dc)
    		return NULL;
    
    	if (cppi41_channel->is_allocated)
    		return NULL;
    
    	cppi41_channel->hw_ep = hw_ep;
    	cppi41_channel->is_allocated = 1;
    
    	return &cppi41_channel->channel;
    }
    
    static void cppi41_dma_channel_release(struct dma_channel *channel)
    {
    	struct cppi41_dma_channel *cppi41_channel = channel->private_data;
    
    	if (cppi41_channel->is_allocated) {
    		cppi41_channel->is_allocated = 0;
    		channel->status = MUSB_DMA_STATUS_FREE;
    		channel->actual_len = 0;
    	}
    }
    
    static int cppi41_dma_channel_program(struct dma_channel *channel,
    				u16 packet_sz, u8 mode,
    				dma_addr_t dma_addr, u32 len)
    {
    	int ret;
    	struct cppi41_dma_channel *cppi41_channel = channel->private_data;
    	int hb_mult = 0;
    
    	BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
    		channel->status == MUSB_DMA_STATUS_BUSY);
    
    	if (is_host_active(cppi41_channel->controller->musb)) {
    		if (cppi41_channel->is_tx)
    			hb_mult = cppi41_channel->hw_ep->out_qh->hb_mult;
    		else
    			hb_mult = cppi41_channel->hw_ep->in_qh->hb_mult;
    	}
    
    	channel->status = MUSB_DMA_STATUS_BUSY;
    	channel->actual_len = 0;
    
    	if (hb_mult)
    		packet_sz = hb_mult * (packet_sz & 0x7FF);
    
    	ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len);
    	if (!ret)
    		channel->status = MUSB_DMA_STATUS_FREE;
    
    	return ret;
    }
    
    static int cppi41_is_compatible(struct dma_channel *channel, u16 maxpacket,
    		void *buf, u32 length)
    {
    	struct cppi41_dma_channel *cppi41_channel = channel->private_data;
    	struct cppi41_dma_controller *controller = cppi41_channel->controller;
    	struct musb *musb = controller->musb;
    
    	if (is_host_active(musb)) {
    		WARN_ON(1);
    		return 1;
    	}
    	if (cppi41_channel->hw_ep->ep_in.type != USB_ENDPOINT_XFER_BULK)
    		return 0;
    	if (cppi41_channel->is_tx)
    		return 1;
    	/* AM335x Advisory 1.0.13. No workaround for device RX mode */
    	return 0;
    }
    
    static int cppi41_dma_channel_abort(struct dma_channel *channel)
    {
    	struct cppi41_dma_channel *cppi41_channel = channel->private_data;
    	struct cppi41_dma_controller *controller = cppi41_channel->controller;
    	struct musb *musb = controller->musb;
    	void __iomem *epio = cppi41_channel->hw_ep->regs;
    	int tdbit;
    	int ret;
    	unsigned is_tx;
    	u16 csr;
    
    	is_tx = cppi41_channel->is_tx;
    	dev_dbg(musb->controller, "abort channel=%d, is_tx=%d\n",
    			cppi41_channel->port_num, is_tx);
    
    	if (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)
    		return 0;
    
    	list_del_init(&cppi41_channel->tx_check);
    	if (is_tx) {
    		csr = musb_readw(epio, MUSB_TXCSR);
    		csr &= ~MUSB_TXCSR_DMAENAB;
    		musb_writew(epio, MUSB_TXCSR, csr);
    	} else {
    		csr = musb_readw(epio, MUSB_RXCSR);
    		csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB);
    		musb_writew(epio, MUSB_RXCSR, csr);
    
    		csr = musb_readw(epio, MUSB_RXCSR);
    		if (csr & MUSB_RXCSR_RXPKTRDY) {
    			csr |= MUSB_RXCSR_FLUSHFIFO;
    			musb_writew(epio, MUSB_RXCSR, csr);
    			musb_writew(epio, MUSB_RXCSR, csr);
    		}
    	}
    
    	tdbit = 1 << cppi41_channel->port_num;
    	if (is_tx)
    		tdbit <<= 16;
    
    	do {
    		musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
    		ret = dmaengine_terminate_all(cppi41_channel->dc);
    	} while (ret == -EAGAIN);
    
    	musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
    
    	if (is_tx) {
    		csr = musb_readw(epio, MUSB_TXCSR);
    		if (csr & MUSB_TXCSR_TXPKTRDY) {
    			csr |= MUSB_TXCSR_FLUSHFIFO;
    			musb_writew(epio, MUSB_TXCSR, csr);
    		}
    	}
    
    	cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE;
    	return 0;
    }
    
    static void cppi41_release_all_dma_chans(struct cppi41_dma_controller *ctrl)
    {
    	struct dma_chan *dc;
    	int i;
    
    	for (i = 0; i < MUSB_DMA_NUM_CHANNELS; i++) {
    		dc = ctrl->tx_channel[i].dc;
    		if (dc)
    			dma_release_channel(dc);
    		dc = ctrl->rx_channel[i].dc;
    		if (dc)
    			dma_release_channel(dc);
    	}
    }
    
    static void cppi41_dma_controller_stop(struct cppi41_dma_controller *controller)
    {
    	cppi41_release_all_dma_chans(controller);
    }
    
    static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
    {
    	struct musb *musb = controller->musb;
    	struct device *dev = musb->controller;
    	struct device_node *np = dev->of_node;
    	struct cppi41_dma_channel *cppi41_channel;
    	int count;
    	int i;
    	int ret;
    
    	count = of_property_count_strings(np, "dma-names");
    	if (count < 0)
    		return count;
    
    	for (i = 0; i < count; i++) {
    		struct dma_chan *dc;
    		struct dma_channel *musb_dma;
    		const char *str;
    		unsigned is_tx;
    		unsigned int port;
    
    		ret = of_property_read_string_index(np, "dma-names", i, &str);
    		if (ret)
    			goto err;
    		if (!strncmp(str, "tx", 2))
    			is_tx = 1;
    		else if (!strncmp(str, "rx", 2))
    			is_tx = 0;
    		else {
    			dev_err(dev, "Wrong dmatype %s\n", str);
    			goto err;
    		}
    		ret = kstrtouint(str + 2, 0, &port);
    		if (ret)
    			goto err;
    
    		ret = -EINVAL;
    		if (port > MUSB_DMA_NUM_CHANNELS || !port)
    			goto err;
    		if (is_tx)
    			cppi41_channel = &controller->tx_channel[port - 1];
    		else
    			cppi41_channel = &controller->rx_channel[port - 1];
    
    		cppi41_channel->controller = controller;
    		cppi41_channel->port_num = port;
    		cppi41_channel->is_tx = is_tx;
    		INIT_LIST_HEAD(&cppi41_channel->tx_check);
    
    		musb_dma = &cppi41_channel->channel;
    		musb_dma->private_data = cppi41_channel;
    		musb_dma->status = MUSB_DMA_STATUS_FREE;
    		musb_dma->max_len = SZ_4M;
    
    		dc = dma_request_slave_channel(dev, str);
    		if (!dc) {
    			dev_err(dev, "Falied to request %s.\n", str);
    			ret = -EPROBE_DEFER;
    			goto err;
    		}
    		cppi41_channel->dc = dc;
    	}
    	return 0;
    err:
    	cppi41_release_all_dma_chans(controller);
    	return ret;
    }
    
    void dma_controller_destroy(struct dma_controller *c)
    {
    	struct cppi41_dma_controller *controller = container_of(c,
    			struct cppi41_dma_controller, controller);
    
    	hrtimer_cancel(&controller->early_tx);
    	cppi41_dma_controller_stop(controller);
    	kfree(controller);
    }
    
    struct dma_controller *dma_controller_create(struct musb *musb,
    					void __iomem *base)
    {
    	struct cppi41_dma_controller *controller;
    	int ret = 0;
    
    	if (!musb->controller->of_node) {
    		dev_err(musb->controller, "Need DT for the DMA engine.\n");
    		return NULL;
    	}
    
    	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
    	if (!controller)
    		goto kzalloc_fail;
    
    	hrtimer_init(&controller->early_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    	controller->early_tx.function = cppi41_recheck_tx_req;
    	INIT_LIST_HEAD(&controller->early_tx_list);
    	controller->musb = musb;
    
    	controller->controller.channel_alloc = cppi41_dma_channel_allocate;
    	controller->controller.channel_release = cppi41_dma_channel_release;
    	controller->controller.channel_program = cppi41_dma_channel_program;
    	controller->controller.channel_abort = cppi41_dma_channel_abort;
    	controller->controller.is_compatible = cppi41_is_compatible;
    
    	ret = cppi41_dma_controller_start(controller);
    	if (ret)
    		goto plat_get_fail;
    	return &controller->controller;
    
    plat_get_fail:
    	kfree(controller);
    kzalloc_fail:
    	if (ret == -EPROBE_DEFER)
    		return ERR_PTR(ret);
    	return NULL;
    }
    

    With the patched Kernel the kernel panic is gone - but the board reboots all the time it the 3G usb stick is applied. Sometimes I got a stable connection for about 20sec - before the Linux crashes and reboots again. I attached a log here (grep "ping" to see a stable connection - and then the reboot...:

    Rebootlog 1405.1 (PD15.3.2) HUAWEI E3131 3G USB Stick.txt

    So may be the uncorrect patching cause the error?
    Or is it possible to avoid DMA usage as in the BBB kernel - which works?

    best regards

      Mario

  • I will look more in your report tomorrow. But it seems #7.6.b is already in your kernel, then you should not apply it again.

    Meanwhile if you want to disable MUSB DMA, you can use kernel menuconfig and set "[ *] Disable DMA (always use PIO)" in "USB Support".
  • Ok - I refetched the kernel sources and disabled MUSB DMA in menuconfig. After that the new kernel shows:

    root@phyboard-wega-am335x-2:~# zcat /proc/config.gz | grep CPPI41
    # CONFIG_USB_TI_CPPI41_DMA is not set
    CONFIG_TI_CPPI41=y

    but same behavior - with plugged 3G USB stick - the linux reboots most of the time after link becomes ready - means, the wwan0 interface gots an IP address via DHCP. Sometimes I have a stable connection for at least 20sec before it crashes...

    So what about CONFIG_TI_CPPI41? This was also not set on BBB?
    And should I try it again to apply all mentioned patches with disabled DMA?

    thanks for your support - read you tomorrow - aeh - today in a couple of hours ;-)

    Mario
  • New Info -

    if I use an additional USB-HUB (unpowered) between SBC and 3G-Modem, then the connection holds some longer - over minutes. But I also have these kernel crashes (without kernel panic log) and the reboots.

    If the USB-HUB is additionally powered, then it seems to work. Will make a test over 5hours now...

  • Mario Schwarz said:
    So what about CONFIG_TI_CPPI41? This was also not set on BBB?

    Since 3.12 kernel, CPPI41 is a separate driver under dmaengine framework. CONFIG_TI_CPPI41 controls to enable/disable this DMA driver, and CONFIG_USB_TI_CPPI41_DMA controls if MUSB driver uses the DMA or not. So You don't need to care about CONFIG_TI_CPPI41, as long as CONFIG_USB_TI_CPPI41_DMA is not set, MUSB does not use CPPI DMA.

    Mario Schwarz said:
    And should I try it again to apply all mentioned patches with disabled DMA?

    You don't have to. You already have DMA disabled, and reboot happens, I believe you have a completely different issue now, especially the issue does not happen with power hub.

    I am thinking of two options to debug it.

    1. test with latest mainline v4.7-rc3 kernel if possible, it has all MUSB bug fixes and others.

    2. does the reboot happens with BBB as while?

    3. I suspect the reboot is that the network layer detects come kind of error and sends reboot command to the system, so kernel does not have any log before reboot. You can first try to turn on debug in dsps_interrupt() in musb_dsps.c to see if any interrupt happened before reboot.

  • Hi Bin,

    I had a stable connection over the whole weekend with a powered USB-HUB and with disabled DMA :-)

    I think, that the reboots with disabled DMA are only caused by unsufficiant USB or Main-Power. I will check this with my Oszilloskop now.
    The SBC manufacturer told me, that they plan to include Kernel Version 4.4 with the next BSP release. So then I will retry the DMA usage.

    Thanks a lot for your hint to disable the DMA usage of the MUSB driver in kernel config. This was the solution for my problem ;-)
  • Mario,

    Mario Schwarz said:
    I think, that the reboots with disabled DMA are only caused by unsufficiant USB or Main-Power.

    This sounds like a board design problem. By USB2.0 Specs, the usb host port should provide min 500mA, which should be what the self-powered hub does.

    Mario Schwarz said:
    Thanks a lot for your hint to disable the DMA usage of the MUSB driver in kernel config.

    If now the issue indeed is power problem, I think you can enable MUSB DMA now, with all the mentioned patches applied, to get better throughput.