dde_linux: Support for Raspberry Pi

At the current stage, the USB HID and storage drivers are prinicpally
working but not stable. If interrupts are not processed fast enough,
devices will get sporadically disconnected.

The USB host-controller driver is not part of the normal Linux kernel.
For this reason, we need to download it separately. There exists a
'prepare_rpi' rule in the 'dde_linux/Makefile' to automate this process.
This commit is contained in:
Norman Feske 2013-09-12 21:34:02 +02:00
parent 0db203fb75
commit 435bdd5755
10 changed files with 503 additions and 23 deletions

View File

@ -5,7 +5,7 @@
#
# denote wich specs are also fullfilled by this spec
SPECS += arm_v6 pl011
SPECS += arm_v6 pl011 usb
# add repository relative include paths
REP_INC_DIR += include/platform/rpi

View File

@ -92,6 +92,10 @@ CONTENT += include/linux/platform_data/usb-omap.h
CONTENT += arch/arm/plat-samsung/include/plat/usb-phy.h
CONTENT += include/linux/platform_data/usb-ehci-s5p.h
# Raspberry Pi
DWC_OTG_GIT_URL := https://github.com/nfeske/dwc_otg.git
DWC_OTG_GIT_BRANCH := r1
#
#
# Utility to check if a tool is installed
@ -116,6 +120,12 @@ help:
prepare: clean $(CONTRIB_DIR)/.prepared
prepare_rpi: prepare
$(VERBOSE)cd $(CONTRIB_DIR)/drivers/usb/host; \
git clone $(DWC_OTG_GIT_URL) dwc_otg
$(VERBOSE)cd $(CONTRIB_DIR)/drivers/usb/host/dwc_otg; \
git reset --hard HEAD && git checkout $(DWC_OTG_GIT_BRANCH)
$(CONTRIB_DIR)/.prepared: Makefile
$(CONTRIB_DIR)/.prepared: $(DOWNLOAD_DIR)/$(LINUX_TBZ2)
$(ECHO) "extracting source code to '$(CONTRIB_DIR)'"

View File

@ -0,0 +1,44 @@
SRC_C += \
usb/host/dwc_otg/dwc_otg/dwc_otg_adp.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_attr.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_cfi.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_cil.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_cil_intr.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_driver.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_hcd.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_hcd_ddma.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_hcd_intr.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_hcd_linux.c \
usb/host/dwc_otg/dwc_otg/dwc_otg_hcd_queue.c
SRC_C += \
usb/host/dwc_otg/dwc_common_port/dwc_cc.c \
usb/host/dwc_otg/dwc_common_port/dwc_common_linux.c \
usb/host/dwc_otg/dwc_common_port/dwc_crypto.c \
usb/host/dwc_otg/dwc_common_port/dwc_dh.c \
usb/host/dwc_otg/dwc_common_port/dwc_mem.c \
usb/host/dwc_otg/dwc_common_port/dwc_modpow.c \
usb/host/dwc_otg/dwc_common_port/dwc_notifier.c
include $(REP_DIR)/lib/mk/usb.inc
include $(REP_DIR)/lib/mk/arm/usb.inc
CC_OPT += -DDWC_LINUX -DPLATFORM_INTERFACE
# needed for 'ehci-hcd.c', which we don't use on the rpi, but it is still
# part of the generic usb USB driver
CC_OPT += -DCONFIG_USB_EHCI_PCI=1
# for 'dwc_otg_hcd_linux.c' for enabling the FIQ, which we don't use anyway
CC_OPT += -DINTERRUPT_VC_USB=9
# for 'dwc_otg_driver.c' for preventing calls to set_irq_type
CC_OPT += -DIRQF_TRIGGER_LOW=1
INC_DIR += $(CONTRIB_DIR)/drivers/usb/host/dwc_otg/dwc_common_port \
$(CONTRIB_DIR)/drivers/usb/host/dwc_otg/dwc_otg
SRC_CC += platform.cc
vpath platform.cc $(LIB_DIR)/arm/platform_rpi
vpath %.c $(CONTRIB_DIR)/drivers/net/usb

View File

@ -0,0 +1,210 @@
/*
* \brief USB initialization for Raspberry Pi
* \author Norman Feske
* \date 2013-09-11
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode */
#include <io_mem_session/connection.h>
#include <util/mmio.h>
/* emulation */
#include <platform/platform.h>
#include <lx_emul.h>
/* dwc-otg */
#define new new_
#include <dwc_otg_dbg.h>
#undef new
using namespace Genode;
/************************************************
** Resource info passed to the dwc_otg driver **
************************************************/
enum {
DWC_BASE = 0x20980000,
DWC_SIZE = 0x20000,
DWC_IRQ = 17,
};
static resource _dwc_otg_resource[] =
{
{ DWC_BASE, DWC_BASE + DWC_SIZE - 1, "dwc_otg", IORESOURCE_MEM },
{ DWC_IRQ, DWC_IRQ, "dwc_otg-irq" /* name unused */, IORESOURCE_IRQ }
};
/***************************************
** Supplement to lx_emul environment **
***************************************/
#if VERBOSE_LX_EMUL
#define TRACE dde_kit_printf("\033[32m%s\033[0m called, not implemented\n", __PRETTY_FUNCTION__)
#else
#define TRACE
#endif
#define DUMMY(retval, name) \
extern "C" long name() { \
dde_kit_printf("\033[32m%s\033[0m called, not implemented, stop\n", #name); \
bt(); \
for (;;); \
return retval; \
}
#define CHECKED_DUMMY(retval, name) \
extern "C" long name() { \
dde_kit_printf("\033[32m%s\033[0m called, not implemented, ignored\n", #name); \
bt(); \
return retval; \
}
#define SILENT_DUMMY(retval, name) \
extern "C" long name() { return retval; }
/*********************
** linux/hardirq.h **
*********************/
int in_irq()
{
TRACE; PDBG("called by %p", __builtin_return_address(0));
return 0;
}
/*******************
** linux/delay.h **
*******************/
unsigned long loops_per_jiffy = 1;
/*********************
** linux/jiffies.h **
*********************/
unsigned int jiffies_to_msecs(const unsigned long j)
{
PDBG("not implemented. stop");
return 1;
}
/***********************************
** Dummies for unused PCD driver **
***********************************/
/*
* The PCD driver is used for driving the DWC-OTG device as gadget. The
* Raspberry Pi solely supports the use of the controller as host device.
* Hence, the PCD parts are not needed.
*/
DUMMY(-1, dwc_otg_pcd_disconnect_us);
DUMMY(-1, dwc_otg_pcd_remote_wakeup);
DUMMY(-1, dwc_otg_pcd_get_rmwkup_enable);
DUMMY(-1, dwc_otg_pcd_initiate_srp);
DUMMY(-1, pcd_remove);
SILENT_DUMMY( 0, pcd_init);
DUMMY(-1, printk_once);
/************************************************************************
** Prevent use of FIQ fix, need to resolve FIQ-related symbols anyway **
************************************************************************/
int fiq_fix_enable = false;
extern int fiq_split_enable;
void local_fiq_disable() { }
void local_fiq_enable() { }
int claim_fiq(struct fiq_handler *f) { return 0; }
void set_fiq_regs(struct pt_regs const *regs) { }
void set_fiq_handler(void *start, unsigned int length) { }
void enable_fiq() { }
void __FIQ_Branch(unsigned long *regs) { TRACE; }
/***********************
** linux/workqueue.h **
***********************/
struct workqueue_struct *create_singlethread_workqueue(char *)
{
workqueue_struct *wq = (workqueue_struct *)kzalloc(sizeof(workqueue_struct), 0);
return wq;
}
void destroy_workqueue(struct workqueue_struct *wq) { TRACE; }
bool queue_work(struct workqueue_struct *wq, struct work_struct *work) { TRACE; return 0; }
/***********************
** asm/dma_mapping.h **
***********************/
void *dma_to_virt(struct device *dev, dma_addr_t phys)
{
return phys_to_virt(phys);
}
/*******************
** linux/timer.h **
*******************/
struct tvec_base { };
struct tvec_base boot_tvec_bases;
/*******************
** Init function **
*******************/
extern "C" void module_dwc_otg_driver_init();
struct Services;
void platform_hcd_init(Services *)
{
/* disable split-enable fix, otherwise, fiq_fix will be implied */
fiq_split_enable = false;
bool const verbose = false;
if (verbose)
g_dbg_lvl = DBG_HCD | DBG_CIL | DBG_HCD_URB;
module_dwc_otg_driver_init();
/* setup host-controller platform device */
platform_device *pdev = (platform_device *)kzalloc(sizeof(platform_device), 0);
pdev->name = (char *)"dwc_otg";
pdev->id = 0;
pdev->num_resources = sizeof(_dwc_otg_resource)/sizeof(resource);
pdev->resource = _dwc_otg_resource;
/* needed for DMA buffer allocation. See 'hcd_buffer_alloc' in 'buffer.c' */
static u64 dma_mask = ~(u64)0;
pdev->dev.dma_mask = &dma_mask;
pdev->dev.coherent_dma_mask = ~0;
platform_device_register(pdev);
}

View File

@ -192,7 +192,6 @@ s64 ktime_us_delta(const ktime_t later, const ktime_t earlier) { TRACE; return 0
*******************/
unsigned long round_jiffies(unsigned long j) { TRACE; return 1; }
void add_timer(struct timer_list *timer) { TRACE; }
void set_timer_slack(struct timer_list *time, int slack_hz) { TRACE; }
/*********************
@ -422,12 +421,11 @@ void devres_free(void *res) { TRACE; }
** linux/platform_device.h **
*****************************/
void *platform_get_drvdata(const struct platform_device *pdev) { TRACE; return NULL; }
void platform_set_drvdata(struct platform_device *pdev, void *data) { TRACE; printk("ret: %p\n", __builtin_return_address(0)); }
int platform_device_del(struct platform_device *pdev) { TRACE; return 0; }
int platform_device_put(struct platform_device *pdev) { TRACE; return 0; }
void platform_device_unregister(struct platform_device *pdev) { TRACE; }
/********************
** linux/dcache.h **
********************/

View File

@ -29,6 +29,7 @@ struct platform_device
struct device dev;
u32 num_resources;
struct resource *resource;
void *data;
};
@ -238,4 +239,31 @@ struct nop_usb_xceiv_platform_data { int type; };
enum samsung_usb_phy_type { USB_PHY_TYPE_HOST = 1 };
/***********************
** asm/dma-mapping.h **
***********************/
/* needed by 'dwc_otg_hcd_linux.c' */
void *dma_to_virt(struct device *dev, dma_addr_t addr);
/********************
** asm/irqflags.h **
********************/
void local_fiq_disable();
void local_fiq_enable();
/***************
** asm/fiq.h **
***************/
struct pt_regs;
int claim_fiq(struct fiq_handler *f);
void set_fiq_regs(struct pt_regs const *regs);
void enable_fiq();
void set_fiq_handler(void *start, unsigned int length);
#endif /* _ARM__PLATFORM__LX_EMUL_H_ */

View File

@ -51,6 +51,19 @@ extern "C" {
#define KBUILD_MODNAME "mod-noname"
static inline void bt()
{
dde_kit_printf("BT: 0x%p\n", __builtin_return_address(0));
}
/*******************
** linux/sizes.h **
*******************/
#define SZ_256K 0x40000 /* needed by 'dwc_otg_attr.c' */
/*****************
** linux/bcd.h **
*****************/
@ -224,6 +237,15 @@ typedef unsigned long dma_addr_t;
*/
typedef unsigned short mode_t;
/*
* Needed by 'dwc_common_port/usb.h'
*/
typedef unsigned int u_int;
typedef unsigned char u_char;
typedef unsigned long u_long;
typedef uint8_t u_int8_t;
typedef uint16_t u_int16_t;
typedef uint32_t u_int32_t;
#include <linux/usb/storage.h>
@ -247,6 +269,8 @@ static inline void barrier() { mb(); }
#define likely
#define unlikely
#define notrace /* needed by 'dwc_otg_hcd_intr.c' */
#define __user /* needed by usb/core/devices.c */
#define __iomem /* needed by usb/hcd.h */
@ -278,6 +302,9 @@ enum irqreturn {
IRQ_HANDLED = (1 << 0),
};
/* needed by 'dwc_otg_hcd_linux.c' */
#define IRQ_RETVAL(x) ((x) != IRQ_NONE)
typedef enum irqreturn irqreturn_t;
/******************
@ -447,6 +474,8 @@ enum {
ENOLINK = 50,
EADDRNOTAVAIL = 51,
EPROBE_DEFER = 52,
ERESTART = 53,
ECONNABORTED = 54,
};
@ -603,6 +632,8 @@ long simple_strtoul(const char *cp, char **endp, unsigned int base);
/*
* Needed by 'usb.h'
*/
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
int vsprintf(char *buf, const char *fmt, va_list args);
int snprintf(char *buf, size_t size, const char *fmt, ...);
int sprintf(char *buf, const char *fmt, ...);
int sscanf(const char *, const char *, ...);
@ -910,6 +941,7 @@ void up_write(struct rw_semaphore *sem);
*/
extern volatile unsigned long jiffies;
unsigned long msecs_to_jiffies(const unsigned int m);
unsigned int jiffies_to_msecs(const unsigned long j);
long time_after(long a, long b);
long time_after_eq(long a, long b);
@ -943,11 +975,15 @@ ktime_t ktime_set(const long, const unsigned long);
** linux/timer.h **
*******************/
struct tvec_base;
extern struct tvec_base boot_tvec_bases; /* needed by 'dwc_common_linux.c' */
struct timer_list {
void (*function)(unsigned long);
unsigned long data;
void *timer;
unsigned long expires;
struct tvec_base *base; /* needed by 'dwc_common_linux.c' */
};
void init_timer(struct timer_list *);
@ -958,8 +994,8 @@ void setup_timer(struct timer_list *timer,void (*function)(unsigned long),
int timer_pending(const struct timer_list * timer);
unsigned long round_jiffies(unsigned long j);
void add_timer(struct timer_list *timer);
void set_timer_slack(struct timer_list *time, int slack_hz);
static inline void add_timer(struct timer_list *timer) { mod_timer(timer, timer->expires); }
static inline
int del_timer_sync(struct timer_list * timer) { return del_timer(timer); }
@ -991,6 +1027,8 @@ void msleep(unsigned int msecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long usecs);
extern unsigned long loops_per_jiffy; /* needed by 'dwc_otg_attr.c' */
/***********************
** linux/workquque.h **
@ -1047,6 +1085,14 @@ struct workqueue_struct { };
bool queue_delayed_work(struct workqueue_struct *,
struct delayed_work *, unsigned long);
/* needed for 'dwc_common_linux.c' */
struct workqueue_struct *create_singlethread_workqueue(char *n);
void destroy_workqueue(struct workqueue_struct *wq);
bool queue_work(struct workqueue_struct *wq, struct work_struct *work);
/******************
** linux/wait.h **
******************/
@ -1622,6 +1668,10 @@ struct platform_device;
void *platform_get_drvdata(const struct platform_device *pdev);
void platform_set_drvdata(struct platform_device *pdev, void *data);
/* needed by 'dwc_otg_driver.c' */
struct platform_driver;
void platform_driver_unregister(struct platform_driver *);
/*********************
** linux/dmapool.h **
@ -1941,6 +1991,7 @@ int seq_printf(struct seq_file *, const char *, ...);
enum {
__GFP_DMA = 0x01u,
GFP_DMA = __GFP_DMA,
GFP_DMA32 = 0x04u, /* needed by 'dwc_common_linux.c' */
__GFP_WAIT = 0x10u,
GFP_ATOMIC = 0x20u,
GFP_KERNEL = 0x0u,
@ -2027,6 +2078,8 @@ void *ioremap_wc(resource_size_t phys_addr, unsigned long size);
#define ioremap_nocache ioremap
void *phys_to_virt(unsigned long address);
#define writel(value, addr) (*(volatile uint32_t *)(addr) = (value))
#define readl(addr) (*(volatile uint32_t *)(addr))
#define readb(addr) (*(volatile uint8_t *)(addr))
@ -2096,6 +2149,35 @@ int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
void free_irq(unsigned int, void *);
/*****************
** linux/irq.h **
*****************/
enum { IRQ_TYPE_LEVEL_LOW = 0x00000008 }; /* needed by 'dwc_otg_driver.c' */
/*********************
** linux/hardirq.h **
*********************/
/* needed by 'dwc_common_linux.c' */
int in_irq();
/***************
** asm/fiq.h **
***************/
/*
* Needed by 'dwc_otg_hcd_linux.c'
*/
struct fiq_handler {
char const *name;
};
void __FIQ_Branch(unsigned long *regs);
/*********************
** linux/hardirq.h **
*********************/
@ -3325,8 +3407,10 @@ struct tasklet_struct
unsigned long data;
};
static inline void tasklet_schedule(struct tasklet_struct *t) { t->func(t->data); }
void tasklet_schedule(struct tasklet_struct *t);
void tasklet_hi_schedule(struct tasklet_struct *t);
void tasklet_kill(struct tasklet_struct *);
void tasklet_init(struct tasklet_struct *t, void (*)(unsigned long), unsigned long);
/*************************
@ -3524,6 +3608,21 @@ void genode_evdev_event(struct input_handle *handle, unsigned int type,
void start_input_service(void *ep);
/******************
** asm/ptrace.h **
******************/
/*
* Needed by 'dwc_otg_hcd_linux.c'
*/
struct pt_regs { unsigned long dummy; };
#define ARM_r8 dummy
#define ARM_r9 dummy
#define ARM_sp dummy
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -14,5 +14,9 @@
#ifndef _X86_32__PLATFORM__LX_EMUL_
#define _X86_32__PLATFORM__LX_EMUL_
struct platform_device
{
void *data;
};
#endif /* _X86_32__PLATFORM__LX_EMUL_ */

View File

@ -139,6 +139,23 @@ class Genode::Slab_backend_alloc : public Genode::Allocator,
return phys;
}
/**
* Translate given physical address to virtual address
*
* \return virtual address, or 0 if no translation exists
*/
addr_t virt_addr(addr_t phys)
{
for (unsigned i = 0; i < ELEMENTS; i++) {
if (_ds_cap[i].valid()
&& phys >= _ds_phys[i] && phys < _ds_phys[i] + BLOCK_SIZE)
return _base + i*BLOCK_SIZE + phys - _ds_phys[i];
}
PWRN("virt_addr(0x%lx) - no translation", phys);
return 0;
}
addr_t start() const { return _base; }
addr_t end() const { return _base + VM_SIZE - 1; }
};
@ -293,6 +310,11 @@ class Malloc
return _back_allocator->phys_addr((addr_t)a);
}
Genode::addr_t virt_addr(Genode::addr_t phys)
{
return _back_allocator->virt_addr(phys);
}
/**
* Belongs given address to this allocator
*/
@ -479,6 +501,15 @@ inline void *memset(void *s, int c, size_t n)
}
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
Genode::String_console sc(buf, size);
sc.vprintf(fmt, args);
return sc.len();
}
int snprintf(char *buf, size_t size, const char *fmt, ...)
{
va_list args;
@ -668,6 +699,12 @@ void *devm_ioremap_nocache(struct device *dev, resource_size_t offset,
}
void *phys_to_virt(unsigned long address)
{
return (void *)Malloc::dma()->virt_addr(address);
}
/********************
** linux/device.h **
********************/
@ -808,6 +845,17 @@ void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
}
void *platform_get_drvdata(const struct platform_device *pdev)
{
return pdev->data;
}
void platform_set_drvdata(struct platform_device *pdev, void *data)
{
pdev->data = data;
}
/*******************************
** linux/byteorder/generic.h **

View File

@ -59,7 +59,8 @@ class Work : public Genode::List<Work>::Element
private:
void *_work;
bool _delayed;
enum Type { NORMAL, DELAYED, TASKLET } _type;
static Genode::List<Work> *_list()
{
@ -69,12 +70,14 @@ class Work : public Genode::List<Work>::Element
public:
Work(void *work, bool delayed) : _work(work), _delayed(delayed) { }
Work(delayed_work *work) : _work(work), _type(DELAYED) { }
Work(work_struct *work) : _work(work), _type(NORMAL) { }
Work(tasklet_struct *work) : _work(work), _type(TASKLET) { }
static void schedule(void *work, bool delayed)
template <typename WORK>
static void schedule(WORK *work)
{
Work *w = new (Genode::env()->heap()) Work(work, delayed);
_list()->insert(w);
_list()->insert(new (Genode::env()->heap()) Work(work));
}
static void exec()
@ -83,18 +86,32 @@ class Work : public Genode::List<Work>::Element
Work *w = _list()->first();
_list()->remove(w);
if (w->_delayed) {
delayed_work *work = static_cast<delayed_work *>(w->_work);
work->work.func(&(work)->work);
}
else {
work_struct *work = static_cast<work_struct *>(w->_work);
work->func(work);
switch (w->_type) {
case NORMAL:
{
work_struct *work = static_cast<work_struct *>(w->_work);
work->func(work);
}
break;
case DELAYED:
{
delayed_work *work = static_cast<delayed_work *>(w->_work);
work->work.func(&(work)->work);
}
break;
case TASKLET:
{
tasklet_struct *tasklet = static_cast<tasklet_struct *>(w->_work);
tasklet->func(tasklet->data);
}
break;
}
destroy(Genode::env()->heap(), w);
}
}
};
@ -226,15 +243,14 @@ int wake_up_process(struct task_struct *tsk)
int schedule_delayed_work(struct delayed_work *work, unsigned long delay)
{
Work::schedule((void *)work, true);
//work->work.func(&(work)->work);
Work::schedule(work);
return 0;
}
int schedule_work(struct work_struct *work)
{
Work::schedule((void *)work, false);
Work::schedule(work);
return 1;
}
@ -242,6 +258,29 @@ int schedule_work(struct work_struct *work)
bool queue_delayed_work(struct workqueue_struct *wq,
struct delayed_work *dwork, unsigned long delay)
{
Work::schedule((void *)dwork, true);
Work::schedule(dwork);
return true;
}
/***********************
** linux/interrupt.h **
***********************/
void tasklet_init(struct tasklet_struct *t, void (*f)(unsigned long), unsigned long d)
{
t->func = f;
t->data = d;
}
void tasklet_schedule(struct tasklet_struct *tasklet)
{
Work::schedule(tasklet);
}
void tasklet_hi_schedule(struct tasklet_struct *tasklet)
{
tasklet_schedule(tasklet);
}