Commit f583a29e authored by David Brownell's avatar David Brownell Committed by Tony Lindgren

musb_hdrc: TUSB EVM support

Add basic support for the TUSB6010 EVM board, as connected to H4.
This consists of

  (a)   mostly-generic setup utility, which should be usable on other
        242x boards with a tusb6010 chip;

  (b)   EVM-specific setup for an H4 board stack.

Tested only using PIO; though the DMA timings look plausible.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
parent 8db67e15
......@@ -18,11 +18,28 @@ config MACH_OMAP_GENERIC
bool "Generic OMAP board"
depends on ARCH_OMAP2 && ARCH_OMAP24XX
config MACH_OMAP2_TUSB6010
bool
depends on ARCH_OMAP2 && ARCH_OMAP2420
config MACH_OMAP_H4
bool "OMAP 2420 H4 board"
depends on ARCH_OMAP2 && ARCH_OMAP24XX
select GPIOEXPANDER_OMAP
config MACH_OMAP_H4_TUSB
bool "TUSB 6010 EVM board"
depends on MACH_OMAP_H4
select MACH_OMAP2_TUSB6010
help
Set this if you've got a TUSB6010 high speed USB board.
You may need to consult the schematics for your revisions
of the Menelaus and TUSB boards, and make changes to be
sure this is set up properly for your board stack.
Be sure to select OTG mode operation, not host-only or
peripheral-only.
config MACH_OMAP_APOLLON
bool "OMAP 2420 Apollon board"
depends on ARCH_OMAP2 && ARCH_OMAP24XX
......@@ -18,3 +18,7 @@ obj-$(CONFIG_OMAP_DSP) += mailbox.o
obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o
obj-$(CONFIG_MACH_OMAP_H4) += board-h4.o
obj-$(CONFIG_MACH_OMAP_APOLLON) += board-apollon.o
# TUSB 6010 chips
obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o
......@@ -412,6 +412,72 @@ static struct omap_board_config_kernel h4_config[] = {
{ OMAP_TAG_LCD, &h4_lcd_config },
};
#ifdef CONFIG_MACH_OMAP_H4_TUSB
#include <linux/usb/musb.h>
static struct musb_hdrc_platform_data tusb_data = {
.mode = MUSB_OTG,
.min_power = 25, /* x2 = 50 mA drawn from VBUS as peripheral */
/* 1.8V supplied by Menelaus, other voltages supplied by VBAT;
* so no switching.
*/
};
static void __init tusb_evm_setup(void)
{
static char announce[] __initdata =
KERN_INFO "TUSB 6010 EVM\n";
int irq;
unsigned dmachan = 0;
/* There are at least 32 different combinations of boards that
* are loosely called "H4", with a 2420 ... different OMAP chip
* revisions (with pin mux changes for DMAREQ, GPMC errata, etc),
* modifications of the CPU board, mainboard, EVM, TUSB etc.
* Plus omap2422, omap2423, etc.
*
* So you might need to tweak this setup to make the TUSB EVM
* behave on your particular setup ...
*/
/* Already set up: GPMC AD[0..15], CLK, nOE, nWE, nADV_ALE */
omap_cfg_reg(E2_GPMC_NCS2);
omap_cfg_reg(L2_GPMC_NCS7);
omap_cfg_reg(M1_GPMC_WAIT2);
switch ((system_rev >> 8) & 0x0f) {
case 0: /* ES 1.0 */
case 1: /* ES 2.0 */
/* Assume early board revision without optional ES2.0
* rework to swap J15 & AA10 so DMAREQ0 works
*/
omap_cfg_reg(AA10_242X_GPIO13);
irq = 13;
// omap_cfg_reg(J15_24XX_DMAREQ0);
break;
default:
/* Later Menelaus boards can support all 6 DMA request
* lines, at the price of boot flash A23-A26.
*/
omap_cfg_reg(J15_24XX_GPIO99);
irq = 99;
omap_cfg_reg(AA10_242X_DMAREQ0);
omap_cfg_reg(AA6_242X_DMAREQ1);
dmachan = (1 << 1) | (1 << 0);
break;
}
if (tusb6010_setup_interface(&tusb_data,
TUSB6010_REFCLK_24, /* waitpin */ 2,
/* async cs */ 2, /* sync cs */ 7,
irq, dmachan) == 0)
printk(announce);
}
#endif
static void __init omap_h4_init(void)
{
/*
......@@ -436,6 +502,11 @@ static void __init omap_h4_init(void)
omap_board_config = h4_config;
omap_board_config_size = ARRAY_SIZE(h4_config);
omap_serial_init();
#ifdef CONFIG_MACH_OMAP_H4_TUSB
tusb_evm_setup();
#endif
}
static void __init omap_h4_map_io(void)
......
/*
* linux/arch/arm/mach-omap/omap2/usb-tusb6010.c
*
* Copyright (C) 2006 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/usb/musb.h>
#include <asm/arch/gpmc.h>
#include <asm/arch/gpio.h>
static u8 async_cs, sync_cs;
static unsigned refclk_psec;
/* t2_ps, when quantized to fclk units, must happen no earlier than
* the clock after after t1_NS.
*
* Return a possibly updated value of t2_ps, converted to nsec.
*/
static unsigned
next_clk(unsigned t1_NS, unsigned t2_ps, unsigned fclk_ps)
{
unsigned t1_ps = t1_NS * 1000;
unsigned t1_f, t2_f;
if ((t1_ps + fclk_ps) < t2_ps)
return t2_ps / 1000;
t1_f = (t1_ps + fclk_ps - 1) / fclk_ps;
t2_f = (t2_ps + fclk_ps - 1) / fclk_ps;
if (t1_f >= t2_f)
t2_f = t1_f + 1;
return (t2_f * fclk_ps) / 1000;
}
/* NOTE: timings are from tusb 6010 datasheet Rev 1.8, 12-Sept 2006 */
static int tusb_set_async_mode(unsigned sysclk_ps, unsigned fclk_ps)
{
struct gpmc_timings t;
unsigned t_acsnh_advnh = sysclk_ps + 3000;
unsigned tmp;
memset(&t, 0, sizeof(t));
/* CS_ON = t_acsnh_acsnl */
t.cs_on = 8;
/* ADV_ON = t_acsnh_advnh - t_advn */
t.adv_on = next_clk(t.cs_on, t_acsnh_advnh - 7000, fclk_ps);
/*
* READ ... from omap2420 TRM fig 12-13
*/
/* ADV_RD_OFF = t_acsnh_advnh */
t.adv_rd_off = next_clk(t.adv_on, t_acsnh_advnh, fclk_ps);
/* OE_ON = t_acsnh_advnh + t_advn_oen (then wait for nRDY) */
t.oe_on = next_clk(t.adv_on, t_acsnh_advnh + 1000, fclk_ps);
/* ACCESS = counters continue only after nRDY */
tmp = t.oe_on * 1000 + 300;
t.access = next_clk(t.oe_on, tmp, fclk_ps);
/* OE_OFF = after data gets sampled */
tmp = t.access * 1000;
t.oe_off = next_clk(t.access, tmp, fclk_ps);
t.cs_rd_off = t.oe_off;
tmp = t.cs_rd_off * 1000 + 7000 /* t_acsn_rdy_z */;
t.rd_cycle = next_clk(t.cs_rd_off, tmp, fclk_ps);
/*
* WRITE ... from omap2420 TRM fig 12-15
*/
/* ADV_WR_OFF = t_acsnh_advnh */
t.adv_wr_off = t.adv_rd_off;
/* WE_ON = t_acsnh_advnh + t_advn_wen (then wait for nRDY) */
t.we_on = next_clk(t.adv_on, t_acsnh_advnh + 1000, fclk_ps);
/* WE_OFF = after data gets sampled */
tmp = t.we_on * 1000 + 300;
t.we_off = next_clk(t.we_on, tmp, fclk_ps);
t.cs_wr_off = t.we_off;
tmp = t.cs_wr_off * 1000 + 7000 /* t_acsn_rdy_z */;
t.wr_cycle = next_clk(t.cs_wr_off, tmp, fclk_ps);
return gpmc_cs_set_timings(async_cs, &t);
}
static int tusb_set_sync_mode(unsigned sysclk_ps, unsigned fclk_ps)
{
struct gpmc_timings t;
unsigned t_scsnh_advnh = sysclk_ps + 3000;
unsigned tmp;
memset(&t, 0, sizeof(t));
t.cs_on = 8;
/* ADV_ON = t_acsnh_advnh - t_advn */
t.adv_on = next_clk(t.cs_on, t_scsnh_advnh - 7000, fclk_ps);
/* GPMC_CLK rate = fclk rate / div */
t.sync_clk = 12 /* 11.1 nsec */;
tmp = (t.sync_clk * 1000 + fclk_ps - 1) / fclk_ps;
if (tmp > 4)
return -ERANGE;
if (tmp <= 0)
tmp = 1;
t.page_burst_access = (fclk_ps * tmp) / 1000;
/*
* READ ... based on omap2420 TRM fig 12-19, 12-20
*/
/* ADV_RD_OFF = t_scsnh_advnh */
t.adv_rd_off = next_clk(t.adv_on, t_scsnh_advnh, fclk_ps);
/* OE_ON = t_scsnh_advnh + t_advn_oen (then wait for nRDY) */
t.oe_on = next_clk(t.adv_on, t_scsnh_advnh + 1000, fclk_ps);
/* ACCESS = counters continue only after nRDY */
tmp = t.oe_on * 1000 + 300;
t.access = next_clk(t.oe_on, tmp, fclk_ps);
/* OE_OFF = after data gets sampled */
tmp = t.access * 1000;
t.oe_off = next_clk(t.access, tmp, fclk_ps);
t.cs_rd_off = t.oe_off;
tmp = t.cs_rd_off * 1000 + 7000 /* t_scsn_rdy_z */;
t.rd_cycle = next_clk(t.cs_rd_off, tmp, fclk_ps);
/*
* WRITE ... based on omap2420 TRM fig 12-21
*/
/* ADV_WR_OFF = t_scsnh_advnh */
t.adv_wr_off = t.adv_rd_off;
/* WE_ON = t_scsnh_advnh + t_advn_wen (then wait for nRDY) */
t.we_on = next_clk(t.adv_on, t_scsnh_advnh + 1000, fclk_ps);
/* WE_OFF = after data gets sampled */
tmp = t.we_on * 1000 + 300;
t.we_off = next_clk(t.we_on, tmp, fclk_ps);
t.cs_wr_off = t.we_off;
tmp = t.cs_wr_off * 1000 + 7000 /* t_scsn_rdy_z */;
t.wr_cycle = next_clk(t.cs_wr_off, tmp, fclk_ps);
return gpmc_cs_set_timings(sync_cs, &t);
}
extern unsigned long gpmc_get_fclk_period(void);
/* tusb driver calls this when it changes the chip's clocking */
int tusb6010_platform_retime(unsigned is_refclk)
{
static const char error[] =
KERN_ERR "tusb6010 %s retime error %d\n";
unsigned fclk_ps = gpmc_get_fclk_period();
unsigned sysclk_ps;
int status;
if (!refclk_psec)
return -ENODEV;
sysclk_ps = is_refclk ? refclk_psec : TUSB6010_OSCCLK_60;
status = tusb_set_async_mode(sysclk_ps, fclk_ps);
if (status < 0) {
printk(error, "async", status);
goto done;
}
status = tusb_set_sync_mode(sysclk_ps, fclk_ps);
if (status < 0)
printk(error, "sync", status);
done:
return status;
}
EXPORT_SYMBOL_GPL(tusb6010_platform_retime);
static struct resource tusb_resources[] = {
/* Order is significant! The start/end fields
* are updated during setup..
*/
{ /* Asynchronous access */
.flags = IORESOURCE_MEM,
},
{ /* Synchronous access */
.flags = IORESOURCE_MEM,
},
{ /* IRQ */
.flags = IORESOURCE_IRQ,
},
};
static u64 tusb_dmamask = ~(u32)0;
static struct platform_device tusb_device = {
.name = "musb_hdrc",
.id = -1,
.dev = {
.dma_mask = &tusb_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(tusb_resources),
.resource = tusb_resources,
};
/* this may be called only from board-*.c setup code */
int __init
tusb6010_setup_interface(struct musb_hdrc_platform_data *data,
unsigned ps_refclk, unsigned waitpin,
unsigned async, unsigned sync,
unsigned irq, unsigned dmachan)
{
int status;
static char error[] __initdata =
KERN_ERR "tusb6010 init error %d, %d\n";
/* ASYNC region, primarily for PIO */
status = gpmc_cs_request(async, SZ_16M, (unsigned long *)
&tusb_resources[0].start);
if (status < 0) {
printk(error, 1, status);
return status;
}
tusb_resources[0].end = tusb_resources[0].start + 0x9ff;
async_cs = async;
gpmc_cs_write_reg(async, GPMC_CS_CONFIG1,
GPMC_CONFIG1_PAGE_LEN(2)
| GPMC_CONFIG1_WAIT_READ_MON
| GPMC_CONFIG1_WAIT_WRITE_MON
| GPMC_CONFIG1_WAIT_PIN_SEL(waitpin)
| GPMC_CONFIG1_READTYPE_ASYNC
| GPMC_CONFIG1_WRITETYPE_ASYNC
| GPMC_CONFIG1_DEVICESIZE_16
| GPMC_CONFIG1_DEVICETYPE_NOR
| GPMC_CONFIG1_MUXADDDATA);
/* SYNC region, primarily for DMA */
status = gpmc_cs_request(sync, SZ_16M, (unsigned long *)
&tusb_resources[1].start);
if (status < 0) {
printk(error, 2, status);
return status;
}
tusb_resources[1].end = tusb_resources[1].start + 0x9ff;
sync_cs = sync;
gpmc_cs_write_reg(sync, GPMC_CS_CONFIG1,
GPMC_CONFIG1_READMULTIPLE_SUPP
| GPMC_CONFIG1_READTYPE_SYNC
| GPMC_CONFIG1_WRITEMULTIPLE_SUPP
| GPMC_CONFIG1_WRITETYPE_SYNC
| GPMC_CONFIG1_CLKACTIVATIONTIME(1)
| GPMC_CONFIG1_PAGE_LEN(2)
| GPMC_CONFIG1_WAIT_READ_MON
| GPMC_CONFIG1_WAIT_WRITE_MON
| GPMC_CONFIG1_WAIT_PIN_SEL(waitpin)
| GPMC_CONFIG1_DEVICESIZE_16
| GPMC_CONFIG1_DEVICETYPE_NOR
| GPMC_CONFIG1_MUXADDDATA
/* fclk divider gets set later */
);
/* IRQ */
status = omap_request_gpio(irq);
if (status < 0) {
printk(error, 3, status);
return status;
}
omap_set_gpio_direction(irq, 0);
tusb_resources[2].start = irq + IH_GPIO_BASE;
/* set up memory timings ... can speed them up later */
if (!ps_refclk) {
printk(error, 4, status);
return -ENODEV;
}
refclk_psec = ps_refclk;
status = tusb6010_platform_retime(1);
if (status < 0) {
printk(error, 5, status);
return status;
}
/* finish device setup ... */
if (!data) {
printk(error, 6, status);
return -ENODEV;
}
data->multipoint = 1;
tusb_device.dev.platform_data = data;
/* REVISIT let the driver know what DMA channels work */
if (!dmachan)
tusb_device.dev.dma_mask = NULL;
/* so far so good ... register the device */
status = platform_device_register(&tusb_device);
if (status < 0) {
printk(error, 7, status);
return status;
}
return 0;
}
......@@ -42,3 +42,21 @@ struct musb_hdrc_platform_data {
int (*set_power)(int state);
};
/* TUSB 6010 support */
#define TUSB6010_OSCCLK_60 16667 /* psec/clk @ 60.0 MHz */
#define TUSB6010_REFCLK_24 41667 /* psec/clk @ 24.0 MHz XI */
#define TUSB6010_REFCLK_19 52633 /* psec/clk @ 19.2 MHz CLKIN */
#ifdef CONFIG_ARCH_OMAP2
extern int __init tusb6010_setup_interface(
struct musb_hdrc_platform_data *data,
unsigned ps_refclk, unsigned waitpin,
unsigned async_cs, unsigned sync_cs,
unsigned irq, unsigned dmachan);
extern int tusb6010_platform_retime(unsigned is_refclk);
#endif /* OMAP2 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment